diff --git a/App.tsx b/App.tsx
index 60635065..4b265f42 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,360 +1,57 @@
import 'react-native-get-random-values'; // react native moment
-import {Component as ReactComponent, useContext, useState} from 'react';
-import {StatusBar, StyleSheet, View} from 'react-native';
+import {useEffect, useState} from 'react';
+import {StyleSheet} from 'react-native';
import {ErrorBoundary} from 'react-error-boundary';
-// import ConfirmHcaptcha from '@hcaptcha/react-native-hcaptcha';
-import AsyncStorage from '@react-native-async-storage/async-storage';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
-import {Channel, Server} from 'revolt.js';
-
-import {client, app, randomizeRemark} from './src/Generic';
-import {setFunction} from './src/Generic';
-import {SideMenuHandler} from './src/SideMenu';
-import {Modals} from './src/Modals';
+import {setFunction} from '@rvmob/Generic';
+import {MainView} from '@rvmob/MainView';
import {ErrorMessage} from '@rvmob/components/ErrorMessage';
-import {NetworkIndicator} from './src/components/NetworkIndicator';
-import {Notification} from './src/components/Notification';
-import {loginWithSavedToken} from './src/lib/auth';
-import {
- createChannel,
- sendNotifeeNotification,
- setUpNotifeeListener,
-} from '@rvmob/lib/notifications';
-import {sleep} from '@rvmob/lib/utils';
-import {LoginViews} from '@rvmob/pages/LoginViews';
-import {themes, Theme, ThemeContext} from '@rvmob/lib/themes';
import {LoadingScreen} from '@rvmob/components/views/LoadingScreen';
+import {storage} from '@rvmob/lib/storage';
+import {migrateToMMKV} from '@rvmob/lib/storage/migration';
+import {initialiseSettings} from '@rvmob/lib/storage/utils';
+import {themes, Theme, ThemeContext} from '@rvmob/lib/themes';
-async function openLastChannel() {
- try {
- const lastServer = await AsyncStorage.getItem('lastServer');
- if (lastServer) {
- app.openServer(client.servers.get(lastServer));
- try {
- const channelData = await AsyncStorage.getItem('serverLastChannels');
- let serverLastChannels = JSON.parse(channelData || '{}') || {};
- let lastChannel = serverLastChannels[lastServer];
- if (lastChannel) {
- let fetchedLastChannel = client.channels.get(lastChannel);
- if (fetchedLastChannel) {
- app.openChannel(fetchedLastChannel);
- }
- }
- } catch (channelErr) {
- console.log(`[APP] Error getting last channel: ${channelErr}`);
- }
- }
- } catch (serverErr) {
- console.log(`[APP] Error getting last server: ${serverErr}`);
- }
-}
-
-async function checkLastVersion() {
- const lastVersion = app.settings.get('app.lastVersion');
- console.log(app.version, lastVersion);
- if (!lastVersion || lastVersion === '') {
- console.log(
- `[APP] lastVersion is null (${lastVersion}), setting to app.version (${app.version})`,
- );
- await app.settings.set('app.lastVersion', app.version);
- } else {
- app.version === lastVersion
- ? console.log(
- `[APP] lastVersion (${lastVersion}) is equal to app.version (${app.version})`,
- )
- : console.log(
- `[APP] lastVersion (${lastVersion}) is different from app.version (${app.version})`,
- );
- }
-}
-
-function LoggedInViews({state, setChannel}: {state: any; setChannel: any}) {
- return (
- <>
-
-
-
-
-
- state.setState({
- notificationMessage: null,
- })
- }
- openChannel={() =>
- state.setState({
- notificationMessage: null,
- currentChannel: state.state.notificationMessage.channel,
- })
- }
- />
-
- >
- );
-}
-
-function AppViews({state, setChannel}: {state: any; setChannel: any}) {
- const {currentTheme} = useContext(ThemeContext);
- const localStyles = generateAppViewStyles(currentTheme);
-
- return (
- <>
-
-
- {state.state.status === 'loggedIn' ? (
-
- ) : state.state.status === 'loggedOut' ? (
- state.setState({status: 'loggedIn'})}
- />
- ) : (
-
- )}
-
- >
- );
-}
-
-class MainView extends ReactComponent {
- constructor(props) {
- super(props);
- this.state = {
- status: 'loggedOut',
- currentChannel: null,
- notificationMessage: null,
- orderedServers: [],
- serverNotifications: null,
- channelNotifications: null,
- };
- setFunction('openChannel', async c => {
- // if (!this.state.currentChannel || this.state.currentChannel?.server?._id != c.server?._id) c.server?.fetchMembers()
- this.setState({currentChannel: c});
- app.openLeftMenu(false);
- });
- setFunction('joinInvite', async (i: string) => {
- await client.joinInvite(i);
- });
- setFunction('logOut', async () => {
- console.log(
- `[AUTH] Logging out of current session... (user: ${client.user?._id})`,
- );
- AsyncStorage.multiSet([
- ['token', ''],
- ['sessionID', ''],
- ]);
- this.setState({status: 'loggedOut', currentChannel: null});
- await client.logout();
- app.setLoggedOutScreen('loginPage');
- });
- }
- componentDidUpdate(_, prevState) {
- if (prevState.status !== this.state.status) {
- randomizeRemark();
- }
- }
- async componentDidMount() {
- console.log(`[APP] Mounted component (${new Date().getTime()})`);
-
- let defaultNotif = await createChannel();
- console.log(`[NOTIFEE] Created channel: ${defaultNotif}`);
-
- await checkLastVersion();
-
- client.on('connecting', () => {
- app.setLoadingStage('connecting');
- console.log(`[APP] Connecting to instance... (${new Date().getTime()})`);
- });
-
- client.on('connected', () => {
- app.setLoadingStage('connected');
- console.log(`[APP] Connected to instance (${new Date().getTime()})`);
- });
-
- client.on('ready', async () => {
- let orderedServers,
- server,
- channel = null;
- try {
- const rawSettings = await client.syncFetchSettings([
- 'ordering',
- 'notifications',
- ]);
- try {
- orderedServers = JSON.parse(rawSettings.ordering[1]).servers;
- ({server, channel} = JSON.parse(rawSettings.notifications[1]));
- } catch (err) {
- console.log(`[APP] Error parsing fetched settings: ${err}`);
- }
- } catch (err) {
- console.log(`[APP] Error fetching settings: ${err}`);
- }
-
- this.setState({
- status: 'loggedIn',
- network: 'ready',
- orderedServers,
- serverNotifications: server,
- channelNotifications: channel,
- });
- console.log(`[APP] Client is ready (${new Date().getTime()})`);
-
- setUpNotifeeListener(client, this.setState);
-
- if (app.settings.get('app.reopenLastChannel')) {
- await openLastChannel();
- }
- });
-
- client.on('dropped', async () => {
- this.setState({network: 'dropped'});
- });
-
- client.on('message', async msg => {
- console.log(`[APP] Handling message ${msg._id}`);
-
- let channelNotif = this.state.channelNotifications
- ? this.state.channelNotifications[msg.channel?._id]
- : undefined;
- let serverNotif = this.state.serverNotifications
- ? this.state.serverNotifications[msg.channel?.server?._id]
- : undefined;
-
- const isMuted =
- (channelNotif && channelNotif === 'none') ||
- channelNotif === 'muted' ||
- (serverNotif && serverNotif === 'none') ||
- serverNotif === 'muted';
-
- const alwaysNotif =
- channelNotif === 'all' || (!isMuted && serverNotif === 'all');
-
- const mentionsUser =
- (msg.mention_ids?.includes(client.user?._id!) &&
- (app.settings.get('app.notifications.notifyOnSelfPing') ||
- msg.author?._id !== client.user?._id)) ||
- msg.channel?.channel_type === 'DirectMessage';
-
- const shouldNotif =
- (alwaysNotif &&
- (app.settings.get('app.notifications.notifyOnSelfPing') ||
- msg.author?._id !== client.user?._id)) ||
- (!isMuted && mentionsUser);
-
- console.log(
- `[NOTIFICATIONS] Should notify for ${msg._id}: ${shouldNotif} (channel/server muted? ${isMuted}, notifications for all messages enabled? ${alwaysNotif}, message mentions the user? ${mentionsUser})`,
- );
+export const App = () => {
+ const [checkedMigration, setCheckedMigration] = useState(false);
+ const [theme, setTheme] = useState(themes.Dark);
- if (app.settings.get('app.notifications.enabled') && shouldNotif) {
- console.log(
- `[NOTIFICATIONS] Pushing notification for message ${msg._id}`,
- );
- if (this.state.currentChannel !== msg.channel) {
- this.setState({notificationMessage: msg});
- await sleep(5000);
- this.setState({notificationMessage: null});
- }
+ const localStyles = generateLocalStyles(theme);
- await sendNotifeeNotification(msg, client, defaultNotif);
- }
- });
+ setFunction('setTheme', (themeName: string) => {
+ const newTheme = themes[themeName] ?? themes.Dark;
+ setTheme(newTheme);
+ });
- client.on('packet', async p => {
- if (p.type === 'UserSettingsUpdate') {
- console.log('[WEBSOCKET] Synced settings updated');
- try {
- if ('ordering' in p.update) {
- const orderedServers = JSON.parse(p.update.ordering[1]).servers;
- this.setState({orderedServers});
- }
- if ('notifications' in p.update) {
- const {server, channel} = JSON.parse(p.update.notifications[1]);
- this.setState({
- serverNotifications: server,
- channelNotifications: channel,
- });
- }
- } catch (err) {
- console.log(`[APP] Error fetching settings: ${err}`);
- }
- }
- });
+ useEffect(() => {
+ async function migrationCheck() {
+ const hasMigrated = storage.getBoolean('hasMigrated');
- client.on('server/delete', async s => {
- const currentServer = app.getCurrentServer();
- if (currentServer === s) {
- app.openServer(undefined);
- app.openChannel(null);
+ if (!hasMigrated) {
+ await migrateToMMKV();
}
- });
- await loginWithSavedToken(this.state.status);
- }
+ initialiseSettings();
- async setChannel(channel: string | Channel | null, server?: Server) {
- this.setState({
- currentChannel: channel,
- messages: [],
- });
- app.openLeftMenu(false);
- if (channel) {
- await AsyncStorage.getItem('serverLastChannels', async (err, data) => {
- if (!err) {
- let parsedData = JSON.parse(data || '{}') || {};
- parsedData[server?._id || 'DirectMessage'] =
- typeof channel === 'string' ? channel : channel._id;
- console.log(parsedData);
- await AsyncStorage.setItem(
- 'serverLastChannels',
- JSON.stringify(parsedData),
- );
- } else {
- console.log(`[APP] Error getting last channel: ${err}`);
- }
- });
+ setCheckedMigration(true);
}
- }
- render() {
- return ;
- }
-}
-
-export const App = () => {
- const [theme, setTheme] = useState(themes.Dark);
-
- setFunction('setTheme', (themeName: string) => {
- const newTheme = themes[themeName] ?? themes.Dark;
- setTheme(newTheme);
- });
-
- const localStyles = generateLocalStyles(theme);
+ migrationCheck();
+ }, []);
return (
-
+ {checkedMigration ? (
+
+ ) : (
+
+ )}
@@ -369,12 +66,3 @@ const generateLocalStyles = (currentTheme: Theme) => {
},
});
};
-
-const generateAppViewStyles = (currentTheme: Theme) => {
- return StyleSheet.create({
- app: {
- flex: 1,
- backgroundColor: currentTheme.backgroundPrimary,
- },
- });
-};
diff --git a/i18n/getLanguage.ts b/i18n/getLanguage.ts
index 46738d22..31f735d9 100644
--- a/i18n/getLanguage.ts
+++ b/i18n/getLanguage.ts
@@ -1,30 +1,39 @@
-import AsyncStorage from '@react-native-async-storage/async-storage';
import type {ModuleType} from 'i18next';
+import { storage } from '@rvmob/lib/storage';
+
const STORE_LANGUAGE_KEY = 'app.language';
export const languageDetectorPlugin = {
type: 'languageDetector' as ModuleType,
- async: true,
init: () => {},
- detect: async function (callback: (lang: string) => void) {
+ detect: function () {
try {
- await AsyncStorage.getItem(STORE_LANGUAGE_KEY).then(language => {
- if (language) {
- return callback(language);
- } else {
- // TODO: use the device's locale
- return 'en';
+ const settings = JSON.parse(storage.getString('settings') ?? '[]');
+ for (const setting of settings) {
+ if (setting.key === STORE_LANGUAGE_KEY) {
+ return setting.value;
}
- });
+ }
+ // TODO: use device language
+ return 'en';
} catch (error) {
console.warn(`[APP] Error reading language: ${error}`);
+ return 'en';
}
},
- cacheUserLanguage: async function (language: string) {
+ cacheUserLanguage: function (language: string) {
try {
- //save a user's language choice in Async storage
- await AsyncStorage.setItem(STORE_LANGUAGE_KEY, language);
- } catch (error) {}
+ const settings = JSON.parse(storage.getString('settings') ?? '[]');
+ for (const setting of settings) {
+ if (setting.key === STORE_LANGUAGE_KEY) {
+ const index = settings.indexOf(setting);
+ settings.splice(index, 1);
+ }
+ }
+ settings.push({key: STORE_LANGUAGE_KEY, value: language});
+ const newData = JSON.stringify(settings);
+ storage.set('settings', newData);
+ } catch (error) {}
},
};
diff --git a/i18n/strings/en.json b/i18n/strings/en.json
index 28a87894..926d4bf0 100644
--- a/i18n/strings/en.json
+++ b/i18n/strings/en.json
@@ -26,6 +26,7 @@
"loading": {
"generic": "Loading…",
"connecting": "Logging in…",
+ "migrating_settings": "Migrating settings…",
"unknown_state": "Uh oh…",
"unknown_state_body": "Please let the developers know that you saw this (value: {{state}})"
},
diff --git a/package.json b/package.json
index 3fc02e43..697c7d12 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
"react-native-drawer-layout": "^4.1.1",
"react-native-gesture-handler": "^2.21.2",
"react-native-get-random-values": "^1.11.0",
+ "react-native-mmkv": "^3.2.0",
"react-native-reanimated": "3.16.6",
"react-native-reanimated-image-viewer": "^1.0.2",
"react-native-svg": "^15.10.1",
diff --git a/src/Generic.tsx b/src/Generic.tsx
index d28e0e9c..b106a23d 100644
--- a/src/Generic.tsx
+++ b/src/Generic.tsx
@@ -1,12 +1,10 @@
-import AsyncStorage from '@react-native-async-storage/async-storage';
-
-import {Client} from 'revolt.js';
import type {API, Channel, Message, Server, User} from 'revolt.js';
import {setLanguage} from '@rvmob-i18n/i18n';
import {languages} from '@rvmob-i18n/languages';
import {DEFAULT_API_URL, LOADING_SCREEN_REMARKS} from '@rvmob/lib/consts';
import {checkNotificationPerms} from '@rvmob/lib/notifications';
+import {storage} from '@rvmob/lib/storage';
import {themes} from '@rvmob/lib/themes';
import {
CreateChannelModalProps,
@@ -65,7 +63,7 @@ export const app = {
setting.type === 'number' ? parseInt(raw as string, 10) || 0 : raw;
return toreturn;
},
- set: async (k: string, v: string | boolean | undefined) => {
+ set: (k: string, v: string | boolean | undefined) => {
try {
const setting = app.settings._fetch(k);
if (!setting) {
@@ -74,12 +72,12 @@ export const app = {
}
setting.value = v;
setting.onChange && setting.onChange(v);
- await app.settings.save();
+ app.settings.save();
} catch (err) {
console.log(`[SETTINGS] Error setting setting ${k} to ${v}: ${err}`);
}
},
- save: async () => {
+ save: () => {
try {
let out: object[] = [];
for (const s of app.settings.list) {
@@ -87,19 +85,18 @@ export const app = {
out.push({key: s.key, value: s.value});
}
}
- await AsyncStorage.setItem('settings', JSON.stringify(out));
+ storage.set('settings', JSON.stringify(out));
} catch (err) {
console.log(`[SETTINGS] Error saving settings: ${err}`);
}
},
- clear: async () => {
+ clear: () => {
try {
- AsyncStorage.setItem('settings', '{}').then(() => {
- for (const s of app.settings.list) {
- delete s.value;
- s.onChange && s.onChange(s.default);
- }
- });
+ storage.set('settings', '{}');
+ for (const s of app.settings.list) {
+ delete s.value;
+ s.onChange && s.onChange(s.default);
+ }
} catch (err) {
console.log(`[SETTINGS] Error saving settings: ${err}`);
}
@@ -409,69 +406,6 @@ export function setFunction(name: string, func: any) {
app[name] = func;
}
-async function initialiseSettings() {
- const s = await AsyncStorage.getItem('settings');
- console.log(s);
- if (s) {
- try {
- const settings = JSON.parse(s) as {key: string; value: any}[];
- settings.forEach(key => {
- let st: Setting | undefined;
- for (const setting of app.settings.list) {
- if (setting.key === key.key) {
- st = setting;
- }
- }
- if (st) {
- st.value = key.value;
- st.onInitialize && st.onInitialize(key.value);
- } else {
- // ignore known good key
- if (key.key !== 'app.lastVersion') {
- console.warn(
- `[SETTINGS] Unknown setting in AsyncStorage settings: ${key}`,
- );
- }
- }
- });
- } catch (e) {
- console.error(e);
- }
- }
-}
-
-async function getAPIURL() {
- await initialiseSettings();
- console.log(`[APP] Initialised settings (${new Date().getTime()})`);
- let url: string = '';
- console.log('[AUTH] Getting API URL...');
- const instance = app.settings.get('app.instance') as
- | string
- | null
- | undefined;
- if (!instance) {
- console.log(
- '[AUTH] Unable to fetch app.instance; setting apiURL to default',
- );
- url = DEFAULT_API_URL;
- } else {
- console.log(`[AUTH] Fetched app.instance; setting apiURL to ${instance}`);
- url = instance;
- }
- return url;
-}
-
-export let client = undefined as unknown as Client;
-
-getAPIURL().then(url => {
- const apiURL = url;
- console.log(`[AUTH] Creating client... (instance: ${apiURL})`);
- client = new Client({
- unreads: true,
- apiURL: apiURL,
- });
-});
-
export var selectedRemark =
LOADING_SCREEN_REMARKS[
Math.floor(Math.random() * LOADING_SCREEN_REMARKS.length)
diff --git a/src/LegacyMessageView.tsx b/src/LegacyMessageView.tsx
index 5c6193e0..241f2a7c 100644
--- a/src/LegacyMessageView.tsx
+++ b/src/LegacyMessageView.tsx
@@ -10,7 +10,8 @@ import {autorun} from 'mobx';
import {Message as RevoltMessage} from 'revolt.js';
-import {client, app, setFunction, randomizeRemark} from './Generic';
+import {app, setFunction, randomizeRemark} from './Generic';
+import { client } from './lib/client';
import {styles} from './Theme';
import {Text} from './components/common/atoms';
import {Message} from './components/common/messaging';
diff --git a/src/MainView.tsx b/src/MainView.tsx
new file mode 100644
index 00000000..a0365dbf
--- /dev/null
+++ b/src/MainView.tsx
@@ -0,0 +1,339 @@
+import {Component as ReactComponent, useCallback, useContext} from 'react';
+import {StatusBar, StyleSheet, View} from 'react-native';
+
+import {Channel, Server} from 'revolt.js';
+
+import {app, randomizeRemark, setFunction} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
+import {Modals} from '@rvmob/Modals';
+import {SideMenuHandler} from '@rvmob/SideMenu';
+import {NetworkIndicator} from '@rvmob/components/NetworkIndicator';
+import {Notification} from '@rvmob/components/Notification';
+import {LoadingScreen} from '@rvmob/components/views/LoadingScreen';
+import {loginWithSavedToken} from '@rvmob/lib/auth';
+import {
+ createChannel,
+ sendNotifeeNotification,
+ setUpNotifeeListener,
+} from '@rvmob/lib/notifications';
+import {storage} from '@rvmob/lib/storage';
+import {Theme, ThemeContext} from '@rvmob/lib/themes';
+import {sleep} from '@rvmob/lib/utils';
+import {LoginViews} from '@rvmob/pages/LoginViews';
+
+function openLastChannel() {
+ try {
+ const lastServer = storage.getString('lastServer');
+ if (lastServer) {
+ app.openServer(client.servers.get(lastServer));
+ try {
+ const channelData = storage.getString('lastOpenedChannels');
+ let lastOpenedChannels = JSON.parse(channelData || '{}') || {};
+ let lastChannel = lastOpenedChannels[lastServer];
+ if (lastChannel) {
+ let fetchedLastChannel = client.channels.get(lastChannel);
+ if (fetchedLastChannel) {
+ app.openChannel(fetchedLastChannel);
+ }
+ }
+ } catch (channelErr) {
+ console.log(`[APP] Error getting last channel: ${channelErr}`);
+ }
+ }
+ } catch (serverErr) {
+ console.log(`[APP] Error getting last server: ${serverErr}`);
+ }
+}
+
+function checkLastVersion() {
+ const lastVersion = app.settings.get('app.lastVersion');
+ console.log(app.version, lastVersion);
+ if (!lastVersion || lastVersion === '') {
+ console.log(
+ `[APP] lastVersion is null (${lastVersion}), setting to app.version (${app.version})`,
+ );
+ app.settings.set('app.lastVersion', app.version);
+ } else {
+ app.version === lastVersion
+ ? console.log(
+ `[APP] lastVersion (${lastVersion}) is equal to app.version (${app.version})`,
+ )
+ : console.log(
+ `[APP] lastVersion (${lastVersion}) is different from app.version (${app.version})`,
+ );
+ }
+}
+
+function LoggedInViews({state, setChannel}: {state: any; setChannel: any}) {
+ return (
+ <>
+
+
+
+
+
+ state.setState({
+ notificationMessage: null,
+ })
+ }
+ openChannel={() =>
+ state.setState({
+ notificationMessage: null,
+ currentChannel: state.state.notificationMessage.channel,
+ })
+ }
+ />
+
+ >
+ );
+}
+
+function AppViews({state}: {state: any}) {
+ const {currentTheme} = useContext(ThemeContext);
+ const localStyles = generateLocalStyles(currentTheme);
+
+ const setChannel = useCallback(
+ (channel: string | Channel | null, server?: Server) => {
+ state.setState({
+ currentChannel: channel,
+ messages: [],
+ });
+ app.openLeftMenu(false);
+ if (channel) {
+ const lastOpenedChannels = storage.getString('lastOpenedChannels');
+ try {
+ let parsedData = JSON.parse(lastOpenedChannels || '{}') || {};
+ parsedData[server?._id || 'DirectMessage'] =
+ typeof channel === 'string' ? channel : channel._id;
+ console.log(parsedData);
+ storage.set('lastOpenedChannels', JSON.stringify(parsedData));
+ } catch (err) {
+ console.log(`[APP] Error getting last channel: ${err}`);
+ }
+ }
+ },
+ [state],
+ );
+
+ return (
+ <>
+
+
+ {state.state.status === 'loggedIn' ? (
+
+ ) : state.state.status === 'loggedOut' ? (
+ state.setState({status: 'loggedIn'})}
+ />
+ ) : (
+
+ )}
+
+ >
+ );
+}
+
+export class MainView extends ReactComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ status: 'loggedOut',
+ currentChannel: null,
+ notificationMessage: null,
+ orderedServers: [],
+ serverNotifications: null,
+ channelNotifications: null,
+ };
+ setFunction('openChannel', async c => {
+ // if (!this.state.currentChannel || this.state.currentChannel?.server?._id != c.server?._id) c.server?.fetchMembers()
+ this.setState({currentChannel: c});
+ app.openLeftMenu(false);
+ });
+ setFunction('joinInvite', async (i: string) => {
+ await client.joinInvite(i);
+ });
+ setFunction('logOut', async () => {
+ console.log(
+ `[AUTH] Logging out of current session... (user: ${client.user?._id})`,
+ );
+ storage.set('token', '');
+ storage.set('sessionID', '');
+ this.setState({status: 'loggedOut', currentChannel: null});
+ await client.logout();
+ app.setLoggedOutScreen('loginPage');
+ });
+ }
+ componentDidUpdate(_, prevState) {
+ if (prevState.status !== this.state.status) {
+ randomizeRemark();
+ }
+ }
+ async componentDidMount() {
+ console.log(`[APP] Mounted component (${new Date().getTime()})`);
+
+ let defaultNotif = await createChannel();
+ console.log(`[NOTIFEE] Created channel: ${defaultNotif}`);
+
+ checkLastVersion();
+
+ client.on('connecting', () => {
+ app.setLoadingStage('connecting');
+ console.log(`[APP] Connecting to instance... (${new Date().getTime()})`);
+ });
+
+ client.on('connected', () => {
+ app.setLoadingStage('connected');
+ console.log(`[APP] Connected to instance (${new Date().getTime()})`);
+ });
+
+ client.on('ready', async () => {
+ let orderedServers,
+ server,
+ channel = null;
+ try {
+ const rawSettings = await client.syncFetchSettings([
+ 'ordering',
+ 'notifications',
+ ]);
+ try {
+ orderedServers = JSON.parse(rawSettings.ordering[1]).servers;
+ ({server, channel} = JSON.parse(rawSettings.notifications[1]));
+ } catch (err) {
+ console.log(`[APP] Error parsing fetched settings: ${err}`);
+ }
+ } catch (err) {
+ console.log(`[APP] Error fetching settings: ${err}`);
+ }
+
+ this.setState({
+ status: 'loggedIn',
+ network: 'ready',
+ orderedServers,
+ serverNotifications: server,
+ channelNotifications: channel,
+ });
+ console.log(`[APP] Client is ready (${new Date().getTime()})`);
+
+ setUpNotifeeListener(client, this.setState);
+
+ if (app.settings.get('app.reopenLastChannel')) {
+ openLastChannel();
+ }
+ });
+
+ client.on('dropped', async () => {
+ this.setState({network: 'dropped'});
+ });
+
+ client.on('message', async msg => {
+ console.log(`[APP] Handling message ${msg._id}`);
+
+ let channelNotif = this.state.channelNotifications
+ ? this.state.channelNotifications[msg.channel?._id]
+ : undefined;
+ let serverNotif = this.state.serverNotifications
+ ? this.state.serverNotifications[msg.channel?.server?._id]
+ : undefined;
+
+ const isMuted =
+ (channelNotif && channelNotif === 'none') ||
+ channelNotif === 'muted' ||
+ (serverNotif && serverNotif === 'none') ||
+ serverNotif === 'muted';
+
+ const alwaysNotif =
+ channelNotif === 'all' || (!isMuted && serverNotif === 'all');
+
+ const mentionsUser =
+ (msg.mention_ids?.includes(client.user?._id!) &&
+ (app.settings.get('app.notifications.notifyOnSelfPing') ||
+ msg.author?._id !== client.user?._id)) ||
+ msg.channel?.channel_type === 'DirectMessage';
+
+ const shouldNotif =
+ (alwaysNotif &&
+ (app.settings.get('app.notifications.notifyOnSelfPing') ||
+ msg.author?._id !== client.user?._id)) ||
+ (!isMuted && mentionsUser);
+
+ console.log(
+ `[NOTIFICATIONS] Should notify for ${msg._id}: ${shouldNotif} (channel/server muted? ${isMuted}, notifications for all messages enabled? ${alwaysNotif}, message mentions the user? ${mentionsUser})`,
+ );
+
+ if (app.settings.get('app.notifications.enabled') && shouldNotif) {
+ console.log(
+ `[NOTIFICATIONS] Pushing notification for message ${msg._id}`,
+ );
+ if (this.state.currentChannel !== msg.channel) {
+ this.setState({notificationMessage: msg});
+ await sleep(5000);
+ this.setState({notificationMessage: null});
+ }
+
+ await sendNotifeeNotification(msg, client, defaultNotif);
+ }
+ });
+
+ client.on('packet', async p => {
+ if (p.type === 'UserSettingsUpdate') {
+ console.log('[WEBSOCKET] Synced settings updated');
+ try {
+ if ('ordering' in p.update) {
+ const orderedServers = JSON.parse(p.update.ordering[1]).servers;
+ this.setState({orderedServers});
+ }
+ if ('notifications' in p.update) {
+ const {server, channel} = JSON.parse(p.update.notifications[1]);
+ this.setState({
+ serverNotifications: server,
+ channelNotifications: channel,
+ });
+ }
+ } catch (err) {
+ console.log(`[APP] Error fetching settings: ${err}`);
+ }
+ }
+ });
+
+ client.on('server/delete', async s => {
+ const currentServer = app.getCurrentServer();
+ if (currentServer === s) {
+ app.openServer(undefined);
+ app.openChannel(null);
+ }
+ });
+
+ await loginWithSavedToken(this.state.status);
+ }
+
+ render() {
+ return ;
+ }
+}
+
+const generateLocalStyles = (currentTheme: Theme) => {
+ return StyleSheet.create({
+ app: {
+ flex: 1,
+ backgroundColor: currentTheme.backgroundPrimary,
+ },
+ });
+};
diff --git a/src/MessageView.tsx b/src/MessageView.tsx
index fc060cab..9dd338d5 100644
--- a/src/MessageView.tsx
+++ b/src/MessageView.tsx
@@ -15,7 +15,8 @@ import {ErrorBoundary} from 'react-error-boundary';
import {Channel, Message as RevoltMessage} from 'revolt.js';
-import {client, app} from './Generic';
+import {app} from './Generic';
+import { client } from './lib/client';
import {MessageBox} from './components/MessageBox';
import {styles} from './Theme';
import {Button, Text} from './components/common/atoms';
diff --git a/src/Modals.tsx b/src/Modals.tsx
index 98e87f97..808a82f0 100644
--- a/src/Modals.tsx
+++ b/src/Modals.tsx
@@ -4,7 +4,8 @@ import {observer} from 'mobx-react-lite';
import {API, Channel, Server, User} from 'revolt.js';
-import {app, client, setFunction} from '@rvmob/Generic';
+import {app, setFunction} from '@rvmob/Generic';
+import { client } from './lib/client';
import {
CreateChannelModalProps,
DeletableObject,
diff --git a/src/SideMenu.tsx b/src/SideMenu.tsx
index ebfa08aa..15606f95 100644
--- a/src/SideMenu.tsx
+++ b/src/SideMenu.tsx
@@ -8,19 +8,20 @@ import {
View,
} from 'react-native';
-import AsyncStorage from '@react-native-async-storage/async-storage';
import {useBackHandler} from '@react-native-community/hooks';
import {Drawer} from 'react-native-drawer-layout';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {Server} from 'revolt.js';
-import {app, setFunction, client} from './Generic';
+import {app, setFunction} from './Generic';
+import { client } from './lib/client';
import {Avatar, Button} from './components/common/atoms';
import {ChannelList} from './components/navigation/ChannelList';
import {ServerList} from './components/navigation/ServerList';
import {ChannelView} from './components/views/ChannelView';
import {DEFAULT_API_URL} from './lib/consts';
+import {storage} from '@rvmob/lib/storage';
import {commonValues, Theme, ThemeContext} from '@rvmob/lib/themes';
const SideMenu = ({
@@ -40,7 +41,7 @@ const SideMenu = ({
);
function setCurrentServer(s: Server | null) {
setCurrentServerInner(s);
- AsyncStorage.setItem('lastServer', s?._id || 'DirectMessage');
+ storage.set('lastServer', s?._id || 'DirectMessage');
}
setFunction('getCurrentServer', () => {
return currentServer?._id ?? undefined;
diff --git a/src/components/ImageViewer.tsx b/src/components/ImageViewer.tsx
index 7f8afde3..84fc0ace 100644
--- a/src/components/ImageViewer.tsx
+++ b/src/components/ImageViewer.tsx
@@ -5,7 +5,7 @@ import {gestureHandlerRootHOC} from 'react-native-gesture-handler';
import ImageViewerCore from 'react-native-reanimated-image-viewer';
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
-import {client} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Text} from '@rvmob/components/common/atoms';
import {GapView} from '@rvmob/components/layout';
import {commonValues, Theme, ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/MessageBox.tsx b/src/components/MessageBox.tsx
index 4b302d33..c8031eb5 100644
--- a/src/components/MessageBox.tsx
+++ b/src/components/MessageBox.tsx
@@ -3,7 +3,6 @@ import {Platform, Pressable, StyleSheet, TextInput, View} from 'react-native';
import {useTranslation} from 'react-i18next';
import {observer} from 'mobx-react-lite';
-import AsyncStorage from '@react-native-async-storage/async-storage';
import {type DocumentPickerResponse} from 'react-native-document-picker';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
@@ -11,11 +10,13 @@ import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIc
import {Channel, Message} from 'revolt.js';
import {ulid} from 'ulid';
-import {app, client, setFunction} from '@rvmob/Generic';
+import {app, setFunction} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {styles} from '@rvmob/Theme';
import {DocumentPicker} from '@rvmob/crossplat/DocumentPicker';
import {Avatar, Text, Username} from '@rvmob/components/common/atoms';
import {USER_IDS} from '@rvmob/lib/consts';
+import {storage} from '@rvmob/lib/storage';
import {commonValues, Theme, ThemeContext} from '@rvmob/lib/themes';
import {ReplyingMessage} from '@rvmob/lib/types';
import {getReadableFileSize, showToast} from '@rvmob/lib/utils';
@@ -277,7 +278,7 @@ export const MessageBox = observer((props: MessageBoxProps) => {
),
});
let uploaded = [];
- const token = await AsyncStorage.getItem('token');
+ const token = storage.getString('token');
if (token) {
for (let a of attachments) {
const formdata = new FormData();
diff --git a/src/components/common/atoms/Avatar.tsx b/src/components/common/atoms/Avatar.tsx
index ac60ed2f..7db0fc8b 100644
--- a/src/components/common/atoms/Avatar.tsx
+++ b/src/components/common/atoms/Avatar.tsx
@@ -5,7 +5,8 @@ import {observer} from 'mobx-react-lite';
import {Server, User, Channel} from 'revolt.js';
import {Image} from '@rvmob/crossplat/Image';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {DEFAULT_MAX_SIDE} from '@rvmob/lib/consts';
import {ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/common/atoms/GeneralAvatar.tsx b/src/components/common/atoms/GeneralAvatar.tsx
index 824621b2..0fcb227f 100644
--- a/src/components/common/atoms/GeneralAvatar.tsx
+++ b/src/components/common/atoms/GeneralAvatar.tsx
@@ -1,7 +1,7 @@
import {View} from 'react-native';
import {Image} from '@rvmob/crossplat/Image';
-import {client} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {DEFAULT_MAX_SIDE} from '@rvmob/lib/consts';
export const GeneralAvatar = ({
diff --git a/src/components/common/atoms/Username.tsx b/src/components/common/atoms/Username.tsx
index ac485697..c9cc0615 100644
--- a/src/components/common/atoms/Username.tsx
+++ b/src/components/common/atoms/Username.tsx
@@ -4,7 +4,8 @@ import {observer} from 'mobx-react-lite';
import {Server, User} from 'revolt.js';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Text} from './Text';
import {USER_IDS} from '@rvmob/lib/consts';
import {ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/common/messaging/Emoji.tsx b/src/components/common/messaging/Emoji.tsx
index a07ff618..24cf4adc 100644
--- a/src/components/common/messaging/Emoji.tsx
+++ b/src/components/common/messaging/Emoji.tsx
@@ -4,7 +4,8 @@ import {StyleSheet} from 'react-native';
import {SvgUri} from 'react-native-svg';
import {Image} from '@rvmob/crossplat/Image';
-import {client, app} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Text} from '../atoms';
import {
RE_CUSTOM_EMOJI,
diff --git a/src/components/common/messaging/InviteEmbed.tsx b/src/components/common/messaging/InviteEmbed.tsx
index feb1f0d6..4970e49d 100644
--- a/src/components/common/messaging/InviteEmbed.tsx
+++ b/src/components/common/messaging/InviteEmbed.tsx
@@ -4,7 +4,8 @@ import {observer} from 'mobx-react-lite';
import {API, Message} from 'revolt.js';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Button, GeneralAvatar, Text} from '../atoms';
import {commonValues, ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/common/messaging/Message.tsx b/src/components/common/messaging/Message.tsx
index f5741120..9d4fd47a 100644
--- a/src/components/common/messaging/Message.tsx
+++ b/src/components/common/messaging/Message.tsx
@@ -13,7 +13,8 @@ import {enGB, enUS} from 'date-fns/locale';
import {Message as RevoltMessage} from 'revolt.js';
import {decodeTime} from 'ulid';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Avatar, Text, Username} from '@rvmob/components/common/atoms';
import {MarkdownView} from '@rvmob/components/common/MarkdownView';
import {InviteEmbed} from '@rvmob/components/common/messaging/InviteEmbed';
diff --git a/src/components/common/messaging/MessageEmbed.tsx b/src/components/common/messaging/MessageEmbed.tsx
index c90f58d5..ab1019b6 100644
--- a/src/components/common/messaging/MessageEmbed.tsx
+++ b/src/components/common/messaging/MessageEmbed.tsx
@@ -5,7 +5,8 @@ import {observer} from 'mobx-react-lite';
import {API} from 'revolt.js';
import {Image} from '@rvmob/crossplat/Image';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {MarkdownView} from '../MarkdownView';
import {Link, Text} from '../atoms';
import {commonValues, ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/common/messaging/MessageReactions.tsx b/src/components/common/messaging/MessageReactions.tsx
index f262087d..264b7703 100644
--- a/src/components/common/messaging/MessageReactions.tsx
+++ b/src/components/common/messaging/MessageReactions.tsx
@@ -5,7 +5,7 @@ import {observer} from 'mobx-react-lite';
import {Message} from 'revolt.js';
-import {client} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Text} from '@rvmob/components/common/atoms';
import {Image} from '@rvmob/crossplat/Image';
import {commonValues, ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/common/messaging/SystemMessage.tsx b/src/components/common/messaging/SystemMessage.tsx
index 83436f4e..3897f1f7 100644
--- a/src/components/common/messaging/SystemMessage.tsx
+++ b/src/components/common/messaging/SystemMessage.tsx
@@ -8,7 +8,8 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {Message as RevoltMessage} from 'revolt.js';
import {Text, Username} from '@rvmob/components/common/atoms';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Theme, ThemeContext} from '@rvmob/lib/themes';
const SYSTEM_MESSAGE_ICONS = {
diff --git a/src/components/common/profile/RoleView.tsx b/src/components/common/profile/RoleView.tsx
index b424dab9..3e1db1b3 100644
--- a/src/components/common/profile/RoleView.tsx
+++ b/src/components/common/profile/RoleView.tsx
@@ -4,7 +4,7 @@ import {observer} from 'mobx-react-lite';
import {Server, User} from 'revolt.js';
-import {client} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Text} from '@rvmob/components/common/atoms/Text';
import {commonValues, ThemeContext} from '@rvmob/lib/themes';
import {getColour} from '@rvmob/lib/utils';
diff --git a/src/components/common/settings/sections/app/AccountSettingsSection.tsx b/src/components/common/settings/sections/app/AccountSettingsSection.tsx
index eed3dc67..dd08a535 100644
--- a/src/components/common/settings/sections/app/AccountSettingsSection.tsx
+++ b/src/components/common/settings/sections/app/AccountSettingsSection.tsx
@@ -2,15 +2,16 @@ import {useContext, useEffect, useState} from 'react';
import {Pressable, View} from 'react-native';
import {observer} from 'mobx-react-lite';
-import AsyncStorage from '@react-native-async-storage/async-storage';
import Clipboard from '@react-native-clipboard/clipboard';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {styles} from '@rvmob/Theme';
import {Text} from '@rvmob/components/common/atoms';
import {GapView} from '@rvmob/components/layout';
import {SettingsEntry} from '@rvmob/components/common/settings/atoms';
+import {storage} from '@rvmob/lib/storage';
import {ThemeContext} from '@rvmob/lib/themes';
export const AccountSettingsSection = observer(() => {
@@ -20,7 +21,7 @@ export const AccountSettingsSection = observer(() => {
email: '',
mfaEnabled: false,
sessions: [] as {_id: string; name: string}[],
- sessionID: '' as string | null,
+ sessionID: '' as string | undefined,
});
const [showEmail, setShowEmail] = useState(false);
@@ -29,7 +30,7 @@ export const AccountSettingsSection = observer(() => {
const e = await client.api.get('/auth/account/');
const m = await client.api.get('/auth/mfa/');
const s = await client.api.get('/auth/session/all');
- const i = await AsyncStorage.getItem('sessionID');
+ const i = storage.getString('sessionID');
setAuthInfo({
email: e.email,
mfaEnabled: m.totp_mfa ?? m.security_key_mfa ?? false,
diff --git a/src/components/common/settings/sections/app/ProfileSettingsSection.tsx b/src/components/common/settings/sections/app/ProfileSettingsSection.tsx
index c0663075..c61dcd73 100644
--- a/src/components/common/settings/sections/app/ProfileSettingsSection.tsx
+++ b/src/components/common/settings/sections/app/ProfileSettingsSection.tsx
@@ -5,7 +5,8 @@ import {observer} from 'mobx-react-lite';
import Clipboard from '@react-native-clipboard/clipboard';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {styles} from '@rvmob/Theme';
import {Text} from '@rvmob/components/common/atoms';
import {SettingsEntry} from '@rvmob/components/common/settings/atoms';
diff --git a/src/components/common/settings/sections/server/EmojiSettingsSection.tsx b/src/components/common/settings/sections/server/EmojiSettingsSection.tsx
index 40afdc4f..12fdbb02 100644
--- a/src/components/common/settings/sections/server/EmojiSettingsSection.tsx
+++ b/src/components/common/settings/sections/server/EmojiSettingsSection.tsx
@@ -7,7 +7,7 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {Server} from 'revolt.js';
-import {client} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {styles} from '@rvmob/Theme';
import {Text} from '@rvmob/components/common/atoms';
import {SettingsEntry} from '@rvmob/components/common/settings/atoms';
diff --git a/src/components/navigation/ChannelList.tsx b/src/components/navigation/ChannelList.tsx
index 8fbe24a4..b62670aa 100644
--- a/src/components/navigation/ChannelList.tsx
+++ b/src/components/navigation/ChannelList.tsx
@@ -14,7 +14,8 @@ import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIc
import type {Channel, Server} from 'revolt.js';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {ChannelButton, Text} from '../common/atoms';
import {commonValues, ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/navigation/ServerList.tsx b/src/components/navigation/ServerList.tsx
index b945d7ea..f0a2132c 100644
--- a/src/components/navigation/ServerList.tsx
+++ b/src/components/navigation/ServerList.tsx
@@ -6,7 +6,8 @@ import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIc
import {decodeTime} from 'ulid';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Text} from '../common/atoms';
import {Image} from '@rvmob/crossplat/Image';
import {DEFAULT_MAX_SIDE} from '@rvmob/lib/consts';
diff --git a/src/components/pages/FriendsPage.tsx b/src/components/pages/FriendsPage.tsx
index 4da8d073..8f95b839 100644
--- a/src/components/pages/FriendsPage.tsx
+++ b/src/components/pages/FriendsPage.tsx
@@ -4,7 +4,8 @@ import {observer} from 'mobx-react-lite';
import {User} from 'revolt.js';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {styles} from '@rvmob/Theme';
import {MiniProfile} from '@rvmob/components/common/profile';
import {ChannelHeader} from '@rvmob/components/navigation/ChannelHeader';
diff --git a/src/components/pages/HomePage.tsx b/src/components/pages/HomePage.tsx
index a8993d32..48133ad1 100644
--- a/src/components/pages/HomePage.tsx
+++ b/src/components/pages/HomePage.tsx
@@ -2,7 +2,8 @@ import {TouchableOpacity, View} from 'react-native';
import {useTranslation} from 'react-i18next';
import {observer} from 'mobx-react-lite';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {
SPECIAL_DATES,
SPECIAL_DATE_OBJECTS,
diff --git a/src/components/sheets/BotInviteSheet.tsx b/src/components/sheets/BotInviteSheet.tsx
index 754c7fc2..6bb81f20 100644
--- a/src/components/sheets/BotInviteSheet.tsx
+++ b/src/components/sheets/BotInviteSheet.tsx
@@ -4,7 +4,7 @@ import {observer} from 'mobx-react-lite';
import {Server, User} from 'revolt.js';
-import {client} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Button, GeneralAvatar, Text} from '@rvmob/components/common/atoms';
import {ServerList} from '@rvmob/components/navigation/ServerList';
import {ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/sheets/ProfileSheet.tsx b/src/components/sheets/ProfileSheet.tsx
index 6a97c86b..fb397843 100644
--- a/src/components/sheets/ProfileSheet.tsx
+++ b/src/components/sheets/ProfileSheet.tsx
@@ -10,7 +10,8 @@ import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIc
import {User, Server} from 'revolt.js';
-import {app, client, setFunction} from '@rvmob/Generic';
+import {app, setFunction} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {styles} from '@rvmob/Theme';
import {
Avatar,
diff --git a/src/components/sheets/ReportSheet.tsx b/src/components/sheets/ReportSheet.tsx
index d32cb22d..c34e4bdb 100644
--- a/src/components/sheets/ReportSheet.tsx
+++ b/src/components/sheets/ReportSheet.tsx
@@ -7,7 +7,8 @@ import {useBackHandler} from '@react-native-community/hooks';
import {Message} from 'revolt.js';
-import {app, client, setFunction} from '@rvmob/Generic';
+import {app, setFunction} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Avatar, Button, Text, Username} from '@rvmob/components/common/atoms';
import {BottomSheet} from '@rvmob/components/common/BottomSheet';
import {MarkdownView} from '@rvmob/components/common/MarkdownView';
diff --git a/src/components/sheets/ServerInfoSheet.tsx b/src/components/sheets/ServerInfoSheet.tsx
index 392c2903..bb1d3252 100644
--- a/src/components/sheets/ServerInfoSheet.tsx
+++ b/src/components/sheets/ServerInfoSheet.tsx
@@ -9,7 +9,8 @@ import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIc
import {Member, Server} from 'revolt.js';
-import {app, client, setFunction} from '@rvmob/Generic';
+import {app, setFunction} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {styles} from '@rvmob/Theme';
import {SERVER_FLAGS, SPECIAL_SERVERS} from '@rvmob/lib/consts';
import {commonValues, ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/sheets/ServerInviteSheet.tsx b/src/components/sheets/ServerInviteSheet.tsx
index b7fe430a..657871b8 100644
--- a/src/components/sheets/ServerInviteSheet.tsx
+++ b/src/components/sheets/ServerInviteSheet.tsx
@@ -6,7 +6,8 @@ import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIc
import {API} from 'revolt.js';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Button, GeneralAvatar, Text} from '@rvmob/components/common/atoms';
import {Image} from '@rvmob/crossplat/Image';
import {commonValues, ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/sheets/ServerSettingsSheet.tsx b/src/components/sheets/ServerSettingsSheet.tsx
index 813dd331..c814d341 100644
--- a/src/components/sheets/ServerSettingsSheet.tsx
+++ b/src/components/sheets/ServerSettingsSheet.tsx
@@ -8,7 +8,8 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {Server} from 'revolt.js';
-import {app, client, setFunction} from '@rvmob/Generic';
+import {app, setFunction} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {Image} from '@rvmob/crossplat/Image';
import {MAX_SIDE_HQ} from '@rvmob/lib/consts';
import {Theme, ThemeContext} from '@rvmob/lib/themes';
diff --git a/src/components/sheets/SettingsSheet.tsx b/src/components/sheets/SettingsSheet.tsx
index 49194734..2238d0b2 100644
--- a/src/components/sheets/SettingsSheet.tsx
+++ b/src/components/sheets/SettingsSheet.tsx
@@ -3,7 +3,6 @@ import {Platform, ScrollView, View} from 'react-native';
import {useTranslation} from 'react-i18next';
import {observer} from 'mobx-react-lite';
-import AsyncStorage from '@react-native-async-storage/async-storage';
import Clipboard from '@react-native-clipboard/clipboard';
import {
getApiLevel,
@@ -14,14 +13,16 @@ import {
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
-import {app, client, setFunction} from '@rvmob/Generic';
+import {app, setFunction} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {OPEN_ISSUES} from '@rvmob/lib/consts';
+import {storage} from '@rvmob/lib/storage';
import {ThemeContext} from '@rvmob/lib/themes';
import {SettingsSection} from '@rvmob/lib/types';
import {openUrl} from '@rvmob/lib/utils';
import {styles} from '@rvmob/Theme';
-import {BackButton, ContextButton, Text} from '../common/atoms';
-import {SettingsCategory} from '../common/settings';
+import {BackButton, ContextButton, Text} from '@rvmob/components/common/atoms';
+import {SettingsCategory} from '@rvmob/components/common/settings';
import {
AppInfoSection,
AccountSettingsSection,
@@ -44,7 +45,7 @@ async function copyDebugInfo() {
appInfo: {
userID: client.user?._id ?? 'ERR_ID_UNDEFINED',
- settings: await AsyncStorage.getItem('settings'),
+ settings: storage.getString('settings'),
version: app.version,
},
};
diff --git a/src/components/sheets/StatusSheet.tsx b/src/components/sheets/StatusSheet.tsx
index c9407d66..2706e86e 100644
--- a/src/components/sheets/StatusSheet.tsx
+++ b/src/components/sheets/StatusSheet.tsx
@@ -5,7 +5,8 @@ import {observer} from 'mobx-react-lite';
import BottomSheetCore from '@gorhom/bottom-sheet';
import {useBackHandler} from '@react-native-community/hooks';
-import {client, setFunction} from '@rvmob/Generic';
+import {setFunction} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {ContextButton, InputWithButton, Text} from '../common/atoms';
import {BottomSheet} from '../common/BottomSheet';
import {STATUSES} from '@rvmob/lib/consts';
diff --git a/src/lib/auth/login.ts b/src/lib/auth/login.ts
index ae4efdb0..c859be7a 100644
--- a/src/lib/auth/login.ts
+++ b/src/lib/auth/login.ts
@@ -1,11 +1,12 @@
import {Platform} from 'react-native';
-import AsyncStorage from '@react-native-async-storage/async-storage';
+import {API} from 'revolt.js';
-import {decodeTime} from 'ulid';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
+import {storage} from '@rvmob/lib/storage';
-import {app, client} from '@rvmob/Generic';
-import {API} from 'revolt.js';
+import {decodeTime} from 'ulid';
async function connectAndSave(
session: API.ResponseLogin,
@@ -21,10 +22,8 @@ async function connectAndSave(
const token = session.token;
console.log('[AUTH] Logging in with a new token...');
await client.useExistingSession({token: token});
- await AsyncStorage.multiSet([
- ['token', token],
- ['sessionID', session._id],
- ]);
+ storage.set('token', token);
+ storage.set('sessionID', session._id);
console.log('[AUTH] Successfuly logged in and saved the token/session ID!');
setStatus('loggedIn');
} catch (err) {
@@ -133,7 +132,7 @@ export async function loginWithToken(
token,
});
console.log(`[AUTH] Setting saved token to ${token}`);
- AsyncStorage.setItem('token', token);
+ storage.set('token', token);
setStatus('loggedIn');
} catch (tokenErr) {
console.error(tokenErr);
@@ -144,30 +143,26 @@ export async function loginWithToken(
}
export async function loginWithSavedToken(status: any) {
- AsyncStorage.getItem('token', async (err, res) => {
- if (!err) {
- if (typeof res !== 'string') {
- console.log(
- `[AUTH] Saved token was not a string: ${typeof res}, ${res}`,
- );
- app.setLoggedOutScreen('loginPage');
- return;
- }
- try {
- await client.useExistingSession({token: res});
- } catch (e: any) {
- console.log(e);
- !(
- e.message?.startsWith('Read error') || e.message === 'Network Error'
- ) && client.user
- ? app.setLoggedOutScreen('loginPage')
- : status === 'loggedIn'
- ? null
- : app.setLoggedOutScreen('loginPage');
- }
- } else {
- console.log(err);
+ try {
+ const res = storage.getString('token');
+ if (typeof res !== 'string') {
+ console.log(`[AUTH] Saved token was not a string: ${typeof res}, ${res}`);
app.setLoggedOutScreen('loginPage');
+ return;
}
- });
+ try {
+ await client.useExistingSession({token: res});
+ } catch (e: any) {
+ console.log(e);
+ !(e.message?.startsWith('Read error') || e.message === 'Network Error') &&
+ client.user
+ ? app.setLoggedOutScreen('loginPage')
+ : status === 'loggedIn'
+ ? null
+ : app.setLoggedOutScreen('loginPage');
+ }
+ } catch (err) {
+ console.log(err);
+ app.setLoggedOutScreen('loginPage');
+ }
}
diff --git a/src/lib/client/index.ts b/src/lib/client/index.ts
new file mode 100644
index 00000000..7c04e0a4
--- /dev/null
+++ b/src/lib/client/index.ts
@@ -0,0 +1,31 @@
+import { Client } from 'revolt.js';
+
+import { app } from '@rvmob/Generic';
+import { DEFAULT_API_URL } from '@rvmob/lib/consts';
+
+function getAPIURL() {
+ console.log(`[APP] Initialised settings (${new Date().getTime()})`);
+ let url: string = '';
+ console.log('[AUTH] Getting API URL...');
+ const instance = app.settings.get('app.instance') as string |
+ null |
+ undefined;
+ if (!instance) {
+ console.log(
+ '[AUTH] Unable to fetch app.instance; setting apiURL to default'
+ );
+ url = DEFAULT_API_URL;
+ } else {
+ console.log(`[AUTH] Fetched app.instance; setting apiURL to ${instance}`);
+ url = instance;
+ }
+ return url;
+}
+
+const apiURL = getAPIURL();
+console.log(`[AUTH] Creating client... (instance: ${apiURL})`);
+
+export let client = new Client({
+ unreads: true,
+ apiURL: apiURL,
+});
diff --git a/src/lib/storage/index.ts b/src/lib/storage/index.ts
new file mode 100644
index 00000000..b70a50e2
--- /dev/null
+++ b/src/lib/storage/index.ts
@@ -0,0 +1,3 @@
+import {MMKV} from 'react-native-mmkv';
+
+export const storage = new MMKV();
diff --git a/src/lib/storage/migration.ts b/src/lib/storage/migration.ts
new file mode 100644
index 00000000..27a661a9
--- /dev/null
+++ b/src/lib/storage/migration.ts
@@ -0,0 +1,18 @@
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import {storage} from '.';
+
+export async function migrateToMMKV() {
+ console.log('hi');
+ const keys = await AsyncStorage.getAllKeys();
+ console.log(keys);
+
+ for (const key of keys) {
+ if (key === 'token' || key === 'settings') {
+ const value = await AsyncStorage.getItem(key);
+ console.log(value);
+ storage.set(key, value ?? '');
+ }
+ }
+
+ storage.set('hasMigrated', true);
+}
diff --git a/src/lib/storage/utils.ts b/src/lib/storage/utils.ts
new file mode 100644
index 00000000..c6c1f5d7
--- /dev/null
+++ b/src/lib/storage/utils.ts
@@ -0,0 +1,31 @@
+import { app } from '@rvmob/Generic';
+import { storage } from '@rvmob/lib/storage';
+import { Setting } from '@rvmob/lib/types';
+
+export function initialiseSettings() {
+ const s = storage.getString('settings');
+ if (s) {
+ try {
+ const settings = JSON.parse(s) as {key: string; value: any}[];
+ settings.forEach(key => {
+ let st: Setting | undefined;
+ for (const setting of app.settings.list) {
+ if (setting.key === key.key) {
+ st = setting;
+ }
+ }
+ if (st) {
+ st.value = key.value;
+ st.onInitialize && st.onInitialize(key.value);
+ } else {
+ // ignore known good key
+ if (key.key !== 'app.lastVersion') {
+ console.warn(`[SETTINGS] Unknown setting in MMKV settings: ${key}`);
+ }
+ }
+ });
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ }
diff --git a/src/lib/utils/utils.ts b/src/lib/utils/utils.ts
index bde4c871..947ee07c 100644
--- a/src/lib/utils/utils.ts
+++ b/src/lib/utils/utils.ts
@@ -4,7 +4,8 @@ import {differenceInMinutes, isSameDay} from 'date-fns';
import {Channel, Message} from 'revolt.js';
import {decodeTime} from 'ulid';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {
DEFAULT_MESSAGE_LOAD_COUNT,
DISCOVER_URL,
diff --git a/src/pages/auth/LoginPage.tsx b/src/pages/auth/LoginPage.tsx
index 404c2998..1c79951c 100644
--- a/src/pages/auth/LoginPage.tsx
+++ b/src/pages/auth/LoginPage.tsx
@@ -5,7 +5,8 @@ import {useTranslation} from 'react-i18next';
import {useBackHandler} from '@react-native-community/hooks';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {
BackButton,
Button,
diff --git a/src/pages/discover/DiscoverPage.tsx b/src/pages/discover/DiscoverPage.tsx
index a245c10b..3c9d7825 100644
--- a/src/pages/discover/DiscoverPage.tsx
+++ b/src/pages/discover/DiscoverPage.tsx
@@ -4,7 +4,8 @@ import {useTranslation} from 'react-i18next';
import {DOMParser} from '@xmldom/xmldom';
-import {app, client} from '@rvmob/Generic';
+import {app} from '@rvmob/Generic';
+import { client } from '@rvmob/lib/client';
import {styles} from '@rvmob/Theme';
import {Button, GeneralAvatar, Text} from '@rvmob/components/common/atoms';
import {ChannelHeader} from '@rvmob/components/navigation/ChannelHeader';
diff --git a/yarn.lock b/yarn.lock
index 757d60ea..ac716e1c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12142,6 +12142,16 @@ __metadata:
languageName: node
linkType: hard
+"react-native-mmkv@npm:^3.2.0":
+ version: 3.2.0
+ resolution: "react-native-mmkv@npm:3.2.0"
+ peerDependencies:
+ react: "*"
+ react-native: "*"
+ checksum: 10c0/41abc5b46d61de7bf0f6af0ed66fab19a391d3fdc30b011cc49ba4dce65ab7c7b1c53eda768bc71f718543e89c7d46848dab2cd537b2af39afd624a8b4f0fec4
+ languageName: node
+ linkType: hard
+
"react-native-reanimated-image-viewer@npm:^1.0.2":
version: 1.0.2
resolution: "react-native-reanimated-image-viewer@npm:1.0.2"
@@ -12902,6 +12912,7 @@ __metadata:
react-native-drawer-layout: "npm:^4.1.1"
react-native-gesture-handler: "npm:^2.21.2"
react-native-get-random-values: "npm:^1.11.0"
+ react-native-mmkv: "npm:^3.2.0"
react-native-reanimated: "npm:3.16.6"
react-native-reanimated-image-viewer: "npm:^1.0.2"
react-native-svg: "npm:^15.10.1"