[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)
This commit is contained in:
Diego Mello 2019-07-29 17:44:39 -03:00 committed by GitHub
parent 8ea6f1647e
commit 2d58a8b983
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
131 changed files with 15148 additions and 7495 deletions

View File

@ -1,13 +1,11 @@
# 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) | ❌ |

2
__mocks__/react-native-localize.js vendored Normal file
View File

@ -0,0 +1,2 @@
export const initialConstants = null;
export const findBestAvailableLanguage = () => null;

3
__mocks__/react-native-realm-path.js vendored Normal file
View File

@ -0,0 +1,3 @@
export default {
realmPath: ''
};

File diff suppressed because it is too large Load Diff

View File

@ -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')

View File

@ -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>

View File

@ -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)
);

View File

@ -0,0 +1,23 @@
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);
}
};
}
}

View File

@ -0,0 +1,38 @@
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;
}
}

View File

@ -1,5 +1,5 @@
<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>

View File

@ -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>

View File

@ -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')

View File

@ -1,4 +1,5 @@
{
"name": "RocketChatRN",
"share": "ShareRocketChatRN",
"displayName": "RocketChatRN"
}

View 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,

15
app/actions/share.js Normal file
View File

@ -0,0 +1,15 @@
import { SHARE } from './actionsTypes';
export function shareSelectServer(server) {
return {
type: SHARE.SELECT_SERVER,
server
};
}
export function shareSetUser(user) {
return {
type: SHARE.SET_USER,
user
};
}

View File

@ -68,6 +68,12 @@ export default {
Threads_enabled: {
type: null
},
FileUpload_MediaTypeWhiteList: {
type: 'valueAsString'
},
FileUpload_MaxFileSize: {
type: 'valueAsNumber'
},
API_Gitlab_URL: {
type: 'valueAsString'
},

View File

@ -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

View File

@ -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 = () => {

View File

@ -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}>
{
(isIOS)
? (
<Button
title={I18n.t('Cancel')}
type='secondary'
backgroundColor={cancelButtonColor}
style={styles.button}
onPress={close}
/>
)
: (
<TouchableHighlight
onPress={close}
style={[styles.androidButton, { backgroundColor: cancelButtonColor }]}
underlayColor={cancelButtonColor}
activeOpacity={0.5}
>
<Text style={[styles.androidButtonText, { ...sharedStyles.textBold, color: COLOR_PRIMARY }]}>{I18n.t('Cancel')}</Text>
</TouchableHighlight>
)
}
</View>
</View>
);
}
renderButtons = () => {
const { close } = this.props;
if (isIOS) {
@ -166,9 +276,27 @@ export default class UploadModal extends Component {
);
}
renderPreview() {
const { file } = this.props;
if (file.mime && file.mime.match(/image/)) {
return (<Image source={{ isStatic: true, uri: file.path }} style={styles.image} />);
}
if (file.mime && file.mime.match(/video/)) {
return (
<View style={styles.video}>
<CustomIcon name='play' size={72} color={COLOR_WHITE} />
</View>
);
}
return (<CustomIcon name='file-generic' size={72} style={styles.fileIcon} />);
}
render() {
const { window: { width }, isVisible, close } = this.props;
const { name, description, file } = this.state;
const {
window: { width }, isVisible, close, file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize
} = this.props;
const { name, description } = this.state;
const showError = !canUploadFile(file, { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize });
return (
<Modal
isVisible={isVisible}
@ -181,26 +309,29 @@ export default class UploadModal extends Component {
hideModalContentWhileAnimating
avoidKeyboard
>
<View style={[styles.container, { width: width - 32 }]}>
<View style={styles.titleContainer}>
<Text style={styles.title}>{I18n.t('Upload_file_question_mark')}</Text>
</View>
{(showError) ? this.renderError()
: (
<View style={[styles.container, { width: width - 32 }]}>
<View style={styles.titleContainer}>
<Text style={styles.title}>{I18n.t('Upload_file_question_mark')}</Text>
</View>
<ScrollView style={styles.scrollView}>
<Image source={{ isStatic: true, uri: file.path }} style={styles.image} />
<TextInput
placeholder={I18n.t('File_name')}
value={name}
onChangeText={value => this.setState({ name: value })}
/>
<TextInput
placeholder={I18n.t('File_description')}
value={description}
onChangeText={value => this.setState({ description: value })}
/>
</ScrollView>
{this.renderButtons()}
</View>
<ScrollView style={styles.scrollView}>
{this.renderPreview()}
<TextInput
placeholder={I18n.t('File_name')}
value={name}
onChangeText={value => this.setState({ name: value })}
/>
<TextInput
placeholder={I18n.t('File_description')}
value={description}
onChangeText={value => this.setState({ description: value })}
/>
</ScrollView>
{this.renderButtons()}
</View>
)}
</Modal>
);
}

View File

@ -8,6 +8,7 @@ import { emojify } from 'react-emojione';
import { KeyboardAccessoryView } from 'react-native-keyboard-input';
import ImagePicker from 'react-native-image-crop-picker';
import equal from 'deep-equal';
import DocumentPicker from 'react-native-document-picker';
import ActionSheet from 'react-native-action-sheet';
import { userTyping as userTypingAction } from '../../actions/room';
@ -46,21 +47,22 @@ const onlyUnique = function onlyUnique(value, index, self) {
const imagePickerConfig = {
cropping: true,
compressImageQuality: 0.8,
avoidEmptySpaceAroundImage: false,
cropperChooseText: I18n.t('Choose'),
cropperCancelText: I18n.t('Cancel')
avoidEmptySpaceAroundImage: false
};
const libraryPickerConfig = {
mediaType: 'any'
};
const videoPickerConfig = {
mediaType: 'video'
};
const fileOptions = [I18n.t('Cancel')];
const FILE_CANCEL_INDEX = 0;
// Photo
fileOptions.push(I18n.t('Take_a_photo'));
const FILE_PHOTO_INDEX = 1;
// Library
fileOptions.push(I18n.t('Choose_from_library'));
const FILE_LIBRARY_INDEX = 2;
const FILE_VIDEO_INDEX = 2;
const FILE_LIBRARY_INDEX = 3;
const FILE_DOCUMENT_INDEX = 4;
class MessageBox extends Component {
static propTypes = {
@ -107,6 +109,30 @@ class MessageBox extends Component {
this.customEmojis = [];
this.onEmojiSelected = this.onEmojiSelected.bind(this);
this.text = '';
this.fileOptions = [
I18n.t('Cancel'),
I18n.t('Take_a_photo'),
I18n.t('Take_a_video'),
I18n.t('Choose_from_library'),
I18n.t('Choose_file')
];
const libPickerLabels = {
cropperChooseText: I18n.t('Choose'),
cropperCancelText: I18n.t('Cancel'),
loadingLabelText: I18n.t('Processing')
};
this.imagePickerConfig = {
...imagePickerConfig,
...libPickerLabels
};
this.libraryPickerConfig = {
...libraryPickerConfig,
...libPickerLabels
};
this.videoPickerConfig = {
...videoPickerConfig,
...libPickerLabels
};
}
componentDidMount() {
@ -462,9 +488,10 @@ class MessageBox extends Component {
this.setShowSend(false);
}
sendImageMessage = async(file) => {
const { rid, tmid } = this.props;
sendMediaMessage = async(file) => {
const {
rid, tmid, baseUrl: server, user
} = this.props;
this.setState({ file: { isVisible: false } });
const fileInfo = {
name: file.name,
@ -475,37 +502,65 @@ class MessageBox extends Component {
path: file.path
};
try {
await RocketChat.sendFileMessage(rid, fileInfo, tmid);
await RocketChat.sendFileMessage(rid, fileInfo, tmid, server, user);
} catch (e) {
log('err_send_image', e);
log('err_send_media_message', e);
}
}
takePhoto = async() => {
try {
const image = await ImagePicker.openCamera(imagePickerConfig);
const image = await ImagePicker.openCamera(this.imagePickerConfig);
this.showUploadModal(image);
} catch (e) {
log('err_take_photo', e);
}
}
takeVideo = async() => {
try {
const video = await ImagePicker.openCamera(this.videoPickerConfig);
this.showUploadModal(video);
} catch (e) {
log('err_take_video', e);
}
}
chooseFromLibrary = async() => {
try {
const image = await ImagePicker.openPicker(imagePickerConfig);
const image = await ImagePicker.openPicker(this.libraryPickerConfig);
this.showUploadModal(image);
} catch (e) {
log('err_choose_from_library', e);
}
}
chooseFile = async() => {
try {
const res = await DocumentPicker.pick({
type: [DocumentPicker.types.allFiles]
});
this.showUploadModal({
filename: res.name,
size: res.size,
mime: res.type,
path: res.uri
});
} catch (error) {
if (!DocumentPicker.isCancel(error)) {
log('chooseFile', error);
}
}
}
showUploadModal = (file) => {
this.setState({ file: { ...file, isVisible: true } });
}
showFileActions = () => {
ActionSheet.showActionSheetWithOptions({
options: fileOptions,
options: this.fileOptions,
cancelButtonIndex: FILE_CANCEL_INDEX
}, (actionIndex) => {
this.handleFileActionPress(actionIndex);
@ -517,9 +572,15 @@ class MessageBox extends Component {
case FILE_PHOTO_INDEX:
this.takePhoto();
break;
case FILE_VIDEO_INDEX:
this.takeVideo();
break;
case FILE_LIBRARY_INDEX:
this.chooseFromLibrary();
break;
case FILE_DOCUMENT_INDEX:
this.chooseFile();
break;
default:
break;
}
@ -543,14 +604,16 @@ class MessageBox extends Component {
}
finishAudioMessage = async(fileInfo) => {
const { rid, tmid } = this.props;
const {
rid, tmid, baseUrl: server, user
} = this.props;
this.setState({
recording: false
});
if (fileInfo) {
try {
await RocketChat.sendFileMessage(rid, fileInfo, tmid);
await RocketChat.sendFileMessage(rid, fileInfo, tmid, server, user);
} catch (e) {
if (e && e.error === 'error-file-too-large') {
return Alert.alert(I18n.t(e.error));
@ -895,7 +958,7 @@ class MessageBox extends Component {
isVisible={(file && file.isVisible)}
file={file}
close={() => this.setState({ file: {} })}
submit={this.sendImageMessage}
submit={this.sendMediaMessage}
/>
</React.Fragment>
);

View File

@ -7,8 +7,12 @@ import { COLOR_TEXT_DESCRIPTION } from '../constants/colors';
const styles = StyleSheet.create({
style: {
marginRight: 7,
marginTop: 3,
tintColor: COLOR_TEXT_DESCRIPTION,
marginTop: 3
},
imageColor: {
tintColor: COLOR_TEXT_DESCRIPTION
},
iconColor: {
color: COLOR_TEXT_DESCRIPTION
},
discussion: {
@ -23,13 +27,15 @@ const RoomTypeIcon = React.memo(({ type, size, style }) => {
if (type === 'discussion') {
// FIXME: These are temporary only. We should have all room icons on <Customicon />, but our design team is still working on this.
return <CustomIcon name='chat' size={13} style={[styles.style, styles.discussion]} />;
return <CustomIcon name='chat' size={13} style={[styles.style, styles.iconColor, styles.discussion]} />;
}
if (type === 'c') {
return <Image source={{ uri: 'hashtag' }} style={[styles.style, style, { width: size, height: size }]} />;
return <Image source={{ uri: 'hashtag' }} style={[styles.style, styles.imageColor, style, { width: size, height: size }]} />;
} if (type === 'd') {
return <CustomIcon name='at' size={13} style={[styles.style, styles.iconColor, styles.discussion]} />;
}
return <Image source={{ uri: 'lock' }} style={[styles.style, style, { width: size, height: size }]} />;
return <Image source={{ uri: 'lock' }} style={[styles.style, styles.imageColor, style, { width: size, height: size }]} />;
});
RoomTypeIcon.propTypes = {

View File

@ -1,6 +1,9 @@
import React from 'react';
import { View, StyleSheet, TextInput } from 'react-native';
import {
View, StyleSheet, TextInput, Text
} from 'react-native';
import PropTypes from 'prop-types';
import Touchable from 'react-native-platform-touchable';
import I18n from '../i18n';
import { isIOS } from '../utils/deviceInfo';
@ -9,7 +12,10 @@ import sharedStyles from '../views/Styles';
const styles = StyleSheet.create({
container: {
backgroundColor: isIOS ? '#F7F8FA' : '#54585E'
backgroundColor: isIOS ? '#F7F8FA' : '#54585E',
flexDirection: 'row',
alignItems: 'center',
flex: 1
},
searchBox: {
alignItems: 'center',
@ -21,7 +27,8 @@ const styles = StyleSheet.create({
height: 36,
margin: 16,
marginVertical: 10,
paddingHorizontal: 10
paddingHorizontal: 10,
flex: 1
},
input: {
color: '#8E8E93',
@ -31,10 +38,26 @@ const styles = StyleSheet.create({
paddingTop: 0,
paddingBottom: 0,
...sharedStyles.textRegular
},
cancel: {
marginRight: 10
},
cancelText: {
...sharedStyles.textRegular,
...sharedStyles.textColorHeaderBack,
fontSize: 17
}
});
const SearchBox = ({ onChangeText, onSubmitEditing, testID }) => (
const CancelButton = onCancelPress => (
<Touchable onPress={onCancelPress} style={styles.cancel}>
<Text style={styles.cancelText}>{I18n.t('Cancel')}</Text>
</Touchable>
);
const SearchBox = ({
onChangeText, onSubmitEditing, testID, hasCancel, onCancelPress, ...props
}) => (
<View style={styles.container}>
<View style={styles.searchBox}>
<CustomIcon name='magnifier' size={14} color='#8E8E93' />
@ -50,14 +73,18 @@ const SearchBox = ({ onChangeText, onSubmitEditing, testID }) => (
underlineColorAndroid='transparent'
onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing}
{...props}
/>
</View>
{ hasCancel ? CancelButton(onCancelPress) : null }
</View>
);
SearchBox.propTypes = {
onChangeText: PropTypes.func.isRequired,
onSubmitEditing: PropTypes.func,
hasCancel: PropTypes.bool,
onCancelPress: PropTypes.func,
testID: PropTypes.string
};

53
app/containers/Toast.js Normal file
View File

@ -0,0 +1,53 @@
import React from 'react';
import { StyleSheet } from 'react-native';
import EasyToast from 'react-native-easy-toast';
import { COLOR_TOAST, COLOR_WHITE } from '../constants/colors';
import sharedStyles from '../views/Styles';
import EventEmitter from '../utils/events';
const styles = StyleSheet.create({
toast: {
backgroundColor: COLOR_TOAST,
maxWidth: 300,
padding: 10
},
text: {
...sharedStyles.textRegular,
color: COLOR_WHITE,
fontSize: 14,
textAlign: 'center'
}
});
export const LISTENER = 'Toast';
export default class Toast extends React.Component {
componentDidMount() {
EventEmitter.addEventListener(LISTENER, this.showToast);
}
shouldComponentUpdate() {
return false;
}
componentWillUnmount() {
EventEmitter.removeListener(LISTENER);
}
showToast = ({ message }) => {
this.toast.show(message, 1000);
}
render() {
return (
<EasyToast
ref={toast => this.toast = toast}
position='center'
style={styles.toast}
textStyle={styles.text}
opacity={0.9}
/>
);
}
}

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Text } from 'react-native';
import { Text, View } from 'react-native';
import PropTypes from 'prop-types';
import I18n from '../../i18n';
@ -12,26 +12,35 @@ const Content = React.memo((props) => {
return <Text style={styles.textInfo}>{getInfoMessage({ ...props })}</Text>;
}
let content = null;
if (props.tmid && !props.msg) {
return <Text style={styles.text}>{I18n.t('Sent_an_attachment')}</Text>;
content = <Text style={styles.text}>{I18n.t('Sent_an_attachment')}</Text>;
} else {
content = (
<Markdown
msg={props.msg}
baseUrl={props.baseUrl}
username={props.user.username}
isEdited={props.isEdited}
mentions={props.mentions}
channels={props.channels}
numberOfLines={props.tmid ? 1 : 0}
getCustomEmoji={props.getCustomEmoji}
useMarkdown={props.useMarkdown}
/>
);
}
return (
<Markdown
msg={props.msg}
baseUrl={props.baseUrl}
username={props.user.username}
isEdited={props.isEdited}
mentions={props.mentions}
channels={props.channels}
numberOfLines={props.tmid ? 1 : 0}
getCustomEmoji={props.getCustomEmoji}
useMarkdown={props.useMarkdown}
/>
<View style={props.isTemp && styles.temp}>
{content}
</View>
);
}, (prevProps, nextProps) => prevProps.msg === nextProps.msg);
}, (prevProps, nextProps) => prevProps.isTemp === nextProps.isTemp && prevProps.msg === nextProps.msg);
Content.propTypes = {
isTemp: PropTypes.bool,
isInfo: PropTypes.bool,
isEdited: PropTypes.bool,
useMarkdown: PropTypes.bool,

View File

@ -4,7 +4,6 @@ import { View } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import User from './User';
import MessageError from './MessageError';
import styles from './styles';
import sharedStyles from '../../views/Styles';
import RepliedThread from './RepliedThread';
@ -45,7 +44,7 @@ const Message = React.memo((props) => {
if (props.isThreadReply || props.isThreadSequential || props.isInfo) {
const thread = props.isThreadReply ? <RepliedThread isTemp={props.isTemp} {...props} /> : null;
return (
<View style={[styles.container, props.style, props.isTemp && styles.temp]}>
<View style={[styles.container, props.style]}>
{thread}
<View style={[styles.flex, sharedStyles.alignItemsCenter]}>
<MessageAvatar small {...props} />
@ -62,7 +61,7 @@ const Message = React.memo((props) => {
);
}
return (
<View style={[styles.container, props.style, props.isTemp && styles.temp]}>
<View style={[styles.container, props.style]}>
<View style={styles.flex}>
<MessageAvatar {...props} />
<View
@ -86,8 +85,7 @@ Message.displayName = 'Message';
const MessageTouchable = React.memo((props) => {
if (props.hasError) {
return (
<View style={styles.root}>
<MessageError {...props} />
<View>
<Message {...props} />
</View>
);

View File

@ -5,14 +5,15 @@ import PropTypes from 'prop-types';
import { CustomIcon } from '../../lib/Icons';
import { COLOR_DANGER } from '../../constants/colors';
import styles from './styles';
import { BUTTON_HIT_SLOP } from './utils';
const MessageError = React.memo(({ hasError, onErrorPress }) => {
if (!hasError) {
return null;
}
return (
<Touchable onPress={onErrorPress} style={styles.errorButton}>
<CustomIcon name='circle-cross' color={COLOR_DANGER} size={20} />
<Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}>
<CustomIcon name='warning' color={COLOR_DANGER} size={18} />
</Touchable>
);
}, (prevProps, nextProps) => prevProps.hasError === nextProps.hasError);

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { View, Text, StyleSheet } from 'react-native';
import moment from 'moment';
import MessageError from './MessageError';
import sharedStyles from '../../views/Styles';
import messageStyles from './styles';
@ -31,9 +32,9 @@ const styles = StyleSheet.create({
});
const User = React.memo(({
isHeader, useRealName, author, alias, ts, timeFormat
isHeader, useRealName, author, alias, ts, timeFormat, hasError, ...props
}) => {
if (isHeader) {
if (isHeader || hasError) {
const username = (useRealName && author.name) || author.username;
const aliasUsername = alias ? (<Text style={styles.alias}> @{username}</Text>) : null;
const time = moment(ts).format(timeFormat);
@ -47,6 +48,7 @@ const User = React.memo(({
</Text>
</View>
<Text style={messageStyles.time}>{time}</Text>
{ hasError && <MessageError hasError={hasError} {...props} /> }
</View>
);
}
@ -55,6 +57,7 @@ const User = React.memo(({
User.propTypes = {
isHeader: PropTypes.bool,
hasError: PropTypes.bool,
useRealName: PropTypes.bool,
author: PropTypes.object,
alias: PropTypes.string,

View File

@ -128,6 +128,9 @@ export default class MessageContainer extends React.Component {
const {
item, previousItem, broadcast, Message_GroupingPeriod
} = this.props;
if (this.hasError || (previousItem && previousItem.status === messagesStatus.ERROR)) {
return true;
}
if (previousItem && (
(previousItem.ts.toDateString() === item.ts.toDateString())
&& (previousItem.u.username === item.u.username)

View File

@ -114,7 +114,7 @@ export default StyleSheet.create({
color: COLOR_PRIMARY
},
errorButton: {
paddingHorizontal: 15,
paddingLeft: 10,
paddingVertical: 5
},
buttonContainer: {

View File

@ -1,4 +1,7 @@
import I18n from 'react-native-i18n';
import i18n from 'i18n-js';
import { I18nManager } from 'react-native';
import * as RNLocalize from 'react-native-localize';
import en from './locales/en';
import ru from './locales/ru';
import fr from './locales/fr';
@ -7,11 +10,22 @@ import ptBR from './locales/pt-BR';
import zhCN from './locales/zh-CN';
import ptPT from './locales/pt-PT';
I18n.fallbacks = true;
I18n.defaultLocale = 'en';
I18n.translations = {
en, ru, 'pt-BR': ptBR, 'zh-CN': zhCN, fr, de, 'pt-PT': ptPT
i18n.translations = {
en,
ru,
'pt-BR': ptBR,
'zh-CN': zhCN,
fr,
de,
'pt-PT': ptPT
};
i18n.fallbacks = true;
export default I18n;
const defaultLanguage = { languageTag: 'en', isRTL: false };
const availableLanguages = Object.keys(i18n.translations);
const { languageTag, isRTL } = RNLocalize.findBestAvailableLanguage(availableLanguages) || defaultLanguage;
I18nManager.forceRTL(isRTL);
i18n.locale = languageTag;
export default i18n;

View File

@ -99,6 +99,7 @@ export default {
Are_you_sure_question_mark: 'Sind Sie sicher?',
Are_you_sure_you_want_to_leave_the_room: 'Möchten Sie den Raum wirklich verlassen {{room}}?',
Authenticating: 'Authentifizierung',
Auto_Translate: 'Automatische Übersetzung',
Avatar_changed_successfully: 'Avatar erfolgreich geändert!',
Avatar_Url: 'Avatar-URL',
Away: 'Abwesend',
@ -155,11 +156,13 @@ export default {
Email_or_password_field_is_empty: 'Das E-Mail- oder Passwortfeld ist leer',
Email: 'Email',
email: 'Email',
Enable_Auto_Translate: 'Automatische Übersetzung aktivieren',
Enable_markdown: 'Markdown aktivieren',
Enable_notifications: 'Benachrichtigungen aktivieren',
Everyone_can_access_this_channel: 'Jeder kann auf diesen Kanal zugreifen',
erasing_room: 'lösche Raum',
Error_uploading: 'Fehler beim Hochladen',
Favorite: 'Favorisieren',
Favorites: 'Favoriten',
Files: 'Dateien',
File_description: 'Dateibeschreibung',
@ -173,6 +176,7 @@ export default {
Forgot_Password: 'Passwort vergessen',
Group_by_favorites: 'Nach Favoriten gruppieren',
Group_by_type: 'Gruppieren nach Typ',
Hide: 'Ausblenden',
Has_joined_the_channel: 'Ist dem Kanal beigetreten',
Has_joined_the_conversation: 'Hat sich dem Gespräch angeschlossen',
Has_left_the_channel: 'Hat den Kanal verlassen',
@ -266,6 +270,7 @@ export default {
Reactions_are_disabled: 'Reaktionen sind deaktiviert',
Reactions_are_enabled: 'Reaktionen sind aktiviert',
Reactions: 'Reaktionen',
Read: 'Gelesen',
Read_Only_Channel: 'Nur-Lese-Kanal',
Read_Only: 'Schreibgeschützt',
Read_Receipt: 'Lesebestätigung',
@ -343,12 +348,14 @@ export default {
Timezone: 'Zeitzone',
topic: 'Thema',
Topic: 'Thema',
Translate: 'Übersetzen',
Try_again: 'Versuchen Sie es nochmal',
Two_Factor_Authentication: 'Zwei-Faktor-Authentifizierung',
Type_the_channel_name_here: 'Geben Sie hier den Kanalnamen ein',
unarchive: 'wiederherstellen',
UNARCHIVE: 'WIEDERHERSTELLEN',
Unblock_user: 'Nutzer entblockieren',
Unfavorite: 'Nicht mehr favorisieren',
Unfollowed_thread: 'Thread nicht mehr folgen',
Unmute: 'Stummschaltung aufheben',
unmuted: 'Stummschaltung aufgehoben',
@ -374,6 +381,7 @@ export default {
Username_or_email: 'Benutzername oder E-Mail-Adresse',
Validating: 'Validierung',
Video_call: 'Videoanruf',
View_Original: 'Original anzeigen',
Voice_call: 'Sprachanruf',
Welcome: 'Herzlich willkommen',
Welcome_to_RocketChat: 'Willkommen bei Rocket.Chat',

View File

@ -103,6 +103,7 @@ export default {
Avatar_changed_successfully: 'Avatar changed successfully!',
Avatar_Url: 'Avatar URL',
Away: 'Away',
Back: 'Back',
Block_user: 'Block user',
Broadcast_channel_Description: 'Only authorized users can write new messages, but the other users will be able to reply',
Broadcast_Channel: 'Broadcast Channel',
@ -120,6 +121,7 @@ export default {
Close_emoji_selector: 'Close emoji selector',
Choose: 'Choose',
Choose_from_library: 'Choose from library',
Choose_file: 'Choose file',
Code: 'Code',
Collaborative: 'Collaborative',
Confirm: 'Confirm',
@ -262,6 +264,7 @@ export default {
Private_Channel: 'Private Channel',
Private_Groups: 'Private Groups',
Private: 'Private',
Processing: 'Processing...',
Profile_saved_successfully: 'Profile saved successfully!',
Profile: 'Profile',
Public_Channel: 'Public Channel',
@ -308,11 +311,13 @@ export default {
Search_global_users: 'Search for global users',
Search_global_users_description: 'If you turn-on, you can search for any user from others companies or servers.',
Select_Avatar: 'Select Avatar',
Select_Server: 'Select Server',
Select_Users: 'Select Users',
Send: 'Send',
Send_audio_message: 'Send audio message',
Send_crash_report: 'Send crash report',
Send_message: 'Send message',
Send_to: 'Send to...',
Sent_an_attachment: 'Sent an attachment',
Server: 'Server',
Servers: 'Servers',
@ -335,6 +340,7 @@ export default {
Started_discussion: 'Started a discussion:',
Submit: 'Submit',
Take_a_photo: 'Take a photo',
Take_a_video: 'Take a video',
tap_to_change_status: 'tap to change status',
Tap_to_view_servers_list: 'Tap to view servers list',
Terms_of_Service: ' Terms of Service ',
@ -346,6 +352,7 @@ export default {
Thread: 'Thread',
Threads: 'Threads',
Timezone: 'Timezone',
To: 'To',
topic: 'topic',
Topic: 'Topic',
Translate: 'Translate',
@ -386,6 +393,7 @@ export default {
Welcome: 'Welcome',
Welcome_to_RocketChat: 'Welcome to Rocket.Chat',
Whats_your_2fa: 'What\'s your 2FA code?',
Without_Servers: 'Without Servers',
Yes_action_it: 'Yes, {{action}} it!',
Yesterday: 'Yesterday',
You_are_in_preview_mode: 'You are in preview mode',
@ -395,6 +403,7 @@ export default {
you_were_mentioned: 'you were mentioned',
you: 'you',
You: 'You',
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'You need to access at least one Rocket.Chat server to share something.',
Version_no: 'Version: {{version}}',
You_will_not_be_able_to_recover_this_message: 'You will not be able to recover this message!',
Change_Language: 'Change Language',

View File

@ -108,6 +108,7 @@ export default {
Avatar_changed_successfully: 'Avatar alterado com sucesso!',
Avatar_Url: 'Avatar URL',
Away: 'Ausente',
Back: 'Voltar',
Block_user: 'Bloquear usuário',
Broadcast_channel_Description: 'Somente usuários autorizados podem escrever novas mensagens, mas os outros usuários poderão responder',
Broadcast_Channel: 'Canal de Transmissão',
@ -125,6 +126,7 @@ export default {
Close_emoji_selector: 'Fechar seletor de emojis',
Choose: 'Escolher',
Choose_from_library: 'Escolha da biblioteca',
Choose_file: 'Enviar arquivo',
Code: 'Código',
Collaborative: 'Colaborativo',
Confirm: 'Confirmar',
@ -255,6 +257,7 @@ export default {
Private_Channel: 'Canal Privado',
Private_Groups: 'Grupo Privado',
Private: 'Privado',
Processing: 'Processando...',
Profile_saved_successfully: 'Perfil salvo com sucesso!',
Profile: 'Perfil',
Public_Channel: 'Canal Público',
@ -299,10 +302,12 @@ export default {
Search_global_users: 'Busca por usuários globais',
Search_global_users_description: 'Caso ativado, busca por usuários de outras empresas ou servidores.',
Select_Avatar: 'Selecionar Avatar',
Select_Server: 'Selecionar Servidor',
Select_Users: 'Selecionar Usuários',
Send: 'Enviar',
Send_audio_message: 'Enviar mensagem de áudio',
Send_message: 'Enviar mensagem',
Send_to: 'Enviar para...',
Sent_an_attachment: 'Enviou um anexo',
Server: 'Servidor',
Set_username_subtitle: 'O usuário é utilizado para permitir que você seja mencionado em mensagens',
@ -322,6 +327,7 @@ export default {
Started_discussion: 'Iniciou uma discussão:',
Submit: 'Enviar',
Take_a_photo: 'Tirar uma foto',
Take_a_video: 'Gravar um vídeo',
Terms_of_Service: ' Termos de Serviço ',
The_URL_is_invalid: 'A URL fornecida é inválida ou não acessível. Por favor tente novamente, mas com uma url diferente.',
There_was_an_error_while_action: 'Aconteceu um erro {{action}}!',
@ -330,6 +336,7 @@ export default {
Thread: 'Tópico',
Threads: 'Tópicos',
Timezone: 'Fuso horário',
To: 'Para',
topic: 'tópico',
Topic: 'Tópico',
Try_again: 'Tentar novamente',
@ -366,6 +373,7 @@ export default {
Welcome: 'Bem vindo',
Welcome_to_RocketChat: 'Bem vindo ao Rocket.Chat',
Whats_your_2fa: 'Qual seu código de autenticação?',
Without_Servers: 'Sem Servidores',
Yes_action_it: 'Sim, {{action}}!',
Yesterday: 'Ontem',
You_are_in_preview_mode: 'Está é uma prévia do canal',
@ -375,5 +383,6 @@ export default {
you_were_mentioned: 'você foi mencionado',
you: 'você',
You: 'Você',
You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Você precisa acessar ao menos um servidor Rocket.Chat para compartilhar.',
You_will_not_be_able_to_recover_this_message: 'Você não será capaz de recuperar essa mensagem!'
};

View File

@ -5,47 +5,18 @@ import {
import { Provider } from 'react-redux';
import { useScreens } from 'react-native-screens'; // eslint-disable-line import/no-unresolved
import { Linking } from 'react-native';
import firebase from 'react-native-firebase';
import PropTypes from 'prop-types';
import { appInit } from './actions';
import { deepLinkingOpen } from './actions/deepLinking';
import OnboardingView from './views/OnboardingView';
import NewServerView from './views/NewServerView';
import LoginSignupView from './views/LoginSignupView';
import AuthLoadingView from './views/AuthLoadingView';
import RoomsListView from './views/RoomsListView';
import RoomView from './views/RoomView';
import NewMessageView from './views/NewMessageView';
import DirectoryView from './views/DirectoryView';
import LoginView from './views/LoginView';
import Navigation from './lib/Navigation';
import Sidebar from './views/SidebarView';
import ProfileView from './views/ProfileView';
import SettingsView from './views/SettingsView';
import LanguageView from './views/LanguageView';
import AdminPanelView from './views/AdminPanelView';
import RoomActionsView from './views/RoomActionsView';
import RoomInfoView from './views/RoomInfoView';
import RoomInfoEditView from './views/RoomInfoEditView';
import RoomMembersView from './views/RoomMembersView';
import SearchMessagesView from './views/SearchMessagesView';
import ReadReceiptsView from './views/ReadReceiptView';
import ThreadMessagesView from './views/ThreadMessagesView';
import MessagesView from './views/MessagesView';
import AutoTranslateView from './views/AutoTranslateView';
import SelectedUsersView from './views/SelectedUsersView';
import CreateChannelView from './views/CreateChannelView';
import LegalView from './views/LegalView';
import ForgotPasswordView from './views/ForgotPasswordView';
import RegisterView from './views/RegisterView';
import OAuthView from './views/OAuthView';
import SetUsernameView from './views/SetUsernameView';
import { HEADER_BACKGROUND, HEADER_TITLE, HEADER_BACK } from './constants/colors';
import parseQuery from './lib/methods/helpers/parseQuery';
import { initializePushNotifications, onNotification } from './notifications/push';
import store from './lib/createStore';
import NotificationBadge from './notifications/inApp';
import { defaultHeader, onNavigationStateChange } from './utils/navigation';
import Toast from './containers/Toast';
useScreens();
@ -63,35 +34,38 @@ const parseDeepLinking = (url) => {
return null;
};
const defaultHeader = {
headerStyle: {
backgroundColor: HEADER_BACKGROUND
},
headerTitleStyle: {
color: HEADER_TITLE
},
headerBackTitle: null,
headerTintColor: HEADER_BACK
};
// Outside
const OutsideStack = createStackNavigator({
OnboardingView: {
screen: OnboardingView,
getScreen: () => require('./views/OnboardingView').default,
header: null
},
NewServerView,
LoginSignupView,
LoginView,
ForgotPasswordView,
RegisterView,
LegalView
NewServerView: {
getScreen: () => require('./views/NewServerView').default
},
LoginSignupView: {
getScreen: () => require('./views/LoginSignupView').default
},
LoginView: {
getScreen: () => require('./views/LoginView').default
},
ForgotPasswordView: {
getScreen: () => require('./views/ForgotPasswordView').default
},
RegisterView: {
getScreen: () => require('./views/RegisterView').default
},
LegalView: {
getScreen: () => require('./views/LegalView').default
}
}, {
defaultNavigationOptions: defaultHeader
});
const OAuthStack = createStackNavigator({
OAuthView
OAuthView: {
getScreen: () => require('./views/OAuthView').default
}
}, {
defaultNavigationOptions: defaultHeader
});
@ -107,19 +81,45 @@ const OutsideStackModal = createStackNavigator({
// Inside
const ChatsStack = createStackNavigator({
RoomsListView,
RoomView,
RoomActionsView,
RoomInfoView,
RoomInfoEditView,
RoomMembersView,
SearchMessagesView,
SelectedUsersView,
ThreadMessagesView,
MessagesView,
AutoTranslateView,
ReadReceiptsView,
DirectoryView
RoomsListView: {
getScreen: () => require('./views/RoomsListView').default
},
RoomView: {
getScreen: () => require('./views/RoomView').default
},
RoomActionsView: {
getScreen: () => require('./views/RoomActionsView').default
},
RoomInfoView: {
getScreen: () => require('./views/RoomInfoView').default
},
RoomInfoEditView: {
getScreen: () => require('./views/RoomInfoEditView').default
},
RoomMembersView: {
getScreen: () => require('./views/RoomMembersView').default
},
SearchMessagesView: {
getScreen: () => require('./views/SearchMessagesView').default
},
SelectedUsersView: {
getScreen: () => require('./views/SelectedUsersView').default
},
ThreadMessagesView: {
getScreen: () => require('./views/ThreadMessagesView').default
},
MessagesView: {
getScreen: () => require('./views/MessagesView').default
},
AutoTranslateView: {
getScreen: () => require('./views/AutoTranslateView').default
},
ReadReceiptsView: {
getScreen: () => require('./views/ReadReceiptView').default
},
DirectoryView: {
getScreen: () => require('./views/DirectoryView').default
}
}, {
defaultNavigationOptions: defaultHeader
});
@ -135,7 +135,9 @@ ChatsStack.navigationOptions = ({ navigation }) => {
};
const ProfileStack = createStackNavigator({
ProfileView
ProfileView: {
getScreen: () => require('./views/ProfileView').default
}
}, {
defaultNavigationOptions: defaultHeader
});
@ -151,14 +153,20 @@ ProfileStack.navigationOptions = ({ navigation }) => {
};
const SettingsStack = createStackNavigator({
SettingsView,
LanguageView
SettingsView: {
getScreen: () => require('./views/SettingsView').default
},
LanguageView: {
getScreen: () => require('./views/LanguageView').default
}
}, {
defaultNavigationOptions: defaultHeader
});
const AdminPanelStack = createStackNavigator({
AdminPanelView
AdminPanelView: {
getScreen: () => require('./views/AdminPanelView').default
}
}, {
defaultNavigationOptions: defaultHeader
});
@ -183,9 +191,15 @@ const ChatsDrawer = createDrawerNavigator({
});
const NewMessageStack = createStackNavigator({
NewMessageView,
SelectedUsersViewCreateChannel: SelectedUsersView,
CreateChannelView
NewMessageView: {
getScreen: () => require('./views/NewMessageView').default
},
SelectedUsersViewCreateChannel: {
getScreen: () => require('./views/SelectedUsersView').default
},
CreateChannelView: {
getScreen: () => require('./views/CreateChannelView').default
}
}, {
defaultNavigationOptions: defaultHeader
});
@ -200,7 +214,9 @@ const InsideStackModal = createStackNavigator({
});
const SetUsernameStack = createStackNavigator({
SetUsernameView
SetUsernameView: {
getScreen: () => require('./views/SetUsernameView').default
}
});
class CustomInsideStack extends React.Component {
@ -216,6 +232,7 @@ class CustomInsideStack extends React.Component {
<React.Fragment>
<InsideStackModal navigation={navigation} />
<NotificationBadge navigation={navigation} />
<Toast />
</React.Fragment>
);
}
@ -225,7 +242,9 @@ const App = createAppContainer(createSwitchNavigator(
{
OutsideStack: OutsideStackModal,
InsideStack: CustomInsideStack,
AuthLoading: AuthLoadingView,
AuthLoading: {
getScreen: () => require('./views/AuthLoadingView').default
},
SetUsernameStack
},
{
@ -233,28 +252,6 @@ const App = createAppContainer(createSwitchNavigator(
}
));
// gets the current screen from navigation state
const getActiveRouteName = (navigationState) => {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return getActiveRouteName(route);
}
return route.routeName;
};
const onNavigationStateChange = (prevState, currentState) => {
const currentScreen = getActiveRouteName(currentState);
const prevScreen = getActiveRouteName(prevState);
if (prevScreen !== currentScreen) {
firebase.analytics().setCurrentScreen(currentScreen);
}
};
export default class Root extends React.Component {
constructor(props) {
super(props);

View File

@ -0,0 +1,21 @@
import { NavigationActions } from 'react-navigation';
let _shareNavigator;
function setTopLevelNavigator(navigatorRef) {
_shareNavigator = navigatorRef;
}
function navigate(routeName, params) {
_shareNavigator.dispatch(
NavigationActions.navigate({
routeName,
params
})
);
}
export default {
navigate,
setTopLevelNavigator
};

View File

@ -40,6 +40,15 @@ export default async function() {
if (setting._id === 'Site_Name') {
updateServer.call(this, { name: setting.valueAsString });
}
if (setting._id === 'UI_Use_Real_Name') {
updateServer.call(this, { useRealName: setting.valueAsBoolean });
}
if (setting._id === 'FileUpload_MediaTypeWhiteList') {
updateServer.call(this, { FileUpload_MediaTypeWhiteList: setting.valueAsString });
}
if (setting._id === 'FileUpload_MaxFileSize') {
updateServer.call(this, { FileUpload_MaxFileSize: setting.valueAsNumber });
}
})
)
);

View File

@ -1,4 +1,3 @@
import reduxStore from '../createStore';
import database from '../realm';
import log from '../../utils/log';
@ -23,11 +22,12 @@ export function cancelUpload(path) {
}
}
export function sendFileMessage(rid, fileInfo, tmid) {
export function sendFileMessage(rid, fileInfo, tmid, server, user) {
return new Promise((resolve, reject) => {
try {
const { FileUpload_MaxFileSize, Site_Url } = reduxStore.getState().settings;
const { id, token } = reduxStore.getState().login.user;
const { serversDB } = database.databases;
const { FileUpload_MaxFileSize, id: Site_Url } = serversDB.objectForPrimaryKey('servers', server);
const { id, token } = user;
// -1 maxFileSize means there is no limit
if (FileUpload_MaxFileSize > -1 && fileInfo.size > FileUpload_MaxFileSize) {

View File

@ -1,12 +1,12 @@
import messagesStatus from '../../constants/messagesStatus';
import buildMessage from './helpers/buildMessage';
import database from '../realm';
import reduxStore from '../createStore';
import log from '../../utils/log';
import random from '../../utils/random';
export const getMessage = (rid, msg = '', tmid) => {
export const getMessage = (rid, msg = '', tmid, user) => {
const _id = random(17);
const { id, username } = user;
const message = {
_id,
rid,
@ -16,8 +16,8 @@ export const getMessage = (rid, msg = '', tmid) => {
_updatedAt: new Date(),
status: messagesStatus.TEMP,
u: {
_id: reduxStore.getState().login.user.id || '1',
username: reduxStore.getState().login.user.username
_id: id || '1',
username
}
};
try {
@ -43,9 +43,9 @@ export async function sendMessageCall(message) {
return data;
}
export default async function(rid, msg, tmid) {
export default async function(rid, msg, tmid, user) {
try {
const message = getMessage(rid, msg, tmid);
const message = getMessage(rid, msg, tmid, user);
const [room] = database.objects('subscriptions').filtered('rid == $0', rid);
if (room) {

View File

@ -1,4 +1,5 @@
import Realm from 'realm';
import RNRealmPath from 'react-native-realm-path';
// import { AsyncStorage } from 'react-native';
// Realm.clearTestState();
@ -25,6 +26,9 @@ const serversSchema = {
id: 'string',
name: { type: 'string', optional: true },
iconURL: { type: 'string', optional: true },
useRealName: { type: 'bool', optional: true },
FileUpload_MediaTypeWhiteList: { type: 'string', optional: true },
FileUpload_MaxFileSize: { type: 'int', optional: true },
roomsUpdatedAt: { type: 'date', optional: true },
version: 'string?'
}
@ -408,12 +412,12 @@ const inMemorySchema = [usersTypingSchema, activeUsersSchema];
class DB {
databases = {
serversDB: new Realm({
path: 'default.realm',
path: `${ RNRealmPath.realmPath }default.realm`,
schema: [
userSchema,
serversSchema
],
schemaVersion: 9,
schemaVersion: 10,
migration: (oldRealm, newRealm) => {
if (oldRealm.schemaVersion >= 1 && newRealm.schemaVersion <= 9) {
const newServers = newRealm.objects('servers');
@ -426,7 +430,7 @@ class DB {
}
}),
inMemoryDB: new Realm({
path: 'memory.realm',
path: `${ RNRealmPath.realmPath }memory.realm`,
schema: inMemorySchema,
schemaVersion: 2,
inMemory: true
@ -468,7 +472,7 @@ class DB {
setActiveDB(database = '') {
const path = database.replace(/(^\w+:|^)\/\//, '');
return this.databases.activeDB = new Realm({
path: `${ path }.realm`,
path: `${ RNRealmPath.realmPath }${ path }.realm`,
schema,
schemaVersion: 13,
migration: (oldRealm, newRealm) => {

View File

@ -14,6 +14,9 @@ import {
setUser, setLoginServices, loginRequest, loginFailure, logout
} from '../actions/login';
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
import {
shareSelectServer, shareSetUser
} from '../actions/share';
import subscribeRooms from './methods/subscriptions/rooms';
import subscribeRoom from './methods/subscriptions/room';
@ -217,6 +220,35 @@ const RocketChat = {
});
},
async shareExtensionInit(server) {
database.setActiveDB(server);
if (this.sdk) {
this.sdk.disconnect();
this.sdk = null;
}
// Use useSsl: false only if server url starts with http://
const useSsl = !/http:\/\//.test(server);
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
// set Server
const { serversDB } = database.databases;
reduxStore.dispatch(shareSelectServer(server));
// set User info
const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
const user = userId && serversDB.objectForPrimaryKey('user', userId);
reduxStore.dispatch(shareSetUser({
id: user.id,
token: user.token,
username: user.username
}));
await RocketChat.login({ resume: user.token });
},
register(credentials) {
// RC 0.50.0
return this.sdk.post('users.register', credentials, false);
@ -730,14 +762,14 @@ const RocketChat = {
return JSON.parse(useMarkdown);
},
async getSortPreferences() {
const prefs = await AsyncStorage.getItem(SORT_PREFS_KEY);
return JSON.parse(prefs);
const prefs = await RNUserDefaults.objectForKey(SORT_PREFS_KEY);
return prefs;
},
async saveSortPreference(param) {
try {
let prefs = await RocketChat.getSortPreferences();
prefs = { ...prefs, ...param };
return await AsyncStorage.setItem(SORT_PREFS_KEY, JSON.stringify(prefs));
return await RNUserDefaults.setObjectForKey(SORT_PREFS_KEY, prefs);
} catch (error) {
console.warn(error);
}

View File

@ -5,7 +5,9 @@ import PropTypes from 'prop-types';
import Avatar from '../../containers/Avatar';
import Touch from '../../utils/touch';
import RoomTypeIcon from '../../containers/RoomTypeIcon';
import styles from './styles';
import styles, { ROW_HEIGHT } from './styles';
export { ROW_HEIGHT };
const DirectoryItemLabel = React.memo(({ text }) => {
if (!text) {
@ -30,10 +32,10 @@ const DirectoryItem = ({
/>
<View style={styles.directoryItemTextContainer}>
<View style={styles.directoryItemTextTitle}>
<RoomTypeIcon type='c' />
<RoomTypeIcon type={type} />
<Text style={styles.directoryItemName} numberOfLines={1}>{title}</Text>
</View>
<Text style={styles.directoryItemUsername} numberOfLines={1}>{description}</Text>
{ description ? <Text style={styles.directoryItemUsername} numberOfLines={1}>{description}</Text> : null }
</View>
<DirectoryItemLabel text={rightLabel} />
</View>

View File

@ -0,0 +1,49 @@
import { StyleSheet } from 'react-native';
import { COLOR_WHITE } from '../../constants/colors';
import sharedStyles from '../../views/Styles';
export const ROW_HEIGHT = 54;
export default StyleSheet.create({
directoryItemButton: {
height: ROW_HEIGHT,
backgroundColor: COLOR_WHITE
},
directoryItemContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 15
},
directoryItemAvatar: {
marginRight: 12
},
directoryItemTextTitle: {
flexDirection: 'row',
alignItems: 'center'
},
directoryItemTextContainer: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center'
},
directoryItemName: {
flex: 1,
fontSize: 17,
...sharedStyles.textMedium,
...sharedStyles.textColorNormal
},
directoryItemUsername: {
fontSize: 14,
...sharedStyles.textRegular,
...sharedStyles.textColorDescription
},
directoryItemLabel: {
fontSize: 14,
paddingLeft: 10,
...sharedStyles.textRegular,
...sharedStyles.textColorDescription
}
});

View File

@ -1,5 +1,4 @@
import React from 'react';
import moment from 'moment';
import PropTypes from 'prop-types';
import { View, Text, Animated } from 'react-native';
import { RectButton, PanGestureHandler, State } from 'react-native-gesture-handler';
@ -12,6 +11,7 @@ import styles, {
import UnreadBadge from './UnreadBadge';
import TypeIcon from './TypeIcon';
import LastMessage from './LastMessage';
import { capitalize, formatDate } from '../../utils/room';
import { LeftActions, RightActions } from './Actions';
export { ROW_HEIGHT };
@ -203,19 +203,12 @@ export default class RoomItem extends React.Component {
}
}
formatDate = date => moment(date).calendar(null, {
lastDay: `[${ I18n.t('Yesterday') }]`,
sameDay: 'h:mm A',
lastWeek: 'dddd',
sameElse: 'MMM D'
})
render() {
const {
unread, userMentions, name, _updatedAt, alert, testID, type, avatarSize, baseUrl, userId, username, token, id, prid, showLastMessage, lastMessage, isRead, width, favorite
} = this.props;
const date = this.formatDate(_updatedAt);
const date = formatDate(_updatedAt);
let accessibilityLabel = name;
if (unread === 1) {
@ -275,7 +268,7 @@ export default class RoomItem extends React.Component {
<View style={styles.titleContainer}>
<TypeIcon type={type} id={id} prid={prid} />
<Text style={[styles.title, alert && styles.alert]} ellipsizeMode='tail' numberOfLines={1}>{ name }</Text>
{_updatedAt ? <Text style={[styles.date, alert && styles.updateAlert]} ellipsizeMode='tail' numberOfLines={1}>{ date }</Text> : null}
{_updatedAt ? <Text style={[styles.date, alert && styles.updateAlert]} ellipsizeMode='tail' numberOfLines={1}>{ capitalize(date) }</Text> : null}
</View>
<View style={styles.row}>
<LastMessage lastMessage={lastMessage} type={type} showLastMessage={showLastMessage} username={username} alert={alert} />

View File

@ -0,0 +1,53 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import FastImage from 'react-native-fast-image';
import { RectButton } from 'react-native-gesture-handler';
import log from '../../utils/log';
import Check from '../../containers/Check';
import styles, { ROW_HEIGHT } from './styles';
export { ROW_HEIGHT };
const ServerItem = React.memo(({
server, item, onPress, hasCheck
}) => (
<RectButton onPress={onPress} style={styles.serverItem} testID={`rooms-list-header-server-${ item.id }`}>
<View style={styles.serverItemContainer}>
{item.iconURL
? (
<FastImage
source={{
uri: item.iconURL,
priority: FastImage.priority.high
}}
defaultSource={{ uri: 'logo' }}
style={styles.serverIcon}
onError={() => log('err_loading_server_icon')}
/>
)
: (
<FastImage
source={{ uri: 'logo' }}
style={styles.serverIcon}
/>
)
}
<View style={styles.serverTextContainer}>
<Text style={styles.serverName}>{item.name || item.id}</Text>
<Text style={styles.serverUrl}>{item.id}</Text>
</View>
{item.id === server && hasCheck ? <Check /> : null}
</View>
</RectButton>
));
ServerItem.propTypes = {
onPress: PropTypes.func.isRequired,
item: PropTypes.object.isRequired,
hasCheck: PropTypes.bool,
server: PropTypes.string
};
export default ServerItem;

View File

@ -0,0 +1,39 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../../views/Styles';
import { COLOR_WHITE } from '../../constants/colors';
export const ROW_HEIGHT = 56;
export default StyleSheet.create({
serverItem: {
height: ROW_HEIGHT,
backgroundColor: COLOR_WHITE,
justifyContent: 'center'
},
serverItemContainer: {
flexDirection: 'row',
alignItems: 'center'
},
serverIcon: {
width: 44,
height: 44,
marginHorizontal: 15,
borderRadius: 4
},
serverTextContainer: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center'
},
serverName: {
fontSize: 18,
...sharedStyles.textColorNormal,
...sharedStyles.textSemibold
},
serverUrl: {
fontSize: 15,
...sharedStyles.textColorDescription,
...sharedStyles.textRegular
}
});

View File

@ -11,6 +11,7 @@ import app from './app';
import sortPreferences from './sortPreferences';
import notification from './notification';
import markdown from './markdown';
import share from './share';
export default combineReducers({
settings,
@ -24,5 +25,6 @@ export default combineReducers({
rooms,
sortPreferences,
notification,
markdown
markdown,
share
});

23
app/reducers/share.js Normal file
View File

@ -0,0 +1,23 @@
import { SHARE } from '../actions/actionsTypes';
const initialState = {
user: {},
server: ''
};
export default function share(state = initialState, action) {
switch (action.type) {
case SHARE.SELECT_SERVER:
return {
...state,
server: action.server
};
case SHARE.SET_USER:
return {
...state,
user: action.user
};
default:
return state;
}
}

View File

@ -2,12 +2,15 @@ import {
put, call, takeLatest, select, take, fork, cancel
} from 'redux-saga/effects';
import RNUserDefaults from 'rn-user-defaults';
import moment from 'moment';
import 'moment/min/locales';
import * as types from '../actions/actionsTypes';
import { appStart } from '../actions';
import { serverFinishAdd, selectServerRequest } from '../actions/server';
import { loginFailure, loginSuccess, setUser } from '../actions/login';
import { roomsRequest } from '../actions/rooms';
import { toMomentLocale } from '../utils/moment';
import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
import I18n from '../i18n';
@ -72,6 +75,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
yield fork(fetchUserPresence);
I18n.locale = user.language;
moment.locale(toMomentLocale(user.language));
const { serversDB } = database.databases;
serversDB.write(() => {
@ -132,6 +136,7 @@ const handleLogout = function* handleLogout() {
const handleSetUser = function handleSetUser({ user }) {
if (user && user.language) {
I18n.locale = user.language;
moment.locale(toMomentLocale(user.language));
}
};

98
app/share.js Normal file
View File

@ -0,0 +1,98 @@
import React from 'react';
import { View } from 'react-native';
import { createAppContainer, createStackNavigator, createSwitchNavigator } from 'react-navigation';
import { Provider } from 'react-redux';
import RNUserDefaults from 'rn-user-defaults';
import Navigation from './lib/ShareNavigation';
import store from './lib/createStore';
import sharedStyles from './views/Styles';
import { isNotch, isIOS } from './utils/deviceInfo';
import { defaultHeader, onNavigationStateChange } from './utils/navigation';
import RocketChat from './lib/rocketchat';
const InsideNavigator = createStackNavigator({
ShareListView: {
getScreen: () => require('./views/ShareListView').default
},
ShareView: {
getScreen: () => require('./views/ShareView').default
},
SelectServerView: {
getScreen: () => require('./views/SelectServerView').default
}
}, {
initialRouteName: 'ShareListView',
defaultNavigationOptions: defaultHeader
});
const OutsideNavigator = createStackNavigator({
WithoutServersView: {
getScreen: () => require('./views/WithoutServersView').default
}
}, {
initialRouteName: 'WithoutServersView',
defaultNavigationOptions: defaultHeader
});
const AppContainer = createAppContainer(createSwitchNavigator({
OutsideStack: OutsideNavigator,
InsideStack: InsideNavigator,
AuthLoading: {
getScreen: () => require('./views/AuthLoadingView').default
}
},
{
initialRouteName: 'AuthLoading'
}));
class Root extends React.Component {
constructor(props) {
super(props);
this.state = {
isLandscape: false
};
this.init();
}
init = async() => {
if (isIOS) {
await RNUserDefaults.setName('group.ios.chat.rocket');
}
const currentServer = await RNUserDefaults.get('currentServer');
const token = await RNUserDefaults.get(RocketChat.TOKEN_KEY);
if (currentServer && token) {
await Navigation.navigate('InsideStack');
await RocketChat.shareExtensionInit(currentServer);
} else {
await Navigation.navigate('OutsideStack');
}
}
handleLayout = (event) => {
const { width, height } = event.nativeEvent.layout;
this.setState({ isLandscape: width > height });
}
render() {
const { isLandscape } = this.state;
return (
<View
style={[sharedStyles.container, isLandscape && isNotch ? sharedStyles.notchLandscapeContainer : {}]}
onLayout={this.handleLayout}
>
<Provider store={store}>
<AppContainer
ref={(navigatorRef) => {
Navigation.setTopLevelNavigator(navigatorRef);
}}
onNavigationStateChange={onNavigationStateChange}
/>
</Provider>
</View>
);
}
}
export default Root;

View File

@ -1,32 +1,3 @@
import React from 'react';
import { Alert, StyleSheet } from 'react-native';
import EasyToast from 'react-native-easy-toast';
import { Alert } from 'react-native';
import { COLOR_TOAST, COLOR_WHITE } from '../constants/colors';
import { isNotch } from './deviceInfo';
import sharedStyles from '../views/Styles';
const styles = StyleSheet.create({
toast: {
backgroundColor: COLOR_TOAST
},
text: {
...sharedStyles.textRegular,
color: COLOR_WHITE,
fontSize: 14
}
});
const positionValue = isNotch ? 230 : 200;
export const Toast = React.forwardRef((props, ref) => (
<EasyToast
{...props}
ref={ref}
positionValue={positionValue}
style={styles.toast}
textStyle={styles.text}
opacity={0.8}
/>
));
export const showErrorAlert = (message, title, onPress = () => {}) => Alert.alert(title, message, [{ text: 'OK', onPress }], { cancelable: true });

23
app/utils/media.js Normal file
View File

@ -0,0 +1,23 @@
export const canUploadFile = (file, serverInfo) => {
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = serverInfo;
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;
};

11
app/utils/moment.js Normal file
View File

@ -0,0 +1,11 @@
const localeKeys = {
en: 'en',
ru: 'ru',
'pt-BR': 'pt-br',
'zh-CN': 'zh-cn',
fr: 'fr',
de: 'de',
'pt-PT': 'pt'
};
export const toMomentLocale = locale => localeKeys[locale];

36
app/utils/navigation.js Normal file
View File

@ -0,0 +1,36 @@
import firebase from 'react-native-firebase';
import { HEADER_BACKGROUND, HEADER_TITLE, HEADER_BACK } from '../constants/colors';
export const defaultHeader = {
headerStyle: {
backgroundColor: HEADER_BACKGROUND
},
headerTitleStyle: {
color: HEADER_TITLE
},
headerBackTitle: null,
headerTintColor: HEADER_BACK
};
// gets the current screen from navigation state
export const getActiveRouteName = (navigationState) => {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return getActiveRouteName(route);
}
return route.routeName;
};
export const onNavigationStateChange = (prevState, currentState) => {
const currentScreen = getActiveRouteName(currentState);
const prevScreen = getActiveRouteName(prevState);
if (prevScreen !== currentScreen) {
firebase.analytics().setCurrentScreen(currentScreen);
}
};

36
app/utils/room.js Normal file
View File

@ -0,0 +1,36 @@
import moment from 'moment';
import I18n from '../i18n';
export const isOwner = room => room && room.roles && room.roles.length && !!room.roles.find(role => role === 'owner');
export const isMuted = (room, user) => room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username);
export const isReadOnly = (room, user) => {
if (isOwner(room)) {
return false;
}
return (room && room.ro) || isMuted(room, user);
};
export const isBlocked = (room) => {
if (room) {
const { t, blocked, blocker } = room;
if (t === 'd' && (blocked || blocker)) {
return true;
}
}
return false;
};
export const capitalize = (s) => {
if (typeof s !== 'string') { return ''; }
return s.charAt(0).toUpperCase() + s.slice(1);
};
export const formatDate = date => moment(date).calendar(null, {
lastDay: `[${ I18n.t('Yesterday') }]`,
sameDay: 'LT',
lastWeek: 'dddd',
sameElse: 'MMM D'
});

View File

@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import RocketChat from '../../lib/rocketchat';
import DirectoryItem from './DirectoryItem';
import DirectoryItem from '../../presentation/DirectoryItem';
import sharedStyles from '../Styles';
import I18n from '../../i18n';
import Touch from '../../utils/touch';

View File

@ -98,46 +98,6 @@ export default StyleSheet.create({
marginHorizontal: 15,
flex: 1
},
directoryItemButton: {
height: 54,
backgroundColor: COLOR_WHITE
},
directoryItemContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 15
},
directoryItemAvatar: {
marginRight: 12
},
directoryItemTextTitle: {
flexDirection: 'row',
alignItems: 'center'
},
directoryItemTextContainer: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center'
},
directoryItemName: {
flex: 1,
fontSize: 17,
...sharedStyles.textMedium,
...sharedStyles.textColorNormal
},
directoryItemUsername: {
fontSize: 14,
...sharedStyles.textRegular,
...sharedStyles.textColorDescription
},
directoryItemLabel: {
fontSize: 14,
paddingLeft: 10,
...sharedStyles.textRegular,
...sharedStyles.textColorDescription
},
inverted: {
transform: [{ scaleY: -1 }]
},

View File

@ -13,7 +13,9 @@ import KeyboardView from '../../presentation/KeyboardView';
import sharedStyles from '../Styles';
import styles from './styles';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { showErrorAlert, Toast } from '../../utils/info';
import { showErrorAlert } from '../../utils/info';
import { LISTENER } from '../../containers/Toast';
import EventEmitter from '../../utils/events';
import RocketChat from '../../lib/rocketchat';
import RCTextInput from '../../containers/TextInput';
import log from '../../utils/log';
@ -222,7 +224,7 @@ export default class ProfileView extends React.Component {
setUser({ ...params });
}
this.setState({ saving: false, showPasswordAlert: false });
this.toast.show(I18n.t('Profile_saved_successfully'));
EventEmitter.emit(LISTENER, { message: I18n.t('Profile_saved_successfully') });
this.init();
}
} catch (e) {
@ -235,7 +237,7 @@ export default class ProfileView extends React.Component {
try {
const { user } = this.props;
await RocketChat.resetAvatar(user.id);
this.toast.show(I18n.t('Avatar_changed_successfully'));
EventEmitter.emit(LISTENER, { message: I18n.t('Avatar_changed_successfully') });
this.init();
} catch (e) {
this.handleError(e, 'resetAvatar', 'changing_avatar');
@ -386,7 +388,6 @@ export default class ProfileView extends React.Component {
keyboardVerticalOffset={128}
>
<StatusBar />
<Toast ref={toast => this.toast = toast} />
<ScrollView
contentContainerStyle={sharedStyles.containerScrollView}
testID='profile-view-list'

View File

@ -12,7 +12,9 @@ import KeyboardView from '../../presentation/KeyboardView';
import sharedStyles from '../Styles';
import styles from './styles';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { showErrorAlert, Toast } from '../../utils/info';
import { showErrorAlert } from '../../utils/info';
import { LISTENER } from '../../containers/Toast';
import EventEmitter from '../../utils/events';
import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat';
import RCTextInput from '../../containers/TextInput';
@ -215,7 +217,7 @@ export default class RoomInfoEditView extends React.Component {
if (error) {
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t('saving_settings') }));
} else {
this.toast.show(I18n.t('Settings_succesfully_changed'));
EventEmitter.emit(LISTENER, { message: I18n.t('Settings_succesfully_changed') });
}
}, 100);
}
@ -428,7 +430,6 @@ export default class RoomInfoEditView extends React.Component {
<Text style={[sharedStyles.button_inverted, styles.colorDanger]} accessibilityTraits='button'>{I18n.t('DELETE')}</Text>
</TouchableOpacity>
<Loading visible={saving} />
<Toast ref={toast => this.toast = toast} />
</SafeAreaView>
</ScrollView>
</KeyboardView>

View File

@ -12,7 +12,8 @@ import UserItem from '../../presentation/UserItem';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import RocketChat from '../../lib/rocketchat';
import database, { safeAddListener } from '../../lib/realm';
import { Toast } from '../../utils/info';
import { LISTENER } from '../../containers/Toast';
import EventEmitter from '../../utils/events';
import log from '../../utils/log';
import I18n from '../../i18n';
import SearchBox from '../../containers/SearchBox';
@ -232,7 +233,7 @@ export default class RoomMembersView extends React.Component {
const { rid, userLongPressed } = this.state;
try {
await RocketChat.toggleMuteUserInRoom(rid, userLongPressed.username, !userLongPressed.muted);
this.toast.show(I18n.t('User_has_been_key', { key: userLongPressed.muted ? I18n.t('unmuted') : I18n.t('muted') }));
EventEmitter.emit(LISTENER, { message: I18n.t('User_has_been_key', { key: userLongPressed.muted ? I18n.t('unmuted') : I18n.t('muted') }) });
} catch (e) {
log('err_handle_mute', e);
}
@ -299,7 +300,6 @@ export default class RoomMembersView extends React.Component {
windowSize={10}
{...scrollPersistTaps}
/>
<Toast ref={toast => this.toast = toast} />
</SafeAreaView>
);
}

View File

@ -64,7 +64,13 @@ const styles = StyleSheet.create({
export default class UploadProgress extends Component {
static propTypes = {
window: PropTypes.object,
rid: PropTypes.string
rid: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
token: PropTypes.string.isRequired
}),
baseUrl: PropTypes.string.isRequired
}
constructor(props) {
@ -124,13 +130,13 @@ export default class UploadProgress extends Component {
}
tryAgain = async(item) => {
const { rid } = this.props;
const { rid, baseUrl: server, user } = this.props;
try {
database.write(() => {
item.error = false;
});
await RocketChat.sendFileMessage(rid, item);
await RocketChat.sendFileMessage(rid, item, undefined, server, user);
} catch (e) {
log('err_upload_progress_try_again', e);
}

View File

@ -41,7 +41,8 @@ import debounce from '../../utils/debounce';
import buildMessage from '../../lib/methods/helpers/buildMessage';
import FileModal from '../../containers/FileModal';
import ReactionsModal from '../../containers/ReactionsModal';
import { Toast } from '../../utils/info';
import { LISTENER } from '../../containers/Toast';
import { isReadOnly, isBlocked } from '../../utils/room';
@connect(state => ({
user: {
@ -409,8 +410,9 @@ export default class RoomView extends React.Component {
}
sendMessage = (message, tmid) => {
const { user } = this.props;
LayoutAnimation.easeInEaseOut();
RocketChat.sendMessage(this.rid, message, this.tmid || tmid).then(() => {
RocketChat.sendMessage(this.rid, message, this.tmid || tmid, user).then(() => {
this.setLastOpen(null);
});
};
@ -455,37 +457,6 @@ export default class RoomView extends React.Component {
}
};
isOwner = () => {
const { room } = this.state;
return room && room.roles && room.roles.length && !!room.roles.find(role => role === 'owner');
}
isMuted = () => {
const { room } = this.state;
const { user } = this.props;
return room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username);
}
isReadOnly = () => {
const { room } = this.state;
if (this.isOwner()) {
return false;
}
return (room && room.ro) || this.isMuted();
}
isBlocked = () => {
const { room } = this.state;
if (room) {
const { t, blocked, blocker } = room;
if (t === 'd' && (blocked || blocker)) {
return true;
}
}
return false;
}
// eslint-disable-next-line react/sort-comp
fetchThreadName = async(tmid) => {
try {
@ -502,7 +473,7 @@ export default class RoomView extends React.Component {
toggleFollowThread = async(isFollowingThread) => {
try {
await RocketChat.toggleFollowMessage(this.tmid, !isFollowingThread);
this.toast.show(isFollowingThread ? 'Unfollowed thread' : 'Following thread');
EventEmitter.emit(LISTENER, { message: isFollowingThread ? 'Unfollowed thread' : 'Following thread' });
} catch (e) {
log('err_toggle_follow_thread', e);
}
@ -576,7 +547,7 @@ export default class RoomView extends React.Component {
renderFooter = () => {
const { joined, room } = this.state;
const { navigation } = this.props;
const { navigation, user } = this.props;
if (!joined && !this.tmid) {
return (
@ -593,14 +564,14 @@ export default class RoomView extends React.Component {
</View>
);
}
if (this.isReadOnly()) {
if (isReadOnly(room, user)) {
return (
<View style={styles.readOnly}>
<Text style={styles.previewMode}>{I18n.t('This_room_is_read_only')}</Text>
</View>
);
}
if (this.isBlocked()) {
if (isBlocked(room)) {
return (
<View style={styles.readOnly}>
<Text style={styles.previewMode}>{I18n.t('This_room_is_blocked')}</Text>
@ -630,7 +601,7 @@ export default class RoomView extends React.Component {
return (
<React.Fragment>
{room._id && showActions
? <MessageActions room={room} tmid={this.tmid} user={user} toast={this.toast} />
? <MessageActions room={room} tmid={this.tmid} user={user} />
: null
}
{showErrorActions ? <MessageErrorActions /> : null}
@ -653,7 +624,7 @@ export default class RoomView extends React.Component {
{this.renderFooter()}
{this.renderActions()}
<ReactionPicker onEmojiSelected={this.onReactionPress} />
<UploadProgress rid={this.rid} />
<UploadProgress rid={this.rid} user={user} baseUrl={baseUrl} />
<FileModal
attachment={selectedAttachment}
isVisible={photoModalVisible}
@ -668,7 +639,6 @@ export default class RoomView extends React.Component {
user={user}
baseUrl={baseUrl}
/>
<Toast ref={toast => this.toast = toast} />
</SafeAreaView>
);
}

View File

@ -0,0 +1,108 @@
import React from 'react';
import {
FlatList, StyleSheet, View
} from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import I18n from '../i18n';
import database from '../lib/realm';
import StatusBar from '../containers/StatusBar';
import { COLOR_BACKGROUND_CONTAINER } from '../constants/colors';
import Navigation from '../lib/ShareNavigation';
import ServerItem, { ROW_HEIGHT } from '../presentation/ServerItem';
import sharedStyles from './Styles';
import RocketChat from '../lib/rocketchat';
const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index });
const keyExtractor = item => item.id;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: COLOR_BACKGROUND_CONTAINER
},
list: {
marginVertical: 32,
...sharedStyles.separatorVertical
},
separator: {
...sharedStyles.separatorBottom,
marginLeft: 48
}
});
@connect(({ share }) => ({
server: share.server
}))
export default class SelectServerView extends React.Component {
static navigationOptions = () => ({
title: I18n.t('Select_Server')
})
static propTypes = {
server: PropTypes.string
}
constructor(props) {
super(props);
const { serversDB } = database.databases;
const servers = serversDB.objects('servers');
const filteredServers = servers.filter(server => server.roomsUpdatedAt);
this.state = {
servers: filteredServers
};
}
select = async(server) => {
const {
server: currentServer
} = this.props;
Navigation.navigate('ShareListView');
if (currentServer !== server) {
await RocketChat.shareExtensionInit(server);
}
}
renderItem = ({ item }) => {
const { server } = this.props;
return (
<ServerItem
server={server}
onPress={() => this.select(item.id)}
item={item}
hasCheck
/>
);
}
renderSeparator = () => <View style={styles.separator} />;
render() {
const { servers } = this.state;
return (
<SafeAreaView
style={styles.container}
forceInset={{ bottom: 'never' }}
>
<StatusBar />
<View style={styles.list}>
<FlatList
data={servers}
keyExtractor={keyExtractor}
renderItem={this.renderItem}
getItemLayout={getItemLayout}
ItemSeparatorComponent={this.renderSeparator}
enableEmptySections
removeClippedSubviews
keyboardShouldPersistTaps='always'
windowSize={7}
bounces={false}
/>
</View>
</SafeAreaView>
);
}
}

View File

@ -0,0 +1,52 @@
import React from 'react';
import {
View, StyleSheet, Text, TextInput
} from 'react-native';
import PropTypes from 'prop-types';
import I18n from '../../../i18n';
import { COLOR_WHITE, HEADER_TITLE } from '../../../constants/colors';
import sharedStyles from '../../Styles';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
},
search: {
fontSize: 20,
color: COLOR_WHITE,
...sharedStyles.textRegular,
marginHorizontal: 14
},
title: {
fontSize: 20,
...sharedStyles.textBold,
color: HEADER_TITLE,
marginHorizontal: 16
}
});
const Header = React.memo(({ searching, onChangeSearchText }) => {
if (searching) {
return (
<View style={styles.container}>
<TextInput
style={styles.search}
placeholder={I18n.t('Search')}
placeholderTextColor='rgba(255, 255, 255, 0.5)'
onChangeText={onChangeSearchText}
autoFocus
/>
</View>
);
}
return <Text style={styles.title}>{I18n.t('Send_to')}</Text>;
});
Header.propTypes = {
searching: PropTypes.bool,
onChangeSearchText: PropTypes.func
};
export default Header;

View File

@ -0,0 +1,76 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import {
Keyboard, LayoutAnimation, View, StyleSheet
} from 'react-native';
import ShareExtension from 'rn-extensions-share';
import SearchBox from '../../../containers/SearchBox';
import { CloseShareExtensionButton } from '../../../containers/HeaderButton';
import { HEADER_BACKGROUND } from '../../../constants/colors';
import sharedStyles from '../../Styles';
const styles = StyleSheet.create({
container: {
backgroundColor: HEADER_BACKGROUND,
flexDirection: 'row',
...sharedStyles.separatorBottom
}
});
const Header = React.memo(({
searching, onChangeSearchText, initSearch, cancelSearch
}) => {
const [text, setText] = useState('');
const onChangeText = (searchText) => {
onChangeSearchText(searchText);
setText(searchText);
};
const onCancelPress = () => {
Keyboard.dismiss();
onChangeText('');
cancelSearch();
LayoutAnimation.easeInEaseOut();
};
const onFocus = () => {
initSearch();
LayoutAnimation.easeInEaseOut();
};
return (
<View style={styles.container}>
{
!searching
? (
<CloseShareExtensionButton
onPress={ShareExtension.close}
testID='share-extension-close'
/>
)
: null
}
<SearchBox
value={text}
hasCancel={searching}
onFocus={onFocus}
onCancelPress={onCancelPress}
onChangeText={onChangeText}
testID='rooms-list-view-search'
key='rooms-list-view-search'
/>
</View>
);
});
Header.propTypes = {
searching: PropTypes.bool,
onChangeSearchText: PropTypes.func,
initSearch: PropTypes.func,
cancelSearch: PropTypes.func
};
export default Header;

View File

@ -0,0 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import Header from './Header';
const ShareListHeader = React.memo(({
searching, initSearch, cancelSearch, search
}) => {
const onSearchChangeText = (text) => {
search(text.trim());
};
return (
<Header
searching={searching}
initSearch={initSearch}
cancelSearch={cancelSearch}
onChangeSearchText={onSearchChangeText}
/>
);
});
ShareListHeader.propTypes = {
searching: PropTypes.bool,
initSearch: PropTypes.func,
cancelSearch: PropTypes.func,
search: PropTypes.func
};
export default ShareListHeader;

View File

@ -0,0 +1,417 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
View, Text, LayoutAnimation, FlatList, ActivityIndicator, Keyboard, BackHandler
} from 'react-native';
import { SafeAreaView } from 'react-navigation';
import ShareExtension from 'rn-extensions-share';
import { connect } from 'react-redux';
import RNFetchBlob from 'rn-fetch-blob';
import * as mime from 'react-native-mime-types';
import { isEqual } from 'lodash';
import Navigation from '../../lib/ShareNavigation';
import database from '../../lib/realm';
import { isIOS, isAndroid } from '../../utils/deviceInfo';
import I18n from '../../i18n';
import { CustomIcon } from '../../lib/Icons';
import log from '../../utils/log';
import { canUploadFile } from '../../utils/media';
import DirectoryItem, { ROW_HEIGHT } from '../../presentation/DirectoryItem';
import ServerItem from '../../presentation/ServerItem';
import { CloseShareExtensionButton, CustomHeaderButtons, Item } from '../../containers/HeaderButton';
import ShareListHeader from './Header';
import styles from './styles';
import StatusBar from '../../containers/StatusBar';
const LIMIT = 50;
const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index });
const keyExtractor = item => item.rid;
@connect(({ share }) => ({
userId: share.user && share.user.id,
token: share.user && share.user.token,
server: share.server,
baseUrl: share ? share.server : ''
}))
/** @extends React.Component */
export default class ShareListView extends React.Component {
static navigationOptions = ({ navigation }) => {
const searching = navigation.getParam('searching');
const initSearch = navigation.getParam('initSearch', () => {});
const cancelSearch = navigation.getParam('cancelSearch', () => {});
const search = navigation.getParam('search', () => {});
if (isIOS) {
return {
headerTitle: (
<ShareListHeader
searching={searching}
initSearch={initSearch}
cancelSearch={cancelSearch}
search={search}
/>
)
};
}
return {
headerBackTitle: null,
headerLeft: searching
? (
<CustomHeaderButtons left>
<Item title='cancel' iconName='cross' onPress={cancelSearch} />
</CustomHeaderButtons>
)
: (
<CloseShareExtensionButton
onPress={ShareExtension.close}
testID='share-extension-close'
/>
),
headerTitle: <ShareListHeader searching={searching} search={search} />,
headerRight: (
searching
? null
: (
<CustomHeaderButtons>
{isAndroid ? <Item title='search' iconName='magnifier' onPress={initSearch} /> : null}
</CustomHeaderButtons>
)
)
};
}
static propTypes = {
navigation: PropTypes.object,
server: PropTypes.string,
baseUrl: PropTypes.string,
token: PropTypes.string,
userId: PropTypes.string
}
constructor(props) {
super(props);
this.data = [];
this.state = {
showError: false,
searching: false,
searchText: '',
value: '',
isMedia: false,
mediaLoading: false,
fileInfo: null,
searchResults: [],
chats: [],
servers: [],
loading: true,
serverInfo: null
};
this.didFocusListener = props.navigation.addListener('didFocus', () => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress));
this.willBlurListener = props.navigation.addListener('willBlur', () => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress));
}
async componentDidMount() {
const { navigation, server } = this.props;
navigation.setParams({
initSearch: this.initSearch,
cancelSearch: this.cancelSearch,
search: this.search
});
try {
const { value, type } = await ShareExtension.data();
let fileInfo = null;
const isMedia = (type === 'media');
if (isMedia) {
this.setState({ mediaLoading: true });
const data = await RNFetchBlob.fs.stat(this.uriToPath(value));
fileInfo = {
name: data.filename,
description: '',
size: data.size,
mime: mime.lookup(data.path),
store: 'Uploads',
path: isIOS ? data.path : `file://${ data.path }`
};
}
this.setState({
value, fileInfo, isMedia, mediaLoading: false
});
} catch (e) {
log('err_process_media_share_extension', e);
this.setState({ mediaLoading: false });
}
this.getSubscriptions(server);
}
componentWillReceiveProps(nextProps) {
const { server } = this.props;
if (nextProps.server !== server) {
this.getSubscriptions(nextProps.server);
}
}
shouldComponentUpdate(nextProps, nextState) {
const { searching } = this.state;
if (nextState.searching !== searching) {
return true;
}
const { isMedia } = this.state;
if (nextState.isMedia !== isMedia) {
this.getSubscriptions(nextProps.server, nextState.fileInfo);
return true;
}
const { server } = this.props;
if (server !== nextProps.server) {
return true;
}
const { searchResults } = this.state;
if (!isEqual(nextState.searchResults, searchResults)) {
return true;
}
return false;
}
// eslint-disable-next-line react/sort-comp
internalSetState = (...args) => {
const { navigation } = this.props;
if (isIOS && navigation.isFocused()) {
LayoutAnimation.easeInEaseOut();
}
this.setState(...args);
}
getSubscriptions = (server, fileInfo) => {
const { fileInfo: fileData } = this.state;
const { serversDB } = database.databases;
if (server) {
this.data = database.objects('subscriptions').filtered('archived != true && open == true').sorted('roomUpdatedAt', true);
this.servers = serversDB.objects('servers');
this.chats = this.data.slice(0, LIMIT);
const serverInfo = serversDB.objectForPrimaryKey('servers', server);
this.internalSetState({
chats: this.chats ? this.chats.slice() : [],
servers: this.servers ? this.servers.slice() : [],
loading: false,
showError: !canUploadFile(fileInfo || fileData, serverInfo),
serverInfo
});
this.forceUpdate();
}
};
uriToPath = uri => decodeURIComponent(isIOS ? uri.replace(/^file:\/\//, '') : uri);
getRoomTitle = (item) => {
const { serverInfo } = this.state;
const { useRealName } = serverInfo;
return ((item.prid || useRealName) && item.fname) || item.name;
}
shareMessage = (item) => {
const { value, isMedia, fileInfo } = this.state;
const { navigation } = this.props;
navigation.navigate('ShareView', {
rid: item.rid,
value,
isMedia,
fileInfo,
name: this.getRoomTitle(item)
});
}
search = (text) => {
const result = database.objects('subscriptions').filtered('name CONTAINS[c] $0', text);
this.internalSetState({
searchResults: result.slice(0, LIMIT),
searchText: text
});
}
initSearch = () => {
const { chats } = this.state;
const { navigation } = this.props;
this.setState({ searching: true, searchResults: chats });
navigation.setParams({ searching: true });
}
cancelSearch = () => {
const { navigation } = this.props;
this.internalSetState({ searching: false, searchResults: [], searchText: '' });
navigation.setParams({ searching: false });
Keyboard.dismiss();
}
handleBackPress = () => {
const { searching } = this.state;
if (searching) {
this.cancelSearch();
return true;
}
return false;
}
renderSectionHeader = (header) => {
const { searching } = this.state;
if (searching) {
return null;
}
return (
<View style={styles.headerContainer}>
<Text style={styles.headerText}>
{I18n.t(header)}
</Text>
</View>
);
}
renderItem = ({ item }) => {
const { userId, token, baseUrl } = this.props;
return (
<DirectoryItem
user={{
userId,
token
}}
title={this.getRoomTitle(item)}
baseUrl={baseUrl}
avatar={this.getRoomTitle(item)}
description={
item.t === 'c'
? (item.topic || item.description)
: item.fname
}
type={item.t}
onPress={() => this.shareMessage(item)}
testID={`share-extension-item-${ item.name }`}
/>
);
}
renderSeparator = () => <View style={styles.separator} />;
renderBorderBottom = () => <View style={styles.borderBottom} />;
renderSelectServer = () => {
const { servers } = this.state;
const { server } = this.props;
const currentServer = servers.find(serverFiltered => serverFiltered.id === server);
return currentServer ? (
<React.Fragment>
{this.renderSectionHeader('Select_Server')}
<View style={styles.bordered}>
<ServerItem
server={server}
onPress={() => Navigation.navigate('SelectServerView')}
item={currentServer}
/>
</View>
</React.Fragment>
) : null;
}
renderEmptyComponent = () => (
<View style={[styles.container, styles.emptyContainer]}>
<Text style={styles.title}>{I18n.t('No_results_found')}</Text>
</View>
);
renderHeader = () => {
const { searching } = this.state;
return (
<React.Fragment>
{ !searching
? (
<React.Fragment>
{this.renderSelectServer()}
{this.renderSectionHeader('Chats')}
</React.Fragment>
)
: null
}
</React.Fragment>
);
}
renderContent = () => {
const {
chats, mediaLoading, loading, searchResults, searching, searchText
} = this.state;
if (mediaLoading || loading) {
return <ActivityIndicator style={styles.loading} />;
}
return (
<FlatList
data={searching ? searchResults : chats}
keyExtractor={keyExtractor}
style={styles.flatlist}
renderItem={this.renderItem}
getItemLayout={getItemLayout}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={!searching && this.renderBorderBottom}
ListHeaderComponentStyle={!searching ? styles.borderBottom : {}}
ListEmptyComponent={searching && searchText ? this.renderEmptyComponent : null}
enableEmptySections
removeClippedSubviews
keyboardShouldPersistTaps='always'
initialNumToRender={12}
windowSize={20}
/>
);
}
renderError = () => {
const {
fileInfo: file, loading, searching, serverInfo
} = this.state;
const { FileUpload_MaxFileSize } = serverInfo;
const errorMessage = (FileUpload_MaxFileSize < file.size)
? 'error-file-too-large'
: 'error-invalid-file-type';
if (loading) {
return <ActivityIndicator style={styles.loading} />;
}
return (
<View style={styles.container}>
{ !searching
? (
<React.Fragment>
{this.renderSelectServer()}
</React.Fragment>
)
: null
}
<View style={[styles.container, styles.centered]}>
<Text style={styles.title}>{I18n.t(errorMessage)}</Text>
<CustomIcon name='circle-cross' size={120} style={styles.errorIcon} />
<Text style={styles.fileMime}>{ file.mime }</Text>
</View>
</View>
);
}
render() {
const { showError } = this.state;
return (
<SafeAreaView style={styles.container} forceInset={{ bottom: 'never' }}>
<StatusBar />
{ showError ? this.renderError() : this.renderContent() }
</SafeAreaView>
);
}
}

View File

@ -0,0 +1,74 @@
import { StyleSheet } from 'react-native';
import { isIOS } from '../../utils/deviceInfo';
import sharedStyles from '../Styles';
import {
COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_DANGER
} from '../../constants/colors';
export default StyleSheet.create({
container: {
flex: 1,
backgroundColor: COLOR_BACKGROUND_CONTAINER
},
emptyContainer: {
padding: 20,
justifyContent: 'center',
alignItems: 'center'
},
content: {
flex: 1,
backgroundColor: isIOS ? COLOR_WHITE : '#E1E5E8',
justifyContent: 'center',
alignItems: 'center'
},
centered: {
justifyContent: 'center',
alignItems: 'center'
},
flatlist: {
marginTop: isIOS ? 6 : 0, // the height of the navigation bar with the searchbar is larger
width: '100%',
backgroundColor: COLOR_BACKGROUND_CONTAINER
},
bordered: {
...sharedStyles.separatorVertical
},
borderBottom: {
...sharedStyles.separatorBottom
},
headerContainer: {
paddingHorizontal: 15,
backgroundColor: COLOR_BACKGROUND_CONTAINER,
paddingBottom: 10,
paddingTop: 17
},
headerText: {
...sharedStyles.textColorNormal,
...sharedStyles.textRegular,
fontSize: 17,
letterSpacing: 0.27
},
separator: {
...sharedStyles.separatorBottom,
marginLeft: 48
},
loading: {
flex: 1
},
errorIcon: {
color: COLOR_DANGER
},
fileMime: {
...sharedStyles.textColorNormal,
...sharedStyles.textBold,
...sharedStyles.textAlignCenter,
fontSize: 20,
marginBottom: 20
},
title: {
fontSize: 14,
...sharedStyles.textColorTitle,
...sharedStyles.textBold
}
});

View File

@ -0,0 +1,23 @@
import React from 'react';
import {
StyleSheet, ActivityIndicator, View
} from 'react-native';
import { COLOR_TEXT } from '../../constants/colors';
const styles = StyleSheet.create({
container: {
height: '100%',
width: '100%',
position: 'absolute',
justifyContent: 'center',
alignItems: 'center'
}
});
const Loading = React.memo(() => (
<View style={styles.container}>
<ActivityIndicator size='large' color={COLOR_TEXT} />
</View>
));
export default Loading;

View File

@ -0,0 +1,245 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
View, Text, TextInput, Image
} from 'react-native';
import { connect } from 'react-redux';
import ShareExtension from 'rn-extensions-share';
import {
COLOR_TEXT_DESCRIPTION
} from '../../constants/colors';
import I18n from '../../i18n';
import RocketChat from '../../lib/rocketchat';
import { CustomIcon } from '../../lib/Icons';
import log from '../../utils/log';
import styles from './styles';
import Loading from './Loading';
import database from '../../lib/realm';
import { CustomHeaderButtons, Item } from '../../containers/HeaderButton';
import { isReadOnly, isBlocked } from '../../utils/room';
@connect(({ share }) => ({
user: {
id: share.user && share.user.id,
username: share.user && share.user.username,
token: share.user && share.user.token
},
baseUrl: share ? share.server : ''
}))
export default class ShareView extends React.Component {
static navigationOptions = ({ navigation }) => {
const canSend = navigation.getParam('canSend', true);
return ({
title: I18n.t('Share'),
headerRight:
canSend
? (
<CustomHeaderButtons>
<Item
title={I18n.t('Send')}
onPress={navigation.getParam('sendMessage')}
testID='send-message-share-view'
buttonStyle={styles.send}
/>
</CustomHeaderButtons>
)
: null
});
}
static propTypes = {
navigation: PropTypes.object,
user: PropTypes.shape({
id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
token: PropTypes.string.isRequired
}),
baseUrl: PropTypes.string.isRequired
};
constructor(props) {
super(props);
const { navigation } = this.props;
const rid = navigation.getParam('rid', '');
const name = navigation.getParam('name', '');
const value = navigation.getParam('value', '');
const isMedia = navigation.getParam('isMedia', false);
const fileInfo = navigation.getParam('fileInfo', {});
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
this.state = {
rid,
value,
isMedia,
name,
fileInfo,
loading: false,
room: this.rooms[0] || { rid },
file: {
name: fileInfo ? fileInfo.name : '',
description: ''
}
};
}
componentDidMount() {
const { room } = this.state;
const { navigation, user } = this.props;
const { username } = user;
navigation.setParams({ sendMessage: this._sendMessage, canSend: !(isReadOnly(room, { username }) || isBlocked(room)) });
}
bytesToSize = bytes => `${ (bytes / 1048576).toFixed(2) }MB`;
_sendMessage = async() => {
const { isMedia } = this.state;
this.setState({ loading: true });
if (isMedia) {
await this.sendMediaMessage();
} else {
await this.sendTextMessage();
}
this.setState({ loading: false });
ShareExtension.close();
}
sendMediaMessage = async() => {
const { rid, fileInfo, file } = this.state;
const { baseUrl: server, user } = this.props;
const { name, description } = file;
const fileMessage = { ...fileInfo, name, description };
if (fileInfo && rid !== '') {
try {
await RocketChat.sendFileMessage(rid, fileMessage, undefined, server, user);
} catch (e) {
log('err_send_media_message', e);
}
}
}
sendTextMessage = async() => {
const { value, rid } = this.state;
const { user } = this.props;
if (value !== '' && rid !== '') {
try {
await RocketChat.sendMessage(rid, value, undefined, user);
} catch (error) {
log('err_share_extension_send_message', error);
}
}
};
renderPreview = () => {
const { fileInfo } = this.state;
const icon = fileInfo.mime.match(/image/)
? <Image source={{ isStatic: true, uri: fileInfo.path }} style={styles.mediaImage} />
: (
<View style={styles.mediaIconContainer}>
<CustomIcon name='file-generic' style={styles.mediaIcon} />
</View>
);
return (
<View style={styles.mediaContent}>
{icon}
<View style={styles.mediaInfo}>
<Text style={styles.mediaText} numberOfLines={1}>{fileInfo.name}</Text>
<Text style={styles.mediaText}>{this.bytesToSize(fileInfo.size)}</Text>
</View>
</View>
);
};
renderMediaContent = () => {
const { fileInfo, file } = this.state;
return fileInfo ? (
<View style={styles.mediaContainer}>
{this.renderPreview()}
<View style={styles.mediaInputContent}>
<TextInput
style={[styles.mediaNameInput, styles.input]}
placeholder={I18n.t('File_name')}
onChangeText={name => this.setState({ file: { ...file, name } })}
underlineColorAndroid='transparent'
defaultValue={file.name}
placeholderTextColor={COLOR_TEXT_DESCRIPTION}
/>
<TextInput
style={[styles.mediaDescriptionInput, styles.input]}
placeholder={I18n.t('File_description')}
onChangeText={description => this.setState({ file: { ...file, description } })}
underlineColorAndroid='transparent'
defaultValue={file.description}
multiline
textAlignVertical='top'
placeholderTextColor={COLOR_TEXT_DESCRIPTION}
autoFocus
/>
</View>
</View>
) : null;
};
renderInput = () => {
const { value } = this.state;
return (
<TextInput
style={[styles.input, styles.textInput]}
placeholder=''
onChangeText={handleText => this.setState({ value: handleText })}
underlineColorAndroid='transparent'
defaultValue={value}
multiline
textAlignVertical='top'
placeholderTextColor={COLOR_TEXT_DESCRIPTION}
autoFocus
/>
);
}
renderError = () => {
const { room } = this.state;
return (
<View style={[styles.container, styles.centered]}>
<Text style={styles.title}>
{
isBlocked(room) ? I18n.t('This_room_is_blocked') : I18n.t('This_room_is_read_only')
}
</Text>
</View>
);
}
render() {
const { user } = this.props;
const { username } = user;
const {
name, loading, isMedia, room
} = this.state;
if (isReadOnly(room, { username }) || isBlocked(room)) {
return this.renderError();
}
return (
<View style={styles.container}>
<View style={isMedia ? styles.toContent : styles.toContentText}>
<Text style={styles.text} numberOfLines={1}>
<Text style={styles.to}>{`${ I18n.t('To') }: `}</Text>
<Text style={styles.name}>{`${ name }`}</Text>
</Text>
</View>
<View style={styles.content}>
{isMedia ? this.renderMediaContent() : this.renderInput()}
</View>
{ loading ? <Loading /> : null }
</View>
);
}
}

View File

@ -0,0 +1,116 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
import {
COLOR_BACKGROUND_CONTAINER, COLOR_WHITE
} from '../../constants/colors';
export default StyleSheet.create({
container: {
flex: 1,
backgroundColor: COLOR_BACKGROUND_CONTAINER
},
centered: {
justifyContent: 'center',
alignItems: 'center'
},
title: {
fontSize: 18,
...sharedStyles.textBold,
...sharedStyles.textColorNormal,
...sharedStyles.textAlignCenter
},
text: {
paddingHorizontal: 16,
paddingVertical: 8,
...sharedStyles.textColorNormal,
...sharedStyles.textRegular
},
to: {
...sharedStyles.textColorDescription,
...sharedStyles.textRegular
},
toContent: {
width: '100%',
backgroundColor: COLOR_WHITE
},
toContentText: {
width: '100%',
backgroundColor: COLOR_BACKGROUND_CONTAINER,
...sharedStyles.textColorNormal,
...sharedStyles.textRegular
},
name: {
...sharedStyles.textRegular,
...sharedStyles.textColorTitle
},
content: {
flex: 1,
backgroundColor: COLOR_WHITE
},
mediaContainer: {
flex: 1,
backgroundColor: COLOR_BACKGROUND_CONTAINER
},
mediaContent: {
flexDirection: 'row',
padding: 16,
backgroundColor: COLOR_BACKGROUND_CONTAINER,
alignItems: 'center'
},
mediaImage: {
height: 64,
width: 64
},
mediaIcon: {
fontSize: 64,
...sharedStyles.textColorNormal
},
mediaIconContainer: {
alignItems: 'center',
justifyContent: 'center'
},
mediaInfo: {
marginLeft: 16,
flex: 1
},
mediaText: {
fontSize: 16,
...sharedStyles.textColorNormal,
...sharedStyles.textRegular
},
mediaInputContent: {
width: '100%',
...sharedStyles.separatorVertical,
backgroundColor: COLOR_WHITE
},
input: {
fontSize: 16,
...sharedStyles.textColorNormal,
...sharedStyles.textRegular,
backgroundColor: COLOR_WHITE
},
textInput: {
flex: 1,
paddingHorizontal: 16
},
mediaNameInput: {
marginLeft: 16,
paddingRight: 16,
paddingVertical: 8,
backgroundColor: COLOR_WHITE,
...sharedStyles.separatorBottom
},
mediaDescriptionInput: {
marginLeft: 16,
paddingRight: 16,
marginVertical: 8,
backgroundColor: COLOR_WHITE,
height: 100
},
send: {
...sharedStyles.textColorHeaderBack,
...sharedStyles.textSemibold,
fontSize: 16
}
});

View File

@ -1,7 +1,7 @@
import { StyleSheet, Platform } from 'react-native';
import {
COLOR_DANGER, COLOR_BUTTON_PRIMARY, COLOR_SEPARATOR, COLOR_TEXT, COLOR_TEXT_DESCRIPTION, COLOR_TITLE, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_PRIMARY
COLOR_DANGER, COLOR_BUTTON_PRIMARY, COLOR_SEPARATOR, COLOR_TEXT, COLOR_TEXT_DESCRIPTION, COLOR_TITLE, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_PRIMARY, HEADER_BACK
} from '../constants/colors';
export default StyleSheet.create({
@ -72,6 +72,9 @@ export default StyleSheet.create({
textAlignRight: {
textAlign: 'right'
},
textAlignCenter: {
textAlign: 'center'
},
opacity5: {
opacity: 0.5
},
@ -176,6 +179,9 @@ export default StyleSheet.create({
textColorDescription: {
color: COLOR_TEXT_DESCRIPTION
},
textColorHeaderBack: {
color: HEADER_BACK
},
colorPrimary: {
color: COLOR_PRIMARY
},
@ -192,5 +198,10 @@ export default StyleSheet.create({
borderBottomWidth: StyleSheet.hairlineWidth,
backgroundColor: COLOR_WHITE,
marginVertical: 10
},
notchLandscapeContainer: {
marginTop: -34,
paddingHorizontal: 30,
backgroundColor: COLOR_BACKGROUND_CONTAINER
}
});

View File

@ -0,0 +1,51 @@
import React from 'react';
import {
StyleSheet, View, Text
} from 'react-native';
import ShareExtension from 'rn-extensions-share';
import { CloseShareExtensionButton } from '../containers/HeaderButton';
import sharedStyles from './Styles';
import I18n from '../i18n';
import { COLOR_WHITE } from '../constants/colors';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: COLOR_WHITE,
justifyContent: 'center',
alignItems: 'center',
padding: 15
},
title: {
fontSize: 18,
...sharedStyles.textBold,
...sharedStyles.textColorNormal
},
content: {
fontSize: 14,
...sharedStyles.textAlignCenter,
...sharedStyles.textColorNormal,
...sharedStyles.textRegular
}
});
export default class WithoutServerView extends React.Component {
static navigationOptions = () => ({
headerLeft: (
<CloseShareExtensionButton
onPress={ShareExtension.close}
testID='share-extension-close'
/>
)
})
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>{I18n.t('Without_Servers')}</Text>
<Text style={styles.content}>{I18n.t('You_need_to_access_at_least_one_RocketChat_server_to_share_something')}</Text>
</View>
);
}
}

View File

@ -2,10 +2,10 @@ import 'react-native-console-time-polyfill';
import './app/ReactotronConfig';
import { AppRegistry } from 'react-native';
import App from './app/index';
import { name as appName } from './app.json';
import { name as appName, share as shareName } from './app.json';
AppRegistry.registerComponent(appName, () => App);
AppRegistry.registerComponent(appName, () => require('./app/index').default);
AppRegistry.registerComponent(shareName, () => require('./app/share').default);
// For storybook, comment everything above and uncomment below
// import './storybook';

View File

@ -27,12 +27,14 @@ target 'RocketChatRN' do
pod 'RNImageCropPicker', :path => '../node_modules/react-native-image-crop-picker'
pod 'RNDeviceInfo', :path => '../node_modules/react-native-device-info'
pod 'RNLocalize', :path => '../node_modules/react-native-localize'
pod 'RNScreens', :path => '../node_modules/react-native-screens'
pod 'react-native-splash-screen', :path => '../node_modules/react-native-splash-screen'
pod 'react-native-orientation-locker', :path => '../node_modules/react-native-orientation-locker'
pod 'react-native-realm-path', :path => '../node_modules/react-native-realm-path'
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
@ -45,11 +47,49 @@ target 'RocketChatRN' do
pod 'Crashlytics', '~> 3.12.0'
pod 'GoogleIDFASupport', '~> 3.14.0'
pod 'Firebase/Performance', '~> 5.20.1'
pod 'react-native-document-picker', :path => '../node_modules/react-native-document-picker'
use_unimodules!
end
target 'ShareRocketChatRN' do
rn_path = '../node_modules/react-native'
pod 'React', path: rn_path, subspecs: [
'Core',
'RCTActionSheet',
'RCTAnimation',
# 'RCTGeolocation',
'RCTImage',
'RCTLinkingIOS',
'RCTNetwork',
'RCTSettings',
'RCTText',
'RCTVibration',
'RCTWebSocket'
]
pod 'RNDeviceInfo', :path => '../node_modules/react-native-device-info'
pod 'RNLocalize', :path => '../node_modules/react-native-localize'
pod 'react-native-realm-path', :path => '../node_modules/react-native-realm-path'
pod 'rn-extensions-share', :path => '../node_modules/rn-extensions-share'
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
pod 'Firebase/Core', '~> 5.20.1'
pod 'Fabric', '~> 1.9.0'
pod 'Crashlytics', '~> 3.12.0'
pod 'GoogleIDFASupport', '~> 3.14.0'
pod 'Firebase/Performance', '~> 5.20.1'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == "React"

View File

@ -109,8 +109,12 @@ PODS:
- QBImagePickerController (3.4.0)
- React (0.59.8):
- React/Core (= 0.59.8)
- react-native-document-picker (3.2.2):
- React
- react-native-orientation-locker (1.1.5):
- React
- react-native-realm-path (1.2.11):
- React
- react-native-splash-screen (3.2.0):
- React
- react-native-webview (5.8.1):
@ -141,12 +145,16 @@ PODS:
- React/Core
- React/fishhook
- React/RCTBlob
- rn-extensions-share (2.3.10):
- React
- RNDeviceInfo (1.6.1):
- React
- RNImageCropPicker (0.21.1):
- RNImageCropPicker (0.24.1):
- QBImagePickerController
- React/Core
- RSKImageCropper
- RNLocalize (1.1.4):
- React
- RNScreens (1.0.0-alpha.22):
- React
- RSKImageCropper (2.2.1)
@ -182,7 +190,9 @@ DEPENDENCIES:
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- GoogleIDFASupport (~> 3.14.0)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
- react-native-realm-path (from `../node_modules/react-native-realm-path`)
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React/Core (from `../node_modules/react-native`)
@ -195,8 +205,10 @@ DEPENDENCIES:
- React/RCTText (from `../node_modules/react-native`)
- React/RCTVibration (from `../node_modules/react-native`)
- React/RCTWebSocket (from `../node_modules/react-native`)
- rn-extensions-share (from `../node_modules/rn-extensions-share`)
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
- RNLocalize (from `../node_modules/react-native-localize`)
- RNScreens (from `../node_modules/react-native-screens`)
- UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`)
- UMCameraInterface (from `../node_modules/unimodules-camera-interface/ios`)
@ -261,16 +273,24 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
React:
:path: "../node_modules/react-native"
react-native-document-picker:
:path: "../node_modules/react-native-document-picker"
react-native-orientation-locker:
:path: "../node_modules/react-native-orientation-locker"
react-native-realm-path:
:path: "../node_modules/react-native-realm-path"
react-native-splash-screen:
:path: "../node_modules/react-native-splash-screen"
react-native-webview:
:path: "../node_modules/react-native-webview"
rn-extensions-share:
:path: "../node_modules/rn-extensions-share"
RNDeviceInfo:
:path: "../node_modules/react-native-device-info"
RNImageCropPicker:
:path: "../node_modules/react-native-image-crop-picker"
RNLocalize:
:path: "../node_modules/react-native-localize"
RNScreens:
:path: "../node_modules/react-native-screens"
UMBarCodeScannerInterface:
@ -341,11 +361,15 @@ SPEC CHECKSUMS:
Protobuf: 7a877b7f3e5964e3fce995e2eb323dbc6831bb5a
QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022
React: 76e6aa2b87d05eb6cccb6926d72685c9a07df152
react-native-document-picker: 94a07ce0494c559e2ae9fa86621d6c624d810fec
react-native-orientation-locker: 132a63bab4dddd2a5709f6f7935ad9676b0af7c5
react-native-realm-path: 868473ea0bc4629850f1ec51a70d81055c06d091
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-webview: f3e28b48461c78db833f727feec08b13285e7b61
rn-extensions-share: 4bfee75806ad54aadeff1dfa535697a6345a50b8
RNDeviceInfo: 958a1ed6f94e04557b865b8ef848cfc83db0ebba
RNImageCropPicker: e608efe182652dc8690268cb99cb5a201f2b5ea3
RNImageCropPicker: 6134b66a3d5bc13e2895a97c630a4254006902b4
RNLocalize: 62a949d2ec5bee0eb8f39a80a48f01e2f4f67080
RNScreens: 720a9e6968beb73e8196239801e887d8401f86ed
RSKImageCropper: 98296ad26b41753f796b6898d015509598f13d97
UMBarCodeScannerInterface: d5602e23de37f95bb4ee49ee3b2711e128058ae9
@ -362,6 +386,6 @@ SPEC CHECKSUMS:
UMTaskManagerInterface: 296793ab2a7e181fe5ebe2ba9b40ae208ab4b8fa
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64
PODFILE CHECKSUM: b5e15bac5f306ea636e16393a7a6eb42c017ea99
PODFILE CHECKSUM: bfa056aa2707bd200eb8a39ada130c51b702380c
COCOAPODS: 1.6.2

View File

@ -0,0 +1 @@
../../../../../node_modules/react-native-localize/ios/RNLocalize.h

View File

@ -0,0 +1 @@
../../../../../node_modules/react-native-document-picker/ios/RNDocumentPicker/RNDocumentPicker.h

View File

@ -0,0 +1 @@
../../../../../node_modules/react-native-realm-path/ios/RNRealmPath.h

View File

@ -0,0 +1 @@
../../../../../node_modules/rn-extensions-share/ios/ReactNativeShareExtension.h

View File

@ -0,0 +1 @@
../../../../../node_modules/react-native-localize/ios/RNLocalize.h

View File

@ -0,0 +1 @@
../../../../../node_modules/react-native-document-picker/ios/RNDocumentPicker/RNDocumentPicker.h

View File

@ -0,0 +1 @@
../../../../../node_modules/react-native-realm-path/ios/RNRealmPath.h

View File

@ -0,0 +1 @@
../../../../../node_modules/rn-extensions-share/ios/ReactNativeShareExtension.h

View File

@ -1,6 +1,6 @@
{
"name": "RNImageCropPicker",
"version": "0.21.1",
"version": "0.24.1",
"summary": "Select single or multiple images, with cropping option",
"requires_arc": true,
"license": "MIT",

View File

@ -0,0 +1,22 @@
{
"name": "RNLocalize",
"dependencies": {
"React": [
]
},
"version": "1.1.4",
"license": "MIT",
"description": "A toolbox for your React Native app localization.",
"summary": "A toolbox for your React Native app localization.",
"authors": "Mathieu Acthernoene <zoontek@gmail.com>",
"homepage": "https://github.com/react-native-community/react-native-localize",
"platforms": {
"ios": "9.0"
},
"source": {
"git": "https://github.com/react-native-community/react-native-localize.git",
"tag": "1.1.4"
},
"source_files": "ios/*.{h,m}"
}

View File

@ -0,0 +1,22 @@
{
"name": "react-native-document-picker",
"version": "3.2.2",
"summary": "A react native interface to access Documents from dropbox google drive, iCloud",
"license": "MIT",
"homepage": "https://github.com/Elyx0/react-native-document-picker#readme",
"authors": {
"Elyx0": "elyx00@gmail.com"
},
"source": {
"git": "https://github.com/Elyx0/react-native-document-picker"
},
"source_files": "ios/RNDocumentPicker/*.{h,m}",
"platforms": {
"ios": "7.0"
},
"dependencies": {
"React": [
]
}
}

View File

@ -0,0 +1,21 @@
{
"name": "react-native-realm-path",
"version": "1.2.11",
"summary": "A helper to Realm Path on AppGroup iOS.",
"license": "MIT",
"authors": "Djorkaeff Alexandre",
"homepage": "https://github.com/rocketchat/react-native-realm-path",
"platforms": {
"ios": "10.0"
},
"source": {
"git": "https://github.com/RocketChat/react-native-realm-path.git",
"tag": "v1.2.11"
},
"source_files": "ios/**/*.{h,m}",
"dependencies": {
"React": [
]
}
}

View File

@ -0,0 +1,25 @@
{
"name": "rn-extensions-share",
"version": "2.3.10",
"summary": "Share-Extension using react-native for both ios and android",
"license": "MIT",
"authors": {
"name": "Djorkaeff Alexandre",
"email": "djorkaeffalexandre@gmail.com",
"url": "http://github.com/djorkaeffalexandre"
},
"homepage": "https://github.com/RocketChat/rn-extensions-share",
"platforms": {
"ios": "8.0"
},
"source": {
"git": "https://github.com/RocketChat/rn-extensions-share.git",
"tag": "master"
},
"source_files": "ios/*.{h,m}",
"dependencies": {
"React": [
]
}
}

30
ios/Pods/Manifest.lock generated
View File

@ -109,8 +109,12 @@ PODS:
- QBImagePickerController (3.4.0)
- React (0.59.8):
- React/Core (= 0.59.8)
- react-native-document-picker (3.2.2):
- React
- react-native-orientation-locker (1.1.5):
- React
- react-native-realm-path (1.2.11):
- React
- react-native-splash-screen (3.2.0):
- React
- react-native-webview (5.8.1):
@ -141,12 +145,16 @@ PODS:
- React/Core
- React/fishhook
- React/RCTBlob
- rn-extensions-share (2.3.10):
- React
- RNDeviceInfo (1.6.1):
- React
- RNImageCropPicker (0.21.1):
- RNImageCropPicker (0.24.1):
- QBImagePickerController
- React/Core
- RSKImageCropper
- RNLocalize (1.1.4):
- React
- RNScreens (1.0.0-alpha.22):
- React
- RSKImageCropper (2.2.1)
@ -182,7 +190,9 @@ DEPENDENCIES:
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- GoogleIDFASupport (~> 3.14.0)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
- react-native-realm-path (from `../node_modules/react-native-realm-path`)
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React/Core (from `../node_modules/react-native`)
@ -195,8 +205,10 @@ DEPENDENCIES:
- React/RCTText (from `../node_modules/react-native`)
- React/RCTVibration (from `../node_modules/react-native`)
- React/RCTWebSocket (from `../node_modules/react-native`)
- rn-extensions-share (from `../node_modules/rn-extensions-share`)
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
- RNLocalize (from `../node_modules/react-native-localize`)
- RNScreens (from `../node_modules/react-native-screens`)
- UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`)
- UMCameraInterface (from `../node_modules/unimodules-camera-interface/ios`)
@ -261,16 +273,24 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
React:
:path: "../node_modules/react-native"
react-native-document-picker:
:path: "../node_modules/react-native-document-picker"
react-native-orientation-locker:
:path: "../node_modules/react-native-orientation-locker"
react-native-realm-path:
:path: "../node_modules/react-native-realm-path"
react-native-splash-screen:
:path: "../node_modules/react-native-splash-screen"
react-native-webview:
:path: "../node_modules/react-native-webview"
rn-extensions-share:
:path: "../node_modules/rn-extensions-share"
RNDeviceInfo:
:path: "../node_modules/react-native-device-info"
RNImageCropPicker:
:path: "../node_modules/react-native-image-crop-picker"
RNLocalize:
:path: "../node_modules/react-native-localize"
RNScreens:
:path: "../node_modules/react-native-screens"
UMBarCodeScannerInterface:
@ -341,11 +361,15 @@ SPEC CHECKSUMS:
Protobuf: 7a877b7f3e5964e3fce995e2eb323dbc6831bb5a
QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022
React: 76e6aa2b87d05eb6cccb6926d72685c9a07df152
react-native-document-picker: 94a07ce0494c559e2ae9fa86621d6c624d810fec
react-native-orientation-locker: 132a63bab4dddd2a5709f6f7935ad9676b0af7c5
react-native-realm-path: 868473ea0bc4629850f1ec51a70d81055c06d091
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-webview: f3e28b48461c78db833f727feec08b13285e7b61
rn-extensions-share: 4bfee75806ad54aadeff1dfa535697a6345a50b8
RNDeviceInfo: 958a1ed6f94e04557b865b8ef848cfc83db0ebba
RNImageCropPicker: e608efe182652dc8690268cb99cb5a201f2b5ea3
RNImageCropPicker: 6134b66a3d5bc13e2895a97c630a4254006902b4
RNLocalize: 62a949d2ec5bee0eb8f39a80a48f01e2f4f67080
RNScreens: 720a9e6968beb73e8196239801e887d8401f86ed
RSKImageCropper: 98296ad26b41753f796b6898d015509598f13d97
UMBarCodeScannerInterface: d5602e23de37f95bb4ee49ee3b2711e128058ae9
@ -362,6 +386,6 @@ SPEC CHECKSUMS:
UMTaskManagerInterface: 296793ab2a7e181fe5ebe2ba9b40ae208ab4b8fa
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64
PODFILE CHECKSUM: b5e15bac5f306ea636e16393a7a6eb42c017ea99
PODFILE CHECKSUM: bfa056aa2707bd200eb8a39ada130c51b702380c
COCOAPODS: 1.6.2

File diff suppressed because it is too large Load Diff

View File

@ -1568,6 +1568,31 @@ redistribute it freely, subject to the following restrictions:
distribution.
## react-native-document-picker
MIT License
Copyright (c) 2016 Elyx0
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## react-native-orientation-locker
MIT License

View File

@ -1739,6 +1739,37 @@ redistribute it freely, subject to the following restrictions:
<key>FooterText</key>
<string>MIT License
Copyright (c) 2016 Elyx0
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>react-native-document-picker</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
Copyright (c) 2017 Wonday (@wonday.org)
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@ -1,8 +1,8 @@
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS" "${PODS_ROOT}/FirebaseABTesting/Frameworks" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/FirebasePerformance/Frameworks" "${PODS_ROOT}/FirebaseRemoteConfig/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/DoubleConversion" "${PODS_ROOT}/Headers/Public/EXAppLoaderProvider" "${PODS_ROOT}/Headers/Public/EXConstants" "${PODS_ROOT}/Headers/Public/EXFileSystem" "${PODS_ROOT}/Headers/Public/EXHaptics" "${PODS_ROOT}/Headers/Public/EXPermissions" "${PODS_ROOT}/Headers/Public/EXWebBrowser" "${PODS_ROOT}/Headers/Public/Firebase" "${PODS_ROOT}/Headers/Public/FirebaseCore" "${PODS_ROOT}/Headers/Public/FirebaseInstanceID" "${PODS_ROOT}/Headers/Public/GTMSessionFetcher" "${PODS_ROOT}/Headers/Public/GoogleToolboxForMac" "${PODS_ROOT}/Headers/Public/GoogleUtilities" "${PODS_ROOT}/Headers/Public/Protobuf" "${PODS_ROOT}/Headers/Public/QBImagePickerController" "${PODS_ROOT}/Headers/Public/RNDeviceInfo" "${PODS_ROOT}/Headers/Public/RNImageCropPicker" "${PODS_ROOT}/Headers/Public/RNScreens" "${PODS_ROOT}/Headers/Public/RSKImageCropper" "${PODS_ROOT}/Headers/Public/React" "${PODS_ROOT}/Headers/Public/UMBarCodeScannerInterface" "${PODS_ROOT}/Headers/Public/UMCameraInterface" "${PODS_ROOT}/Headers/Public/UMConstantsInterface" "${PODS_ROOT}/Headers/Public/UMCore" "${PODS_ROOT}/Headers/Public/UMFaceDetectorInterface" "${PODS_ROOT}/Headers/Public/UMFileSystemInterface" "${PODS_ROOT}/Headers/Public/UMFontInterface" "${PODS_ROOT}/Headers/Public/UMImageLoaderInterface" "${PODS_ROOT}/Headers/Public/UMPermissionsInterface" "${PODS_ROOT}/Headers/Public/UMReactNativeAdapter" "${PODS_ROOT}/Headers/Public/UMSensorsInterface" "${PODS_ROOT}/Headers/Public/UMTaskManagerInterface" "${PODS_ROOT}/Headers/Public/glog" "${PODS_ROOT}/Headers/Public/nanopb" "${PODS_ROOT}/Headers/Public/react-native-orientation-locker" "${PODS_ROOT}/Headers/Public/react-native-splash-screen" "${PODS_ROOT}/Headers/Public/react-native-webview" "${PODS_ROOT}/Headers/Public/yoga" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion" "${PODS_CONFIGURATION_BUILD_DIR}/EXAppLoaderProvider" "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants" "${PODS_CONFIGURATION_BUILD_DIR}/EXFileSystem" "${PODS_CONFIGURATION_BUILD_DIR}/EXHaptics" "${PODS_CONFIGURATION_BUILD_DIR}/EXPermissions" "${PODS_CONFIGURATION_BUILD_DIR}/EXWebBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/Folly" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_CONFIGURATION_BUILD_DIR}/QBImagePickerController" "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo" "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker" "${PODS_CONFIGURATION_BUILD_DIR}/RNScreens" "${PODS_CONFIGURATION_BUILD_DIR}/RSKImageCropper" "${PODS_CONFIGURATION_BUILD_DIR}/React" "${PODS_CONFIGURATION_BUILD_DIR}/UMCore" "${PODS_CONFIGURATION_BUILD_DIR}/UMReactNativeAdapter" "${PODS_CONFIGURATION_BUILD_DIR}/glog" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-orientation-locker" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-splash-screen" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-webview" "${PODS_CONFIGURATION_BUILD_DIR}/yoga" "${PODS_ROOT}/GoogleIDFASupport/Libraries"
OTHER_LDFLAGS = $(inherited) -ObjC -l"AdIdAccessLibrary" -l"DoubleConversion" -l"EXAppLoaderProvider" -l"EXConstants" -l"EXFileSystem" -l"EXHaptics" -l"EXPermissions" -l"EXWebBrowser" -l"FirebaseCore" -l"FirebaseInstanceID" -l"Folly" -l"GTMSessionFetcher" -l"GoogleToolboxForMac" -l"GoogleUtilities" -l"Protobuf" -l"QBImagePickerController" -l"RNDeviceInfo" -l"RNImageCropPicker" -l"RNScreens" -l"RSKImageCropper" -l"React" -l"UMCore" -l"UMReactNativeAdapter" -l"c++" -l"glog" -l"nanopb" -l"react-native-orientation-locker" -l"react-native-splash-screen" -l"react-native-webview" -l"sqlite3" -l"stdc++" -l"yoga" -l"z" -framework "AdSupport" -framework "CoreTelephony" -framework "Crashlytics" -framework "FIRAnalyticsConnector" -framework "Fabric" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseCoreDiagnostics" -framework "FirebasePerformance" -framework "FirebaseRemoteConfig" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "JavaScriptCore" -framework "Photos" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "UIKit"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/DoubleConversion" "${PODS_ROOT}/Headers/Public/EXAppLoaderProvider" "${PODS_ROOT}/Headers/Public/EXConstants" "${PODS_ROOT}/Headers/Public/EXFileSystem" "${PODS_ROOT}/Headers/Public/EXHaptics" "${PODS_ROOT}/Headers/Public/EXPermissions" "${PODS_ROOT}/Headers/Public/EXWebBrowser" "${PODS_ROOT}/Headers/Public/Firebase" "${PODS_ROOT}/Headers/Public/FirebaseCore" "${PODS_ROOT}/Headers/Public/FirebaseInstanceID" "${PODS_ROOT}/Headers/Public/GTMSessionFetcher" "${PODS_ROOT}/Headers/Public/GoogleToolboxForMac" "${PODS_ROOT}/Headers/Public/GoogleUtilities" "${PODS_ROOT}/Headers/Public/Protobuf" "${PODS_ROOT}/Headers/Public/QBImagePickerController" "${PODS_ROOT}/Headers/Public/RNDeviceInfo" "${PODS_ROOT}/Headers/Public/RNImageCropPicker" "${PODS_ROOT}/Headers/Public/RNLocalize" "${PODS_ROOT}/Headers/Public/RNScreens" "${PODS_ROOT}/Headers/Public/RSKImageCropper" "${PODS_ROOT}/Headers/Public/React" "${PODS_ROOT}/Headers/Public/UMBarCodeScannerInterface" "${PODS_ROOT}/Headers/Public/UMCameraInterface" "${PODS_ROOT}/Headers/Public/UMConstantsInterface" "${PODS_ROOT}/Headers/Public/UMCore" "${PODS_ROOT}/Headers/Public/UMFaceDetectorInterface" "${PODS_ROOT}/Headers/Public/UMFileSystemInterface" "${PODS_ROOT}/Headers/Public/UMFontInterface" "${PODS_ROOT}/Headers/Public/UMImageLoaderInterface" "${PODS_ROOT}/Headers/Public/UMPermissionsInterface" "${PODS_ROOT}/Headers/Public/UMReactNativeAdapter" "${PODS_ROOT}/Headers/Public/UMSensorsInterface" "${PODS_ROOT}/Headers/Public/UMTaskManagerInterface" "${PODS_ROOT}/Headers/Public/glog" "${PODS_ROOT}/Headers/Public/nanopb" "${PODS_ROOT}/Headers/Public/react-native-document-picker" "${PODS_ROOT}/Headers/Public/react-native-orientation-locker" "${PODS_ROOT}/Headers/Public/react-native-realm-path" "${PODS_ROOT}/Headers/Public/react-native-splash-screen" "${PODS_ROOT}/Headers/Public/react-native-webview" "${PODS_ROOT}/Headers/Public/rn-extensions-share" "${PODS_ROOT}/Headers/Public/yoga" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion" "${PODS_CONFIGURATION_BUILD_DIR}/EXAppLoaderProvider" "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants" "${PODS_CONFIGURATION_BUILD_DIR}/EXFileSystem" "${PODS_CONFIGURATION_BUILD_DIR}/EXHaptics" "${PODS_CONFIGURATION_BUILD_DIR}/EXPermissions" "${PODS_CONFIGURATION_BUILD_DIR}/EXWebBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/Folly" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_CONFIGURATION_BUILD_DIR}/QBImagePickerController" "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo" "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker" "${PODS_CONFIGURATION_BUILD_DIR}/RNLocalize" "${PODS_CONFIGURATION_BUILD_DIR}/RNScreens" "${PODS_CONFIGURATION_BUILD_DIR}/RSKImageCropper" "${PODS_CONFIGURATION_BUILD_DIR}/React" "${PODS_CONFIGURATION_BUILD_DIR}/UMCore" "${PODS_CONFIGURATION_BUILD_DIR}/UMReactNativeAdapter" "${PODS_CONFIGURATION_BUILD_DIR}/glog" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-document-picker" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-orientation-locker" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-realm-path" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-splash-screen" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-webview" "${PODS_CONFIGURATION_BUILD_DIR}/yoga" "${PODS_ROOT}/GoogleIDFASupport/Libraries"
OTHER_LDFLAGS = $(inherited) -ObjC -l"AdIdAccessLibrary" -l"DoubleConversion" -l"EXAppLoaderProvider" -l"EXConstants" -l"EXFileSystem" -l"EXHaptics" -l"EXPermissions" -l"EXWebBrowser" -l"FirebaseCore" -l"FirebaseInstanceID" -l"Folly" -l"GTMSessionFetcher" -l"GoogleToolboxForMac" -l"GoogleUtilities" -l"Protobuf" -l"QBImagePickerController" -l"RNDeviceInfo" -l"RNImageCropPicker" -l"RNLocalize" -l"RNScreens" -l"RSKImageCropper" -l"React" -l"UMCore" -l"UMReactNativeAdapter" -l"c++" -l"glog" -l"nanopb" -l"react-native-document-picker" -l"react-native-orientation-locker" -l"react-native-realm-path" -l"react-native-splash-screen" -l"react-native-webview" -l"sqlite3" -l"stdc++" -l"yoga" -l"z" -framework "AdSupport" -framework "CoreTelephony" -framework "Crashlytics" -framework "FIRAnalyticsConnector" -framework "Fabric" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseCoreDiagnostics" -framework "FirebasePerformance" -framework "FirebaseRemoteConfig" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "JavaScriptCore" -framework "Photos" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.

View File

@ -1,8 +1,8 @@
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS" "${PODS_ROOT}/FirebaseABTesting/Frameworks" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/FirebasePerformance/Frameworks" "${PODS_ROOT}/FirebaseRemoteConfig/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/DoubleConversion" "${PODS_ROOT}/Headers/Public/EXAppLoaderProvider" "${PODS_ROOT}/Headers/Public/EXConstants" "${PODS_ROOT}/Headers/Public/EXFileSystem" "${PODS_ROOT}/Headers/Public/EXHaptics" "${PODS_ROOT}/Headers/Public/EXPermissions" "${PODS_ROOT}/Headers/Public/EXWebBrowser" "${PODS_ROOT}/Headers/Public/Firebase" "${PODS_ROOT}/Headers/Public/FirebaseCore" "${PODS_ROOT}/Headers/Public/FirebaseInstanceID" "${PODS_ROOT}/Headers/Public/GTMSessionFetcher" "${PODS_ROOT}/Headers/Public/GoogleToolboxForMac" "${PODS_ROOT}/Headers/Public/GoogleUtilities" "${PODS_ROOT}/Headers/Public/Protobuf" "${PODS_ROOT}/Headers/Public/QBImagePickerController" "${PODS_ROOT}/Headers/Public/RNDeviceInfo" "${PODS_ROOT}/Headers/Public/RNImageCropPicker" "${PODS_ROOT}/Headers/Public/RNScreens" "${PODS_ROOT}/Headers/Public/RSKImageCropper" "${PODS_ROOT}/Headers/Public/React" "${PODS_ROOT}/Headers/Public/UMBarCodeScannerInterface" "${PODS_ROOT}/Headers/Public/UMCameraInterface" "${PODS_ROOT}/Headers/Public/UMConstantsInterface" "${PODS_ROOT}/Headers/Public/UMCore" "${PODS_ROOT}/Headers/Public/UMFaceDetectorInterface" "${PODS_ROOT}/Headers/Public/UMFileSystemInterface" "${PODS_ROOT}/Headers/Public/UMFontInterface" "${PODS_ROOT}/Headers/Public/UMImageLoaderInterface" "${PODS_ROOT}/Headers/Public/UMPermissionsInterface" "${PODS_ROOT}/Headers/Public/UMReactNativeAdapter" "${PODS_ROOT}/Headers/Public/UMSensorsInterface" "${PODS_ROOT}/Headers/Public/UMTaskManagerInterface" "${PODS_ROOT}/Headers/Public/glog" "${PODS_ROOT}/Headers/Public/nanopb" "${PODS_ROOT}/Headers/Public/react-native-orientation-locker" "${PODS_ROOT}/Headers/Public/react-native-splash-screen" "${PODS_ROOT}/Headers/Public/react-native-webview" "${PODS_ROOT}/Headers/Public/yoga" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion" "${PODS_CONFIGURATION_BUILD_DIR}/EXAppLoaderProvider" "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants" "${PODS_CONFIGURATION_BUILD_DIR}/EXFileSystem" "${PODS_CONFIGURATION_BUILD_DIR}/EXHaptics" "${PODS_CONFIGURATION_BUILD_DIR}/EXPermissions" "${PODS_CONFIGURATION_BUILD_DIR}/EXWebBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/Folly" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_CONFIGURATION_BUILD_DIR}/QBImagePickerController" "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo" "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker" "${PODS_CONFIGURATION_BUILD_DIR}/RNScreens" "${PODS_CONFIGURATION_BUILD_DIR}/RSKImageCropper" "${PODS_CONFIGURATION_BUILD_DIR}/React" "${PODS_CONFIGURATION_BUILD_DIR}/UMCore" "${PODS_CONFIGURATION_BUILD_DIR}/UMReactNativeAdapter" "${PODS_CONFIGURATION_BUILD_DIR}/glog" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-orientation-locker" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-splash-screen" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-webview" "${PODS_CONFIGURATION_BUILD_DIR}/yoga" "${PODS_ROOT}/GoogleIDFASupport/Libraries"
OTHER_LDFLAGS = $(inherited) -ObjC -l"AdIdAccessLibrary" -l"DoubleConversion" -l"EXAppLoaderProvider" -l"EXConstants" -l"EXFileSystem" -l"EXHaptics" -l"EXPermissions" -l"EXWebBrowser" -l"FirebaseCore" -l"FirebaseInstanceID" -l"Folly" -l"GTMSessionFetcher" -l"GoogleToolboxForMac" -l"GoogleUtilities" -l"Protobuf" -l"QBImagePickerController" -l"RNDeviceInfo" -l"RNImageCropPicker" -l"RNScreens" -l"RSKImageCropper" -l"React" -l"UMCore" -l"UMReactNativeAdapter" -l"c++" -l"glog" -l"nanopb" -l"react-native-orientation-locker" -l"react-native-splash-screen" -l"react-native-webview" -l"sqlite3" -l"stdc++" -l"yoga" -l"z" -framework "AdSupport" -framework "CoreTelephony" -framework "Crashlytics" -framework "FIRAnalyticsConnector" -framework "Fabric" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseCoreDiagnostics" -framework "FirebasePerformance" -framework "FirebaseRemoteConfig" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "JavaScriptCore" -framework "Photos" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "UIKit"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/DoubleConversion" "${PODS_ROOT}/Headers/Public/EXAppLoaderProvider" "${PODS_ROOT}/Headers/Public/EXConstants" "${PODS_ROOT}/Headers/Public/EXFileSystem" "${PODS_ROOT}/Headers/Public/EXHaptics" "${PODS_ROOT}/Headers/Public/EXPermissions" "${PODS_ROOT}/Headers/Public/EXWebBrowser" "${PODS_ROOT}/Headers/Public/Firebase" "${PODS_ROOT}/Headers/Public/FirebaseCore" "${PODS_ROOT}/Headers/Public/FirebaseInstanceID" "${PODS_ROOT}/Headers/Public/GTMSessionFetcher" "${PODS_ROOT}/Headers/Public/GoogleToolboxForMac" "${PODS_ROOT}/Headers/Public/GoogleUtilities" "${PODS_ROOT}/Headers/Public/Protobuf" "${PODS_ROOT}/Headers/Public/QBImagePickerController" "${PODS_ROOT}/Headers/Public/RNDeviceInfo" "${PODS_ROOT}/Headers/Public/RNImageCropPicker" "${PODS_ROOT}/Headers/Public/RNLocalize" "${PODS_ROOT}/Headers/Public/RNScreens" "${PODS_ROOT}/Headers/Public/RSKImageCropper" "${PODS_ROOT}/Headers/Public/React" "${PODS_ROOT}/Headers/Public/UMBarCodeScannerInterface" "${PODS_ROOT}/Headers/Public/UMCameraInterface" "${PODS_ROOT}/Headers/Public/UMConstantsInterface" "${PODS_ROOT}/Headers/Public/UMCore" "${PODS_ROOT}/Headers/Public/UMFaceDetectorInterface" "${PODS_ROOT}/Headers/Public/UMFileSystemInterface" "${PODS_ROOT}/Headers/Public/UMFontInterface" "${PODS_ROOT}/Headers/Public/UMImageLoaderInterface" "${PODS_ROOT}/Headers/Public/UMPermissionsInterface" "${PODS_ROOT}/Headers/Public/UMReactNativeAdapter" "${PODS_ROOT}/Headers/Public/UMSensorsInterface" "${PODS_ROOT}/Headers/Public/UMTaskManagerInterface" "${PODS_ROOT}/Headers/Public/glog" "${PODS_ROOT}/Headers/Public/nanopb" "${PODS_ROOT}/Headers/Public/react-native-document-picker" "${PODS_ROOT}/Headers/Public/react-native-orientation-locker" "${PODS_ROOT}/Headers/Public/react-native-realm-path" "${PODS_ROOT}/Headers/Public/react-native-splash-screen" "${PODS_ROOT}/Headers/Public/react-native-webview" "${PODS_ROOT}/Headers/Public/rn-extensions-share" "${PODS_ROOT}/Headers/Public/yoga" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion" "${PODS_CONFIGURATION_BUILD_DIR}/EXAppLoaderProvider" "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants" "${PODS_CONFIGURATION_BUILD_DIR}/EXFileSystem" "${PODS_CONFIGURATION_BUILD_DIR}/EXHaptics" "${PODS_CONFIGURATION_BUILD_DIR}/EXPermissions" "${PODS_CONFIGURATION_BUILD_DIR}/EXWebBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/Folly" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_CONFIGURATION_BUILD_DIR}/QBImagePickerController" "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo" "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker" "${PODS_CONFIGURATION_BUILD_DIR}/RNLocalize" "${PODS_CONFIGURATION_BUILD_DIR}/RNScreens" "${PODS_CONFIGURATION_BUILD_DIR}/RSKImageCropper" "${PODS_CONFIGURATION_BUILD_DIR}/React" "${PODS_CONFIGURATION_BUILD_DIR}/UMCore" "${PODS_CONFIGURATION_BUILD_DIR}/UMReactNativeAdapter" "${PODS_CONFIGURATION_BUILD_DIR}/glog" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-document-picker" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-orientation-locker" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-realm-path" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-splash-screen" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-webview" "${PODS_CONFIGURATION_BUILD_DIR}/yoga" "${PODS_ROOT}/GoogleIDFASupport/Libraries"
OTHER_LDFLAGS = $(inherited) -ObjC -l"AdIdAccessLibrary" -l"DoubleConversion" -l"EXAppLoaderProvider" -l"EXConstants" -l"EXFileSystem" -l"EXHaptics" -l"EXPermissions" -l"EXWebBrowser" -l"FirebaseCore" -l"FirebaseInstanceID" -l"Folly" -l"GTMSessionFetcher" -l"GoogleToolboxForMac" -l"GoogleUtilities" -l"Protobuf" -l"QBImagePickerController" -l"RNDeviceInfo" -l"RNImageCropPicker" -l"RNLocalize" -l"RNScreens" -l"RSKImageCropper" -l"React" -l"UMCore" -l"UMReactNativeAdapter" -l"c++" -l"glog" -l"nanopb" -l"react-native-document-picker" -l"react-native-orientation-locker" -l"react-native-realm-path" -l"react-native-splash-screen" -l"react-native-webview" -l"sqlite3" -l"stdc++" -l"yoga" -l"z" -framework "AdSupport" -framework "CoreTelephony" -framework "Crashlytics" -framework "FIRAnalyticsConnector" -framework "Fabric" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseCoreDiagnostics" -framework "FirebasePerformance" -framework "FirebaseRemoteConfig" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "JavaScriptCore" -framework "Photos" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_Pods_ShareRocketChatRN : NSObject
@end
@implementation PodsDummy_Pods_ShareRocketChatRN
@end

View File

@ -0,0 +1,9 @@
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS" "${PODS_ROOT}/FirebaseABTesting/Frameworks" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/FirebasePerformance/Frameworks" "${PODS_ROOT}/FirebaseRemoteConfig/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/DoubleConversion" "${PODS_ROOT}/Headers/Public/EXAppLoaderProvider" "${PODS_ROOT}/Headers/Public/EXConstants" "${PODS_ROOT}/Headers/Public/EXFileSystem" "${PODS_ROOT}/Headers/Public/EXHaptics" "${PODS_ROOT}/Headers/Public/EXPermissions" "${PODS_ROOT}/Headers/Public/EXWebBrowser" "${PODS_ROOT}/Headers/Public/Firebase" "${PODS_ROOT}/Headers/Public/FirebaseCore" "${PODS_ROOT}/Headers/Public/FirebaseInstanceID" "${PODS_ROOT}/Headers/Public/GTMSessionFetcher" "${PODS_ROOT}/Headers/Public/GoogleToolboxForMac" "${PODS_ROOT}/Headers/Public/GoogleUtilities" "${PODS_ROOT}/Headers/Public/Protobuf" "${PODS_ROOT}/Headers/Public/QBImagePickerController" "${PODS_ROOT}/Headers/Public/RNDeviceInfo" "${PODS_ROOT}/Headers/Public/RNImageCropPicker" "${PODS_ROOT}/Headers/Public/RNLocalize" "${PODS_ROOT}/Headers/Public/RNScreens" "${PODS_ROOT}/Headers/Public/RSKImageCropper" "${PODS_ROOT}/Headers/Public/React" "${PODS_ROOT}/Headers/Public/UMBarCodeScannerInterface" "${PODS_ROOT}/Headers/Public/UMCameraInterface" "${PODS_ROOT}/Headers/Public/UMConstantsInterface" "${PODS_ROOT}/Headers/Public/UMCore" "${PODS_ROOT}/Headers/Public/UMFaceDetectorInterface" "${PODS_ROOT}/Headers/Public/UMFileSystemInterface" "${PODS_ROOT}/Headers/Public/UMFontInterface" "${PODS_ROOT}/Headers/Public/UMImageLoaderInterface" "${PODS_ROOT}/Headers/Public/UMPermissionsInterface" "${PODS_ROOT}/Headers/Public/UMReactNativeAdapter" "${PODS_ROOT}/Headers/Public/UMSensorsInterface" "${PODS_ROOT}/Headers/Public/UMTaskManagerInterface" "${PODS_ROOT}/Headers/Public/glog" "${PODS_ROOT}/Headers/Public/nanopb" "${PODS_ROOT}/Headers/Public/react-native-document-picker" "${PODS_ROOT}/Headers/Public/react-native-orientation-locker" "${PODS_ROOT}/Headers/Public/react-native-realm-path" "${PODS_ROOT}/Headers/Public/react-native-splash-screen" "${PODS_ROOT}/Headers/Public/react-native-webview" "${PODS_ROOT}/Headers/Public/rn-extensions-share" "${PODS_ROOT}/Headers/Public/yoga" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/Folly" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo" "${PODS_CONFIGURATION_BUILD_DIR}/RNLocalize" "${PODS_CONFIGURATION_BUILD_DIR}/React" "${PODS_CONFIGURATION_BUILD_DIR}/glog" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-realm-path" "${PODS_CONFIGURATION_BUILD_DIR}/rn-extensions-share" "${PODS_CONFIGURATION_BUILD_DIR}/yoga" "${PODS_ROOT}/GoogleIDFASupport/Libraries"
OTHER_LDFLAGS = $(inherited) -ObjC -l"AdIdAccessLibrary" -l"DoubleConversion" -l"FirebaseCore" -l"FirebaseInstanceID" -l"Folly" -l"GTMSessionFetcher" -l"GoogleToolboxForMac" -l"GoogleUtilities" -l"Protobuf" -l"RNDeviceInfo" -l"RNLocalize" -l"React" -l"c++" -l"glog" -l"nanopb" -l"react-native-realm-path" -l"rn-extensions-share" -l"sqlite3" -l"stdc++" -l"yoga" -l"z" -framework "AdSupport" -framework "CoreTelephony" -framework "Crashlytics" -framework "FIRAnalyticsConnector" -framework "Fabric" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseCoreDiagnostics" -framework "FirebasePerformance" -framework "FirebaseRemoteConfig" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "JavaScriptCore" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods

View File

@ -0,0 +1,9 @@
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS" "${PODS_ROOT}/FirebaseABTesting/Frameworks" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/FirebasePerformance/Frameworks" "${PODS_ROOT}/FirebaseRemoteConfig/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/DoubleConversion" "${PODS_ROOT}/Headers/Public/EXAppLoaderProvider" "${PODS_ROOT}/Headers/Public/EXConstants" "${PODS_ROOT}/Headers/Public/EXFileSystem" "${PODS_ROOT}/Headers/Public/EXHaptics" "${PODS_ROOT}/Headers/Public/EXPermissions" "${PODS_ROOT}/Headers/Public/EXWebBrowser" "${PODS_ROOT}/Headers/Public/Firebase" "${PODS_ROOT}/Headers/Public/FirebaseCore" "${PODS_ROOT}/Headers/Public/FirebaseInstanceID" "${PODS_ROOT}/Headers/Public/GTMSessionFetcher" "${PODS_ROOT}/Headers/Public/GoogleToolboxForMac" "${PODS_ROOT}/Headers/Public/GoogleUtilities" "${PODS_ROOT}/Headers/Public/Protobuf" "${PODS_ROOT}/Headers/Public/QBImagePickerController" "${PODS_ROOT}/Headers/Public/RNDeviceInfo" "${PODS_ROOT}/Headers/Public/RNImageCropPicker" "${PODS_ROOT}/Headers/Public/RNLocalize" "${PODS_ROOT}/Headers/Public/RNScreens" "${PODS_ROOT}/Headers/Public/RSKImageCropper" "${PODS_ROOT}/Headers/Public/React" "${PODS_ROOT}/Headers/Public/UMBarCodeScannerInterface" "${PODS_ROOT}/Headers/Public/UMCameraInterface" "${PODS_ROOT}/Headers/Public/UMConstantsInterface" "${PODS_ROOT}/Headers/Public/UMCore" "${PODS_ROOT}/Headers/Public/UMFaceDetectorInterface" "${PODS_ROOT}/Headers/Public/UMFileSystemInterface" "${PODS_ROOT}/Headers/Public/UMFontInterface" "${PODS_ROOT}/Headers/Public/UMImageLoaderInterface" "${PODS_ROOT}/Headers/Public/UMPermissionsInterface" "${PODS_ROOT}/Headers/Public/UMReactNativeAdapter" "${PODS_ROOT}/Headers/Public/UMSensorsInterface" "${PODS_ROOT}/Headers/Public/UMTaskManagerInterface" "${PODS_ROOT}/Headers/Public/glog" "${PODS_ROOT}/Headers/Public/nanopb" "${PODS_ROOT}/Headers/Public/react-native-document-picker" "${PODS_ROOT}/Headers/Public/react-native-orientation-locker" "${PODS_ROOT}/Headers/Public/react-native-realm-path" "${PODS_ROOT}/Headers/Public/react-native-splash-screen" "${PODS_ROOT}/Headers/Public/react-native-webview" "${PODS_ROOT}/Headers/Public/rn-extensions-share" "${PODS_ROOT}/Headers/Public/yoga" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DoubleConversion" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID" "${PODS_CONFIGURATION_BUILD_DIR}/Folly" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf" "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo" "${PODS_CONFIGURATION_BUILD_DIR}/RNLocalize" "${PODS_CONFIGURATION_BUILD_DIR}/React" "${PODS_CONFIGURATION_BUILD_DIR}/glog" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_CONFIGURATION_BUILD_DIR}/react-native-realm-path" "${PODS_CONFIGURATION_BUILD_DIR}/rn-extensions-share" "${PODS_CONFIGURATION_BUILD_DIR}/yoga" "${PODS_ROOT}/GoogleIDFASupport/Libraries"
OTHER_LDFLAGS = $(inherited) -ObjC -l"AdIdAccessLibrary" -l"DoubleConversion" -l"FirebaseCore" -l"FirebaseInstanceID" -l"Folly" -l"GTMSessionFetcher" -l"GoogleToolboxForMac" -l"GoogleUtilities" -l"Protobuf" -l"RNDeviceInfo" -l"RNLocalize" -l"React" -l"c++" -l"glog" -l"nanopb" -l"react-native-realm-path" -l"rn-extensions-share" -l"sqlite3" -l"stdc++" -l"yoga" -l"z" -framework "AdSupport" -framework "CoreTelephony" -framework "Crashlytics" -framework "FIRAnalyticsConnector" -framework "Fabric" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseCoreDiagnostics" -framework "FirebasePerformance" -framework "FirebaseRemoteConfig" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "JavaScriptCore" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_RNLocalize : NSObject
@end
@implementation PodsDummy_RNLocalize
@end

Some files were not shown because too many files have changed in this diff Show More