Deep linking (#291)
* deep linking * Basic deep link working * Deep link routing * Multiple servers working * Send user to the room
This commit is contained in:
parent
33baf35de6
commit
69513a8327
|
@ -33,11 +33,19 @@
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
|
android:launchMode="singleTop"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter android:label="RocketChatRN">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="https" android:host="go.rocket.chat" />
|
||||||
|
<data android:scheme="rocketchat" android:host="*" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,7 @@ export const PINNED_MESSAGES = createRequestTypes('PINNED_MESSAGES', ['OPEN', 'R
|
||||||
export const MENTIONED_MESSAGES = createRequestTypes('MENTIONED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
export const MENTIONED_MESSAGES = createRequestTypes('MENTIONED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||||
export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||||
export const ROOM_FILES = createRequestTypes('ROOM_FILES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
export const ROOM_FILES = createRequestTypes('ROOM_FILES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||||
|
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
|
||||||
|
|
||||||
export const INCREMENT = 'INCREMENT';
|
export const INCREMENT = 'INCREMENT';
|
||||||
export const DECREMENT = 'DECREMENT';
|
export const DECREMENT = 'DECREMENT';
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import * as types from './actionsTypes';
|
||||||
|
|
||||||
|
export function deepLinkingOpen(params) {
|
||||||
|
return {
|
||||||
|
type: types.DEEP_LINKING.OPEN,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,13 +1,16 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Linking } from 'react-native';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import SplashScreen from 'react-native-splash-screen';
|
import SplashScreen from 'react-native-splash-screen';
|
||||||
import { appInit } from '../actions';
|
|
||||||
|
|
||||||
|
import { appInit } from '../actions';
|
||||||
|
import { deepLinkingOpen } from '../actions/deepLinking';
|
||||||
import AuthRoutes from './routes/AuthRoutes';
|
import AuthRoutes from './routes/AuthRoutes';
|
||||||
import PublicRoutes from './routes/PublicRoutes';
|
import PublicRoutes from './routes/PublicRoutes';
|
||||||
import * as NavigationService from './routes/NavigationService';
|
import * as NavigationService from './routes/NavigationService';
|
||||||
|
import parseQuery from '../lib/methods/helpers/parseQuery';
|
||||||
|
|
||||||
@connect(
|
@connect(
|
||||||
state => ({
|
state => ({
|
||||||
|
@ -16,7 +19,7 @@ import * as NavigationService from './routes/NavigationService';
|
||||||
background: state.app.background
|
background: state.app.background
|
||||||
}),
|
}),
|
||||||
dispatch => bindActionCreators({
|
dispatch => bindActionCreators({
|
||||||
appInit
|
appInit, deepLinkingOpen
|
||||||
}, dispatch)
|
}, dispatch)
|
||||||
)
|
)
|
||||||
export default class Routes extends React.Component {
|
export default class Routes extends React.Component {
|
||||||
|
@ -26,11 +29,22 @@ export default class Routes extends React.Component {
|
||||||
appInit: PropTypes.func.isRequired
|
appInit: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.handleOpenURL = this.handleOpenURL.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.app.ready) {
|
if (this.props.app.ready) {
|
||||||
return SplashScreen.hide();
|
return SplashScreen.hide();
|
||||||
}
|
}
|
||||||
this.props.appInit();
|
this.props.appInit();
|
||||||
|
|
||||||
|
Linking
|
||||||
|
.getInitialURL()
|
||||||
|
.then(url => this.handleOpenURL({ url }))
|
||||||
|
.catch(console.error);
|
||||||
|
Linking.addEventListener('url', this.handleOpenURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
@ -43,6 +57,18 @@ export default class Routes extends React.Component {
|
||||||
NavigationService.setNavigator(this.navigator);
|
NavigationService.setNavigator(this.navigator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOpenURL({ url }) {
|
||||||
|
if (url) {
|
||||||
|
url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, '');
|
||||||
|
const regex = /^(room|auth)\?/;
|
||||||
|
if (url.match(regex)) {
|
||||||
|
url = url.replace(regex, '');
|
||||||
|
const params = parseQuery(url);
|
||||||
|
this.props.deepLinkingOpen(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { login } = this.props;
|
const { login } = this.props;
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ export function goRoomsList() {
|
||||||
|
|
||||||
export function goRoom({ rid, name }, counter = 0) {
|
export function goRoom({ rid, name }, counter = 0) {
|
||||||
// about counter: we can call this method before navigator be set. so we have to wait, if we tried a lot, we give up ...
|
// about counter: we can call this method before navigator be set. so we have to wait, if we tried a lot, we give up ...
|
||||||
if (!rid || !name || counter > 10) {
|
if (!rid || counter > 10) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!config.navigator) {
|
if (!config.navigator) {
|
||||||
|
|
|
@ -224,6 +224,8 @@ export default class Socket extends EventEmitter {
|
||||||
}
|
}
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this._close();
|
this._close();
|
||||||
|
this._login = null;
|
||||||
|
this.subscriptions = {};
|
||||||
}
|
}
|
||||||
async reconnect() {
|
async reconnect() {
|
||||||
if (this._timer) {
|
if (this._timer) {
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { post } from './helpers/rest';
|
||||||
|
import database from '../realm';
|
||||||
|
|
||||||
|
// TODO: api fix
|
||||||
|
const ddpTypes = {
|
||||||
|
channel: 'c', direct: 'd', group: 'p'
|
||||||
|
};
|
||||||
|
const restTypes = {
|
||||||
|
channel: 'channels', direct: 'im', group: 'groups'
|
||||||
|
};
|
||||||
|
|
||||||
|
async function canOpenRoomREST({ type, rid }) {
|
||||||
|
try {
|
||||||
|
const { token, id } = this.ddp._login;
|
||||||
|
const server = this.ddp.url.replace('ws', 'http');
|
||||||
|
await post({ token, id, server }, `${ restTypes[type] }.open`, { roomId: rid });
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
// TODO: workround for 'already open for the sender' error
|
||||||
|
if (!error.errorType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function canOpenRoomDDP(...args) {
|
||||||
|
try {
|
||||||
|
const [{ type, name }] = args;
|
||||||
|
await this.ddp.call('getRoomByTypeAndName', ddpTypes[type], name);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.isClientSafe) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return canOpenRoomREST.call(this, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function canOpenRoom({ rid, path }) {
|
||||||
|
const { database: db } = database;
|
||||||
|
const room = db.objects('subscriptions').filtered('rid == $0', rid);
|
||||||
|
if (room.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [type, name] = path.split('/');
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const data = await (this.ddp && this.ddp.status ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid }));
|
||||||
|
return data;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
export default function(query) {
|
||||||
|
return (/^[?#]/.test(query) ? query.slice(1) : query)
|
||||||
|
.split('&')
|
||||||
|
.reduce((params, param) => {
|
||||||
|
const [key, value] = param.split('=');
|
||||||
|
params[key] = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : '';
|
||||||
|
return params;
|
||||||
|
}, { });
|
||||||
|
}
|
|
@ -21,8 +21,10 @@ const stop = (ddp) => {
|
||||||
promises = false;
|
promises = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ddp.removeListener('logged', logged);
|
if (ddp) {
|
||||||
ddp.removeListener('disconnected', disconnected);
|
ddp.removeListener('logged', logged);
|
||||||
|
ddp.removeListener('disconnected', disconnected);
|
||||||
|
}
|
||||||
|
|
||||||
logged = false;
|
logged = false;
|
||||||
disconnected = false;
|
disconnected = false;
|
||||||
|
@ -50,18 +52,21 @@ export default async function subscribeRoom({ rid, t }) {
|
||||||
}, 5000);
|
}, 5000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!this.ddp || !this.ddp.status) {
|
||||||
logged = this.ddp.on('logged', () => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
timer = false;
|
|
||||||
promises = subscribe(this.ddp, rid);
|
|
||||||
});
|
|
||||||
|
|
||||||
disconnected = this.ddp.on('disconnected', () => { loop(); });
|
|
||||||
|
|
||||||
if (!this.ddp.status) {
|
|
||||||
loop();
|
loop();
|
||||||
} else {
|
} else {
|
||||||
|
logged = this.ddp.on('logged', () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = false;
|
||||||
|
promises = subscribe(this.ddp, rid);
|
||||||
|
});
|
||||||
|
|
||||||
|
disconnected = this.ddp.on('disconnected', () => {
|
||||||
|
if (this._login) {
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
promises = subscribe(this.ddp, rid);
|
promises = subscribe(this.ddp, rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,35 +24,41 @@ export default async function subscribeRooms(id) {
|
||||||
}, 5000);
|
}, 5000);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ddp.on('logged', () => {
|
if (this.ddp) {
|
||||||
clearTimeout(timer);
|
this.ddp.on('logged', () => {
|
||||||
timer = false;
|
clearTimeout(timer);
|
||||||
});
|
timer = false;
|
||||||
|
});
|
||||||
|
|
||||||
this.ddp.on('logout', () => {
|
this.ddp.on('logout', () => {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = true;
|
timer = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.ddp.on('disconnected', () => { loop(); });
|
this.ddp.on('disconnected', () => {
|
||||||
|
if (this._login) {
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.ddp.on('stream-notify-user', (ddpMessage) => {
|
this.ddp.on('stream-notify-user', (ddpMessage) => {
|
||||||
const [type, data] = ddpMessage.fields.args;
|
const [type, data] = ddpMessage.fields.args;
|
||||||
const [, ev] = ddpMessage.fields.eventName.split('/');
|
const [, ev] = ddpMessage.fields.eventName.split('/');
|
||||||
if (/subscriptions/.test(ev)) {
|
if (/subscriptions/.test(ev)) {
|
||||||
const tpm = merge(data);
|
const tpm = merge(data);
|
||||||
return database.write(() => {
|
return database.write(() => {
|
||||||
database.create('subscriptions', tpm, true);
|
database.create('subscriptions', tpm, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (/rooms/.test(ev) && type === 'updated') {
|
if (/rooms/.test(ev) && type === 'updated') {
|
||||||
const [sub] = database.objects('subscriptions').filtered('rid == $0', data._id);
|
const [sub] = database.objects('subscriptions').filtered('rid == $0', data._id);
|
||||||
database.write(() => {
|
database.write(() => {
|
||||||
merge(sub, data);
|
merge(sub, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await subscriptions;
|
await subscriptions;
|
||||||
console.log(this.ddp.subscriptions);
|
// console.log(this.ddp.subscriptions);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ import getSettings from './methods/getSettings';
|
||||||
import getRooms from './methods/getRooms';
|
import getRooms from './methods/getRooms';
|
||||||
import getPermissions from './methods/getPermissions';
|
import getPermissions from './methods/getPermissions';
|
||||||
import getCustomEmoji from './methods/getCustomEmojis';
|
import getCustomEmoji from './methods/getCustomEmojis';
|
||||||
|
import canOpenRoom from './methods/canOpenRoom';
|
||||||
|
|
||||||
import _buildMessage from './methods/helpers/buildMessage';
|
import _buildMessage from './methods/helpers/buildMessage';
|
||||||
import loadMessagesForRoom from './methods/loadMessagesForRoom';
|
import loadMessagesForRoom from './methods/loadMessagesForRoom';
|
||||||
|
@ -51,6 +51,7 @@ const RocketChat = {
|
||||||
TOKEN_KEY,
|
TOKEN_KEY,
|
||||||
subscribeRooms,
|
subscribeRooms,
|
||||||
subscribeRoom,
|
subscribeRoom,
|
||||||
|
canOpenRoom,
|
||||||
createChannel({ name, users, type }) {
|
createChannel({ name, users, type }) {
|
||||||
return call(type ? 'createChannel' : 'createPrivateGroup', name, users, type);
|
return call(type ? 'createChannel' : 'createPrivateGroup', name, users, type);
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,7 +17,10 @@ const getToken = function* getToken() {
|
||||||
}
|
}
|
||||||
return JSON.parse(user);
|
return JSON.parse(user);
|
||||||
}
|
}
|
||||||
return yield put(setToken());
|
|
||||||
|
yield AsyncStorage.removeItem(RocketChat.TOKEN_KEY);
|
||||||
|
yield put(setToken());
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { AsyncStorage } from 'react-native';
|
||||||
|
import { delay } from 'redux-saga';
|
||||||
|
import { takeLatest, take, select, call, put } from 'redux-saga/effects';
|
||||||
|
import * as types from '../actions/actionsTypes';
|
||||||
|
import { setServer, addServer } from '../actions/server';
|
||||||
|
import * as NavigationService from '../containers/routes/NavigationService';
|
||||||
|
import database from '../lib/realm';
|
||||||
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
|
||||||
|
const navigate = function* go({ server, params, sameServer = true }) {
|
||||||
|
const user = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||||
|
if (user) {
|
||||||
|
const { rid, path } = params;
|
||||||
|
if (rid) {
|
||||||
|
const canOpenRoom = yield RocketChat.canOpenRoom({ rid, path });
|
||||||
|
if (canOpenRoom) {
|
||||||
|
return yield call(NavigationService.goRoom, { rid: params.rid });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!sameServer) {
|
||||||
|
yield call(NavigationService.goRoomsList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpen = function* handleOpen({ params }) {
|
||||||
|
const isReady = yield select(state => state.app.ready);
|
||||||
|
const server = yield select(state => state.server.server);
|
||||||
|
|
||||||
|
if (!isReady) {
|
||||||
|
yield take(types.APP.READY);
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = `https://${ params.host }`;
|
||||||
|
|
||||||
|
// TODO: needs better test
|
||||||
|
// if deep link is from same server
|
||||||
|
if (server === host) {
|
||||||
|
yield navigate({ server, params });
|
||||||
|
} else { // if deep link is from a different server
|
||||||
|
// search if deep link's server already exists
|
||||||
|
const servers = yield database.databases.serversDB.objects('servers').filtered('id = $0', host); // TODO: need better test
|
||||||
|
if (servers.length) {
|
||||||
|
// if server exists, select it
|
||||||
|
yield put(setServer(servers[0].id));
|
||||||
|
yield delay(2000);
|
||||||
|
yield navigate({ server: servers[0].id, params, sameServer: false });
|
||||||
|
} else {
|
||||||
|
yield put(addServer(host));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const root = function* root() {
|
||||||
|
yield takeLatest(types.DEEP_LINKING.OPEN, handleOpen);
|
||||||
|
};
|
||||||
|
export default root;
|
|
@ -12,6 +12,7 @@ import pinnedMessages from './pinnedMessages';
|
||||||
import mentionedMessages from './mentionedMessages';
|
import mentionedMessages from './mentionedMessages';
|
||||||
import snippetedMessages from './snippetedMessages';
|
import snippetedMessages from './snippetedMessages';
|
||||||
import roomFiles from './roomFiles';
|
import roomFiles from './roomFiles';
|
||||||
|
import deepLinking from './deepLinking';
|
||||||
|
|
||||||
const root = function* root() {
|
const root = function* root() {
|
||||||
yield all([
|
yield all([
|
||||||
|
@ -27,7 +28,8 @@ const root = function* root() {
|
||||||
pinnedMessages(),
|
pinnedMessages(),
|
||||||
mentionedMessages(),
|
mentionedMessages(),
|
||||||
snippetedMessages(),
|
snippetedMessages(),
|
||||||
roomFiles()
|
roomFiles(),
|
||||||
|
deepLinking()
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { put, call, takeLatest, race, take } from 'redux-saga/effects';
|
import { put, call, takeLatest, take } from 'redux-saga/effects';
|
||||||
import { delay } from 'redux-saga';
|
import { delay } from 'redux-saga';
|
||||||
import { AsyncStorage } from 'react-native';
|
import { AsyncStorage } from 'react-native';
|
||||||
import { SERVER } from '../actions/actionsTypes';
|
import { SERVER, LOGIN } from '../actions/actionsTypes';
|
||||||
import * as actions from '../actions';
|
import * as actions from '../actions';
|
||||||
import { connectRequest } from '../actions/connect';
|
import { connectRequest } from '../actions/connect';
|
||||||
import { serverSuccess, serverFailure, serverRequest, setServer } from '../actions/server';
|
import { serverSuccess, serverFailure, setServer } from '../actions/server';
|
||||||
import { setRoles } from '../actions/roles';
|
import { setRoles } from '../actions/roles';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import * as NavigationService from '../containers/routes/NavigationService';
|
import { navigate } from '../containers/routes/NavigationService';
|
||||||
|
|
||||||
const validate = function* validate(server) {
|
const validate = function* validate(server) {
|
||||||
return yield RocketChat.testServer(server);
|
return yield RocketChat.testServer(server);
|
||||||
|
@ -21,6 +21,7 @@ const selectServer = function* selectServer({ server }) {
|
||||||
// yield RocketChat.disconnect();
|
// yield RocketChat.disconnect();
|
||||||
|
|
||||||
yield call([AsyncStorage, 'setItem'], 'currentServer', server);
|
yield call([AsyncStorage, 'setItem'], 'currentServer', server);
|
||||||
|
// yield AsyncStorage.removeItem(RocketChat.TOKEN_KEY);
|
||||||
const settings = database.objects('settings');
|
const settings = database.objects('settings');
|
||||||
yield put(actions.setAllSettings(RocketChat.parseSettings(settings.slice(0, settings.length))));
|
yield put(actions.setAllSettings(RocketChat.parseSettings(settings.slice(0, settings.length))));
|
||||||
const permissions = database.objects('permissions');
|
const permissions = database.objects('permissions');
|
||||||
|
@ -33,7 +34,7 @@ const selectServer = function* selectServer({ server }) {
|
||||||
return result;
|
return result;
|
||||||
}, {})));
|
}, {})));
|
||||||
|
|
||||||
yield put(connectRequest(server));
|
yield put(connectRequest());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('selectServer', e);
|
console.warn('selectServer', e);
|
||||||
}
|
}
|
||||||
|
@ -51,23 +52,17 @@ const validateServer = function* validateServer({ server }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const addServer = function* addServer({ server }) {
|
const addServer = function* addServer({ server }) {
|
||||||
yield put(serverRequest(server));
|
database.databases.serversDB.write(() => {
|
||||||
|
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
||||||
const { error } = yield race({
|
|
||||||
error: take(SERVER.FAILURE),
|
|
||||||
success: take(SERVER.SUCCESS)
|
|
||||||
});
|
});
|
||||||
if (!error) {
|
yield put(setServer(server));
|
||||||
database.databases.serversDB.write(() => {
|
yield take(LOGIN.SET_TOKEN);
|
||||||
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
navigate('LoginSignup');
|
||||||
});
|
|
||||||
yield put(setServer(server));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGotoAddServer = function* handleGotoAddServer() {
|
const handleGotoAddServer = function* handleGotoAddServer() {
|
||||||
yield call(AsyncStorage.removeItem, RocketChat.TOKEN_KEY);
|
yield call(AsyncStorage.removeItem, RocketChat.TOKEN_KEY);
|
||||||
yield call(NavigationService.navigate, 'AddServer');
|
yield call(navigate, 'AddServer');
|
||||||
};
|
};
|
||||||
|
|
||||||
const root = function* root() {
|
const root = function* root() {
|
||||||
|
|
|
@ -49,7 +49,6 @@ export default class NewServerView extends LoggedView {
|
||||||
|
|
||||||
submit = () => {
|
submit = () => {
|
||||||
this.props.addServer(this.completeUrl(this.state.text));
|
this.props.addServer(this.completeUrl(this.state.text));
|
||||||
this.props.navigation.navigate('LoginSignup');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
completeUrl = (url) => {
|
completeUrl = (url) => {
|
||||||
|
|
|
@ -54,16 +54,22 @@ export default class RoomHeaderView extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
room: realm.objects('subscriptions').filtered('rid = $0', this.rid)[0] || {},
|
room: props.navigation.state.params.room
|
||||||
roomName: props.navigation.state.params.room.name
|
|
||||||
};
|
};
|
||||||
this.rid = props.navigation.state.params.room.rid;
|
this.room = realm.objects('subscriptions').filtered('rid = $0', this.state.room.rid);
|
||||||
this.room = realm.objects('subscriptions').filtered('rid = $0', this.rid);
|
|
||||||
this.room.addListener(this.updateState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.updateState();
|
this.updateState();
|
||||||
|
this.room.addListener(this.updateState);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.navigation.state.params.room !== this.props.navigation.state.params.room) {
|
||||||
|
this.room.removeAllListeners();
|
||||||
|
this.room = realm.objects('subscriptions').filtered('rid = $0', nextProps.navigation.state.params.room.rid);
|
||||||
|
this.room.addListener(this.updateState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -71,7 +77,7 @@ export default class RoomHeaderView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserStatus() {
|
getUserStatus() {
|
||||||
const userId = this.rid.replace(this.props.user.id, '').trim();
|
const userId = this.state.room.rid.replace(this.props.user.id, '').trim();
|
||||||
const userInfo = this.props.activeUsers[userId];
|
const userInfo = this.props.activeUsers[userId];
|
||||||
return (userInfo && userInfo.status) || 'offline';
|
return (userInfo && userInfo.status) || 'offline';
|
||||||
}
|
}
|
||||||
|
@ -82,7 +88,9 @@ export default class RoomHeaderView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState = () => {
|
updateState = () => {
|
||||||
this.setState({ room: this.room[0] });
|
if (this.room.length > 0) {
|
||||||
|
this.setState({ room: this.room[0] });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
isDirect = () => this.state.room && this.state.room.t === 'd';
|
isDirect = () => this.state.room && this.state.room.t === 'd';
|
||||||
|
@ -98,11 +106,11 @@ export default class RoomHeaderView extends React.PureComponent {
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
renderCenter() {
|
renderCenter() {
|
||||||
if (!this.state.roomName) {
|
if (!this.state.room.name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let accessibilityLabel = this.state.roomName;
|
let accessibilityLabel = this.state.room.name;
|
||||||
|
|
||||||
if (this.isDirect()) {
|
if (this.isDirect()) {
|
||||||
accessibilityLabel += `, ${ this.getUserStatusLabel() }`;
|
accessibilityLabel += `, ${ this.getUserStatusLabel() }`;
|
||||||
|
@ -125,11 +133,11 @@ export default class RoomHeaderView extends React.PureComponent {
|
||||||
style={styles.titleContainer}
|
style={styles.titleContainer}
|
||||||
accessibilityLabel={accessibilityLabel}
|
accessibilityLabel={accessibilityLabel}
|
||||||
accessibilityTraits='header'
|
accessibilityTraits='header'
|
||||||
onPress={() => this.props.navigation.navigate({ key: 'RoomInfo', routeName: 'RoomInfo', params: { rid: this.rid } })}
|
onPress={() => this.props.navigation.navigate({ key: 'RoomInfo', routeName: 'RoomInfo', params: { rid: this.state.rid } })}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Avatar
|
<Avatar
|
||||||
text={this.state.roomName}
|
text={this.state.room.name}
|
||||||
size={24}
|
size={24}
|
||||||
style={styles.avatar}
|
style={styles.avatar}
|
||||||
type={this.state.room.t}
|
type={this.state.room.t}
|
||||||
|
@ -140,7 +148,7 @@ export default class RoomHeaderView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<View style={styles.titleTextContainer}>
|
<View style={styles.titleTextContainer}>
|
||||||
<Text style={styles.title} allowFontScaling={false}>{this.state.roomName}</Text>
|
<Text style={styles.title} allowFontScaling={false}>{this.state.room.name}</Text>
|
||||||
|
|
||||||
{ t && <Text style={styles.userStatus} allowFontScaling={false} numberOfLines={1}>{t}</Text>}
|
{ t && <Text style={styles.userStatus} allowFontScaling={false} numberOfLines={1}>{t}</Text>}
|
||||||
|
|
||||||
|
|
|
@ -65,22 +65,17 @@ export default class RoomView extends LoggedView {
|
||||||
this.rid =
|
this.rid =
|
||||||
props.rid ||
|
props.rid ||
|
||||||
props.navigation.state.params.room.rid;
|
props.navigation.state.params.room.rid;
|
||||||
this.name = props.name ||
|
|
||||||
props.navigation.state.params.name ||
|
|
||||||
props.navigation.state.params.room.name;
|
|
||||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
|
this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
|
||||||
this.state = {
|
this.state = {
|
||||||
loaded: true,
|
loaded: true,
|
||||||
joined: typeof props.rid === 'undefined',
|
joined: typeof props.rid === 'undefined',
|
||||||
room: JSON.parse(JSON.stringify(this.rooms[0]))
|
room: {}
|
||||||
};
|
};
|
||||||
this.onReactionPress = this.onReactionPress.bind(this);
|
this.onReactionPress = this.onReactionPress.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
this.props.navigation.setParams({
|
await this.updateRoom();
|
||||||
title: this.name
|
|
||||||
});
|
|
||||||
await this.props.openRoom({
|
await this.props.openRoom({
|
||||||
...this.state.room
|
...this.state.room
|
||||||
});
|
});
|
||||||
|
@ -89,7 +84,6 @@ export default class RoomView extends LoggedView {
|
||||||
} else {
|
} else {
|
||||||
this.props.setLastOpen(null);
|
this.props.setLastOpen(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rooms.addListener(this.updateRoom);
|
this.rooms.addListener(this.updateRoom);
|
||||||
}
|
}
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
@ -129,8 +123,10 @@ export default class RoomView extends LoggedView {
|
||||||
RocketChat.setReaction(shortname, messageId);
|
RocketChat.setReaction(shortname, messageId);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateRoom = () => {
|
updateRoom = async() => {
|
||||||
this.setState({ room: JSON.parse(JSON.stringify(this.rooms[0])) });
|
if (this.rooms.length > 0) {
|
||||||
|
await this.setState({ room: JSON.parse(JSON.stringify(this.rooms[0])) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage = (message) => {
|
sendMessage = (message) => {
|
||||||
|
|
|
@ -123,13 +123,17 @@ export default class RoomsListView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPressItem = async(item = {}) => {
|
_onPressItem = async(item = {}) => {
|
||||||
// if user is using the search we need first to join/create room
|
|
||||||
if (!item.search) {
|
if (!item.search) {
|
||||||
return this.props.navigation.navigate({ key: `Room-${ item._id }`, routeName: 'Room', params: { room: item, ...item } });
|
return goRoom({ rid: item.rid });
|
||||||
}
|
}
|
||||||
if (item.t === 'd') {
|
if (item.t === 'd') {
|
||||||
const sub = await RocketChat.createDirectMessageAndWait(item.username);
|
// if user is using the search we need first to join/create room
|
||||||
return goRoom({ room: sub, name: sub.name });
|
try {
|
||||||
|
const sub = await RocketChat.createDirectMessage(item.username);
|
||||||
|
return goRoom(sub);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('_onPressItem', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return goRoom(item);
|
return goRoom(item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#import "SplashScreen.h"
|
#import "SplashScreen.h"
|
||||||
#import <Fabric/Fabric.h>
|
#import <Fabric/Fabric.h>
|
||||||
#import <Crashlytics/Crashlytics.h>
|
#import <Crashlytics/Crashlytics.h>
|
||||||
|
#import <React/RCTLinkingManager.h>
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
@ -70,4 +71,20 @@
|
||||||
{
|
{
|
||||||
[RCTPushNotificationManager didReceiveLocalNotification:notification];
|
[RCTPushNotificationManager didReceiveLocalNotification:notification];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
|
||||||
|
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
|
||||||
|
{
|
||||||
|
return [RCTLinkingManager application:application openURL:url
|
||||||
|
sourceApplication:sourceApplication annotation:annotation];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only if your app is using [Universal Links](https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html).
|
||||||
|
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
|
||||||
|
restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
|
||||||
|
{
|
||||||
|
return [RCTLinkingManager application:application
|
||||||
|
continueUserActivity:userActivity
|
||||||
|
restorationHandler:restorationHandler];
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -20,6 +20,20 @@
|
||||||
<string>1.0.0</string>
|
<string>1.0.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>rocketchat</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>rocketchat</string>
|
||||||
|
<string>https://go.rocket.chat</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>100</string>
|
<string>100</string>
|
||||||
<key>Fabric</key>
|
<key>Fabric</key>
|
||||||
|
|
Loading…
Reference in New Issue