[NEW] Delete Server (#1975)
* [NEW] Delete server Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com> Co-authored-by: Calebe Rios <calebersmendes@gmail.com> * [FIX] Revert removed function Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com> Co-authored-by: Calebe Rios <calebersmendes@gmail.com> * pods * i18n * Revert "pods" This reverts commit 2854a1650538159aeeafe90fdb2118d12b76a82f. Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com> Co-authored-by: Calebe Rios <calebersmendes@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
f272038ca1
commit
ee5b7592b4
|
@ -560,6 +560,7 @@ export default {
|
||||||
You_will_be_logged_out_of_this_application: 'You will be logged out of this application.',
|
You_will_be_logged_out_of_this_application: 'You will be logged out of this application.',
|
||||||
Clear: 'Clear',
|
Clear: 'Clear',
|
||||||
This_will_clear_all_your_offline_data: 'This will clear all your offline data.',
|
This_will_clear_all_your_offline_data: 'This will clear all your offline data.',
|
||||||
|
This_will_remove_all_data_from_this_server: 'This will remove all data from this server.',
|
||||||
Mark_unread: 'Mark Unread',
|
Mark_unread: 'Mark Unread',
|
||||||
Wait_activation_warning: 'Before you can login, your account must be manually activated by an administrator.'
|
Wait_activation_warning: 'Before you can login, your account must be manually activated by an administrator.'
|
||||||
};
|
};
|
||||||
|
|
|
@ -499,6 +499,7 @@ export default {
|
||||||
You_will_be_logged_out_of_this_application: 'Você sairá deste aplicativo.',
|
You_will_be_logged_out_of_this_application: 'Você sairá deste aplicativo.',
|
||||||
Clear: 'Limpar',
|
Clear: 'Limpar',
|
||||||
This_will_clear_all_your_offline_data: 'Isto limpará todos os seus dados offline.',
|
This_will_clear_all_your_offline_data: 'Isto limpará todos os seus dados offline.',
|
||||||
|
This_will_remove_all_data_from_this_server: 'Isto removerá todos os dados desse servidor.',
|
||||||
Mark_unread: 'Marcar como não Lida',
|
Mark_unread: 'Marcar como não Lida',
|
||||||
Wait_activation_warning: 'Antes que você possa fazer o login, sua conta deve ser manualmente ativada por um administrador.'
|
Wait_activation_warning: 'Antes que você possa fazer o login, sua conta deve ser manualmente ativada por um administrador.'
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,6 +33,36 @@ if (__DEV__ && isIOS) {
|
||||||
console.log(appGroupPath);
|
console.log(appGroupPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getDatabase = (database = '') => {
|
||||||
|
const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.');
|
||||||
|
const dbName = `${ appGroupPath }${ path }.db`;
|
||||||
|
|
||||||
|
const adapter = new SQLiteAdapter({
|
||||||
|
dbName,
|
||||||
|
schema: appSchema,
|
||||||
|
migrations
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Database({
|
||||||
|
adapter,
|
||||||
|
modelClasses: [
|
||||||
|
Subscription,
|
||||||
|
Room,
|
||||||
|
Message,
|
||||||
|
Thread,
|
||||||
|
ThreadMessage,
|
||||||
|
CustomEmoji,
|
||||||
|
FrequentlyUsedEmoji,
|
||||||
|
Upload,
|
||||||
|
Setting,
|
||||||
|
Role,
|
||||||
|
Permission,
|
||||||
|
SlashCommand
|
||||||
|
],
|
||||||
|
actionsEnabled: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
class DB {
|
class DB {
|
||||||
databases = {
|
databases = {
|
||||||
serversDB: new Database({
|
serversDB: new Database({
|
||||||
|
@ -86,34 +116,8 @@ class DB {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setActiveDB(database = '') {
|
setActiveDB(database) {
|
||||||
const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.');
|
this.databases.activeDB = getDatabase(database);
|
||||||
const dbName = `${ appGroupPath }${ path }.db`;
|
|
||||||
|
|
||||||
const adapter = new SQLiteAdapter({
|
|
||||||
dbName,
|
|
||||||
schema: appSchema,
|
|
||||||
migrations
|
|
||||||
});
|
|
||||||
|
|
||||||
this.databases.activeDB = new Database({
|
|
||||||
adapter,
|
|
||||||
modelClasses: [
|
|
||||||
Subscription,
|
|
||||||
Room,
|
|
||||||
Message,
|
|
||||||
Thread,
|
|
||||||
ThreadMessage,
|
|
||||||
CustomEmoji,
|
|
||||||
FrequentlyUsedEmoji,
|
|
||||||
Upload,
|
|
||||||
Setting,
|
|
||||||
Role,
|
|
||||||
Permission,
|
|
||||||
SlashCommand
|
|
||||||
],
|
|
||||||
actionsEnabled: true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
import RNUserDefaults from 'rn-user-defaults';
|
||||||
|
import * as FileSystem from 'expo-file-system';
|
||||||
|
import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
|
||||||
|
|
||||||
|
import { SERVERS, SERVER_URL } from '../../constants/userDefaults';
|
||||||
|
import { getDeviceToken } from '../../notifications/push';
|
||||||
|
import { extractHostname } from '../../utils/server';
|
||||||
|
import { BASIC_AUTH_KEY } from '../../utils/fetch';
|
||||||
|
import database, { getDatabase } from '../database';
|
||||||
|
import RocketChat from '../rocketchat';
|
||||||
|
import { useSsl } from '../../utils/url';
|
||||||
|
|
||||||
|
async function removeServerKeys({ server, userId }) {
|
||||||
|
await RNUserDefaults.clear(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||||
|
await RNUserDefaults.clear(`${ RocketChat.TOKEN_KEY }-${ userId }`);
|
||||||
|
await RNUserDefaults.clear(`${ BASIC_AUTH_KEY }-${ server }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeSharedCredentials({ server }) {
|
||||||
|
try {
|
||||||
|
const servers = await RNUserDefaults.objectForKey(SERVERS);
|
||||||
|
await RNUserDefaults.setObjectForKey(SERVERS, servers && servers.filter(srv => srv[SERVER_URL] !== server));
|
||||||
|
|
||||||
|
// clear certificate for server - SSL Pinning
|
||||||
|
const certificate = await RNUserDefaults.objectForKey(extractHostname(server));
|
||||||
|
if (certificate && certificate.path) {
|
||||||
|
await RNUserDefaults.clear(extractHostname(server));
|
||||||
|
await FileSystem.deleteAsync(certificate.path);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('removeSharedCredentials', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeServerData({ server }) {
|
||||||
|
try {
|
||||||
|
const batch = [];
|
||||||
|
const serversDB = database.servers;
|
||||||
|
const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||||
|
|
||||||
|
const usersCollection = serversDB.collections.get('users');
|
||||||
|
if (userId) {
|
||||||
|
const userRecord = await usersCollection.find(userId);
|
||||||
|
batch.push(userRecord.prepareDestroyPermanently());
|
||||||
|
}
|
||||||
|
const serverCollection = serversDB.collections.get('servers');
|
||||||
|
const serverRecord = await serverCollection.find(server);
|
||||||
|
batch.push(serverRecord.prepareDestroyPermanently());
|
||||||
|
|
||||||
|
await serversDB.action(() => serversDB.batch(...batch));
|
||||||
|
await removeSharedCredentials({ server });
|
||||||
|
await removeServerKeys({ server });
|
||||||
|
} catch (e) {
|
||||||
|
console.log('removeServerData', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeCurrentServer() {
|
||||||
|
await RNUserDefaults.clear('currentServer');
|
||||||
|
await RNUserDefaults.clear(RocketChat.TOKEN_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeServerDatabase({ server }) {
|
||||||
|
try {
|
||||||
|
const db = getDatabase(server);
|
||||||
|
await db.action(() => db.unsafeResetDatabase());
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeServer({ server }) {
|
||||||
|
try {
|
||||||
|
const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||||
|
if (userId) {
|
||||||
|
const resume = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ userId }`);
|
||||||
|
|
||||||
|
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
|
||||||
|
await sdk.login({ resume });
|
||||||
|
|
||||||
|
const token = getDeviceToken();
|
||||||
|
if (token) {
|
||||||
|
await sdk.del('push.token', { token });
|
||||||
|
}
|
||||||
|
|
||||||
|
await sdk.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
await removeServerData({ server });
|
||||||
|
await removeServerDatabase({ server });
|
||||||
|
} catch (e) {
|
||||||
|
console.log('removePush', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function logout({ server }) {
|
||||||
|
if (this.roomsSub) {
|
||||||
|
this.roomsSub.stop();
|
||||||
|
this.roomsSub = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.activeUsersSubTimeout) {
|
||||||
|
clearTimeout(this.activeUsersSubTimeout);
|
||||||
|
this.activeUsersSubTimeout = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.removePushToken();
|
||||||
|
} catch (e) {
|
||||||
|
console.log('removePushToken', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// RC 0.60.0
|
||||||
|
await this.sdk.logout();
|
||||||
|
} catch (e) {
|
||||||
|
console.log('logout', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sdk) {
|
||||||
|
this.sdk = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await removeServerData({ server });
|
||||||
|
await removeCurrentServer();
|
||||||
|
await removeServerDatabase({ server });
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ import semver from 'semver';
|
||||||
import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
|
import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
|
||||||
import RNUserDefaults from 'rn-user-defaults';
|
import RNUserDefaults from 'rn-user-defaults';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import * as FileSystem from 'expo-file-system';
|
|
||||||
|
|
||||||
import reduxStore from './createStore';
|
import reduxStore from './createStore';
|
||||||
import defaultSettings from '../constants/settings';
|
import defaultSettings from '../constants/settings';
|
||||||
|
@ -11,8 +10,7 @@ import messagesStatus from '../constants/messagesStatus';
|
||||||
import database from './database';
|
import database from './database';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import { isIOS, getBundleId } from '../utils/deviceInfo';
|
import { isIOS, getBundleId } from '../utils/deviceInfo';
|
||||||
import { extractHostname } from '../utils/server';
|
import fetch from '../utils/fetch';
|
||||||
import fetch, { BASIC_AUTH_KEY } from '../utils/fetch';
|
|
||||||
|
|
||||||
import { setUser, setLoginServices, loginRequest } from '../actions/login';
|
import { setUser, setLoginServices, loginRequest } from '../actions/login';
|
||||||
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
|
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
|
||||||
|
@ -43,12 +41,13 @@ import sendMessage, { sendMessageCall } from './methods/sendMessage';
|
||||||
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
|
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
|
||||||
|
|
||||||
import callJitsi from './methods/callJitsi';
|
import callJitsi from './methods/callJitsi';
|
||||||
|
import logout, { removeServer } from './methods/logout';
|
||||||
|
|
||||||
import { getDeviceToken } from '../notifications/push';
|
import { getDeviceToken } from '../notifications/push';
|
||||||
import { SERVERS, SERVER_URL } from '../constants/userDefaults';
|
|
||||||
import { setActiveUsers } from '../actions/activeUsers';
|
import { setActiveUsers } from '../actions/activeUsers';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { twoFactor } from '../utils/twoFactor';
|
import { twoFactor } from '../utils/twoFactor';
|
||||||
|
import { useSsl } from '../utils/url';
|
||||||
|
|
||||||
const TOKEN_KEY = 'reactnativemeteor_usertoken';
|
const TOKEN_KEY = 'reactnativemeteor_usertoken';
|
||||||
const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
|
const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
|
||||||
|
@ -86,10 +85,7 @@ const RocketChat = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getWebsocketInfo({ server }) {
|
async getWebsocketInfo({ server }) {
|
||||||
// Use useSsl: false only if server url starts with http://
|
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
|
||||||
const useSsl = !/http:\/\//.test(server);
|
|
||||||
|
|
||||||
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sdk.connect();
|
await sdk.connect();
|
||||||
|
@ -200,10 +196,7 @@ const RocketChat = {
|
||||||
this.code = null;
|
this.code = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use useSsl: false only if server url starts with http://
|
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
|
||||||
const useSsl = !/http:\/\//.test(server);
|
|
||||||
|
|
||||||
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
|
|
||||||
this.getSettings();
|
this.getSettings();
|
||||||
|
|
||||||
const sdkConnect = () => this.sdk.connect()
|
const sdkConnect = () => this.sdk.connect()
|
||||||
|
@ -270,10 +263,7 @@ const RocketChat = {
|
||||||
this.shareSDK = null;
|
this.shareSDK = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use useSsl: false only if server url starts with http://
|
this.shareSDK = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
|
||||||
const useSsl = !/http:\/\//.test(server);
|
|
||||||
|
|
||||||
this.shareSDK = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
|
|
||||||
|
|
||||||
// set Server
|
// set Server
|
||||||
const serversDB = database.servers;
|
const serversDB = database.servers;
|
||||||
|
@ -408,73 +398,8 @@ const RocketChat = {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async logout({ server }) {
|
logout,
|
||||||
if (this.roomsSub) {
|
removeServer,
|
||||||
this.roomsSub.stop();
|
|
||||||
this.roomsSub = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.activeUsersSubTimeout) {
|
|
||||||
clearTimeout(this.activeUsersSubTimeout);
|
|
||||||
this.activeUsersSubTimeout = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.removePushToken();
|
|
||||||
} catch (error) {
|
|
||||||
console.log('logout -> removePushToken -> catch -> error', error);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// RC 0.60.0
|
|
||||||
await this.sdk.logout();
|
|
||||||
} catch (error) {
|
|
||||||
console.log('logout -> api logout -> catch -> error', error);
|
|
||||||
}
|
|
||||||
this.sdk = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const servers = await RNUserDefaults.objectForKey(SERVERS);
|
|
||||||
await RNUserDefaults.setObjectForKey(SERVERS, servers && servers.filter(srv => srv[SERVER_URL] !== server));
|
|
||||||
// clear certificate for server - SSL Pinning
|
|
||||||
const certificate = await RNUserDefaults.objectForKey(extractHostname(server));
|
|
||||||
if (certificate && certificate.path) {
|
|
||||||
await RNUserDefaults.clear(extractHostname(server));
|
|
||||||
await FileSystem.deleteAsync(certificate.path);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log('logout_rn_user_defaults', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userId = await RNUserDefaults.get(`${ TOKEN_KEY }-${ server }`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const serversDB = database.servers;
|
|
||||||
await serversDB.action(async() => {
|
|
||||||
const usersCollection = serversDB.collections.get('users');
|
|
||||||
const userRecord = await usersCollection.find(userId);
|
|
||||||
const serverCollection = serversDB.collections.get('servers');
|
|
||||||
const serverRecord = await serverCollection.find(server);
|
|
||||||
await serversDB.batch(
|
|
||||||
userRecord.prepareDestroyPermanently(),
|
|
||||||
serverRecord.prepareDestroyPermanently()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
await RNUserDefaults.clear('currentServer');
|
|
||||||
await RNUserDefaults.clear(TOKEN_KEY);
|
|
||||||
await RNUserDefaults.clear(`${ TOKEN_KEY }-${ server }`);
|
|
||||||
await RNUserDefaults.clear(`${ BASIC_AUTH_KEY }-${ server }`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const db = database.active;
|
|
||||||
await db.action(() => db.unsafeResetDatabase());
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async clearCache({ server }) {
|
async clearCache({ server }) {
|
||||||
try {
|
try {
|
||||||
const serversDB = database.servers;
|
const serversDB = database.servers;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, View, StyleSheet } from 'react-native';
|
import { Text, View, StyleSheet } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { LongPressGestureHandler, State } from 'react-native-gesture-handler';
|
|
||||||
|
|
||||||
import Avatar from '../containers/Avatar';
|
import Avatar from '../containers/Avatar';
|
||||||
import { CustomIcon } from '../lib/Icons';
|
import { CustomIcon } from '../lib/Icons';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import Touch from '../utils/touch';
|
import Touch from '../utils/touch';
|
||||||
|
import LongPress from '../utils/longPress';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
button: {
|
button: {
|
||||||
|
@ -41,38 +41,25 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
const UserItem = ({
|
const UserItem = ({
|
||||||
name, username, onPress, testID, onLongPress, style, icon, baseUrl, user, theme
|
name, username, onPress, testID, onLongPress, style, icon, baseUrl, user, theme
|
||||||
}) => {
|
}) => (
|
||||||
const longPress = ({ nativeEvent }) => {
|
<LongPress onLongPress={onLongPress}>
|
||||||
if (nativeEvent.state === State.ACTIVE) {
|
<Touch
|
||||||
if (onLongPress) {
|
onPress={onPress}
|
||||||
onLongPress();
|
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||||
}
|
testID={testID}
|
||||||
}
|
theme={theme}
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LongPressGestureHandler
|
|
||||||
onHandlerStateChange={longPress}
|
|
||||||
minDurationMs={800}
|
|
||||||
>
|
>
|
||||||
<Touch
|
<View style={[styles.container, styles.button, style]}>
|
||||||
onPress={onPress}
|
<Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} userId={user.id} token={user.token} />
|
||||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
<View style={styles.textContainer}>
|
||||||
testID={testID}
|
<Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>{name}</Text>
|
||||||
theme={theme}
|
<Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>@{username}</Text>
|
||||||
>
|
|
||||||
<View style={[styles.container, styles.button, style]}>
|
|
||||||
<Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} userId={user.id} token={user.token} />
|
|
||||||
<View style={styles.textContainer}>
|
|
||||||
<Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>{name}</Text>
|
|
||||||
<Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>@{username}</Text>
|
|
||||||
</View>
|
|
||||||
{icon ? <CustomIcon name={icon} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null}
|
|
||||||
</View>
|
</View>
|
||||||
</Touch>
|
{icon ? <CustomIcon name={icon} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null}
|
||||||
</LongPressGestureHandler>
|
</View>
|
||||||
);
|
</Touch>
|
||||||
};
|
</LongPress>
|
||||||
|
);
|
||||||
|
|
||||||
UserItem.propTypes = {
|
UserItem.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { State, LongPressGestureHandler } from 'react-native-gesture-handler';
|
||||||
|
|
||||||
|
class LongPress extends React.Component {
|
||||||
|
setNativeProps(props) {
|
||||||
|
this.ref.setNativeProps(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRef = (ref) => {
|
||||||
|
this.ref = ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
longPress = ({ nativeEvent }) => {
|
||||||
|
const { onLongPress } = this.props;
|
||||||
|
if (nativeEvent.state === State.ACTIVE) {
|
||||||
|
if (onLongPress) {
|
||||||
|
onLongPress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { children, ...props } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LongPressGestureHandler
|
||||||
|
onHandlerStateChange={this.longPress}
|
||||||
|
minDurationMs={800}
|
||||||
|
ref={this.getRef}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</LongPressGestureHandler>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LongPress.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
onLongPress: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LongPress;
|
|
@ -7,3 +7,6 @@ export const isValidURL = (url) => {
|
||||||
+ '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
|
+ '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
|
||||||
return !!pattern.test(url);
|
return !!pattern.test(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Use useSsl: false only if server url starts with http://
|
||||||
|
export const useSsl = url => !/http:\/\//.test(url);
|
||||||
|
|
|
@ -22,6 +22,8 @@ import { withTheme } from '../../theme';
|
||||||
import { KEY_COMMAND, handleCommandSelectServer } from '../../commands';
|
import { KEY_COMMAND, handleCommandSelectServer } from '../../commands';
|
||||||
import { isTablet } from '../../utils/deviceInfo';
|
import { isTablet } from '../../utils/deviceInfo';
|
||||||
import { withSplit } from '../../split';
|
import { withSplit } from '../../split';
|
||||||
|
import LongPress from '../../utils/longPress';
|
||||||
|
import { showConfirmationAlert } from '../../utils/info';
|
||||||
|
|
||||||
const ROW_HEIGHT = 68;
|
const ROW_HEIGHT = 68;
|
||||||
const ANIMATION_DURATION = 200;
|
const ANIMATION_DURATION = 200;
|
||||||
|
@ -132,7 +134,6 @@ class ServerDropdown extends Component {
|
||||||
const {
|
const {
|
||||||
server: currentServer, selectServerRequest, navigation, split
|
server: currentServer, selectServerRequest, navigation, split
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
if (currentServer !== server) {
|
if (currentServer !== server) {
|
||||||
const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||||
|
@ -152,6 +153,19 @@ class ServerDropdown extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remove = server => showConfirmationAlert({
|
||||||
|
message: I18n.t('This_will_remove_all_data_from_this_server'),
|
||||||
|
callToAction: I18n.t('Delete'),
|
||||||
|
onPress: async() => {
|
||||||
|
this.close();
|
||||||
|
try {
|
||||||
|
await RocketChat.removeServer({ server });
|
||||||
|
} catch {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
handleCommands = ({ event }) => {
|
handleCommands = ({ event }) => {
|
||||||
const { servers } = this.state;
|
const { servers } = this.state;
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
|
@ -173,35 +187,37 @@ class ServerDropdown extends Component {
|
||||||
const { server, theme } = this.props;
|
const { server, theme } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Touch
|
<LongPress onLongPress={() => (item.id === server || this.remove(item.id))}>
|
||||||
onPress={() => this.select(item.id)}
|
<Touch
|
||||||
testID={`rooms-list-header-server-${ item.id }`}
|
onPress={() => this.select(item.id)}
|
||||||
theme={theme}
|
testID={`rooms-list-header-server-${ item.id }`}
|
||||||
>
|
theme={theme}
|
||||||
<View style={styles.serverItemContainer}>
|
>
|
||||||
{item.iconURL
|
<View style={styles.serverItemContainer}>
|
||||||
? (
|
{item.iconURL
|
||||||
<Image
|
? (
|
||||||
source={{ uri: item.iconURL }}
|
<Image
|
||||||
defaultSource={{ uri: 'logo' }}
|
source={{ uri: item.iconURL }}
|
||||||
style={styles.serverIcon}
|
defaultSource={{ uri: 'logo' }}
|
||||||
onError={() => console.warn('error loading serverIcon')}
|
style={styles.serverIcon}
|
||||||
/>
|
onError={() => console.warn('error loading serverIcon')}
|
||||||
)
|
/>
|
||||||
: (
|
)
|
||||||
<Image
|
: (
|
||||||
source={{ uri: 'logo' }}
|
<Image
|
||||||
style={styles.serverIcon}
|
source={{ uri: 'logo' }}
|
||||||
/>
|
style={styles.serverIcon}
|
||||||
)
|
/>
|
||||||
}
|
)
|
||||||
<View style={styles.serverTextContainer}>
|
}
|
||||||
<Text style={[styles.serverName, { color: themes[theme].titleText }]}>{item.name || item.id}</Text>
|
<View style={styles.serverTextContainer}>
|
||||||
<Text style={[styles.serverUrl, { color: themes[theme].auxiliaryText }]}>{item.id}</Text>
|
<Text style={[styles.serverName, { color: themes[theme].titleText }]}>{item.name || item.id}</Text>
|
||||||
|
<Text style={[styles.serverUrl, { color: themes[theme].auxiliaryText }]}>{item.id}</Text>
|
||||||
|
</View>
|
||||||
|
{item.id === server ? <Check theme={theme} /> : null}
|
||||||
</View>
|
</View>
|
||||||
{item.id === server ? <Check theme={theme} /> : null}
|
</Touch>
|
||||||
</View>
|
</LongPress>
|
||||||
</Touch>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue