[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.',
|
||||
Clear: 'Clear',
|
||||
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',
|
||||
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.',
|
||||
Clear: 'Limpar',
|
||||
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',
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
databases = {
|
||||
serversDB: new Database({
|
||||
|
@ -86,34 +116,8 @@ class DB {
|
|||
});
|
||||
}
|
||||
|
||||
setActiveDB(database = '') {
|
||||
const path = database.replace(/(^\w+:|^)\/\//, '').replace(/\//g, '.');
|
||||
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
|
||||
});
|
||||
setActiveDB(database) {
|
||||
this.databases.activeDB = getDatabase(database);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 RNUserDefaults from 'rn-user-defaults';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
|
||||
import reduxStore from './createStore';
|
||||
import defaultSettings from '../constants/settings';
|
||||
|
@ -11,8 +10,7 @@ import messagesStatus from '../constants/messagesStatus';
|
|||
import database from './database';
|
||||
import log from '../utils/log';
|
||||
import { isIOS, getBundleId } from '../utils/deviceInfo';
|
||||
import { extractHostname } from '../utils/server';
|
||||
import fetch, { BASIC_AUTH_KEY } from '../utils/fetch';
|
||||
import fetch from '../utils/fetch';
|
||||
|
||||
import { setUser, setLoginServices, loginRequest } from '../actions/login';
|
||||
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 callJitsi from './methods/callJitsi';
|
||||
import logout, { removeServer } from './methods/logout';
|
||||
|
||||
import { getDeviceToken } from '../notifications/push';
|
||||
import { SERVERS, SERVER_URL } from '../constants/userDefaults';
|
||||
import { setActiveUsers } from '../actions/activeUsers';
|
||||
import I18n from '../i18n';
|
||||
import { twoFactor } from '../utils/twoFactor';
|
||||
import { useSsl } from '../utils/url';
|
||||
|
||||
const TOKEN_KEY = 'reactnativemeteor_usertoken';
|
||||
const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
|
||||
|
@ -86,10 +85,7 @@ const RocketChat = {
|
|||
}
|
||||
},
|
||||
async getWebsocketInfo({ server }) {
|
||||
// Use useSsl: false only if server url starts with http://
|
||||
const useSsl = !/http:\/\//.test(server);
|
||||
|
||||
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
|
||||
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
|
||||
|
||||
try {
|
||||
await sdk.connect();
|
||||
|
@ -200,10 +196,7 @@ const RocketChat = {
|
|||
this.code = 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 });
|
||||
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
|
||||
this.getSettings();
|
||||
|
||||
const sdkConnect = () => this.sdk.connect()
|
||||
|
@ -270,10 +263,7 @@ const RocketChat = {
|
|||
this.shareSDK = null;
|
||||
}
|
||||
|
||||
// Use useSsl: false only if server url starts with http://
|
||||
const useSsl = !/http:\/\//.test(server);
|
||||
|
||||
this.shareSDK = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
|
||||
this.shareSDK = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
|
||||
|
||||
// set Server
|
||||
const serversDB = database.servers;
|
||||
|
@ -408,73 +398,8 @@ const RocketChat = {
|
|||
throw e;
|
||||
}
|
||||
},
|
||||
async 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 (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);
|
||||
}
|
||||
},
|
||||
logout,
|
||||
removeServer,
|
||||
async clearCache({ server }) {
|
||||
try {
|
||||
const serversDB = database.servers;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React from 'react';
|
||||
import { Text, View, StyleSheet } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { LongPressGestureHandler, State } from 'react-native-gesture-handler';
|
||||
|
||||
import Avatar from '../containers/Avatar';
|
||||
import { CustomIcon } from '../lib/Icons';
|
||||
import sharedStyles from '../views/Styles';
|
||||
import { themes } from '../constants/colors';
|
||||
import Touch from '../utils/touch';
|
||||
import LongPress from '../utils/longPress';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
|
@ -41,20 +41,8 @@ const styles = StyleSheet.create({
|
|||
|
||||
const UserItem = ({
|
||||
name, username, onPress, testID, onLongPress, style, icon, baseUrl, user, theme
|
||||
}) => {
|
||||
const longPress = ({ nativeEvent }) => {
|
||||
if (nativeEvent.state === State.ACTIVE) {
|
||||
if (onLongPress) {
|
||||
onLongPress();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<LongPressGestureHandler
|
||||
onHandlerStateChange={longPress}
|
||||
minDurationMs={800}
|
||||
>
|
||||
}) => (
|
||||
<LongPress onLongPress={onLongPress}>
|
||||
<Touch
|
||||
onPress={onPress}
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
|
@ -70,9 +58,8 @@ const UserItem = ({
|
|||
{icon ? <CustomIcon name={icon} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null}
|
||||
</View>
|
||||
</Touch>
|
||||
</LongPressGestureHandler>
|
||||
</LongPress>
|
||||
);
|
||||
};
|
||||
|
||||
UserItem.propTypes = {
|
||||
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
|
||||
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 { isTablet } from '../../utils/deviceInfo';
|
||||
import { withSplit } from '../../split';
|
||||
import LongPress from '../../utils/longPress';
|
||||
import { showConfirmationAlert } from '../../utils/info';
|
||||
|
||||
const ROW_HEIGHT = 68;
|
||||
const ANIMATION_DURATION = 200;
|
||||
|
@ -132,7 +134,6 @@ class ServerDropdown extends Component {
|
|||
const {
|
||||
server: currentServer, selectServerRequest, navigation, split
|
||||
} = this.props;
|
||||
|
||||
this.close();
|
||||
if (currentServer !== 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 }) => {
|
||||
const { servers } = this.state;
|
||||
const { navigation } = this.props;
|
||||
|
@ -173,6 +187,7 @@ class ServerDropdown extends Component {
|
|||
const { server, theme } = this.props;
|
||||
|
||||
return (
|
||||
<LongPress onLongPress={() => (item.id === server || this.remove(item.id))}>
|
||||
<Touch
|
||||
onPress={() => this.select(item.id)}
|
||||
testID={`rooms-list-header-server-${ item.id }`}
|
||||
|
@ -202,6 +217,7 @@ class ServerDropdown extends Component {
|
|||
{item.id === server ? <Check theme={theme} /> : null}
|
||||
</View>
|
||||
</Touch>
|
||||
</LongPress>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue