Unverified Commit 2d58a8b9 authored by Diego Mello's avatar Diego Mello Committed by GitHub
Browse files

[RELEASE] Merge beta into master (#1082)

* Bump version to 1.16.0 (#1014)

* [IMPROVEMENT] Share credentials with Rocket.Chat.iOS (#982)

*  Create user table

*  Introduce user table

* 🔥 Remove unused table

*  Add userdefaults to storage data

* 💚 Fix android build

*  Get credentials from iOS native client

* 🔥 Remove unused code

*  Revert sign xcode

* 🐛 Fix first login-logout

* 🎨 Use constants to UserDefaults Keys

* 🐛 Fix clear server-user-info on logout

* 🐛 Fix filter null value

* 🚑 Remove user object in logout

*  Fix get servers from native-client

* 🚑 Fix error on change server

* [FIX] Don't run UserDefaults credentials on Android (#1015)

* 🐛 Fix native credentials (android)

* Fix migration loop

* [IMPROVEMENT] Hide frequently used emoji tab when empty (#792)

* [IMPROVEMENT] Bigger emoji in emoji only messages (#793)

* issue #725: bigger emoji in emoji only message

* issue-725/add storybook for Message/Emoji

* issue-725: update storybook/Message jest snapshot

* comment storybook import

* allow spaces and line breaks in emoji only message

* merge develop

* revert unnecessary spacing

* [FIX] Empty message if contains only a link (#787)

* Fix empty message if contains only a link

* 🐛 Fix empty space

* [IMPROVEMENT] Refactor empty space regex on quote (#1017)

* 🎨 Improve regex to empty space on quote

* 🎨 Improve on regex to empty space on quote

* [NEW] Custom fields on signup (#1013)

* added custom feilds on registration

* added flag as leftIcon and removed lable

* added try and catch

* typo

* [CHORE] Renew provisioning profiles (#1020)

* [NEW] Auto-translate (#1012)

* Update realm

* View original and translate working

* Read AutoTranslate_Enabled setting

* RocketChat.canAutoTranslate()

* AutoTranslateView

* Save language

* Auto-translate switch

* Translate message

* [IMPROVEMENT] Use haptics rather than vibration (#1016)

* Install expo-haptics

* Use expo-haptics rather than RN's Vibration module

* [IMPROVEMENT] Use Rest API for file upload (#1005)

* removed rn-fetch-blob and use native XMLHttpRequest instead

* removed unnessary changes

* fix android bug

* fix android bug

* added tmid support

* fix bug

* fixed isssue with cacel model

* fix problems with audio

* done requested changes

* fix bug with android

* [CHORE] [CI] [TESTS] update detox to make ci pass (#1026)

* feat: update detox to 12.11.3 to make CI pass

* ci: comment all jobs but leave e2e-test job

* commit to rerun IC e2e-test job

* ci: uncomment all CI jobs

* [NEW] Room swipe actions: mark as read/unread, hide, fav (#976)

* added unread and fav feature

* changed the layout

* fix jest

* done requested changes

* added requested changes

* [FIX] Android build (#1027)

* [FIX] Android build

* CircleCI error

* [FIX] iOS share credentials build (#1028)

* [FIX] iOS share credentials build

* Use `hasMigration` as a string

* [CI] Restore cache on CI (#1029)

* feat: add fastlane save\restore cache config; comment not needed jobs;

* install fastlane using 'bundle install'

* install fastlane using 'sudo bundle install'

* uncomment ios build commands

* run set up google services in ios folder

* add working_directory: ios to ios-build steps

* remove 'cd ios' from Fastlane build step

* add save\restore cache for npm modules

* group save_cache steps

* cache fastlane in ios-testflight job

* uncomment previously commented jobs\steps

* fix: add missing colon

* use key for caching: node-modules-{{ checksum ".circleci/config.yml" }}-{{ checksum "yarn.lock" }}

* add names for save\restore steps

* ci: add `default` step with `working_directory: ~/repo` to ios-build job

* return back caching npm: `node-v1-{{ checksum "package.json" }}-{{ arch }}`

* fix: add missing curly braces

* save\restore cache in e2e-test job; remove {{arch}} from cache names

* add names to restore_cache steps in android-build job

* add names to save_cache steps in android-build job

* add names to all save\restore steps; change checksum package.json to yarn.lock

* change `npm` to `NPM` in steps naming

* remove {{ checksum circle ci }} from android-build job and fix naming of steps

* [FIX] Rooms swipes (#1034)

* Regression: on press style feedback

* Action button styles

* Fix animations

* Styles changed

* Update subscription without having to wait for socket

* Calculate width on RoomsListView instead

* [FIX] Decrease bigger emoji size to 30 (#1031)

* [FIX] Append server URL on avatar if necessary (#1038)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Bump version to 1.16.1 (#1045)

* [FIX] Set UserDefaults AppGroup on notification tap (#1047)

* [FIX] Auto-translate messages as they arrive

* Fix favorite button

* [FIX] Swipe animations (#1044)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Fix favorite button

* [FIX] Auto-translate messages as they arrive (#1049)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* [FIX] Auto-translate messages as they arrive

* [i18n] Add missing de translations (#1040)

* [CHORE] Switch to react-native-localize (#1043)

* Bump version to 1.17.0 (#1057)

* Load views as needed (#1056)

* [IMPROVEMENT] Change "resend" icon position (#1048)

* [NEW] Video support (#801)

* [NEW] File upload (#882)

* [NEW] Share extension (#942)

* [FIX] Share extension CI build (#1060)

* Change bundleID

* Provisioning

* get provisioning profile

* [IMPROVEMENT] Reusable toast (#1065)

* [FIX] Moment locales (#1066)

* [FIX] Share Extension issues (#1064)

* [FIX] Empty white list enables all media types upload (#1077)

* Merge branch 'master' into develop (#1079)

* [FIX] Empty white list enables all media types upload (#1080)

* Create utils to media (canUpload)

* Fix variable name

* [CHORE] Update README (#1081)
parent 8ea6f164
# Rocket.Chat React Native Mobile
[![Greenkeeper badge](https://badges.greenkeeper.io/RocketChat/Rocket.Chat.ReactNative.svg)](https://greenkeeper.io/)
[![Build Status](https://img.shields.io/travis/RocketChat/Rocket.Chat.ReactNative/master.svg)](https://travis-ci.org/RocketChat/Rocket.Chat.ReactNative)
[![Project Dependencies](https://david-dm.org/RocketChat/Rocket.Chat.ReactNative.svg)](https://david-dm.org/RocketChat/Rocket.Chat.ReactNative)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/bb15e2392a71473ea59d3f634f35c54e)](https://www.codacy.com/app/RocketChat/Rocket.Chat.ReactNative?utm_source=github.com&utm_medium=referral&utm_content=RocketChat/Rocket.Chat.ReactNative&utm_campaign=badger)
[![codecov](https://codecov.io/gh/RocketChat/Rocket.Chat.ReactNative/branch/master/graph/badge.svg)](https://codecov.io/gh/RocketChat/Rocket.Chat.ReactNative)
[![CodeFactor](https://www.codefactor.io/repository/github/rocketchat/rocket.chat.reactnative/badge)](https://www.codefactor.io/repository/github/rocketchat/rocket.chat.reactnative)
**Supported Server Versions:** 0.66.0+
**Supported Server Versions:** 0.70.0+
## Download
<a href="https://play.google.com/store/apps/details?id=chat.rocket.reactnative">
......@@ -59,55 +57,53 @@ If you don't need multiple servers, there is a branch `single-server` just for t
Readme will guide you on how to config.
## Current priorities
1) [NEW] Jitsi integration ([#711][i711])
2) [NEW] Federation ([#706][i706])
3) [NEW] Record video ([#712][i712])
4) [NEW] Slash Commands ([#405][i405])
5) [NEW] Share extension ([#391][i391])
[i711]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/711
[i706]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/706
[i707]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/707
[i712]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/712
[i708]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/708
[i391]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/391
[i405]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/405
1) Jitsi integration
2) Notification Preferences
3) Two-way authentication
4) Authentication via SAML
5) Authentication via Custom OAuth
6) Authentication via CAS
7) Bugsnag
8) Optional Analytics
9) Typescript
10) Prettier
## Features
| Feature | Status |
|--------------------------------------------------------------- |-------- |
| Jitsi Integration | ❌ |
| Federation (Directory) | ❌ |
| Threads | ✅ |
| Federation (Directory) | ✅ |
| Discussions | ❌ |
| Threads | ✅ |
| Record Audio | ✅ |
| Record Video | |
| Commands | |
| Record Video | |
| Commands | |
| Draft message per room | ✅ |
| Share Extension | |
| Share Extension | |
| Notifications Preferences | ✅ |
| Edited status | ✅ |
| Upload video | |
| Upload video | |
| Grouped messages | ✅ |
| Mark room as read | |
| Mark room as unread | |
| Mark room as read | |
| Mark room as unread | |
| Tablet Support | ❌ |
| Read receipt | |
| Read receipt | |
| Broadbast Channel | ✅ |
| Authentication via SAML | ❌ |
| Authentication via CAS | ❌ |
| Custom Fields on Signup | |
| Report message | |
| Custom Fields on Signup | |
| Report message | |
| Theming | ❌ |
| Settings -> Review the App | ❌ |
| Settings -> Default Browser | ❌ |
| Admin panel | ✅ |
| Reply message from notification | ❌ |
| Unread counter banner on message list | ✅ |
| E2E | ❌ |
| E2E Encryption | ❌ |
| Join a Protected Room | ❌ |
| Optional Analytics | ❌ |
| Settings -> About us | ❌ |
| Settings -> Contact us | |
| Settings -> Contact us | |
| Settings -> Update App Icon | ❌ |
| Settings -> Share | ❌ |
| Accessibility (Medium) | ❌ |
......
export const initialConstants = null;
export const findBestAvailableLanguage = () => null;
export default {
realmPath: ''
};
......@@ -110,7 +110,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "1.16.1"
versionName "1.17.0"
vectorDrawables.useSupportLibrary = true
}
......@@ -174,6 +174,9 @@ android {
dependencies {
addUnimodulesDependencies()
implementation "org.webkit:android-jsc:r241213"
implementation project(':rn-extensions-share')
implementation project(':rn-fetch-blob')
implementation project(':react-native-document-picker')
implementation project(':react-native-firebase')
implementation project(':react-native-webview')
implementation project(':react-native-orientation-locker')
......@@ -185,7 +188,7 @@ dependencies {
})
implementation project(':react-native-gesture-handler')
implementation project(':react-native-image-crop-picker')
implementation project(':react-native-i18n')
implementation project(':react-native-localize')
implementation project(':react-native-audio')
implementation project(":reactnativekeyboardinput")
implementation project(':react-native-video')
......
......@@ -5,8 +5,10 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission-sdk-23 android:name="android.permission.VIBRATE"/>
<application
......@@ -25,6 +27,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
......@@ -36,6 +39,19 @@
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<activity
android:noHistory="true"
android:name=".share.ShareActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="@string/share_extension_name"
android:screenOrientation="portrait"
android:theme="@style/AppTheme" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
</application>
</manifest>
......@@ -3,6 +3,7 @@ package chat.rocket.reactnative;
import android.app.Application;
import com.facebook.react.ReactApplication;
import io.github.elyx0.reactnativedocumentpicker.DocumentPickerPackage;
import io.invertase.firebase.RNFirebasePackage;
import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage;
import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage;
......@@ -15,7 +16,7 @@ import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.AlexanderZaytsev.RNI18n.RNI18nPackage;
import com.reactcommunity.rnlocalize.RNLocalizePackage;
import com.reactnative.ivpusic.imagepicker.PickerPackage;
import com.brentvatne.react.ReactVideoPackage;
import com.dylanvann.fastimage.FastImageViewPackage;
......@@ -33,6 +34,8 @@ import com.learnium.RNDeviceInfo.RNDeviceInfo;
import com.actionsheet.ActionSheetPackage;
import io.realm.react.RealmReactPackage;
import com.swmansion.rnscreens.RNScreensPackage;
import chat.rocket.SharePackage;
import com.RNFetchBlob.RNFetchBlobPackage;
import chat.rocket.reactnative.generated.BasePackageList;
......@@ -60,6 +63,7 @@ public class MainApplication extends Application implements ReactApplication, IN
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new DocumentPickerPackage(),
new RNFirebasePackage(),
new RNFirebaseCrashlyticsPackage(),
new RNFirebaseAnalyticsPackage(),
......@@ -67,6 +71,8 @@ public class MainApplication extends Application implements ReactApplication, IN
new RNCWebViewPackage(),
new OrientationPackage(),
new SplashScreenReactPackage(),
new SharePackage(),
new RNFetchBlobPackage(),
new RNGestureHandlerPackage(),
new RNScreensPackage(),
new ActionSheetPackage(),
......@@ -78,7 +84,7 @@ public class MainApplication extends Application implements ReactApplication, IN
new ReactNativeAudioPackage(),
new KeyboardInputPackage(MainApplication.this),
new FastImageViewPackage(),
new RNI18nPackage(),
new RNLocalizePackage(),
new RNNotificationsPackage(MainApplication.this),
new ModuleRegistryAdapter(mModuleRegistryProvider)
);
......
package chat.rocket.reactnative.share;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class ShareActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "ShareRocketChatRN";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(ShareActivity.this);
}
};
}
}
\ No newline at end of file
package chat.rocket.reactnative.share;
import chat.rocket.reactnative.BuildConfig;
import chat.rocket.SharePackage;
import android.app.Application;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactPackage;
import java.util.Arrays;
import java.util.List;
public class ShareApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new SharePackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}
\ No newline at end of file
<resources>
<string name="app_name">Rocket.Chat Experimental</string>
<string name="share_extension_name">Rocket.Chat Experimental</string>
<string name="no_browser_found">No Browser Found</string>
</resources>
......@@ -2,4 +2,19 @@
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:colorEdgeEffect">#aaaaaa</item>
</style>
<style name="Share.Window" parent="android:Theme">
<item name="android:windowEnterAnimation">@null</item>
<item name="android:windowExitAnimation">@null</item>
</style>
<style name="Theme.Share.Transparent" parent="android:Theme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@color/primary_dark</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowAnimationStyle">@style/Share.Window</item>
</style>
</resources>
......@@ -2,6 +2,8 @@ apply from: '../node_modules/react-native-unimodules/gradle.groovy'
includeUnimodulesProjects()
rootProject.name = 'RocketChatRN'
include ':react-native-document-picker'
project(':react-native-document-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-document-picker/android')
include ':react-native-firebase'
project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')
include ':react-native-webview'
......@@ -20,8 +22,8 @@ include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':react-native-image-crop-picker'
project(':react-native-image-crop-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-crop-picker/android')
include ':react-native-i18n'
project(':react-native-i18n').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-i18n/android')
include ':react-native-localize'
project(':react-native-localize').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-localize/android')
include ':react-native-fast-image'
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
include ':react-native-audio'
......@@ -36,4 +38,8 @@ include ':realm'
project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
include ':reactnativenotifications'
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android')
include ':app'
include ':rn-fetch-blob'
project(':rn-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/rn-fetch-blob/android')
include ':app', ':rn-extensions-share'
project(':rn-extensions-share').projectDir = new File(rootProject.projectDir, '../node_modules/rn-extensions-share/android')
{
"name": "RocketChatRN",
"share": "ShareRocketChatRN",
"displayName": "RocketChatRN"
}
\ No newline at end of file
......@@ -14,6 +14,11 @@ export const LOGIN = createRequestTypes('LOGIN', [
'SET_SERVICES',
'SET_PREFERENCE'
]);
export const SHARE = createRequestTypes('SHARE', [
'SELECT_SERVER',
'SET_USER',
'SET_SERVER_INFO'
]);
export const USER = createRequestTypes('USER', ['SET']);
export const ROOMS = createRequestTypes('ROOMS', [
...defaultTypes,
......
import { SHARE } from './actionsTypes';
export function shareSelectServer(server) {
return {
type: SHARE.SELECT_SERVER,
server
};
}
export function shareSetUser(user) {
return {
type: SHARE.SET_USER,
user
};
}
......@@ -68,6 +68,12 @@ export default {
Threads_enabled: {
type: null
},
FileUpload_MediaTypeWhiteList: {
type: 'valueAsString'
},
FileUpload_MaxFileSize: {
type: 'valueAsNumber'
},
API_Gitlab_URL: {
type: 'valueAsString'
},
......
......@@ -5,6 +5,7 @@ import HeaderButtons, { HeaderButton, Item } from 'react-navigation-header-butto
import { CustomIcon } from '../lib/Icons';
import { isIOS } from '../utils/deviceInfo';
import { COLOR_PRIMARY, COLOR_WHITE } from '../constants/colors';
import I18n from '../i18n';
const color = isIOS ? COLOR_PRIMARY : COLOR_WHITE;
export const headerIconSize = 23;
......@@ -32,6 +33,15 @@ export const CloseModalButton = React.memo(({ navigation, testID }) => (
</CustomHeaderButtons>
));
export const CloseShareExtensionButton = React.memo(({ onPress, testID }) => (
<CustomHeaderButtons left>
{isIOS
? <Item title={I18n.t('Cancel')} onPress={onPress} testID={testID} />
: <Item title='close' iconName='cross' onPress={onPress} testID={testID} />
}
</CustomHeaderButtons>
));
export const MoreButton = React.memo(({ onPress, testID }) => (
<CustomHeaderButtons>
<Item title='more' iconName='menu' onPress={onPress} testID={testID} />
......@@ -50,6 +60,10 @@ CloseModalButton.propTypes = {
navigation: PropTypes.object.isRequired,
testID: PropTypes.string.isRequired
};
CloseShareExtensionButton.propTypes = {
onPress: PropTypes.func.isRequired,
testID: PropTypes.string.isRequired
};
MoreButton.propTypes = {
onPress: PropTypes.func.isRequired,
testID: PropTypes.string.isRequired
......
......@@ -21,6 +21,8 @@ import I18n from '../i18n';
import log from '../utils/log';
import Navigation from '../lib/Navigation';
import { getMessageTranslation } from './message/utils';
import { LISTENER } from './Toast';
import EventEmitter from '../utils/events';
@connect(
state => ({
......@@ -48,7 +50,6 @@ export default class MessageActions extends React.Component {
actionsHide: PropTypes.func.isRequired,
room: PropTypes.object.isRequired,
actionMessage: PropTypes.object,
toast: PropTypes.element,
user: PropTypes.object,
deleteRequest: PropTypes.func.isRequired,
editInit: PropTypes.func.isRequired,
......@@ -275,9 +276,9 @@ export default class MessageActions extends React.Component {
}
handleCopy = async() => {
const { actionMessage, toast } = this.props;
const { actionMessage } = this.props;
await Clipboard.setString(actionMessage.msg);
toast.show(I18n.t('Copied_to_clipboard'));
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
}
handleShare = async() => {
......@@ -294,10 +295,10 @@ export default class MessageActions extends React.Component {
}
handlePermalink = async() => {
const { actionMessage, toast } = this.props;
const { actionMessage } = this.props;
const permalink = await this.getPermalink(actionMessage);
Clipboard.setString(permalink);
toast.show(I18n.t('Permalink_copied_to_clipboard'));
EventEmitter.emit(LISTENER, { message: I18n.t('Permalink_copied_to_clipboard') });
}
handlePin = () => {
......
......@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import {
View, Text, StyleSheet, Image, ScrollView, TouchableHighlight
} from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Modal from 'react-native-modal';
import { responsive } from 'react-native-responsive-ui';
......@@ -12,7 +13,11 @@ import Button from '../Button';
import I18n from '../../i18n';
import sharedStyles from '../../views/Styles';
import { isIOS } from '../../utils/deviceInfo';
import { COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE } from '../../constants/colors';
import { canUploadFile } from '../../utils/media';
import {
COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_DANGER
} from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons';
const cancelButtonColor = COLOR_BACKGROUND_CONTAINER;
......@@ -63,18 +68,56 @@ const styles = StyleSheet.create({
androidButtonText: {
fontSize: 18,
textAlign: 'center'
},
fileIcon: {
color: COLOR_PRIMARY,
margin: 20,
flex: 1,
textAlign: 'center'
},
errorIcon: {
color: COLOR_DANGER
},
fileMime: {
...sharedStyles.textColorTitle,
...sharedStyles.textBold,
textAlign: 'center',
fontSize: 20,
marginBottom: 20
},
errorContainer: {
margin: 20,
flex: 1,
textAlign: 'center',
justifyContent: 'center',
alignItems: 'center'
},
video: {
flex: 1,
borderRadius: 4,
height: 150,
backgroundColor: '#1f2329',
marginBottom: 6,
alignItems: 'center',
justifyContent: 'center'
}
});
@responsive
@connect(state => ({
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize
}))
export default class UploadModal extends Component {
static propTypes = {
isVisible: PropTypes.bool,
file: PropTypes.object,
close: PropTypes.func,
submit: PropTypes.func,
window: PropTypes.object
window: PropTypes.object,
FileUpload_MediaTypeWhiteList: PropTypes.string,
FileUpload_MaxFileSize: PropTypes.number
}
state = {
......@@ -116,12 +159,79 @@ export default class UploadModal extends Component {
return false;
}
canUploadFile = () => {
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize, file } = this.props;
if (!(file && file.path)) {
return true;
}
if (file.size > FileUpload_MaxFileSize) {
return false;
}
// if white list is empty, all media types are enabled
if (!FileUpload_MediaTypeWhiteList) {
return true;
}
const allowedMime = FileUpload_MediaTypeWhiteList.split(',');
if (allowedMime.includes(file.mime)) {
return true;
}
const wildCardGlob = '/*';
const wildCards = allowedMime.filter(item => item.indexOf(wildCardGlob) > 0);
if (wildCards.includes(file.mime.replace(/(\/.*)$/, wildCardGlob))) {
return true;
}
return false;
}
submit = () => {
const { file, submit } = this.props;
const { name, description } = this.state;
submit({ ...file, name, description });
}
renderError = () => {
const { file, FileUpload_MaxFileSize, close } = this.props;
const { window: { width } } = this.props;
const errorMessage = (FileUpload_MaxFileSize < file.size)
? 'error-file-too-large'
: 'error-invalid-file-type';
return (
<View style={[styles.container, { width: width - 32 }]}>
<View style={styles.titleContainer}>
<Text style={styles.title}>{I18n.t(errorMessage)}</Text>
</View>
<View style={styles.errorContainer}>
<CustomIcon name='circle-cross' size={120} style={styles.errorIcon} />
</View>
<Text style={styles.fileMime}>{ file.mime }</Text>
<View style={styles.buttonContainer}>
{