diff --git a/README.md b/README.md index 4d6ed53f2..2883ffe23 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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) | ❌ | diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 45d2886f7..4e466bcaa 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -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, diff --git a/app/actions/share.js b/app/actions/share.js new file mode 100644 index 000000000..56e8ced9b --- /dev/null +++ b/app/actions/share.js @@ -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 + }; +} diff --git a/app/containers/HeaderButton.js b/app/containers/HeaderButton.js index e1550b4c0..076dd9380 100644 --- a/app/containers/HeaderButton.js +++ b/app/containers/HeaderButton.js @@ -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; @@ -35,7 +36,7 @@ export const CloseModalButton = React.memo(({ navigation, testID }) => ( export const CloseShareExtensionButton = React.memo(({ onPress, testID }) => ( {isIOS - ? + ? : } diff --git a/app/containers/MessageBox/UploadModal.js b/app/containers/MessageBox/UploadModal.js index 4a5baa3c1..5c8e5bb54 100644 --- a/app/containers/MessageBox/UploadModal.js +++ b/app/containers/MessageBox/UploadModal.js @@ -13,6 +13,7 @@ import Button from '../Button'; import I18n from '../../i18n'; import sharedStyles from '../../views/Styles'; import { isIOS } from '../../utils/deviceInfo'; +import { canUploadFile } from '../../utils/media'; import { COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_DANGER } from '../../constants/colors'; @@ -166,8 +167,9 @@ export default class UploadModal extends Component { if (file.size > FileUpload_MaxFileSize) { return false; } + // if white list is empty, all media types are enabled if (!FileUpload_MediaTypeWhiteList) { - return false; + return true; } const allowedMime = FileUpload_MediaTypeWhiteList.split(','); if (allowedMime.includes(file.mime)) { @@ -290,9 +292,11 @@ export default class UploadModal extends Component { } render() { - const { window: { width }, isVisible, close } = this.props; + const { + window: { width }, isVisible, close, file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize + } = this.props; const { name, description } = this.state; - const showError = !this.canUploadFile(); + const showError = !canUploadFile(file, { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize }); return ( { - const { rid, tmid } = this.props; + const { + rid, tmid, baseUrl: server, user + } = this.props; this.setState({ file: { isVisible: false } }); const fileInfo = { name: file.name, @@ -500,7 +502,7 @@ 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_media_message', e); } @@ -602,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)); diff --git a/app/containers/SearchBox.js b/app/containers/SearchBox.js index 87a3cd8e3..35e8d8ea3 100644 --- a/app/containers/SearchBox.js +++ b/app/containers/SearchBox.js @@ -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 => ( + + {I18n.t('Cancel')} + +); + +const SearchBox = ({ + onChangeText, onSubmitEditing, testID, hasCancel, onCancelPress, ...props +}) => ( @@ -50,14 +73,18 @@ const SearchBox = ({ onChangeText, onSubmitEditing, testID }) => ( underlineColorAndroid='transparent' onChangeText={onChangeText} onSubmitEditing={onSubmitEditing} + {...props} /> + { hasCancel ? CancelButton(onCancelPress) : null } ); SearchBox.propTypes = { onChangeText: PropTypes.func.isRequired, onSubmitEditing: PropTypes.func, + hasCancel: PropTypes.bool, + onCancelPress: PropTypes.func, testID: PropTypes.string }; diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index f26cfd8de..547f7db33 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -311,13 +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_Channels: 'Select Channels', 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', diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js index cb321841e..2e8ea6051 100644 --- a/app/i18n/locales/pt-BR.js +++ b/app/i18n/locales/pt-BR.js @@ -302,12 +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_Channels: 'Selecionar Canais', 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', @@ -383,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!' }; diff --git a/app/lib/ShareNavigation.js b/app/lib/ShareNavigation.js new file mode 100644 index 000000000..84bc27908 --- /dev/null +++ b/app/lib/ShareNavigation.js @@ -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 +}; diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.js index c751b5182..11e55c1c1 100644 --- a/app/lib/methods/getSettings.js +++ b/app/lib/methods/getSettings.js @@ -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 }); + } }) ) ); diff --git a/app/lib/methods/sendFileMessage.js b/app/lib/methods/sendFileMessage.js index 08da0b374..f7f774d5c 100644 --- a/app/lib/methods/sendFileMessage.js +++ b/app/lib/methods/sendFileMessage.js @@ -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) { diff --git a/app/lib/methods/sendMessage.js b/app/lib/methods/sendMessage.js index eae4a8240..52c4f6e94 100644 --- a/app/lib/methods/sendMessage.js +++ b/app/lib/methods/sendMessage.js @@ -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) { diff --git a/app/lib/realm.js b/app/lib/realm.js index f611f2f33..3a17584cc 100644 --- a/app/lib/realm.js +++ b/app/lib/realm.js @@ -26,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?' } diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 7b3e75ae5..735b0b0a8 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -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); diff --git a/app/presentation/DirectoryItem/index.js b/app/presentation/DirectoryItem/index.js index 652113e62..f9c0d8883 100644 --- a/app/presentation/DirectoryItem/index.js +++ b/app/presentation/DirectoryItem/index.js @@ -35,7 +35,7 @@ const DirectoryItem = ({ {title} - {description} + { description ? {description} : null } diff --git a/app/presentation/ServerItem/styles.js b/app/presentation/ServerItem/styles.js index c8eb4f260..07669158f 100644 --- a/app/presentation/ServerItem/styles.js +++ b/app/presentation/ServerItem/styles.js @@ -16,8 +16,8 @@ export default StyleSheet.create({ alignItems: 'center' }, serverIcon: { - width: 38, - height: 38, + width: 44, + height: 44, marginHorizontal: 15, borderRadius: 4 }, diff --git a/app/reducers/index.js b/app/reducers/index.js index e7bfb41d1..6d2bd447f 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -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 }); diff --git a/app/reducers/share.js b/app/reducers/share.js new file mode 100644 index 000000000..9c3a7b38c --- /dev/null +++ b/app/reducers/share.js @@ -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; + } +} diff --git a/app/share.js b/app/share.js index a1441164f..7ea3ce299 100644 --- a/app/share.js +++ b/app/share.js @@ -2,31 +2,34 @@ 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/Navigation'; +import Navigation from './lib/ShareNavigation'; import store from './lib/createStore'; -import { appInit } from './actions'; -import ShareListView from './views/ShareListView'; -import ShareView from './views/ShareView'; -import SelectServerView from './views/SelectServerView'; -import AuthLoadingView from './views/AuthLoadingView'; -import WithoutServersView from './views/WithoutServersView'; import sharedStyles from './views/Styles'; -import { isNotch } from './utils/deviceInfo'; +import { isNotch, isIOS } from './utils/deviceInfo'; import { defaultHeader, onNavigationStateChange } from './utils/navigation'; - +import RocketChat from './lib/rocketchat'; const InsideNavigator = createStackNavigator({ - ShareListView, - ShareView, - SelectServerView + 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 + WithoutServersView: { + getScreen: () => require('./views/WithoutServersView').default + } }, { initialRouteName: 'WithoutServersView', defaultNavigationOptions: defaultHeader @@ -35,7 +38,9 @@ const OutsideNavigator = createStackNavigator({ const AppContainer = createAppContainer(createSwitchNavigator({ OutsideStack: OutsideNavigator, InsideStack: InsideNavigator, - AuthLoading: AuthLoadingView + AuthLoading: { + getScreen: () => require('./views/AuthLoadingView').default + } }, { initialRouteName: 'AuthLoading' @@ -44,10 +49,25 @@ const AppContainer = createAppContainer(createSwitchNavigator({ class Root extends React.Component { constructor(props) { super(props); - store.dispatch(appInit()); 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) => { diff --git a/app/utils/media.js b/app/utils/media.js new file mode 100644 index 000000000..8915caf6d --- /dev/null +++ b/app/utils/media.js @@ -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; +}; diff --git a/app/views/RoomView/UploadProgress.js b/app/views/RoomView/UploadProgress.js index 30aa95cad..90264941d 100644 --- a/app/views/RoomView/UploadProgress.js +++ b/app/views/RoomView/UploadProgress.js @@ -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); } diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index d5cdc099e..07c6bbe6b 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -410,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); }); }; @@ -623,7 +624,7 @@ export default class RoomView extends React.Component { {this.renderFooter()} {this.renderActions()} - + ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); const keyExtractor = item => item.id; @@ -36,10 +33,8 @@ const styles = StyleSheet.create({ } }); -@connect(state => ({ - server: state.server.server -}), dispatch => ({ - selectServerRequest: server => dispatch(selectServerRequestAction(server)) +@connect(({ share }) => ({ + server: share.server })) export default class SelectServerView extends React.Component { static navigationOptions = () => ({ @@ -47,8 +42,7 @@ export default class SelectServerView extends React.Component { }) static propTypes = { - server: PropTypes.string, - selectServerRequest: PropTypes.func + server: PropTypes.string } constructor(props) { @@ -61,15 +55,15 @@ export default class SelectServerView extends React.Component { }; } - select = (server) => { + select = async(server) => { const { - server: currentServer, selectServerRequest + server: currentServer } = this.props; - if (currentServer !== server) { - selectServerRequest(server); - } Navigation.navigate('ShareListView'); + if (currentServer !== server) { + await RocketChat.shareExtensionInit(server); + } } renderItem = ({ item }) => { diff --git a/app/views/ShareListView/Header.js b/app/views/ShareListView/Header.js deleted file mode 100644 index fcd87498b..000000000 --- a/app/views/ShareListView/Header.js +++ /dev/null @@ -1,92 +0,0 @@ -import React, { PureComponent } from 'react'; -import { - View, StyleSheet, Text, Platform -} from 'react-native'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { TextInput } from 'react-native-gesture-handler'; - -import I18n from '../../i18n'; -import { COLOR_WHITE, HEADER_TITLE } from '../../constants/colors'; -import sharedStyles from '../Styles'; -import { setSearch as setSearchAction } from '../../actions/rooms'; -import { isAndroid } from '../../utils/deviceInfo'; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center' - }, - search: { - fontSize: 20, - color: COLOR_WHITE, - ...sharedStyles.textRegular - }, - title: { - ...Platform.select({ - ios: { - fontSize: 17, - ...sharedStyles.textSemibold, - color: HEADER_TITLE - }, - android: { - fontSize: 20, - ...sharedStyles.textRegular, - color: HEADER_TITLE - } - }) - } -}); - -@connect(state => ({ - showSearchHeader: state.rooms.showSearchHeader -}), dispatch => ({ - setSearch: searchText => dispatch(setSearchAction(searchText)) -})) -class ShareListHeader extends PureComponent { - static propTypes = { - showSearchHeader: PropTypes.bool, - setSearch: PropTypes.func - } - - componentDidUpdate(prevProps) { - const { showSearchHeader } = this.props; - if (showSearchHeader && prevProps.showSearchHeader !== showSearchHeader) { - setTimeout(() => { - this.searchInputRef.focus(); - }, 300); - } - } - - onSearchChangeText = (text) => { - const { setSearch } = this.props; - setSearch(text.trim()); - } - - setSearchInputRef = (ref) => { - this.searchInputRef = ref; - } - - render() { - const { - showSearchHeader - } = this.props; - - if (showSearchHeader && isAndroid) { - return ( - - - - ); - } - return {I18n.t('Select_Channels')}; - } -} - -export default ShareListHeader; diff --git a/app/views/ShareListView/Header/Header.android.js b/app/views/ShareListView/Header/Header.android.js new file mode 100644 index 000000000..55720d920 --- /dev/null +++ b/app/views/ShareListView/Header/Header.android.js @@ -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 ( + + + + ); + } + return {I18n.t('Send_to')}; +}); + +Header.propTypes = { + searching: PropTypes.bool, + onChangeSearchText: PropTypes.func +}; + +export default Header; diff --git a/app/views/ShareListView/Header/Header.ios.js b/app/views/ShareListView/Header/Header.ios.js new file mode 100644 index 000000000..fabe3c499 --- /dev/null +++ b/app/views/ShareListView/Header/Header.ios.js @@ -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 ( + + { + !searching + ? ( + + ) + : null + } + + + ); +}); + +Header.propTypes = { + searching: PropTypes.bool, + onChangeSearchText: PropTypes.func, + initSearch: PropTypes.func, + cancelSearch: PropTypes.func +}; + +export default Header; diff --git a/app/views/ShareListView/Header/index.js b/app/views/ShareListView/Header/index.js new file mode 100644 index 000000000..3cca590fa --- /dev/null +++ b/app/views/ShareListView/Header/index.js @@ -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 ( +
+ ); +}); + +ShareListHeader.propTypes = { + searching: PropTypes.bool, + initSearch: PropTypes.func, + cancelSearch: PropTypes.func, + search: PropTypes.func +}; + +export default ShareListHeader; diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js index 535207882..5e3e24753 100644 --- a/app/views/ShareListView/index.js +++ b/app/views/ShareListView/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - View, Text, LayoutAnimation, InteractionManager, FlatList, ScrollView, ActivityIndicator, Keyboard + View, Text, LayoutAnimation, FlatList, ActivityIndicator, Keyboard, BackHandler } from 'react-native'; import { SafeAreaView } from 'react-navigation'; import ShareExtension from 'rn-extensions-share'; @@ -10,60 +10,58 @@ import RNFetchBlob from 'rn-fetch-blob'; import * as mime from 'react-native-mime-types'; import { isEqual } from 'lodash'; -import Navigation from '../../lib/Navigation'; -import database, { safeAddListener } from '../../lib/realm'; -import debounce from '../../utils/debounce'; +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 { - openSearchHeader as openSearchHeaderAction, - closeSearchHeader as closeSearchHeaderAction -} from '../../actions/rooms'; +import { canUploadFile } from '../../utils/media'; import DirectoryItem, { ROW_HEIGHT } from '../../presentation/DirectoryItem'; -import ServerItem, { ROW_HEIGHT as ROW_HEIGHT_SERVER } from '../../presentation/ServerItem'; +import ServerItem from '../../presentation/ServerItem'; import { CloseShareExtensionButton, CustomHeaderButtons, Item } from '../../containers/HeaderButton'; -import SearchBar from '../RoomsListView/ListHeader/SearchBar'; import ShareListHeader from './Header'; import styles from './styles'; +import StatusBar from '../../containers/StatusBar'; -const SCROLL_OFFSET = 56; -const getItemLayoutChannel = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); -const getItemLayoutServer = (data, index) => ({ length: ROW_HEIGHT_SERVER, offset: ROW_HEIGHT_SERVER * index, index }); +const LIMIT = 50; +const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); const keyExtractor = item => item.rid; -@connect(state => ({ - userId: state.login.user && state.login.user.id, - token: state.login.user && state.login.user.token, - useRealName: state.settings.UI_Use_Real_Name, - searchText: state.rooms.searchText, - server: state.server.server, - loading: state.server.loading, - FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList, - FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize, - baseUrl: state.settings.baseUrl || state.server ? state.server.server : '', - sortBy: state.sortPreferences.sortBy, - groupByType: state.sortPreferences.groupByType, - showFavorites: state.sortPreferences.showFavorites -}), dispatch => ({ - openSearchHeader: () => dispatch(openSearchHeaderAction()), - closeSearchHeader: () => dispatch(closeSearchHeaderAction()) +@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 cancelSearchingAndroid = navigation.getParam('cancelSearchingAndroid'); - const initSearchingAndroid = navigation.getParam('initSearchingAndroid', () => {}); + const initSearch = navigation.getParam('initSearch', () => {}); + const cancelSearch = navigation.getParam('cancelSearch', () => {}); + const search = navigation.getParam('search', () => {}); + + if (isIOS) { + return { + headerTitle: ( + + ) + }; + } return { - headerBackTitle: isIOS ? I18n.t('Back') : null, + headerBackTitle: null, headerLeft: searching ? ( - + ) : ( @@ -72,13 +70,13 @@ export default class ShareListView extends React.Component { testID='share-extension-close' /> ), - headerTitle: , + headerTitle: , headerRight: ( searching ? null : ( - {isAndroid ? : null} + {isAndroid ? : null} ) ) @@ -88,50 +86,38 @@ export default class ShareListView extends React.Component { static propTypes = { navigation: PropTypes.object, server: PropTypes.string, - useRealName: PropTypes.bool, - searchText: PropTypes.string, - FileUpload_MediaTypeWhiteList: PropTypes.string, - FileUpload_MaxFileSize: PropTypes.number, - openSearchHeader: PropTypes.func, - closeSearchHeader: PropTypes.func, baseUrl: PropTypes.string, token: PropTypes.string, - userId: PropTypes.string, - sortBy: PropTypes.string, - groupByType: PropTypes.bool, - showFavorites: PropTypes.bool, - loading: PropTypes.bool + userId: PropTypes.string } constructor(props) { super(props); this.data = []; this.state = { + showError: false, searching: false, + searchText: '', value: '', isMedia: false, mediaLoading: false, - loading: true, fileInfo: null, - search: [], - discussions: [], - channels: [], - favorites: [], + searchResults: [], chats: [], - privateGroup: [], - direct: [], - livechat: [], - servers: [] + 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() { - this.getSubscriptions(); - - const { navigation } = this.props; + const { navigation, server } = this.props; navigation.setParams({ - initSearchingAndroid: this.initSearchingAndroid, - cancelSearchingAndroid: this.cancelSearchingAndroid + initSearch: this.initSearch, + cancelSearch: this.cancelSearch, + search: this.search }); try { @@ -145,8 +131,7 @@ export default class ShareListView extends React.Component { name: data.filename, description: '', size: data.size, - type: mime.lookup(data.path), - store: 'Uploads', + mime: mime.lookup(data.path), path: isIOS ? data.path : `file://${ data.path }` }; } @@ -157,33 +142,36 @@ export default class ShareListView extends React.Component { log('err_process_media_share_extension', e); this.setState({ mediaLoading: false }); } + + this.getSubscriptions(server); } componentWillReceiveProps(nextProps) { - const { searchText, loading } = this.props; - - if (nextProps.server && loading !== nextProps.loading) { - if (nextProps.loading) { - this.internalSetState({ loading: true }); - } else { - this.getSubscriptions(); - } - } else if (searchText !== nextProps.searchText) { - this.search(nextProps.searchText); + const { server } = this.props; + if (nextProps.server !== server) { + this.getSubscriptions(nextProps.server); } } shouldComponentUpdate(nextProps, nextState) { - const { loading, searching } = this.state; - if (nextState.loading !== loading) { - return true; - } + const { searching } = this.state; if (nextState.searching !== searching) { return true; } - const { search } = this.state; - if (!isEqual(nextState.search, search)) { + 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; @@ -198,69 +186,32 @@ export default class ShareListView extends React.Component { this.setState(...args); } - getSubscriptions = debounce(() => { - if (this.data && this.data.removeAllListeners) { - this.data.removeAllListeners(); - } - - const { - server, sortBy, showFavorites, groupByType - } = this.props; + getSubscriptions = (server, fileInfo) => { + const { fileInfo: fileData } = this.state; const { serversDB } = database.databases; if (server) { - this.data = database.objects('subscriptions').filtered('archived != true && open == true'); - if (sortBy === 'alphabetical') { - this.data = this.data.sorted('name', false); - } else { - this.data = this.data.sorted('roomUpdatedAt', true); - } - // servers + 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); - // favorites - if (showFavorites) { - this.favorites = this.data.filtered('f == true'); - } else { - this.favorites = []; - } - - // type - if (groupByType) { - this.discussions = this.data.filtered('prid != null'); - this.channels = this.data.filtered('t == $0 AND prid == null', 'c'); - this.privateGroup = this.data.filtered('t == $0 AND prid == null', 'p'); - this.direct = this.data.filtered('t == $0 AND prid == null', 'd'); - this.livechat = this.data.filtered('t == $0 AND prid == null', 'l'); - } else { - this.chats = this.data; - } - safeAddListener(this.data, this.updateState); + this.internalSetState({ + chats: this.chats ? this.chats.slice() : [], + servers: this.servers ? this.servers.slice() : [], + loading: false, + showError: !canUploadFile(fileInfo || fileData, serverInfo), + serverInfo + }); + this.forceUpdate(); } - }, 300); + }; uriToPath = uri => decodeURIComponent(isIOS ? uri.replace(/^file:\/\//, '') : uri); - // eslint-disable-next-line react/sort-comp - updateState = debounce(() => { - this.updateStateInteraction = InteractionManager.runAfterInteractions(() => { - this.internalSetState({ - chats: this.chats ? this.chats.slice() : [], - favorites: this.favorites ? this.favorites.slice() : [], - discussions: this.discussions ? this.discussions.slice() : [], - channels: this.channels ? this.channels.slice() : [], - privateGroup: this.privateGroup ? this.privateGroup.slice() : [], - direct: this.direct ? this.direct.slice() : [], - livechat: this.livechat ? this.livechat.slice() : [], - servers: this.servers ? this.servers.slice() : [], - loading: false - }); - this.forceUpdate(); - }); - }, 300); - getRoomTitle = (item) => { - const { useRealName } = this.props; + const { serverInfo } = this.state; + const { useRealName } = serverInfo; return ((item.prid || useRealName) && item.fname) || item.name; } @@ -277,71 +228,52 @@ export default class ShareListView extends React.Component { }); } - canUploadFile = () => { - const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props; - const { fileInfo: file, mediaLoading, loading } = this.state; + search = (text) => { + const result = database.objects('subscriptions').filtered('name CONTAINS[c] $0', text); + this.internalSetState({ + searchResults: result.slice(0, LIMIT), + searchText: text + }); + } - if (loading || mediaLoading) { - return true; - } - if (!(file && file.path)) { - return true; - } - if (file.size > FileUpload_MaxFileSize) { - return false; - } - if (!FileUpload_MediaTypeWhiteList) { - return false; - } - const allowedMime = FileUpload_MediaTypeWhiteList.split(','); - if (allowedMime.includes(file.type)) { - return true; - } - const wildCardGlob = '/*'; - const wildCards = allowedMime.filter(item => item.indexOf(wildCardGlob) > 0); - if (wildCards.includes(file.type.replace(/(\/.*)$/, wildCardGlob))) { + 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; } - search = (text) => { - const result = database.objects('subscriptions').filtered('name CONTAINS[c] $0', text); - const subscriptions = database.objects('subscriptions'); - const data = result.length !== subscriptions.length ? result : []; - this.internalSetState({ - search: data - }); - } - - initSearchingAndroid = () => { - const { openSearchHeader, navigation } = this.props; - this.setState({ searching: true }); - navigation.setParams({ searching: true }); - openSearchHeader(); - } - - cancelSearchingAndroid = () => { - if (isAndroid) { - const { closeSearchHeader, navigation } = this.props; - this.setState({ searching: false }); - navigation.setParams({ searching: false }); - closeSearchHeader(); - this.internalSetState({ search: [] }); - Keyboard.dismiss(); + renderSectionHeader = (header) => { + const { searching } = this.state; + if (searching) { + return null; } + + return ( + + + {I18n.t(header)} + + + ); } - renderListHeader = () => ; - - renderSectionHeader = header => ( - - - {I18n.t(header)} - - - ) - renderItem = ({ item }) => { const { userId, token, baseUrl } = this.props; return ( @@ -367,33 +299,9 @@ export default class ShareListView extends React.Component { renderSeparator = () => ; - renderSection = (data, header) => { - if (data && data.length > 0) { - return ( - - {this.renderSectionHeader(header)} - - - - - ); - } - return null; - } + renderBorderBottom = () => ; - renderServerSelector = () => { + renderSelectServer = () => { const { servers } = this.state; const { server } = this.props; const currentServer = servers.find(serverFiltered => serverFiltered.id === server); @@ -411,83 +319,97 @@ export default class ShareListView extends React.Component { ) : null; } - renderContent = () => { - const { - discussions, channels, privateGroup, direct, livechat, search, chats, favorites - } = this.state; - - if (search.length > 0) { - return ( - - ); - } + renderEmptyComponent = () => ( + + {I18n.t('No_results_found')} + + ); + renderHeader = () => { + const { searching } = this.state; return ( - - {this.renderServerSelector()} - {this.renderSection(favorites, 'Favorites')} - {this.renderSection(discussions, 'Discussions')} - {this.renderSection(channels, 'Channels')} - {this.renderSection(direct, 'Direct_Messages')} - {this.renderSection(privateGroup, 'Private_Groups')} - {this.renderSection(livechat, 'Livechat')} - {this.renderSection(chats, 'Chats')} - + + { !searching + ? ( + + {this.renderSelectServer()} + {this.renderSectionHeader('Chats')} + + ) + : null + } + ); } + renderContent = () => { + const { + chats, mediaLoading, loading, searchResults, searching, searchText + } = this.state; - renderScrollView = () => { - const { mediaLoading, loading } = this.state; if (mediaLoading || loading) { return ; } return ( - - {this.renderListHeader()} - {this.renderContent()} - + initialNumToRender={12} + windowSize={20} + /> ); } renderError = () => { - const { fileInfo: file } = this.state; - const { FileUpload_MaxFileSize } = this.props; + 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 ; + } + return ( - {I18n.t(errorMessage)} - - { file.type } + { !searching + ? ( + + {this.renderSelectServer()} + + ) + : null + } + + {I18n.t(errorMessage)} + + { file.mime } + ); } render() { - const showError = !this.canUploadFile(); + const { showError } = this.state; return ( - { showError ? this.renderError() : this.renderScrollView() } + + { showError ? this.renderError() : this.renderContent() } ); } diff --git a/app/views/ShareListView/styles.js b/app/views/ShareListView/styles.js index 4269db2e3..63d7e39ff 100644 --- a/app/views/ShareListView/styles.js +++ b/app/views/ShareListView/styles.js @@ -8,24 +8,34 @@ import { export default StyleSheet.create({ container: { - justifyContent: 'center', - alignItems: 'center', flex: 1, backgroundColor: COLOR_BACKGROUND_CONTAINER }, + emptyContainer: { + padding: 20, + justifyContent: 'center', + alignItems: 'center' + }, content: { flex: 1, - backgroundColor: isIOS ? COLOR_WHITE : '#E1E5E8' + 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_WHITE + backgroundColor: COLOR_BACKGROUND_CONTAINER }, bordered: { ...sharedStyles.separatorVertical }, - scroll: { - width: '100%' + borderBottom: { + ...sharedStyles.separatorBottom }, headerContainer: { paddingHorizontal: 15, @@ -37,8 +47,7 @@ export default StyleSheet.create({ ...sharedStyles.textColorNormal, ...sharedStyles.textRegular, fontSize: 17, - letterSpacing: 0.27, - flex: 1 + letterSpacing: 0.27 }, separator: { ...sharedStyles.separatorBottom, diff --git a/app/views/ShareView/index.js b/app/views/ShareView/index.js index f0b787d2a..4aab9c1c9 100644 --- a/app/views/ShareView/index.js +++ b/app/views/ShareView/index.js @@ -19,12 +19,17 @@ import database from '../../lib/realm'; import { CustomHeaderButtons, Item } from '../../containers/HeaderButton'; import { isReadOnly, isBlocked } from '../../utils/room'; -@connect(state => ({ - username: state.login.user && state.login.user.username +@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', false); + const canSend = navigation.getParam('canSend', true); return ({ title: I18n.t('Share'), @@ -46,7 +51,12 @@ export default class ShareView extends React.Component { static propTypes = { navigation: PropTypes.object, - username: PropTypes.string.isRequired + user: PropTypes.shape({ + id: PropTypes.string.isRequired, + username: PropTypes.string.isRequired, + token: PropTypes.string.isRequired + }), + baseUrl: PropTypes.string.isRequired }; constructor(props) { @@ -77,11 +87,12 @@ export default class ShareView extends React.Component { componentDidMount() { const { room } = this.state; - const { navigation, username } = this.props; + const { navigation, user } = this.props; + const { username } = user; navigation.setParams({ sendMessage: this._sendMessage, canSend: !(isReadOnly(room, { username }) || isBlocked(room)) }); } - bytesToSize = bits => `${ ((bits / 8) / 1048576).toFixed(2) }MB`; + bytesToSize = bytes => `${ (bytes / 1048576).toFixed(2) }MB`; _sendMessage = async() => { const { isMedia } = this.state; @@ -99,11 +110,19 @@ export default class ShareView extends React.Component { sendMediaMessage = async() => { const { rid, fileInfo, file } = this.state; + const { baseUrl: server, user } = this.props; const { name, description } = file; - const fileMessage = { ...fileInfo, name, description }; + const fileMessage = { + name, + description, + size: fileInfo.size, + type: fileInfo.mime, + store: 'Uploads', + path: fileInfo.path + }; if (fileInfo && rid !== '') { try { - await RocketChat.sendFileMessage(rid, fileMessage, undefined); + await RocketChat.sendFileMessage(rid, fileMessage, undefined, server, user); } catch (e) { log('err_send_media_message', e); } @@ -112,9 +131,10 @@ export default class ShareView extends React.Component { sendTextMessage = async() => { const { value, rid } = this.state; + const { user } = this.props; if (value !== '' && rid !== '') { try { - await RocketChat.sendMessage(rid, value, undefined); + await RocketChat.sendMessage(rid, value, undefined, user); } catch (error) { log('err_share_extension_send_message', error); } @@ -124,7 +144,7 @@ export default class ShareView extends React.Component { renderPreview = () => { const { fileInfo } = this.state; - const icon = fileInfo.type.match(/image/) + const icon = fileInfo.mime.match(/image/) ? : ( @@ -204,7 +224,8 @@ export default class ShareView extends React.Component { } render() { - const { username } = this.props; + const { user } = this.props; + const { username } = user; const { name, loading, isMedia, room } = this.state; diff --git a/app/views/Styles.js b/app/views/Styles.js index 171354876..041f9ea05 100644 --- a/app/views/Styles.js +++ b/app/views/Styles.js @@ -200,7 +200,7 @@ export default StyleSheet.create({ marginVertical: 10 }, notchLandscapeContainer: { - marginTop: -44, + marginTop: -34, paddingHorizontal: 30, backgroundColor: COLOR_BACKGROUND_CONTAINER } diff --git a/app/views/WithoutServersView.js b/app/views/WithoutServersView.js index fd8258981..cd763cc1b 100644 --- a/app/views/WithoutServersView.js +++ b/app/views/WithoutServersView.js @@ -14,7 +14,8 @@ const styles = StyleSheet.create({ flex: 1, backgroundColor: COLOR_WHITE, justifyContent: 'center', - alignItems: 'center' + alignItems: 'center', + padding: 15 }, title: { fontSize: 18, diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3e6ff7296..35bbcb65c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,6 +1,6 @@ PODS: - boost-for-react-native (1.63.0) - - Crashlytics (3.13.2): + - Crashlytics (3.13.4): - Fabric (~> 1.10.2) - DoubleConversion (1.1.6) - EXAppLoaderProvider (6.0.0) @@ -18,24 +18,24 @@ PODS: - EXWebBrowser (6.0.0): - UMCore - Fabric (1.10.2) - - Firebase/Core (6.3.0): + - Firebase/Core (6.5.0): - Firebase/CoreOnly - - FirebaseAnalytics (= 6.0.2) - - Firebase/CoreOnly (6.3.0): - - FirebaseCore (= 6.0.3) - - FirebaseAnalytics (6.0.2): - - FirebaseCore (~> 6.0) + - FirebaseAnalytics (= 6.0.4) + - Firebase/CoreOnly (6.5.0): + - FirebaseCore (= 6.1.0) + - FirebaseAnalytics (6.0.4): + - FirebaseCore (~> 6.1) - FirebaseInstanceID (~> 4.2) - - GoogleAppMeasurement (= 6.0.2) + - GoogleAppMeasurement (= 6.0.4) - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - nanopb (~> 0.3) - - FirebaseCore (6.0.3): + - FirebaseCore (6.1.0): - GoogleUtilities/Environment (~> 6.0) - GoogleUtilities/Logger (~> 6.0) - - FirebaseInstanceID (4.2.0): + - FirebaseInstanceID (4.2.2): - FirebaseCore (~> 6.0) - GoogleUtilities/Environment (~> 6.0) - GoogleUtilities/UserDefaults (~> 6.0) @@ -49,29 +49,29 @@ PODS: - DoubleConversion - glog - glog (0.3.5) - - GoogleAppMeasurement (6.0.2): + - GoogleAppMeasurement (6.0.4): - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - nanopb (~> 0.3) - - GoogleUtilities/AppDelegateSwizzler (6.2.1): + - GoogleUtilities/AppDelegateSwizzler (6.2.3): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (6.2.1) - - GoogleUtilities/Logger (6.2.1): + - GoogleUtilities/Environment (6.2.3) + - GoogleUtilities/Logger (6.2.3): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.2.1): + - GoogleUtilities/MethodSwizzler (6.2.3): - GoogleUtilities/Logger - - GoogleUtilities/Network (6.2.1): + - GoogleUtilities/Network (6.2.3): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.2.1)" - - GoogleUtilities/Reachability (6.2.1): + - "GoogleUtilities/NSData+zlib (6.2.3)" + - GoogleUtilities/Reachability (6.2.3): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.2.1): + - GoogleUtilities/UserDefaults (6.2.3): - GoogleUtilities/Logger - libwebp (1.0.2): - libwebp/core (= 1.0.2) @@ -230,7 +230,7 @@ PODS: - React - RNVectorIcons (6.4.2): - React - - RSKImageCropper (2.2.1) + - RSKImageCropper (2.2.3) - SDWebImage (5.0.6): - SDWebImage/Core (= 5.0.6) - SDWebImage/Core (5.0.6) @@ -479,7 +479,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c - Crashlytics: 611738c7847f8291a1a51084e35987b86ba6b3ee + Crashlytics: 2dfd686bcb918dc10ee0e76f7f853fe42c7bd552 DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 EXAppLoaderProvider: 7a8185228d8ba9e689a0e2d6d957fe9bdd49c8a0 EXConstants: 5d81e84ca71b9a552529889cc798b4a04e9e22b3 @@ -488,14 +488,14 @@ SPEC CHECKSUMS: EXPermissions: 99e52dc3e5f8e55153f1958004f6df2a30a1f2f5 EXWebBrowser: def838b95aa9d396f9ce71ace4e614ee16e7ee30 Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74 - Firebase: 8432d732974498afd5987e9001a05f90f1a3d625 - FirebaseAnalytics: 470ddab7253b21ad5a40bebd4a9903d7ae19386a - FirebaseCore: 68f8a7f50cdae542715d4e86afa37c4067217dcb - FirebaseInstanceID: f20243a1d828e0e9a3798b995174dedc16f1b32a + Firebase: dedc9e48ea3f3649ad5f6b982f8a0c73508a14b5 + FirebaseAnalytics: 3fb375bc9d13779add4039716f868d233a473fad + FirebaseCore: aecf02fb2274ec361b9bebeac112f5daa18273bd + FirebaseInstanceID: 662b8108a09fe9ed01aafdedba100fde8536b0f6 Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 glog: 1f3da668190260b06b429bb211bfbee5cd790c28 - GoogleAppMeasurement: a35a645835bae31b6bdc0576396bc23908f12a22 - GoogleUtilities: c7a0b08bda3bf808be823ed151f0e28ac6866e71 + GoogleAppMeasurement: 183bd916af7f80deb67c01888368f1108d641832 + GoogleUtilities: d2b0e277a95962e09bb27f5cd42f5f0b6a506c7d libwebp: b068a3bd7c45f7460f6715be7bed1a18fd5d6b48 nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022 @@ -538,7 +538,7 @@ SPEC CHECKSUMS: RNScreens: f28b48b8345f2f5f39ed6195518291515032a788 RNUserDefaults: 8a4928443510aa99e4ccb3b53f1bf186593d690b RNVectorIcons: 6607bd3a30291d0edb56f9bbe7ae411ee2b928b0 - RSKImageCropper: 98296ad26b41753f796b6898d015509598f13d97 + RSKImageCropper: a446db0e8444a036b34f3c43db01b2373baa4b2a SDWebImage: 920f1a2ff1ca8296ad34f6e0510a1ef1d70ac965 SDWebImageWebPCoder: 7568737603c50f6237850afedd7e9e28e5917e6b UMBarCodeScannerInterface: 84ea2d6b58ff0dc27ef9b68bab71286be18ee020 diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics index 40a6046b1..968b70585 100755 Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics and b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics differ diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist index f988851aa..b537397b9 100644 Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist and b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist differ diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols index 4da1eb21d..6275ba5b3 100755 Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols and b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols differ diff --git a/ios/Pods/Firebase/CoreOnly/Sources/Firebase.h b/ios/Pods/Firebase/CoreOnly/Sources/Firebase.h index c9f033de9..5e060e0c1 100755 --- a/ios/Pods/Firebase/CoreOnly/Sources/Firebase.h +++ b/ios/Pods/Firebase/CoreOnly/Sources/Firebase.h @@ -61,6 +61,10 @@ Firebase Messaging works as intended." #endif #endif + #if __has_include() + #import + #endif + #if __has_include() #import #endif diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector index 04c530822..388ad2f6e 100755 Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector and b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector differ diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/Modules/module.modulemap b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/Modules/module.modulemap index 270ad21f1..99a4b1dc2 100755 --- a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/Modules/module.modulemap +++ b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/Modules/module.modulemap @@ -3,6 +3,7 @@ framework module FIRAnalyticsConnector { module * { export * } link "sqlite3" link "z" + link framework "CoreData" link framework "Security" link framework "StoreKit" link framework "SystemConfiguration" diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics index 599ead001..3c2f2cd9c 100755 Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics and b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics differ diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap index 6118e372e..d7c59054a 100755 --- a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap +++ b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap @@ -4,6 +4,7 @@ framework module FirebaseAnalytics { module * { export * } link "sqlite3" link "z" + link framework "CoreData" link framework "Security" link framework "StoreKit" link framework "SystemConfiguration" diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics index fadbea4b8..ad795ac61 100755 Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics and b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics differ diff --git a/ios/Pods/FirebaseCore/Firebase/Core/FIROptions.m b/ios/Pods/FirebaseCore/Firebase/Core/FIROptions.m index 3e6a3a045..89a679a74 100644 --- a/ios/Pods/FirebaseCore/Firebase/Core/FIROptions.m +++ b/ios/Pods/FirebaseCore/Firebase/Core/FIROptions.m @@ -179,6 +179,7 @@ static NSDictionary *sDefaultOptionsDictionary = nil; if (newOptions) { newOptions.optionsDictionary = self.optionsDictionary; newOptions.deepLinkURLScheme = self.deepLinkURLScheme; + newOptions.appGroupID = self.appGroupID; newOptions.editingLocked = self.isEditingLocked; newOptions.usingOptionsFromDefaultPlist = self.usingOptionsFromDefaultPlist; } @@ -340,6 +341,11 @@ static NSDictionary *sDefaultOptionsDictionary = nil; _optionsDictionary[kFIRBundleID] = [bundleID copy]; } +- (void)setAppGroupID:(NSString *)appGroupID { + [self checkEditingLocked]; + _appGroupID = [appGroupID copy]; +} + #pragma mark - Internal instance methods - (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary { diff --git a/ios/Pods/FirebaseCore/Firebase/Core/Public/FIROptions.h b/ios/Pods/FirebaseCore/Firebase/Core/Public/FIROptions.h index 87a01ddc7..67fbe5ba4 100644 --- a/ios/Pods/FirebaseCore/Firebase/Core/Public/FIROptions.h +++ b/ios/Pods/FirebaseCore/Firebase/Core/Public/FIROptions.h @@ -90,6 +90,13 @@ NS_SWIFT_NAME(FirebaseOptions) */ @property(nonatomic, copy, nullable) NSString *storageBucket; +/** + * The App Group identifier to share data between the application and the application extensions. + * The App Group must be configured in the application and on the Apple Developer Portal. Default + * value `nil`. + */ +@property(nonatomic, copy, nullable) NSString *appGroupID; + /** * Initializes a customized instance of FIROptions from the file at the given plist file path. This * will read the file synchronously from disk. diff --git a/ios/Pods/FirebaseCore/README.md b/ios/Pods/FirebaseCore/README.md index 031b03604..bf397f057 100644 --- a/ios/Pods/FirebaseCore/README.md +++ b/ios/Pods/FirebaseCore/README.md @@ -70,20 +70,24 @@ Instructions for installing binary frameworks via ## Development -Follow the subsequent instructions to develop, debug, unit test, run integration -tests, and try out reference samples: +To develop Firebase software in this repository, ensure that you have at least +the following software: -``` -$ git clone git@github.com:firebase/firebase-ios-sdk.git -$ cd firebase-ios-sdk/Example -$ pod update -$ open Firebase.xcworkspace -``` + * Xcode 10.1 (or later) + * CocoaPods 1.7.2 (or later) + +For the pod that you want to develop: + +`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open` Firestore and Functions have self contained Xcode projects. See [Firestore/README.md](Firestore/README.md) and [Functions/README.md](Functions/README.md). +### Adding a New Firebase Pod + +See [AddNewPod.md](AddNewPod.md). + ### Code Formatting To ensure that the code is formatted consistently, run the script @@ -92,9 +96,15 @@ before creating a PR. Travis will verify that any code changes are done in a style compliant way. Install `clang-format` and `swiftformat`. -This command will get the right `clang-format` version: +These commands will get the right versions: -`brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb` +``` +brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb +brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb +``` + +Note: if you already have a newer version of these installed you may need to +`brew switch` to this version. ### Running Unit Tests @@ -188,9 +198,9 @@ To install, add a subset of the following to the Podfile: pod 'FirebaseAuth' pod 'FirebaseCore' pod 'FirebaseDatabase' -pod 'FirebaseFirestore' # Only iOS and macOS +pod 'FirebaseFirestore' pod 'FirebaseFunctions' -pod 'FirebaseMessaging' # Only iOS and tvOS +pod 'FirebaseMessaging' pod 'FirebaseStorage' ``` diff --git a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID.m b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID.m index adc869549..735955573 100644 --- a/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID.m +++ b/ios/Pods/FirebaseInstanceID/Firebase/InstanceID/FIRInstanceID.m @@ -697,13 +697,7 @@ static FIRInstanceID *gInstanceID; userInfo:userInfo]; } -// If the firebaseApp is available we should send logs for the error through it before -// raising an exception. + (void)exitWithReason:(nonnull NSString *)reason forFirebaseApp:(FIRApp *)firebaseApp { - [firebaseApp sendLogsWithServiceName:kFIRIIDServiceInstanceID - version:FIRInstanceIDCurrentLibraryVersion() - error:[self configureErrorWithReason:reason]]; - [NSException raise:kFIRIIDErrorDomain format:@"Could not configure Firebase InstanceID. %@", reason]; } @@ -946,9 +940,9 @@ static FIRInstanceID *gInstanceID; object:[self.defaultFCMToken copy]]; [[NSNotificationQueue defaultQueue] enqueueNotification:tokenRefreshNotification postingStyle:NSPostASAP]; - - [self performDefaultTokenHandlerWithToken:token error:nil]; } + + [self performDefaultTokenHandlerWithToken:token error:nil]; } }; diff --git a/ios/Pods/FirebaseInstanceID/README.md b/ios/Pods/FirebaseInstanceID/README.md index 031b03604..bf397f057 100644 --- a/ios/Pods/FirebaseInstanceID/README.md +++ b/ios/Pods/FirebaseInstanceID/README.md @@ -70,20 +70,24 @@ Instructions for installing binary frameworks via ## Development -Follow the subsequent instructions to develop, debug, unit test, run integration -tests, and try out reference samples: +To develop Firebase software in this repository, ensure that you have at least +the following software: -``` -$ git clone git@github.com:firebase/firebase-ios-sdk.git -$ cd firebase-ios-sdk/Example -$ pod update -$ open Firebase.xcworkspace -``` + * Xcode 10.1 (or later) + * CocoaPods 1.7.2 (or later) + +For the pod that you want to develop: + +`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open` Firestore and Functions have self contained Xcode projects. See [Firestore/README.md](Firestore/README.md) and [Functions/README.md](Functions/README.md). +### Adding a New Firebase Pod + +See [AddNewPod.md](AddNewPod.md). + ### Code Formatting To ensure that the code is formatted consistently, run the script @@ -92,9 +96,15 @@ before creating a PR. Travis will verify that any code changes are done in a style compliant way. Install `clang-format` and `swiftformat`. -This command will get the right `clang-format` version: +These commands will get the right versions: -`brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb` +``` +brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb +brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb +``` + +Note: if you already have a newer version of these installed you may need to +`brew switch` to this version. ### Running Unit Tests @@ -188,9 +198,9 @@ To install, add a subset of the following to the Podfile: pod 'FirebaseAuth' pod 'FirebaseCore' pod 'FirebaseDatabase' -pod 'FirebaseFirestore' # Only iOS and macOS +pod 'FirebaseFirestore' pod 'FirebaseFunctions' -pod 'FirebaseMessaging' # Only iOS and tvOS +pod 'FirebaseMessaging' pod 'FirebaseStorage' ``` diff --git a/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement b/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement index 4be923424..f6585b4f7 100755 Binary files a/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement and b/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/GoogleAppMeasurement differ diff --git a/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/Modules/module.modulemap b/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/Modules/module.modulemap index b66fb6459..de80e9eae 100755 --- a/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/Modules/module.modulemap +++ b/ios/Pods/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.framework/Modules/module.modulemap @@ -3,6 +3,7 @@ framework module GoogleAppMeasurement { module * { export * } link "sqlite3" link "z" + link framework "CoreData" link framework "Security" link framework "StoreKit" link framework "SystemConfiguration" diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h b/ios/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h index aeb85df64..d7ebd8680 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h @@ -14,6 +14,7 @@ * limitations under the License. */ +#import #import #import diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h b/ios/Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h index fd22ba637..2320ed36d 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#import + typedef NS_ENUM(NSInteger, GULSwizzlerMessageCode) { // App Delegate Swizzling. kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000, // I-SWZ001000 diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m b/ios/Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m index 57e9d60ca..b177c3d01 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m @@ -17,7 +17,7 @@ #include #import -#import "Public/GULLoggerLevel.h" +#import /// ASL client facility name used by GULLogger. const char *kGULLoggerASLClientFacilityName = "com.google.utilities.logger"; diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GULLoggerLevel.h b/ios/Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GULLoggerLevel.h index 81ff212d7..f0ee435b8 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GULLoggerLevel.h +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GULLoggerLevel.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#import + /** * The log levels used by internal logging. */ diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkMessageCode.h b/ios/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkMessageCode.h index f9d1628a8..507bc5a5d 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkMessageCode.h +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Network/Private/GULNetworkMessageCode.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#import + // Make sure these codes do not overlap with any contained in the FIRAMessageCode enum. typedef NS_ENUM(NSInteger, GULNetworkMessageCode) { // GULNetwork.m diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h b/ios/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h index 283cdd5c1..373e0af40 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h +++ b/ios/Pods/GoogleUtilities/GoogleUtilities/Reachability/Private/GULReachabilityMessageCode.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#import + // Make sure these codes do not overlap with any contained in the FIRAMessageCode enum. typedef NS_ENUM(NSInteger, GULReachabilityMessageCode) { // GULReachabilityChecker.m diff --git a/ios/Pods/Manifest.lock b/ios/Pods/Manifest.lock index 3e6ff7296..35bbcb65c 100644 --- a/ios/Pods/Manifest.lock +++ b/ios/Pods/Manifest.lock @@ -1,6 +1,6 @@ PODS: - boost-for-react-native (1.63.0) - - Crashlytics (3.13.2): + - Crashlytics (3.13.4): - Fabric (~> 1.10.2) - DoubleConversion (1.1.6) - EXAppLoaderProvider (6.0.0) @@ -18,24 +18,24 @@ PODS: - EXWebBrowser (6.0.0): - UMCore - Fabric (1.10.2) - - Firebase/Core (6.3.0): + - Firebase/Core (6.5.0): - Firebase/CoreOnly - - FirebaseAnalytics (= 6.0.2) - - Firebase/CoreOnly (6.3.0): - - FirebaseCore (= 6.0.3) - - FirebaseAnalytics (6.0.2): - - FirebaseCore (~> 6.0) + - FirebaseAnalytics (= 6.0.4) + - Firebase/CoreOnly (6.5.0): + - FirebaseCore (= 6.1.0) + - FirebaseAnalytics (6.0.4): + - FirebaseCore (~> 6.1) - FirebaseInstanceID (~> 4.2) - - GoogleAppMeasurement (= 6.0.2) + - GoogleAppMeasurement (= 6.0.4) - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - nanopb (~> 0.3) - - FirebaseCore (6.0.3): + - FirebaseCore (6.1.0): - GoogleUtilities/Environment (~> 6.0) - GoogleUtilities/Logger (~> 6.0) - - FirebaseInstanceID (4.2.0): + - FirebaseInstanceID (4.2.2): - FirebaseCore (~> 6.0) - GoogleUtilities/Environment (~> 6.0) - GoogleUtilities/UserDefaults (~> 6.0) @@ -49,29 +49,29 @@ PODS: - DoubleConversion - glog - glog (0.3.5) - - GoogleAppMeasurement (6.0.2): + - GoogleAppMeasurement (6.0.4): - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - nanopb (~> 0.3) - - GoogleUtilities/AppDelegateSwizzler (6.2.1): + - GoogleUtilities/AppDelegateSwizzler (6.2.3): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (6.2.1) - - GoogleUtilities/Logger (6.2.1): + - GoogleUtilities/Environment (6.2.3) + - GoogleUtilities/Logger (6.2.3): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.2.1): + - GoogleUtilities/MethodSwizzler (6.2.3): - GoogleUtilities/Logger - - GoogleUtilities/Network (6.2.1): + - GoogleUtilities/Network (6.2.3): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.2.1)" - - GoogleUtilities/Reachability (6.2.1): + - "GoogleUtilities/NSData+zlib (6.2.3)" + - GoogleUtilities/Reachability (6.2.3): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.2.1): + - GoogleUtilities/UserDefaults (6.2.3): - GoogleUtilities/Logger - libwebp (1.0.2): - libwebp/core (= 1.0.2) @@ -230,7 +230,7 @@ PODS: - React - RNVectorIcons (6.4.2): - React - - RSKImageCropper (2.2.1) + - RSKImageCropper (2.2.3) - SDWebImage (5.0.6): - SDWebImage/Core (= 5.0.6) - SDWebImage/Core (5.0.6) @@ -479,7 +479,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c - Crashlytics: 611738c7847f8291a1a51084e35987b86ba6b3ee + Crashlytics: 2dfd686bcb918dc10ee0e76f7f853fe42c7bd552 DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 EXAppLoaderProvider: 7a8185228d8ba9e689a0e2d6d957fe9bdd49c8a0 EXConstants: 5d81e84ca71b9a552529889cc798b4a04e9e22b3 @@ -488,14 +488,14 @@ SPEC CHECKSUMS: EXPermissions: 99e52dc3e5f8e55153f1958004f6df2a30a1f2f5 EXWebBrowser: def838b95aa9d396f9ce71ace4e614ee16e7ee30 Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74 - Firebase: 8432d732974498afd5987e9001a05f90f1a3d625 - FirebaseAnalytics: 470ddab7253b21ad5a40bebd4a9903d7ae19386a - FirebaseCore: 68f8a7f50cdae542715d4e86afa37c4067217dcb - FirebaseInstanceID: f20243a1d828e0e9a3798b995174dedc16f1b32a + Firebase: dedc9e48ea3f3649ad5f6b982f8a0c73508a14b5 + FirebaseAnalytics: 3fb375bc9d13779add4039716f868d233a473fad + FirebaseCore: aecf02fb2274ec361b9bebeac112f5daa18273bd + FirebaseInstanceID: 662b8108a09fe9ed01aafdedba100fde8536b0f6 Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 glog: 1f3da668190260b06b429bb211bfbee5cd790c28 - GoogleAppMeasurement: a35a645835bae31b6bdc0576396bc23908f12a22 - GoogleUtilities: c7a0b08bda3bf808be823ed151f0e28ac6866e71 + GoogleAppMeasurement: 183bd916af7f80deb67c01888368f1108d641832 + GoogleUtilities: d2b0e277a95962e09bb27f5cd42f5f0b6a506c7d libwebp: b068a3bd7c45f7460f6715be7bed1a18fd5d6b48 nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022 @@ -538,7 +538,7 @@ SPEC CHECKSUMS: RNScreens: f28b48b8345f2f5f39ed6195518291515032a788 RNUserDefaults: 8a4928443510aa99e4ccb3b53f1bf186593d690b RNVectorIcons: 6607bd3a30291d0edb56f9bbe7ae411ee2b928b0 - RSKImageCropper: 98296ad26b41753f796b6898d015509598f13d97 + RSKImageCropper: a446db0e8444a036b34f3c43db01b2373baa4b2a SDWebImage: 920f1a2ff1ca8296ad34f6e0510a1ef1d70ac965 SDWebImageWebPCoder: 7568737603c50f6237850afedd7e9e28e5917e6b UMBarCodeScannerInterface: 84ea2d6b58ff0dc27ef9b68bab71286be18ee020 diff --git a/ios/Pods/RSKImageCropper/README.md b/ios/Pods/RSKImageCropper/README.md index 272c4445a..0bf3b265e 100644 --- a/ios/Pods/RSKImageCropper/README.md +++ b/ios/Pods/RSKImageCropper/README.md @@ -1,4 +1,4 @@ -## RSKImageCropper [![Build Status](https://travis-ci.org/ruslanskorb/RSKImageCropper.svg)](https://travis-ci.org/ruslanskorb/RSKImageCropper) [![CocoaPods](https://img.shields.io/cocoapods/dt/RSKImageCropper.svg?maxAge=3600)](https://cocoapods.org/pods/RSKImageCropper) [![Coverage Status](https://coveralls.io/repos/ruslanskorb/RSKImageCropper/badge.svg)](https://coveralls.io/r/ruslanskorb/RSKImageCropper) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/ruslanskorb/RSKImageCropper) +## RSKImageCropper [![Build Status](https://travis-ci.org/ruslanskorb/RSKImageCropper.svg)](https://travis-ci.org/ruslanskorb/RSKImageCropper) [![Coverage Status](https://coveralls.io/repos/ruslanskorb/RSKImageCropper/badge.svg)](https://coveralls.io/r/ruslanskorb/RSKImageCropper) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/ruslanskorb/RSKImageCropper)

Sample diff --git a/ios/Pods/RSKImageCropper/RSKImageCropper/CGGeometry+RSKImageCropper.h b/ios/Pods/RSKImageCropper/RSKImageCropper/CGGeometry+RSKImageCropper.h index 785727fa9..74dfa0d10 100644 --- a/ios/Pods/RSKImageCropper/RSKImageCropper/CGGeometry+RSKImageCropper.h +++ b/ios/Pods/RSKImageCropper/RSKImageCropper/CGGeometry+RSKImageCropper.h @@ -29,34 +29,42 @@ // Open Radar - http://www.openradar.me/16744288 // Work around this by redeclaring things here. -#undef cos -#define cos(__x) __tg_cos(__tg_promote1((__x))(__x)) +#ifdef __tg_promote1 -#undef sin -#define sin(__x) __tg_sin(__tg_promote1((__x))(__x)) + #undef cos + #define cos(__x) __tg_cos(__tg_promote1((__x))(__x)) -#undef atan2 -#define atan2(__x, __y) __tg_atan2(__tg_promote2((__x), (__y))(__x), \ -__tg_promote2((__x), (__y))(__y)) + #undef sin + #define sin(__x) __tg_sin(__tg_promote1((__x))(__x)) -#undef pow -#define pow(__x, __y) __tg_pow(__tg_promote2((__x), (__y))(__x), \ -__tg_promote2((__x), (__y))(__y)) + #undef sqrt + #define sqrt(__x) __tg_sqrt(__tg_promote1((__x))(__x)) -#undef sqrt -#define sqrt(__x) __tg_sqrt(__tg_promote1((__x))(__x)) + #undef fabs + #define fabs(__x) __tg_fabs(__tg_promote1((__x))(__x)) -#undef fabs -#define fabs(__x) __tg_fabs(__tg_promote1((__x))(__x)) + #undef ceil + #define ceil(__x) __tg_ceil(__tg_promote1((__x))(__x)) -#undef ceil -#define ceil(__x) __tg_ceil(__tg_promote1((__x))(__x)) + #undef floor + #define floor(__x) __tg_floor(__tg_promote1((__x))(__x)) -#undef floor -#define floor(__x) __tg_floor(__tg_promote1((__x))(__x)) + #undef round + #define round(__x) __tg_round(__tg_promote1((__x))(__x)) -#undef round -#define round(__x) __tg_round(__tg_promote1((__x))(__x)) +#endif /* __tg_promote1 */ + +#ifdef __tg_promote2 + + #undef atan2 + #define atan2(__x, __y) __tg_atan2(__tg_promote2((__x), (__y))(__x), \ + __tg_promote2((__x), (__y))(__y)) + + #undef pow + #define pow(__x, __y) __tg_pow(__tg_promote2((__x), (__y))(__x), \ + __tg_promote2((__x), (__y))(__y)) + +#endif /* __tg_promote2 */ #ifdef CGFLOAT_IS_DOUBLE #define RSK_EPSILON DBL_EPSILON diff --git a/ios/Pods/RSKImageCropper/RSKImageCropper/RSKImageCropViewController.m b/ios/Pods/RSKImageCropper/RSKImageCropper/RSKImageCropViewController.m index 8daf83db9..1f584da1f 100644 --- a/ios/Pods/RSKImageCropper/RSKImageCropper/RSKImageCropViewController.m +++ b/ios/Pods/RSKImageCropper/RSKImageCropper/RSKImageCropViewController.m @@ -448,6 +448,7 @@ static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25; CGAffineTransform imageScrollViewTransform = self.imageScrollView.transform; self.imageScrollView.transform = CGAffineTransformIdentity; + CGPoint imageScrollViewContentOffset = self.imageScrollView.contentOffset; CGRect imageScrollViewFrame = self.imageScrollView.frame; self.imageScrollView.frame = self.maskRect; @@ -485,6 +486,7 @@ static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25; cropRect = CGRectApplyAffineTransform(cropRect, CGAffineTransformMakeScale(imageScale, imageScale)); self.imageScrollView.frame = imageScrollViewFrame; + self.imageScrollView.contentOffset = imageScrollViewContentOffset; self.imageScrollView.transform = imageScrollViewTransform; return cropRect; @@ -594,6 +596,7 @@ static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25; CGFloat rotation = (rotationAngle - self.rotationAngle); CGAffineTransform transform = CGAffineTransformRotate(self.imageScrollView.transform, rotation); self.imageScrollView.transform = transform; + [self layoutImageScrollView]; } } @@ -630,7 +633,10 @@ static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25; - (void)handleRotation:(UIRotationGestureRecognizer *)gestureRecognizer { - [self setRotationAngle:(self.rotationAngle + gestureRecognizer.rotation)]; + CGFloat rotation = gestureRecognizer.rotation; + CGAffineTransform transform = CGAffineTransformRotate(self.imageScrollView.transform, rotation); + self.imageScrollView.transform = transform; + gestureRecognizer.rotation = 0; if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { @@ -668,7 +674,6 @@ static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25; } [self resetRotation]; - [self resetFrame]; [self resetZoomScale]; [self resetContentOffset]; @@ -697,11 +702,6 @@ static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25; self.imageScrollView.contentOffset = contentOffset; } -- (void)resetFrame -{ - [self layoutImageScrollView]; -} - - (void)resetRotation { [self setRotationAngle:0.0]; @@ -766,6 +766,25 @@ static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25; } } +- (void)centerImageScrollViewZoomView +{ + // center imageScrollView.zoomView as it becomes smaller than the size of the screen + + CGPoint contentOffset = self.imageScrollView.contentOffset; + + // center vertically + if (self.imageScrollView.contentSize.height < CGRectGetHeight(self.imageScrollView.bounds)) { + contentOffset.y = -(CGRectGetHeight(self.imageScrollView.bounds) - self.imageScrollView.contentSize.height) * 0.5f; + } + + // center horizontally + if (self.imageScrollView.contentSize.width < CGRectGetWidth(self.imageScrollView.bounds)) { + contentOffset.x = -(CGRectGetWidth(self.imageScrollView.bounds) - self.imageScrollView.contentSize.width) * 0.5f;; + } + + self.imageScrollView.contentOffset = contentOffset; +} + - (void)layoutImageScrollView { CGRect frame = CGRectZero; @@ -842,7 +861,10 @@ static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25; CGAffineTransform transform = self.imageScrollView.transform; self.imageScrollView.transform = CGAffineTransformIdentity; + self.imageScrollView.frame = frame; + [self centerImageScrollViewZoomView]; + self.imageScrollView.transform = transform; } diff --git a/ios/Pods/RSKImageCropper/RSKImageCropper/RSKImageScrollView.m b/ios/Pods/RSKImageCropper/RSKImageCropper/RSKImageScrollView.m index b171cd4ad..05a71d036 100755 --- a/ios/Pods/RSKImageCropper/RSKImageCropper/RSKImageScrollView.m +++ b/ios/Pods/RSKImageCropper/RSKImageCropper/RSKImageScrollView.m @@ -136,41 +136,20 @@ { // center zoomView as it becomes smaller than the size of the screen - // we need to use contentInset instead of contentOffset for better positioning when zoomView fills the screen - if (self.aspectFill) { - CGFloat top = 0; - CGFloat left = 0; - - // center vertically - if (self.contentSize.height < CGRectGetHeight(self.bounds)) { - top = (CGRectGetHeight(self.bounds) - self.contentSize.height) * 0.5f; - } - - // center horizontally - if (self.contentSize.width < CGRectGetWidth(self.bounds)) { - left = (CGRectGetWidth(self.bounds) - self.contentSize.width) * 0.5f; - } - - self.contentInset = UIEdgeInsetsMake(top, left, top, left); - } else { - CGRect frameToCenter = self.zoomView.frame; - - // center horizontally - if (CGRectGetWidth(frameToCenter) < CGRectGetWidth(self.bounds)) { - frameToCenter.origin.x = (CGRectGetWidth(self.bounds) - CGRectGetWidth(frameToCenter)) * 0.5f; - } else { - frameToCenter.origin.x = 0; - } - - // center vertically - if (CGRectGetHeight(frameToCenter) < CGRectGetHeight(self.bounds)) { - frameToCenter.origin.y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(frameToCenter)) * 0.5f; - } else { - frameToCenter.origin.y = 0; - } - - self.zoomView.frame = frameToCenter; + CGFloat top = 0; + CGFloat left = 0; + + // center vertically + if (self.contentSize.height < CGRectGetHeight(self.bounds)) { + top = (CGRectGetHeight(self.bounds) - self.contentSize.height) * 0.5f; } + + // center horizontally + if (self.contentSize.width < CGRectGetWidth(self.bounds)) { + left = (CGRectGetWidth(self.bounds) - self.contentSize.width) * 0.5f; + } + + self.contentInset = UIEdgeInsetsMake(top, left, top, left); } #pragma mark - Configure scrollView to display new image @@ -231,7 +210,7 @@ if (minScale > maxScale) { minScale = maxScale; } - + self.maximumZoomScale = maxScale; self.minimumZoomScale = minScale; } diff --git a/ios/Pods/Target Support Files/FirebaseCore/FirebaseCore.xcconfig b/ios/Pods/Target Support Files/FirebaseCore/FirebaseCore.xcconfig index 506f9440a..abc644f4b 100644 --- a/ios/Pods/Target Support Files/FirebaseCore/FirebaseCore.xcconfig +++ b/ios/Pods/Target Support Files/FirebaseCore/FirebaseCore.xcconfig @@ -1,6 +1,6 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore GCC_C_LANGUAGE_STANDARD = c99 -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 FIRCore_VERSION=6.0.3 Firebase_VERSION=6.3.0 +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 FIRCore_VERSION=6.1.0 Firebase_VERSION=6.5.0 HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/FirebaseCore" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/FirebaseCore" "${PODS_ROOT}/Headers/Public/GoogleUtilities" OTHER_CFLAGS = -fno-autolink PODS_BUILD_DIR = ${BUILD_DIR} diff --git a/ios/Pods/Target Support Files/FirebaseInstanceID/FirebaseInstanceID.xcconfig b/ios/Pods/Target Support Files/FirebaseInstanceID/FirebaseInstanceID.xcconfig index 094c9719b..c7761a133 100644 --- a/ios/Pods/Target Support Files/FirebaseInstanceID/FirebaseInstanceID.xcconfig +++ b/ios/Pods/Target Support Files/FirebaseInstanceID/FirebaseInstanceID.xcconfig @@ -1,6 +1,6 @@ CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstanceID GCC_C_LANGUAGE_STANDARD = c99 -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 FIRInstanceID_LIB_VERSION=4.2.0 +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 FIRInstanceID_LIB_VERSION=4.2.2 HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/FirebaseInstanceID" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/FirebaseCore" "${PODS_ROOT}/Headers/Public/FirebaseInstanceID" "${PODS_ROOT}/Headers/Public/GoogleUtilities" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index d72dba862..38d6afd3b 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -737,7 +737,7 @@ $PODS_CONFIGURATION_BUILD_DIR/Firebase, ); INFOPLIST_FILE = ShareRocketChatRN/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -745,7 +745,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "Development chat.rocket.reactnative.ShareExtension"; SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -785,14 +785,14 @@ $PODS_CONFIGURATION_BUILD_DIR/Firebase, ); INFOPLIST_FILE = ShareRocketChatRN/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.2; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "chat.rocket.reactnative.ShareExtension AppStore"; SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git a/ios/ShareRocketChatRN/Info.plist b/ios/ShareRocketChatRN/Info.plist index 926b25005..6875a03a1 100644 --- a/ios/ShareRocketChatRN/Info.plist +++ b/ios/ShareRocketChatRN/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.0 + 1.17.0 CFBundleVersion 1 NSAppTransportSecurity diff --git a/yarn.lock b/yarn.lock index 1bd1afd03..154e213d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3688,11 +3688,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base-64@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" - integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs= - base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.3: version "1.3.0" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" @@ -6792,18 +6787,6 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" - integrity sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo= - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" @@ -9934,7 +9917,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==