Merge branch 'develop' into fix.e2efetchkeys-startup

This commit is contained in:
Gerzon Z 2022-03-10 10:19:34 -04:00
commit 9a9edc7f94
45 changed files with 351 additions and 431 deletions

View File

@ -20,6 +20,26 @@ jest.mock('react-native-file-viewer', () => ({
jest.mock('../app/lib/database', () => jest.fn(() => null)); jest.mock('../app/lib/database', () => jest.fn(() => null));
global.Date.now = jest.fn(() => new Date('2019-10-10').getTime()); global.Date.now = jest.fn(() => new Date('2019-10-10').getTime());
jest.mock('react-native-mmkv-storage', () => {
return {
Loader: jest.fn().mockImplementation(() => {
return {
setProcessingMode: jest.fn().mockImplementation(() => {
return {
withEncryption: jest.fn().mockImplementation(() => {
return {
initialize: jest.fn()
};
})
};
})
};
}),
create: jest.fn(),
MODES: { MULTI_PROCESS: '' }
};
});
const converter = new Stories2SnapsConverter(); const converter = new Stories2SnapsConverter();
initStoryshots({ initStoryshots({

View File

@ -12,7 +12,6 @@ import com.facebook.soloader.SoLoader;
import com.reactnativecommunity.viewpager.RNCViewPagerPackage; import com.reactnativecommunity.viewpager.RNCViewPagerPackage;
import com.facebook.react.bridge.JSIModulePackage; import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage; import com.swmansion.reanimated.ReanimatedJSIModulePackage;
import org.unimodules.adapters.react.ModuleRegistryAdapter; import org.unimodules.adapters.react.ModuleRegistryAdapter;
import org.unimodules.adapters.react.ReactModuleRegistryProvider; import org.unimodules.adapters.react.ReactModuleRegistryProvider;
@ -54,7 +53,7 @@ public class MainApplication extends Application implements ReactApplication {
@Override @Override
protected JSIModulePackage getJSIModulePackage() { protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage(); // <- add return new ReanimatedJSIModulePackage();
} }
@Override @Override

View File

@ -53,16 +53,8 @@ public class Ejson {
String alias = Utils.toHex("com.MMKV.default"); String alias = Utils.toHex("com.MMKV.default");
// Retrieve container password // Retrieve container password
secureKeystore.getSecureKey(alias, new RNCallback() { String password = secureKeystore.getSecureKey(alias);
@Override mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
public void invoke(Object... args) {
String error = (String) args[0];
if (error == null) {
String password = (String) args[1];
mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
}
}
});
} }
public String getAvatarUri() { public String getAvatarUri() {

View File

@ -10,7 +10,7 @@ import { TYPE } from './constants';
import { ATTEMPTS_KEY, LOCKED_OUT_TIMER_KEY, MAX_ATTEMPTS, PASSCODE_KEY } from '../../constants/localAuthentication'; import { ATTEMPTS_KEY, LOCKED_OUT_TIMER_KEY, MAX_ATTEMPTS, PASSCODE_KEY } from '../../constants/localAuthentication';
import { biometryAuth, resetAttempts } from '../../utils/localAuthentication'; import { biometryAuth, resetAttempts } from '../../utils/localAuthentication';
import { getDiff, getLockedUntil } from './utils'; import { getDiff, getLockedUntil } from './utils';
import UserPreferences from '../../lib/userPreferences'; import { useUserPreferences } from '../../lib/userPreferences';
import I18n from '../../i18n'; import I18n from '../../i18n';
interface IPasscodePasscodeEnter { interface IPasscodePasscodeEnter {
@ -23,16 +23,11 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeE
const ref = useRef(null); const ref = useRef(null);
let attempts: any = 0; let attempts: any = 0;
let lockedUntil: any = false; let lockedUntil: any = false;
const [passcode, setPasscode] = useState(null); const [passcode] = useUserPreferences(PASSCODE_KEY);
const [status, setStatus] = useState(null); const [status, setStatus] = useState(null);
const { getItem: getAttempts, setItem: setAttempts } = useAsyncStorage(ATTEMPTS_KEY); const { getItem: getAttempts, setItem: setAttempts } = useAsyncStorage(ATTEMPTS_KEY);
const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY); const { setItem: setLockedUntil } = useAsyncStorage(LOCKED_OUT_TIMER_KEY);
const fetchPasscode = async () => {
const p: any = await UserPreferences.getStringAsync(PASSCODE_KEY);
setPasscode(p);
};
const biometry = async () => { const biometry = async () => {
if (hasBiometry && status === TYPE.ENTER) { if (hasBiometry && status === TYPE.ENTER) {
const result = await biometryAuth(); const result = await biometryAuth();
@ -56,7 +51,6 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeE
} else { } else {
setStatus(TYPE.ENTER); setStatus(TYPE.ENTER);
} }
await fetchPasscode();
biometry(); biometry();
}; };

View File

@ -1,7 +1,7 @@
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import React from 'react'; import React from 'react';
import { ProfileStackParamList } from '../../stacks/types'; import { ProfileStackParamList } from '../stacks/types';
export interface IUser { export interface IUser {
id: string; id: string;
@ -57,6 +57,14 @@ export interface IAvatar {
service?: any; service?: any;
} }
export interface IAvatarSuggestion {
[service: string]: {
url: string;
blob: string;
contentType: string;
};
}
export interface IProfileViewState { export interface IProfileViewState {
saving: boolean; saving: boolean;
name: string; name: string;
@ -66,13 +74,7 @@ export interface IProfileViewState {
currentPassword: string | null; currentPassword: string | null;
avatarUrl: string | null; avatarUrl: string | null;
avatar: IAvatar; avatar: IAvatar;
avatarSuggestions: { avatarSuggestions: IAvatarSuggestion;
[service: string]: {
url: string;
blob: string;
contentType: string;
};
};
customFields: { customFields: {
[key: string | number]: string; [key: string | number]: string;
}; };

View File

@ -4,7 +4,7 @@ import { IMessage } from './IMessage';
import { IRocketChatRecord } from './IRocketChatRecord'; import { IRocketChatRecord } from './IRocketChatRecord';
import { IServedBy } from './IServedBy'; import { IServedBy } from './IServedBy';
import { IVisitor, SubscriptionType } from './ISubscription'; import { IVisitor, SubscriptionType } from './ISubscription';
import { IUser } from './IUser'; import { IUser, TNotifications } from './IUser';
interface IRequestTranscript { interface IRequestTranscript {
email: string; email: string;
@ -202,3 +202,13 @@ export interface IServerRoom extends IRocketChatRecord {
livechatData?: any; livechatData?: any;
tags?: string[]; tags?: string[];
} }
export interface IRoomNotifications {
disableNotifications?: boolean;
muteGroupMentions?: boolean;
hideUnreadStatus?: boolean;
audioNotificationsValue?: string;
desktopNotifications?: TNotifications;
mobilePushNotifications?: TNotifications;
emailNotifications?: TNotifications;
}

View File

@ -103,7 +103,7 @@ export interface IUserSettings {
[key: string]: any; [key: string]: any;
}; };
} }
type TNotifications = 'default' | 'all' | 'mentions' | 'nothing'; export type TNotifications = 'default' | 'all' | 'mentions' | 'nothing';
export interface INotificationPreferences { export interface INotificationPreferences {
id: string; id: string;
@ -111,6 +111,7 @@ export interface INotificationPreferences {
desktopNotifications: TNotifications; desktopNotifications: TNotifications;
pushNotifications: TNotifications; pushNotifications: TNotifications;
emailNotificationMode?: 'mentions' | 'nothing'; emailNotificationMode?: 'mentions' | 'nothing';
language?: string;
} }
export interface IUserPreferences { export interface IUserPreferences {

View File

@ -1,5 +1,5 @@
import type { IMessage } from '../../IMessage'; import type { IMessage } from '../../IMessage';
import type { IServerRoom } from '../../IRoom'; import type { IRoomNotifications, IServerRoom } from '../../IRoom';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
export type RoomsEndpoints = { export type RoomsEndpoints = {
@ -41,4 +41,7 @@ export type RoomsEndpoints = {
'rooms.favorite': { 'rooms.favorite': {
POST: (params: { roomId: string; favorite: boolean }) => {}; POST: (params: { roomId: string; favorite: boolean }) => {};
}; };
'rooms.saveNotification': {
POST: (params: { roomId: string; notifications: IRoomNotifications }) => {};
};
}; };

View File

@ -1,3 +1,4 @@
import { IParams } from '../../IProfileViewInterfaces';
import type { ITeam } from '../../ITeam'; import type { ITeam } from '../../ITeam';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
import { INotificationPreferences, IUserPreferences, IUserRegistered } from '../../IUser'; import { INotificationPreferences, IUserPreferences, IUserRegistered } from '../../IUser';
@ -26,7 +27,7 @@ export type UsersEndpoints = {
}; };
}; };
'users.setPreferences': { 'users.setPreferences': {
POST: (params: { userId: IUser['_id']; data: Partial<INotificationPreferences> }) => { POST: (params: { userId?: IUser['_id']; data: Partial<INotificationPreferences> }) => {
user: IUserPreferences; user: IUserPreferences;
success: boolean; success: boolean;
}; };
@ -37,4 +38,21 @@ export type UsersEndpoints = {
'users.setStatus': { 'users.setStatus': {
POST: (params: { status: string; message: string }) => {}; POST: (params: { status: string; message: string }) => {};
}; };
'users.updateOwnBasicInfo': {
POST: (params: { data: IParams | Pick<IParams, 'username'>; customFields?: { [key: string | number]: string } }) => {
user: IUser;
};
};
'users.getUsernameSuggestion': {
GET: () => { result: string };
};
'users.resetAvatar': {
POST: (params: { userId: string }) => {};
};
'users.getPreferences': {
GET: (params: { userId: IUser['_id'] }) => {
preferences: INotificationPreferences;
success: boolean;
};
};
}; };

View File

@ -6,8 +6,7 @@ import { KeyCommandsEmitter } from 'react-native-keycommands';
import RNScreens from 'react-native-screens'; import RNScreens from 'react-native-screens';
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context'; import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context';
import { defaultTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme'; import { getTheme, initialTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme';
import UserPreferences from './lib/userPreferences';
import EventEmitter from './utils/events'; import EventEmitter from './utils/events';
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app'; import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
import { deepLinkingOpen } from './actions/deepLinking'; import { deepLinkingOpen } from './actions/deepLinking';
@ -17,9 +16,9 @@ import store from './lib/createStore';
import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './utils/log'; import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './utils/log';
import { ThemeContext } from './theme'; import { ThemeContext } from './theme';
import { DimensionsContext } from './dimensions'; import { DimensionsContext } from './dimensions';
import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat'; import RocketChat from './lib/rocketchat';
import { MIN_WIDTH_MASTER_DETAIL_LAYOUT } from './constants/tablet'; import { MIN_WIDTH_MASTER_DETAIL_LAYOUT } from './constants/tablet';
import { isTablet, supportSystemTheme } from './utils/deviceInfo'; import { isTablet } from './utils/deviceInfo';
import { KEY_COMMAND } from './commands'; import { KEY_COMMAND } from './commands';
import AppContainer from './AppContainer'; import AppContainer from './AppContainer';
import TwoFactor from './containers/TwoFactor'; import TwoFactor from './containers/TwoFactor';
@ -33,6 +32,7 @@ import { isFDroidBuild } from './constants/environment';
import { IThemePreference } from './definitions/ITheme'; import { IThemePreference } from './definitions/ITheme';
import { ICommand } from './definitions/ICommand'; import { ICommand } from './definitions/ICommand';
import { initStore } from './lib/auxStore'; import { initStore } from './lib/auxStore';
import { themes } from './constants/colors';
RNScreens.enableScreens(); RNScreens.enableScreens();
initStore(store); initStore(store);
@ -88,12 +88,10 @@ export default class Root extends React.Component<{}, IState> {
this.initCrashReport(); this.initCrashReport();
} }
const { width, height, scale, fontScale } = Dimensions.get('window'); const { width, height, scale, fontScale } = Dimensions.get('window');
const theme = initialTheme();
this.state = { this.state = {
theme: defaultTheme(), theme: getTheme(theme),
themePreferences: { themePreferences: theme,
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
darkLevel: 'black'
},
width, width,
height, height,
scale, scale,
@ -128,7 +126,6 @@ export default class Root extends React.Component<{}, IState> {
} }
init = async () => { init = async () => {
UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then((theme: any) => this.setTheme(theme));
store.dispatch(appInitLocalSettings()); store.dispatch(appInitLocalSettings());
// Open app from push notification // Open app from push notification
@ -209,7 +206,9 @@ export default class Root extends React.Component<{}, IState> {
render() { render() {
const { themePreferences, theme, width, height, scale, fontScale } = this.state; const { themePreferences, theme, width, height, scale, fontScale } = this.state;
return ( return (
<SafeAreaProvider initialMetrics={initialWindowMetrics}> <SafeAreaProvider
initialMetrics={initialWindowMetrics}
style={{ backgroundColor: themes[this.state.theme].backgroundColor }}>
<AppearanceProvider> <AppearanceProvider>
<Provider store={store}> <Provider store={store}>
<ThemeContext.Provider <ThemeContext.Provider

View File

@ -118,8 +118,8 @@ class Encryption {
// Persist keys on UserPreferences // Persist keys on UserPreferences
persistKeys = async (server: string, publicKey: string, privateKey: string) => { persistKeys = async (server: string, publicKey: string, privateKey: string) => {
this.privateKey = await SimpleCrypto.RSA.importKey(EJSON.parse(privateKey)); this.privateKey = await SimpleCrypto.RSA.importKey(EJSON.parse(privateKey));
await UserPreferences.setStringAsync(`${server}-${E2E_PUBLIC_KEY}`, EJSON.stringify(publicKey)); UserPreferences.setString(`${server}-${E2E_PUBLIC_KEY}`, EJSON.stringify(publicKey));
await UserPreferences.setStringAsync(`${server}-${E2E_PRIVATE_KEY}`, privateKey); UserPreferences.setString(`${server}-${E2E_PRIVATE_KEY}`, privateKey);
}; };
// Could not obtain public-private keypair from server. // Could not obtain public-private keypair from server.
@ -182,9 +182,9 @@ class Encryption {
}; };
// Create a random password to local created keys // Create a random password to local created keys
createRandomPassword = async (server: string) => { createRandomPassword = (server: string) => {
const password = randomPassword(); const password = randomPassword();
await UserPreferences.setStringAsync(`${server}-${E2E_RANDOM_PASSWORD_KEY}`, password); UserPreferences.setString(`${server}-${E2E_RANDOM_PASSWORD_KEY}`, password);
return password; return password;
}; };
@ -194,7 +194,7 @@ class Encryption {
// Encode the private key // Encode the private key
const encodedPrivateKey = await this.encodePrivateKey(EJSON.stringify(privateKey), password, this.userId as string); const encodedPrivateKey = await this.encodePrivateKey(EJSON.stringify(privateKey), password, this.userId as string);
const publicKey = await UserPreferences.getStringAsync(`${server}-${E2E_PUBLIC_KEY}`); const publicKey = UserPreferences.getString(`${server}-${E2E_PUBLIC_KEY}`);
// Send the new keys to the server // Send the new keys to the server
await RocketChat.e2eSetUserPublicAndPrivateKeys(EJSON.stringify(publicKey), encodedPrivateKey); await RocketChat.e2eSetUserPublicAndPrivateKeys(EJSON.stringify(publicKey), encodedPrivateKey);

View File

@ -14,23 +14,23 @@ import UserPreferences from '../userPreferences';
import { ICertificate, IRocketChat } from '../../definitions'; import { ICertificate, IRocketChat } from '../../definitions';
import sdk from '../rocketchat/services/sdk'; import sdk from '../rocketchat/services/sdk';
async function removeServerKeys({ server, userId }: { server: string; userId?: string | null }) { function removeServerKeys({ server, userId }: { server: string; userId?: string | null }) {
await UserPreferences.removeItem(`${RocketChat.TOKEN_KEY}-${server}`); UserPreferences.removeItem(`${RocketChat.TOKEN_KEY}-${server}`);
if (userId) { if (userId) {
await UserPreferences.removeItem(`${RocketChat.TOKEN_KEY}-${userId}`); UserPreferences.removeItem(`${RocketChat.TOKEN_KEY}-${userId}`);
} }
await UserPreferences.removeItem(`${BASIC_AUTH_KEY}-${server}`); UserPreferences.removeItem(`${BASIC_AUTH_KEY}-${server}`);
await UserPreferences.removeItem(`${server}-${E2E_PUBLIC_KEY}`); UserPreferences.removeItem(`${server}-${E2E_PUBLIC_KEY}`);
await UserPreferences.removeItem(`${server}-${E2E_PRIVATE_KEY}`); UserPreferences.removeItem(`${server}-${E2E_PRIVATE_KEY}`);
await UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`); UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
} }
async function removeSharedCredentials({ server }: { server: string }) { async function removeSharedCredentials({ server }: { server: string }) {
// clear certificate for server - SSL Pinning // clear certificate for server - SSL Pinning
try { try {
const certificate = (await UserPreferences.getMapAsync(extractHostname(server))) as ICertificate | null; const certificate = UserPreferences.getMap(extractHostname(server)) as ICertificate | null;
if (certificate?.path) { if (certificate?.path) {
await UserPreferences.removeItem(extractHostname(server)); UserPreferences.removeItem(extractHostname(server));
await FileSystem.deleteAsync(certificate.path); await FileSystem.deleteAsync(certificate.path);
} }
} catch (e) { } catch (e) {
@ -42,7 +42,7 @@ async function removeServerData({ server }: { server: string }) {
try { try {
const batch: Model[] = []; const batch: Model[] = [];
const serversDB = database.servers; const serversDB = database.servers;
const userId = await UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`); const userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
const usersCollection = serversDB.get('users'); const usersCollection = serversDB.get('users');
if (userId) { if (userId) {
@ -55,14 +55,14 @@ async function removeServerData({ server }: { server: string }) {
await serversDB.write(() => serversDB.batch(...batch)); await serversDB.write(() => serversDB.batch(...batch));
await removeSharedCredentials({ server }); await removeSharedCredentials({ server });
await removeServerKeys({ server, userId }); removeServerKeys({ server, userId });
} catch (e) { } catch (e) {
log(e); log(e);
} }
} }
async function removeCurrentServer() { function removeCurrentServer() {
await UserPreferences.removeItem(RocketChat.CURRENT_SERVER); UserPreferences.removeItem(RocketChat.CURRENT_SERVER);
} }
async function removeServerDatabase({ server }: { server: string }) { async function removeServerDatabase({ server }: { server: string }) {
@ -76,9 +76,9 @@ async function removeServerDatabase({ server }: { server: string }) {
export async function removeServer({ server }: { server: string }): Promise<void> { export async function removeServer({ server }: { server: string }): Promise<void> {
try { try {
const userId = await UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`); const userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
if (userId) { if (userId) {
const resume = await UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${userId}`); const resume = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${userId}`);
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) }); const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
await sdk.login({ resume }); await sdk.login({ resume });

View File

@ -115,8 +115,8 @@ const RocketChat = {
database.setShareDB(server); database.setShareDB(server);
try { try {
const certificate = await UserPreferences.getStringAsync(`${RocketChat.CERTIFICATE_KEY}-${server}`); const certificate = UserPreferences.getString(`${RocketChat.CERTIFICATE_KEY}-${server}`);
await SSLPinning.setCertificate(certificate, server); SSLPinning.setCertificate(certificate, server);
} catch { } catch {
// Do nothing // Do nothing
} }
@ -155,7 +155,7 @@ const RocketChat = {
reduxStore.dispatch(shareSetSettings(this.parseSettings(parsed))); reduxStore.dispatch(shareSetSettings(this.parseSettings(parsed)));
// set User info // set User info
const userId = await UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`); const userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
const userCollections = serversDB.get('users'); const userCollections = serversDB.get('users');
let user = null; let user = null;
if (userId) { if (userId) {
@ -461,14 +461,13 @@ const RocketChat = {
} }
return JSON.parse(allowAnalyticsEvents); return JSON.parse(allowAnalyticsEvents);
}, },
async getSortPreferences() { getSortPreferences() {
const prefs = await UserPreferences.getMapAsync(SORT_PREFS_KEY); return UserPreferences.getMap(SORT_PREFS_KEY);
return prefs;
}, },
async saveSortPreference(param) { saveSortPreference(param) {
let prefs = await RocketChat.getSortPreferences(); let prefs = RocketChat.getSortPreferences();
prefs = { ...prefs, ...param }; prefs = { ...prefs, ...param };
return UserPreferences.setMapAsync(SORT_PREFS_KEY, prefs); return UserPreferences.setMap(SORT_PREFS_KEY, prefs);
}, },
getLoginServices, getLoginServices,
determineAuthType, determineAuthType,

View File

@ -1,8 +1,9 @@
import sdk from './sdk'; import sdk from './sdk';
import { TEAM_TYPE } from '../../../definitions/ITeam'; import { TEAM_TYPE } from '../../../definitions/ITeam';
import roomTypeToApiType, { RoomTypes } from '../methods/roomTypeToApiType'; import roomTypeToApiType, { RoomTypes } from '../methods/roomTypeToApiType';
import { SubscriptionType, INotificationPreferences } from '../../../definitions'; import { SubscriptionType, INotificationPreferences, IRoomNotifications } from '../../../definitions';
import { ISpotlight } from '../../../definitions/ISpotlight'; import { ISpotlight } from '../../../definitions/ISpotlight';
import { IAvatarSuggestion, IParams } from '../../../definitions/IProfileViewInterfaces';
export const createChannel = ({ export const createChannel = ({
name, name,
@ -289,10 +290,8 @@ export const getChannelInfo = (roomId: string) =>
// RC 0.48.0 // RC 0.48.0
sdk.get('channels.info', { roomId }); sdk.get('channels.info', { roomId });
export const getUserPreferences = (userId: string): any => export const getUserPreferences = (userId: string) =>
// RC 0.62.0 // RC 0.62.0
// TODO: missing definitions from server
// @ts-ignore
sdk.get('users.getPreferences', { userId }); sdk.get('users.getPreferences', { userId });
export const getRoomInfo = (roomId: string) => export const getRoomInfo = (roomId: string) =>
@ -545,22 +544,16 @@ export const saveRoomSettings = (
// RC 0.55.0 // RC 0.55.0
sdk.methodCallWrapper('saveRoomSettings', rid, params); sdk.methodCallWrapper('saveRoomSettings', rid, params);
export const saveUserProfile = (data: any, customFields?: any): any => export const saveUserProfile = (data: IParams | Pick<IParams, 'username'>, customFields?: { [key: string | number]: string }) =>
// RC 0.62.2 // RC 0.62.2
// TODO: missing definitions from server
// @ts-ignore
sdk.post('users.updateOwnBasicInfo', { data, customFields }); sdk.post('users.updateOwnBasicInfo', { data, customFields });
export const saveUserPreferences = (data: any): any => export const saveUserPreferences = (data: Partial<INotificationPreferences>) =>
// RC 0.62.0 // RC 0.62.0
// TODO: missing definitions from server
// @ts-ignore
sdk.post('users.setPreferences', { data }); sdk.post('users.setPreferences', { data });
export const saveNotificationSettings = (roomId: string, notifications: any): any => export const saveNotificationSettings = (roomId: string, notifications: IRoomNotifications) =>
// RC 0.63.0 // RC 0.63.0
// TODO: missing definitions from server
// @ts-ignore
sdk.post('rooms.saveNotification', { roomId, notifications }); sdk.post('rooms.saveNotification', { roomId, notifications });
export const getSingleMessage = (msgId: string) => export const getSingleMessage = (msgId: string) =>
@ -573,14 +566,12 @@ export const getRoomRoles = (roomId: string, type: SubscriptionType): any =>
// @ts-ignore // @ts-ignore
sdk.get(`${roomTypeToApiType(type)}.roles`, { roomId }); sdk.get(`${roomTypeToApiType(type)}.roles`, { roomId });
export const getAvatarSuggestion = () => export const getAvatarSuggestion = (): Promise<IAvatarSuggestion> =>
// RC 0.51.0 // RC 0.51.0
sdk.methodCallWrapper('getAvatarSuggestion'); sdk.methodCallWrapper('getAvatarSuggestion');
export const resetAvatar = (userId: string): any => export const resetAvatar = (userId: string) =>
// RC 0.55.0 // RC 0.55.0
// TODO: missing definitions from server
// @ts-ignore
sdk.post('users.resetAvatar', { userId }); sdk.post('users.resetAvatar', { userId });
export const setAvatarFromService = ({ export const setAvatarFromService = ({
@ -591,14 +582,12 @@ export const setAvatarFromService = ({
data: any; data: any;
contentType?: string; contentType?: string;
service?: string | null; service?: string | null;
}) => }): Promise<void> =>
// RC 0.51.0 // RC 0.51.0
sdk.methodCallWrapper('setAvatarFromService', data, contentType, service); sdk.methodCallWrapper('setAvatarFromService', data, contentType, service);
export const getUsernameSuggestion = (): any => export const getUsernameSuggestion = () =>
// RC 0.65.0 // RC 0.65.0
// TODO: missing definitions from server
// @ts-ignore
sdk.get('users.getUsernameSuggestion'); sdk.get('users.getUsernameSuggestion');
export const getFiles = (roomId: string, type: RoomTypes, offset: number): any => export const getFiles = (roomId: string, type: RoomTypes, offset: number): any =>

View File

@ -1,4 +1,4 @@
import MMKVStorage from 'react-native-mmkv-storage'; import MMKVStorage, { create } from 'react-native-mmkv-storage';
const MMKV = new MMKVStorage.Loader() const MMKV = new MMKVStorage.Loader()
// MODES.MULTI_PROCESS = ACCESSIBLE BY APP GROUP (iOS) // MODES.MULTI_PROCESS = ACCESSIBLE BY APP GROUP (iOS)
@ -6,52 +6,51 @@ const MMKV = new MMKVStorage.Loader()
.withEncryption() .withEncryption()
.initialize(); .initialize();
export const useUserPreferences = create(MMKV);
class UserPreferences { class UserPreferences {
private mmkv: MMKVStorage.API; private mmkv: MMKVStorage.API;
constructor() { constructor() {
this.mmkv = MMKV; this.mmkv = MMKV;
} }
async getStringAsync(key: string) { getString(key: string): string | null {
try { try {
const value = await this.mmkv.getStringAsync(key); return this.mmkv.getString(key) || null;
return value;
} catch { } catch {
return null; return null;
} }
} }
setStringAsync(key: string, value: string) { setString(key: string, value: string): boolean | undefined {
return this.mmkv.setStringAsync(key, value); return this.mmkv.setString(key, value);
} }
async getBoolAsync(key: string) { getBool(key: string): boolean | null {
try { try {
const value = await this.mmkv.getBoolAsync(key); return this.mmkv.getBool(key) || null;
return value;
} catch { } catch {
return null; return null;
} }
} }
setBoolAsync(key: string, value: boolean) { setBool(key: string, value: boolean): boolean | undefined {
return this.mmkv.setBoolAsync(key, value); return this.mmkv.setBool(key, value);
} }
async getMapAsync(key: string) { getMap(key: string): object | null {
try { try {
const value = await this.mmkv.getMapAsync(key); return this.mmkv.getMap(key) || null;
return value;
} catch { } catch {
return null; return null;
} }
} }
setMapAsync(key: string, value: object) { setMap(key: string, value: object): boolean | undefined {
return this.mmkv.setMapAsync(key, value); return this.mmkv.setMap(key, value);
} }
removeItem(key: string) { removeItem(key: string): boolean | undefined {
return this.mmkv.removeItem(key); return this.mmkv.removeItem(key);
} }
} }

View File

@ -158,8 +158,8 @@ const handleOpen = function* handleOpen({ params }) {
} }
const [server, user] = yield all([ const [server, user] = yield all([
UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER), UserPreferences.getString(RocketChat.CURRENT_SERVER),
UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${host}`) UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${host}`)
]); ]);
// TODO: needs better test // TODO: needs better test

View File

@ -39,7 +39,7 @@ const handleEncryptionInit = function* handleEncryptionInit() {
} }
// Fetch stored private e2e key for this server // Fetch stored private e2e key for this server
const storedPrivateKey = yield UserPreferences.getStringAsync(`${server}-${E2E_PRIVATE_KEY}`); const storedPrivateKey = UserPreferences.getString(`${server}-${E2E_PRIVATE_KEY}`);
// Fetch server stored e2e keys // Fetch server stored e2e keys
const keys = yield RocketChat.e2eFetchMyKeys(); const keys = yield RocketChat.e2eFetchMyKeys();
@ -52,13 +52,15 @@ const handleEncryptionInit = function* handleEncryptionInit() {
} }
// If the user has a private key stored, but never entered the password // If the user has a private key stored, but never entered the password
const storedRandomPassword = yield UserPreferences.getStringAsync(`${server}-${E2E_RANDOM_PASSWORD_KEY}`); const storedRandomPassword = UserPreferences.getString(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
if (storedRandomPassword) { if (storedRandomPassword) {
yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD)); yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD));
} }
// Fetch stored public e2e key for this server // Fetch stored public e2e key for this server
let storedPublicKey = yield UserPreferences.getStringAsync(`${server}-${E2E_PUBLIC_KEY}`); let storedPublicKey = UserPreferences.getString(`${server}-${E2E_PUBLIC_KEY}`);
// Prevent parse undefined // Prevent parse undefined
if (storedPublicKey) { if (storedPublicKey) {
storedPublicKey = EJSON.parse(storedPublicKey); storedPublicKey = EJSON.parse(storedPublicKey);

View File

@ -14,7 +14,7 @@ import { appReady, appStart } from '../actions/app';
import { RootEnum } from '../definitions'; import { RootEnum } from '../definitions';
export const initLocalSettings = function* initLocalSettings() { export const initLocalSettings = function* initLocalSettings() {
const sortPreferences = yield RocketChat.getSortPreferences(); const sortPreferences = RocketChat.getSortPreferences();
yield put(setAllPreferences(sortPreferences)); yield put(setAllPreferences(sortPreferences));
}; };
@ -22,19 +22,19 @@ const BIOMETRY_MIGRATION_KEY = 'kBiometryMigration';
const restore = function* restore() { const restore = function* restore() {
try { try {
const server = yield UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER); const server = UserPreferences.getString(RocketChat.CURRENT_SERVER);
let userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`); let userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
// Migration biometry setting from WatermelonDB to MMKV // Migration biometry setting from WatermelonDB to MMKV
// TODO: remove it after a few versions // TODO: remove it after a few versions
const hasMigratedBiometry = yield UserPreferences.getBoolAsync(BIOMETRY_MIGRATION_KEY); const hasMigratedBiometry = UserPreferences.getBool(BIOMETRY_MIGRATION_KEY);
if (!hasMigratedBiometry) { if (!hasMigratedBiometry) {
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.get('servers'); const serversCollection = serversDB.get('servers');
const servers = yield serversCollection.query().fetch(); const servers = yield serversCollection.query().fetch();
const isBiometryEnabled = servers.some(server => !!server.biometry); const isBiometryEnabled = servers.some(server => !!server.biometry);
yield UserPreferences.setBoolAsync(BIOMETRY_ENABLED_KEY, isBiometryEnabled); UserPreferences.setBool(BIOMETRY_ENABLED_KEY, isBiometryEnabled);
yield UserPreferences.setBoolAsync(BIOMETRY_MIGRATION_KEY, true); UserPreferences.setBool(BIOMETRY_MIGRATION_KEY, true);
} }
if (!server) { if (!server) {
@ -48,7 +48,7 @@ const restore = function* restore() {
if (servers.length > 0) { if (servers.length > 0) {
for (let i = 0; i < servers.length; i += 1) { for (let i = 0; i < servers.length; i += 1) {
const newServer = servers[i].id; const newServer = servers[i].id;
userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${newServer}`); userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${newServer}`);
if (userId) { if (userId) {
return yield put(selectServerRequest(newServer)); return yield put(selectServerRequest(newServer));
} }

View File

@ -161,8 +161,8 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
} }
}); });
yield UserPreferences.setStringAsync(`${RocketChat.TOKEN_KEY}-${server}`, user.id); UserPreferences.setString(`${RocketChat.TOKEN_KEY}-${server}`, user.id);
yield UserPreferences.setStringAsync(`${RocketChat.TOKEN_KEY}-${user.id}`, user.token); UserPreferences.setString(`${RocketChat.TOKEN_KEY}-${user.id}`, user.token);
yield put(setUser(user)); yield put(setUser(user));
EventEmitter.emit('connected'); EventEmitter.emit('connected');
@ -200,7 +200,7 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
if (servers.length > 0) { if (servers.length > 0) {
for (let i = 0; i < servers.length; i += 1) { for (let i = 0; i < servers.length; i += 1) {
const newServer = servers[i].id; const newServer = servers[i].id;
const token = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${newServer}`); const token = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${newServer}`);
if (token) { if (token) {
yield put(selectServerRequest(newServer)); yield put(selectServerRequest(newServer));
return; return;

View File

@ -69,15 +69,14 @@ const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
const handleSelectServer = function* handleSelectServer({ server, version, fetchVersion }) { const handleSelectServer = function* handleSelectServer({ server, version, fetchVersion }) {
try { try {
// SSL Pinning - Read certificate alias and set it to be used by network requests // SSL Pinning - Read certificate alias and set it to be used by network requests
const certificate = yield UserPreferences.getStringAsync(`${RocketChat.CERTIFICATE_KEY}-${server}`); const certificate = UserPreferences.getString(`${RocketChat.CERTIFICATE_KEY}-${server}`);
yield SSLPinning.setCertificate(certificate, server); SSLPinning.setCertificate(certificate, server);
yield put(inquiryReset()); yield put(inquiryReset());
yield put(encryptionStop()); yield put(encryptionStop());
yield put(clearActiveUsers()); yield put(clearActiveUsers());
const serversDB = database.servers; const serversDB = database.servers;
yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server); UserPreferences.setString(RocketChat.CURRENT_SERVER, server);
const userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`); const userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
const userCollections = serversDB.get('users'); const userCollections = serversDB.get('users');
let user = null; let user = null;
if (userId) { if (userId) {
@ -97,14 +96,14 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
}; };
} catch { } catch {
// search credentials on shared credentials (Experimental/Official) // search credentials on shared credentials (Experimental/Official)
const token = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${userId}`); const token = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${userId}`);
if (token) { if (token) {
user = { token }; user = { token };
} }
} }
} }
const basicAuth = yield UserPreferences.getStringAsync(`${BASIC_AUTH_KEY}-${server}`); const basicAuth = UserPreferences.getString(`${BASIC_AUTH_KEY}-${server}`);
setBasicAuth(basicAuth); setBasicAuth(basicAuth);
// Check for running requests and abort them before connecting to the server // Check for running requests and abort them before connecting to the server
@ -148,8 +147,8 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
const handleServerRequest = function* handleServerRequest({ server, username, fromServerHistory }) { const handleServerRequest = function* handleServerRequest({ server, username, fromServerHistory }) {
try { try {
// SSL Pinning - Read certificate alias and set it to be used by network requests // SSL Pinning - Read certificate alias and set it to be used by network requests
const certificate = yield UserPreferences.getStringAsync(`${RocketChat.CERTIFICATE_KEY}-${server}`); const certificate = UserPreferences.getString(`${RocketChat.CERTIFICATE_KEY}-${server}`);
yield SSLPinning.setCertificate(certificate, server); SSLPinning.setCertificate(certificate, server);
const serverInfo = yield getServerInfo({ server }); const serverInfo = yield getServerInfo({ server });
const serversDB = database.servers; const serversDB = database.servers;

View File

@ -5,14 +5,13 @@ import { AppearanceProvider } from 'react-native-appearance';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { defaultTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme'; import { getTheme, initialTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme';
import UserPreferences from './lib/userPreferences'; import UserPreferences from './lib/userPreferences';
import Navigation from './lib/ShareNavigation'; import Navigation from './lib/ShareNavigation';
import store from './lib/createStore'; import store from './lib/createStore';
import { initStore } from './lib/auxStore'; import { initStore } from './lib/auxStore';
import { supportSystemTheme } from './utils/deviceInfo';
import { defaultHeader, getActiveRouteName, navigationTheme, themedHeader } from './utils/navigation'; import { defaultHeader, getActiveRouteName, navigationTheme, themedHeader } from './utils/navigation';
import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat'; import RocketChat from './lib/rocketchat';
import { ThemeContext } from './theme'; import { ThemeContext } from './theme';
import { localAuthenticate } from './utils/localAuthentication'; import { localAuthenticate } from './utils/localAuthentication';
import { IThemePreference } from './definitions/ITheme'; import { IThemePreference } from './definitions/ITheme';
@ -94,12 +93,10 @@ class Root extends React.Component<{}, IState> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
const { width, height, scale, fontScale } = Dimensions.get('screen'); const { width, height, scale, fontScale } = Dimensions.get('screen');
const theme = initialTheme();
this.state = { this.state = {
theme: defaultTheme(), theme: getTheme(theme),
themePreferences: { themePreferences: theme,
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
darkLevel: 'black'
},
root: '', root: '',
width, width,
height, height,
@ -115,9 +112,7 @@ class Root extends React.Component<{}, IState> {
} }
init = async () => { init = async () => {
UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then((theme: any) => this.setTheme(theme)); const currentServer = UserPreferences.getString(RocketChat.CURRENT_SERVER);
const currentServer = await UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER);
if (currentServer) { if (currentServer) {
await localAuthenticate(currentServer); await localAuthenticate(currentServer);

View File

@ -69,7 +69,7 @@ const openChangePasscodeModal = ({ force }: { force: boolean }) =>
export const changePasscode = async ({ force = false }: { force: boolean }): Promise<void> => { export const changePasscode = async ({ force = false }: { force: boolean }): Promise<void> => {
const passcode = await openChangePasscodeModal({ force }); const passcode = await openChangePasscodeModal({ force });
await UserPreferences.setStringAsync(PASSCODE_KEY, sha256(passcode)); UserPreferences.setString(PASSCODE_KEY, sha256(passcode));
}; };
export const biometryAuth = (force?: boolean): Promise<LocalAuthentication.LocalAuthenticationResult> => export const biometryAuth = (force?: boolean): Promise<LocalAuthentication.LocalAuthenticationResult> =>
@ -86,12 +86,12 @@ export const biometryAuth = (force?: boolean): Promise<LocalAuthentication.Local
const checkBiometry = async () => { const checkBiometry = async () => {
const result = await biometryAuth(true); const result = await biometryAuth(true);
const isBiometryEnabled = !!result?.success; const isBiometryEnabled = !!result?.success;
await UserPreferences.setBoolAsync(BIOMETRY_ENABLED_KEY, isBiometryEnabled); UserPreferences.setBool(BIOMETRY_ENABLED_KEY, isBiometryEnabled);
return isBiometryEnabled; return isBiometryEnabled;
}; };
export const checkHasPasscode = async ({ force = true }: { force?: boolean }): Promise<{ newPasscode?: boolean } | void> => { export const checkHasPasscode = async ({ force = true }: { force?: boolean }): Promise<{ newPasscode?: boolean } | void> => {
const storedPasscode = await UserPreferences.getStringAsync(PASSCODE_KEY); const storedPasscode = UserPreferences.getString(PASSCODE_KEY);
if (!storedPasscode) { if (!storedPasscode) {
await changePasscode({ force }); await changePasscode({ force });
await checkBiometry(); await checkBiometry();
@ -137,7 +137,7 @@ export const localAuthenticate = async (server: string): Promise<void> => {
store.dispatch(setLocalAuthenticated(false)); store.dispatch(setLocalAuthenticated(false));
// let hasBiometry = false; // let hasBiometry = false;
let hasBiometry = (await UserPreferences.getBoolAsync(BIOMETRY_ENABLED_KEY)) ?? false; let hasBiometry = UserPreferences.getBool(BIOMETRY_ENABLED_KEY) ?? false;
// if biometry is enabled on the app // if biometry is enabled on the app
if (hasBiometry) { if (hasBiometry) {

View File

@ -37,7 +37,7 @@ const appSchemeURL = (url: string, browser: string): string => {
const openLink = async (url: string, theme = 'light'): Promise<void> => { const openLink = async (url: string, theme = 'light'): Promise<void> => {
try { try {
const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY); const browser = UserPreferences.getString(DEFAULT_BROWSER_KEY);
if (browser === 'inApp') { if (browser === 'inApp') {
await WebBrowser.openBrowserAsync(url, { await WebBrowser.openBrowserAsync(url, {

View File

@ -14,13 +14,13 @@ const extractFileScheme = (path: string) => path.replace('file://', ''); // file
const getPath = (name: string) => `${documentDirectory}/${name}`; const getPath = (name: string) => `${documentDirectory}/${name}`;
const persistCertificate = async (name: string, password: string) => { const persistCertificate = (name: string, password: string) => {
const certificatePath = getPath(name); const certificatePath = getPath(name);
const certificate: ICertificate = { const certificate: ICertificate = {
path: extractFileScheme(certificatePath), path: extractFileScheme(certificatePath),
password password
}; };
await UserPreferences.setMapAsync(name, certificate); UserPreferences.setMap(name, certificate);
return certificate; return certificate;
}; };
@ -44,7 +44,7 @@ const RCSSLPinning = Platform.select({
try { try {
const certificatePath = getPath(name); const certificatePath = getPath(name);
await FileSystem.copyAsync({ from: uri, to: certificatePath }); await FileSystem.copyAsync({ from: uri, to: certificatePath });
await persistCertificate(name, password!); persistCertificate(name, password!);
resolve(name); resolve(name);
} catch (e) { } catch (e) {
reject(e); reject(e);
@ -58,13 +58,13 @@ const RCSSLPinning = Platform.select({
reject(e); reject(e);
} }
}), }),
setCertificate: async (name: string, server: string) => { setCertificate: (name: string, server: string) => {
if (name) { if (name) {
let certificate = (await UserPreferences.getMapAsync(name)) as ICertificate; let certificate = UserPreferences.getMap(name) as ICertificate;
if (!certificate.path.match(extractFileScheme(documentDirectory!))) { if (!certificate.path.match(extractFileScheme(documentDirectory!))) {
certificate = await persistCertificate(name, certificate.password); certificate = persistCertificate(name, certificate.password);
} }
await UserPreferences.setMapAsync(extractHostname(server), certificate); UserPreferences.setMap(extractHostname(server), certificate);
} }
} }
}, },

View File

@ -5,9 +5,20 @@ import setRootViewColor from 'rn-root-view';
import { IThemePreference, TThemeMode } from '../definitions/ITheme'; import { IThemePreference, TThemeMode } from '../definitions/ITheme';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import { isAndroid } from './deviceInfo'; import { isAndroid } from './deviceInfo';
import UserPreferences from '../lib/userPreferences';
import { THEME_PREFERENCES_KEY } from '../lib/rocketchat';
let themeListener: { remove: () => void } | null; let themeListener: { remove: () => void } | null;
export const initialTheme = (): IThemePreference => {
const theme = UserPreferences.getMap(THEME_PREFERENCES_KEY) as IThemePreference;
const initialTheme: IThemePreference = {
currentTheme: defaultTheme(),
darkLevel: 'black'
};
return theme || initialTheme;
};
export const defaultTheme = (): TThemeMode => { export const defaultTheme = (): TThemeMode => {
const systemTheme = Appearance.getColorScheme(); const systemTheme = Appearance.getColorScheme();
if (systemTheme && systemTheme !== 'no-preference') { if (systemTheme && systemTheme !== 'no-preference') {

View File

@ -73,9 +73,9 @@ class DefaultBrowserView extends React.Component<IDefaultBrowserViewProps, IDefa
} }
} }
async componentDidMount() { componentDidMount() {
this.mounted = true; this.mounted = true;
const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY); const browser = UserPreferences.getString(DEFAULT_BROWSER_KEY);
this.setState({ browser }); this.setState({ browser });
} }
@ -104,11 +104,11 @@ class DefaultBrowserView extends React.Component<IDefaultBrowserViewProps, IDefa
return browser === value; return browser === value;
}; };
changeDefaultBrowser = async (newBrowser: TValue) => { changeDefaultBrowser = (newBrowser: TValue) => {
logEvent(events.DB_CHANGE_DEFAULT_BROWSER, { browser: newBrowser }); logEvent(events.DB_CHANGE_DEFAULT_BROWSER, { browser: newBrowser });
try { try {
const browser = newBrowser || 'systemDefault:'; const browser = newBrowser || 'systemDefault:';
await UserPreferences.setStringAsync(DEFAULT_BROWSER_KEY, browser); UserPreferences.setString(DEFAULT_BROWSER_KEY, browser);
this.setState({ browser }); this.setState({ browser });
} catch { } catch {
logEvent(events.DB_CHANGE_DEFAULT_BROWSER_F); logEvent(events.DB_CHANGE_DEFAULT_BROWSER_F);

View File

@ -17,7 +17,7 @@ import I18n from '../i18n';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { SettingsStackParamList } from '../stacks/types'; import { SettingsStackParamList } from '../stacks/types';
import { useTheme } from '../theme'; import { useTheme } from '../theme';
import log, { events, logEvent } from '../utils/log'; import { events, logEvent } from '../utils/log';
interface IDisplayPrefsView { interface IDisplayPrefsView {
navigation: StackNavigationProp<SettingsStackParamList, 'DisplayPrefsView'>; navigation: StackNavigationProp<SettingsStackParamList, 'DisplayPrefsView'>;
@ -45,53 +45,49 @@ const DisplayPrefsView = (props: IDisplayPrefsView): JSX.Element => {
} }
}, []); }, []);
const setSortPreference = async (param: Partial<IPreferences>) => { const setSortPreference = (param: Partial<IPreferences>) => {
try { dispatch(setPreference(param));
dispatch(setPreference(param)); RocketChat.saveSortPreference(param);
await RocketChat.saveSortPreference(param);
} catch (e) {
log(e);
}
}; };
const sortByName = async () => { const sortByName = () => {
logEvent(events.DP_SORT_CHANNELS_BY_NAME); logEvent(events.DP_SORT_CHANNELS_BY_NAME);
await setSortPreference({ sortBy: SortBy.Alphabetical }); setSortPreference({ sortBy: SortBy.Alphabetical });
}; };
const sortByActivity = async () => { const sortByActivity = () => {
logEvent(events.DP_SORT_CHANNELS_BY_ACTIVITY); logEvent(events.DP_SORT_CHANNELS_BY_ACTIVITY);
await setSortPreference({ sortBy: SortBy.Activity }); setSortPreference({ sortBy: SortBy.Activity });
}; };
const toggleGroupByType = async () => { const toggleGroupByType = () => {
logEvent(events.DP_GROUP_CHANNELS_BY_TYPE); logEvent(events.DP_GROUP_CHANNELS_BY_TYPE);
await setSortPreference({ groupByType: !groupByType }); setSortPreference({ groupByType: !groupByType });
}; };
const toggleGroupByFavorites = async () => { const toggleGroupByFavorites = () => {
logEvent(events.DP_GROUP_CHANNELS_BY_FAVORITE); logEvent(events.DP_GROUP_CHANNELS_BY_FAVORITE);
await setSortPreference({ showFavorites: !showFavorites }); setSortPreference({ showFavorites: !showFavorites });
}; };
const toggleUnread = async () => { const toggleUnread = () => {
logEvent(events.DP_GROUP_CHANNELS_BY_UNREAD); logEvent(events.DP_GROUP_CHANNELS_BY_UNREAD);
await setSortPreference({ showUnread: !showUnread }); setSortPreference({ showUnread: !showUnread });
}; };
const toggleAvatar = async () => { const toggleAvatar = () => {
logEvent(events.DP_TOGGLE_AVATAR); logEvent(events.DP_TOGGLE_AVATAR);
await setSortPreference({ showAvatar: !showAvatar }); setSortPreference({ showAvatar: !showAvatar });
}; };
const displayExpanded = async () => { const displayExpanded = () => {
logEvent(events.DP_DISPLAY_EXPANDED); logEvent(events.DP_DISPLAY_EXPANDED);
await setSortPreference({ displayMode: DisplayMode.Expanded }); setSortPreference({ displayMode: DisplayMode.Expanded });
}; };
const displayCondensed = async () => { const displayCondensed = () => {
logEvent(events.DP_DISPLAY_CONDENSED); logEvent(events.DP_DISPLAY_CONDENSED);
await setSortPreference({ displayMode: DisplayMode.Condensed }); setSortPreference({ displayMode: DisplayMode.Condensed });
}; };
const renderCheckBox = (value: boolean) => ( const renderCheckBox = (value: boolean) => (

View File

@ -81,11 +81,11 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
this.mounted = true; this.mounted = true;
} }
init = async () => { init = () => {
const { server } = this.props; const { server } = this.props;
try { try {
// Set stored password on local state // Set stored password on local state
const password = await UserPreferences.getStringAsync(`${server}-${E2E_RANDOM_PASSWORD_KEY}`); const password = UserPreferences.getString(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
if (this.mounted) { if (this.mounted) {
this.setState({ password: password! }); this.setState({ password: password! });
} else { } else {
@ -97,11 +97,11 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
} }
}; };
onSaved = async () => { onSaved = () => {
logEvent(events.E2E_SAVE_PW_SAVED); logEvent(events.E2E_SAVE_PW_SAVED);
const { navigation, server, dispatch } = this.props; const { navigation, server, dispatch } = this.props;
// Remove stored password // Remove stored password
await UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`); UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
// Hide encryption banner // Hide encryption banner
dispatch(encryptionSetBanner()); dispatch(encryptionSetBanner());
navigation.pop(); navigation.pop();

View File

@ -181,7 +181,7 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
this.setState({ text: serverHistory.url }, () => this.submit({ fromServerHistory: true, username: serverHistory?.username })); this.setState({ text: serverHistory.url }, () => this.submit({ fromServerHistory: true, username: serverHistory?.username }));
}; };
submit = async ({ fromServerHistory = false, username }: ISubmitParams = {}) => { submit = ({ fromServerHistory = false, username }: ISubmitParams = {}) => {
logEvent(events.NS_CONNECT_TO_WORKSPACE); logEvent(events.NS_CONNECT_TO_WORKSPACE);
const { text, certificate } = this.state; const { text, certificate } = this.state;
const { dispatch } = this.props; const { dispatch } = this.props;
@ -193,10 +193,12 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
const server = this.completeUrl(text); const server = this.completeUrl(text);
// Save info - SSL Pinning // Save info - SSL Pinning
await UserPreferences.setStringAsync(`${RocketChat.CERTIFICATE_KEY}-${server}`, certificate); if (certificate) {
UserPreferences.setString(`${RocketChat.CERTIFICATE_KEY}-${server}`, certificate);
}
// Save info - HTTP Basic Authentication // Save info - HTTP Basic Authentication
await this.basicAuth(server, text); this.basicAuth(server, text);
if (fromServerHistory) { if (fromServerHistory) {
dispatch(serverRequest(server, username, true)); dispatch(serverRequest(server, username, true));
@ -213,12 +215,12 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
dispatch(serverRequest('https://open.rocket.chat')); dispatch(serverRequest('https://open.rocket.chat'));
}; };
basicAuth = async (server: string, text: string) => { basicAuth = (server: string, text: string) => {
try { try {
const parsedUrl = parse(text, true); const parsedUrl = parse(text, true);
if (parsedUrl.auth.length) { if (parsedUrl.auth.length) {
const credentials = Base64.encode(parsedUrl.auth); const credentials = Base64.encode(parsedUrl.auth);
await UserPreferences.setStringAsync(`${BASIC_AUTH_KEY}-${server}`, credentials); UserPreferences.setString(`${BASIC_AUTH_KEY}-${server}`, credentials);
setBasicAuth(credentials); setBasicAuth(credentials);
} }
} catch { } catch {

View File

@ -18,6 +18,7 @@ import log, { events, logEvent } from '../../utils/log';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import { OPTIONS } from './options'; import { OPTIONS } from './options';
import { ChatsStackParamList } from '../../stacks/types'; import { ChatsStackParamList } from '../../stacks/types';
import { IRoomNotifications } from '../../definitions';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
pickerText: { pickerText: {
@ -73,7 +74,7 @@ class NotificationPreferencesView extends React.Component<INotificationPreferenc
} }
} }
saveNotificationSettings = async (key: string, value: string | boolean, params: any) => { saveNotificationSettings = async (key: string, value: string | boolean, params: IRoomNotifications) => {
// @ts-ignore // @ts-ignore
logEvent(events[`NP_${key.toUpperCase()}`]); logEvent(events[`NP_${key.toUpperCase()}`]);
const { room } = this.state; const { room } = this.state;

View File

@ -31,7 +31,15 @@ import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login'; import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import styles from './styles'; import styles from './styles';
import { IAvatar, IAvatarButton, INavigationOptions, IParams, IProfileViewProps, IProfileViewState, IUser } from './interfaces'; import {
IAvatar,
IAvatarButton,
INavigationOptions,
IParams,
IProfileViewProps,
IProfileViewState,
IUser
} from '../../definitions/IProfileViewInterfaces';
class ProfileView extends React.Component<IProfileViewProps, IProfileViewState> { class ProfileView extends React.Component<IProfileViewProps, IProfileViewState> {
private name: any; private name: any;

View File

@ -34,7 +34,7 @@ import log, { events, logEvent } from '../../utils/log';
import { MessageTypeValues } from '../../utils/messageTypes'; import { MessageTypeValues } from '../../utils/messageTypes';
import random from '../../utils/random'; import random from '../../utils/random';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { IAvatar } from '../ProfileView/interfaces'; import { IAvatar } from '../../definitions/IProfileViewInterfaces';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import styles from './styles'; import styles from './styles';
import SwitchContainer from './SwitchContainer'; import SwitchContainer from './SwitchContainer';

View File

@ -135,7 +135,7 @@ class ServerDropdown extends Component<IServerDropdownProps, IServerDropdownStat
this.close(); this.close();
if (currentServer !== server) { if (currentServer !== server) {
logEvent(events.RL_CHANGE_SERVER); logEvent(events.RL_CHANGE_SERVER);
const userId = await UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`); const userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
if (isMasterDetail) { if (isMasterDetail) {
goRoom({ item: {}, isMasterDetail }); goRoom({ item: {}, isMasterDetail });
} }

View File

@ -339,7 +339,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
this.getSubscriptions(); this.getSubscriptions();
} }
// Update current item in case of another action triggers an update on rooms reducer // Update current item in case of another action triggers an update on rooms reducer
if (isMasterDetail && item?.rid !== rooms[0].rid && !dequal(rooms, prevProps.rooms)) { if (isMasterDetail && rooms[0] && item?.rid !== rooms[0].rid && !dequal(rooms, prevProps.rooms)) {
// eslint-disable-next-line react/no-did-update-set-state // eslint-disable-next-line react/no-did-update-set-state
// @ts-ignore // @ts-ignore
this.setState({ item: { rid: rooms[0] } }); this.setState({ item: { rid: rooms[0] } });

View File

@ -92,7 +92,7 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
const { server } = this.props; const { server } = this.props;
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.get('servers'); const serversCollection = serversDB.get('servers');
const hasBiometry = (await userPreferences.getBoolAsync(BIOMETRY_ENABLED_KEY)) ?? DEFAULT_BIOMETRY; const hasBiometry = userPreferences.getBool(BIOMETRY_ENABLED_KEY) ?? DEFAULT_BIOMETRY;
try { try {
this.serverRecord = await serversCollection.find(server); this.serverRecord = await serversCollection.find(server);
this.setState({ this.setState({
@ -147,9 +147,9 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
logEvent(events.SLC_TOGGLE_BIOMETRY); logEvent(events.SLC_TOGGLE_BIOMETRY);
this.setState( this.setState(
({ biometry }) => ({ biometry: !biometry }), ({ biometry }) => ({ biometry: !biometry }),
async () => { () => {
const { biometry } = this.state; const { biometry } = this.state;
await userPreferences.setBoolAsync(BIOMETRY_ENABLED_KEY, biometry); userPreferences.setBool(BIOMETRY_ENABLED_KEY, biometry);
} }
); );
}; };

View File

@ -97,11 +97,11 @@ class ThemeView extends React.Component<IThemeViewProps> {
this.setTheme(changes); this.setTheme(changes);
}; };
setTheme = async (theme: Partial<IThemePreference>) => { setTheme = (theme: Partial<IThemePreference>) => {
const { setTheme, themePreferences } = this.props; const { setTheme, themePreferences } = this.props;
const newTheme = { ...themePreferences, ...theme }; const newTheme = { ...themePreferences, ...theme };
setTheme(newTheme); setTheme(newTheme);
await UserPreferences.setMapAsync(THEME_PREFERENCES_KEY, newTheme); UserPreferences.setMap(THEME_PREFERENCES_KEY, newTheme);
}; };
renderIcon = () => { renderIcon = () => {

View File

@ -15,6 +15,7 @@ import { getUserSelector } from '../../selectors/login';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import { OPTIONS } from './options'; import { OPTIONS } from './options';
import { ProfileStackParamList } from '../../stacks/types'; import { ProfileStackParamList } from '../../stacks/types';
import { INotificationPreferences } from '../../definitions';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
pickerText: { pickerText: {
@ -26,11 +27,7 @@ const styles = StyleSheet.create({
type TKey = 'desktopNotifications' | 'pushNotifications' | 'emailNotificationMode'; type TKey = 'desktopNotifications' | 'pushNotifications' | 'emailNotificationMode';
interface IUserNotificationPreferencesViewState { interface IUserNotificationPreferencesViewState {
preferences: { preferences: INotificationPreferences;
desktopNotifications?: string;
pushNotifications?: string;
emailNotificationMode?: string;
};
loading: boolean; loading: boolean;
} }
@ -53,7 +50,7 @@ class UserNotificationPreferencesView extends React.Component<
constructor(props: IUserNotificationPreferencesViewProps) { constructor(props: IUserNotificationPreferencesViewProps) {
super(props); super(props);
this.state = { this.state = {
preferences: {}, preferences: {} as INotificationPreferences,
loading: false loading: false
}; };
} }
@ -62,8 +59,10 @@ class UserNotificationPreferencesView extends React.Component<
const { user } = this.props; const { user } = this.props;
const { id } = user; const { id } = user;
const result = await RocketChat.getUserPreferences(id); const result = await RocketChat.getUserPreferences(id);
const { preferences } = result; if (result.success) {
this.setState({ preferences, loading: true }); const { preferences } = result;
this.setState({ preferences, loading: true });
}
} }
findDefaultOption = (key: TKey) => { findDefaultOption = (key: TKey) => {

View File

@ -177,9 +177,9 @@ PODS:
- libwebp/mux (1.1.0): - libwebp/mux (1.1.0):
- libwebp/demux - libwebp/demux
- libwebp/webp (1.1.0) - libwebp/webp (1.1.0)
- MMKV (1.2.1): - MMKV (1.2.10):
- MMKVCore (~> 1.2.1) - MMKVCore (~> 1.2.10)
- MMKVCore (1.2.1) - MMKVCore (1.2.12)
- nanopb (1.30905.0): - nanopb (1.30905.0):
- nanopb/decode (= 1.30905.0) - nanopb/decode (= 1.30905.0)
- nanopb/encode (= 1.30905.0) - nanopb/encode (= 1.30905.0)
@ -410,9 +410,9 @@ PODS:
- react-native-jitsi-meet (3.6.0): - react-native-jitsi-meet (3.6.0):
- JitsiMeetSDK (= 3.6.0) - JitsiMeetSDK (= 3.6.0)
- React - React
- react-native-mmkv-storage (0.3.5): - react-native-mmkv-storage (0.6.12):
- MMKV (= 1.2.1) - MMKV (= 1.2.10)
- React - React-Core
- react-native-netinfo (6.0.0): - react-native-netinfo (6.0.0):
- React-Core - React-Core
- react-native-notifications (2.1.7): - react-native-notifications (2.1.7):
@ -950,7 +950,7 @@ SPEC CHECKSUMS:
EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7 EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7
EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5 EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
FBReactNativeSpec: 8a1012a62d7a3d667376b413656d780b8e4680ce FBReactNativeSpec: 110d69378fce79af38271c39894b59fec7890221
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892 Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4 FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085 FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
@ -974,8 +974,8 @@ SPEC CHECKSUMS:
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
MMKV: 67253edee25a34edf332f91d73fa94a9e038b971 MMKV: 76033b9ace2006623308910a3afcc0e25eba3140
MMKVCore: fe398984acac1fa33f92795d1b5fd0a334c944af MMKVCore: df0565f6b58463604731a68ba6cd89bc0b2d1d55
nanopb: c43f40fadfe79e8b8db116583945847910cbabc9 nanopb: c43f40fadfe79e8b8db116583945847910cbabc9
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75 PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75
@ -997,7 +997,7 @@ SPEC CHECKSUMS:
react-native-cookies: 2cb6ef472da68610dfcf0eaee68464c244943abd react-native-cookies: 2cb6ef472da68610dfcf0eaee68464c244943abd
react-native-document-picker: f1b5398801b332c77bc62ae0eae2116f49bdff26 react-native-document-picker: f1b5398801b332c77bc62ae0eae2116f49bdff26
react-native-jitsi-meet: 3e3ac5d0445091154119f94342efd55c8b1124ce react-native-jitsi-meet: 3e3ac5d0445091154119f94342efd55c8b1124ce
react-native-mmkv-storage: 48729fe90e850ef2fdc9d3714b7030c7c51d82b0 react-native-mmkv-storage: 88bcb10bbe85a8122061d17d03abcc64a02fe1c9
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227 react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227
react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d

View File

@ -25,10 +25,8 @@ class Storage {
// get mmkv instance password from keychain // get mmkv instance password from keychain
var key: Data? var key: Data?
secureStorage.getSecureKey(instanceID.toHex()) { (response) -> () in if let password: String = secureStorage.getSecureKey(instanceID.toHex()) {
if let password = response?[1] as? String { key = password.data(using: .utf8)
key = password.data(using: .utf8)
}
} }
guard let cryptKey = key else { guard let cryptKey = key else {

View File

@ -94,7 +94,7 @@
"react-native-keycommands": "2.0.3", "react-native-keycommands": "2.0.3",
"react-native-localize": "2.1.1", "react-native-localize": "2.1.1",
"react-native-mime-types": "2.3.0", "react-native-mime-types": "2.3.0",
"react-native-mmkv-storage": "0.3.5", "react-native-mmkv-storage": "0.6.12",
"react-native-modal": "11.10.0", "react-native-modal": "11.10.0",
"react-native-navigation-bar-color": "2.0.1", "react-native-navigation-bar-color": "2.0.1",
"react-native-notifications": "2.1.7", "react-native-notifications": "2.1.7",

View File

@ -1,157 +0,0 @@
diff --git a/node_modules/react-native-mmkv-storage/android/src/main/java/com/ammarahmed/mmkv/StorageGetters.java b/node_modules/react-native-mmkv-storage/android/src/main/java/com/ammarahmed/mmkv/StorageGetters.java
index 568e369..229b911 100644
--- a/node_modules/react-native-mmkv-storage/android/src/main/java/com/ammarahmed/mmkv/StorageGetters.java
+++ b/node_modules/react-native-mmkv-storage/android/src/main/java/com/ammarahmed/mmkv/StorageGetters.java
@@ -52,8 +52,12 @@ public class StorageGetters {
case Constants.DATA_TYPE_MAP:
case Constants.DATA_TYPE_ARRAY:
Bundle bundle = kv.decodeParcelable(key, Bundle.class);
- WritableMap map = Arguments.fromBundle(bundle);
- callback.invoke(null, map);
+ if (bundle == null) {
+ callback.invoke(null, null);
+ } else {
+ WritableMap map = Arguments.fromBundle(bundle);
+ callback.invoke(null, map);
+ }
break;
}
diff --git a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
index 70f3a01..30d7251 100644
--- a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
+++ b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
@@ -46,7 +46,6 @@ - (void) setSecureKey: (NSString *)key value:(NSString *)value
- (NSString *) getSecureKey:(NSString *)key
callback:(RCTResponseSenderBlock)callback
{
-
@try {
[self handleAppUninstallation];
NSString *value = [self searchKeychainCopyMatching:key];
@@ -130,7 +129,8 @@ - (void) removeSecureKey:(NSString *)key
- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
- serviceName = [[NSBundle mainBundle] bundleIdentifier];
+ // this value is shared by main app and extensions, so, is the best to be used here
+ serviceName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"];
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
@@ -139,6 +139,9 @@ - (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
[searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount];
[searchDictionary setObject:serviceName forKey:(id)kSecAttrService];
+ NSString *keychainGroup = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"KeychainGroup"];
+ [searchDictionary setObject:keychainGroup forKey:(id)kSecAttrAccessGroup];
+
return searchDictionary;
}
@@ -240,10 +243,13 @@ - (void)clearSecureKeyStore
- (void)handleAppUninstallation
{
- if (![[NSUserDefaults standardUserDefaults] boolForKey:@"RnSksIsAppInstalled"]) {
+ // use app group user defaults to prevent clear when it's share extension
+ NSString *suiteName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"];
+ NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
+ if (![userDefaults boolForKey:@"RnSksIsAppInstalled"]) {
[self clearSecureKeyStore];
- [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"RnSksIsAppInstalled"];
- [[NSUserDefaults standardUserDefaults] synchronize];
+ [userDefaults setBool:YES forKey:@"RnSksIsAppInstalled"];
+ [userDefaults synchronize];
}
}
diff --git a/node_modules/react-native-mmkv-storage/ios/StorageGetters.m b/node_modules/react-native-mmkv-storage/ios/StorageGetters.m
index 909d056..d62814a 100644
--- a/node_modules/react-native-mmkv-storage/ios/StorageGetters.m
+++ b/node_modules/react-native-mmkv-storage/ios/StorageGetters.m
@@ -38,8 +38,13 @@ +(void) getItem:(NSString *)ID
if ([kv containsKey:key]) {
switch (type.integerValue) {
case 1:
-
- callback(@[[NSNull null], [kv getObjectOfClass:NSString.class forKey:key]]);
+ {
+ NSString* string = [kv getObjectOfClass:NSString.class forKey:key];
+ if (!string) {
+ string = (NSString *)[NSNull null];
+ }
+ callback(@[[NSNull null], string]);
+ }
break;
case 2:
diff --git a/node_modules/react-native-mmkv-storage/ios/StorageIndexer.m b/node_modules/react-native-mmkv-storage/ios/StorageIndexer.m
index e7c914b..891cf93 100644
--- a/node_modules/react-native-mmkv-storage/ios/StorageIndexer.m
+++ b/node_modules/react-native-mmkv-storage/ios/StorageIndexer.m
@@ -58,7 +58,11 @@ + (void) removeKeyFromIndexer:(MMKV *)kv
if (index != NULL && [index containsObject:key]) {
[index removeObject:key];
- [kv setObject:index forKey:stringsIndexKey];
+ if (!index || [index count] == 0) {
+ [kv removeValueForKey:stringsIndexKey];
+ } else {
+ [kv setObject:index forKey:stringsIndexKey];
+ }
return;
}
@@ -67,7 +71,11 @@ + (void) removeKeyFromIndexer:(MMKV *)kv
if (index != NULL && [index containsObject:key]) {
[index removeObject:key];
- [kv setObject:index forKey:intIndexKey];
+ if (!index || [index count] == 0) {
+ [kv removeValueForKey:intIndexKey];
+ } else {
+ [kv setObject:index forKey:intIndexKey];
+ }
return;
}
@@ -76,7 +84,11 @@ + (void) removeKeyFromIndexer:(MMKV *)kv
if (index != NULL && [index containsObject:key]) {
[index removeObject:key];
- [kv setObject:index forKey:boolIndexKey];
+ if (!index || [index count] == 0) {
+ [kv removeValueForKey:boolIndexKey];
+ } else {
+ [kv setObject:index forKey:boolIndexKey];
+ }
return;
}
@@ -85,7 +97,11 @@ + (void) removeKeyFromIndexer:(MMKV *)kv
if (index != NULL && [index containsObject:key]) {
[index removeObject:key];
- [kv setObject:index forKey:mapIndexKey];
+ if (!index || [index count] == 0) {
+ [kv removeValueForKey:mapIndexKey];
+ } else {
+ [kv setObject:index forKey:mapIndexKey];
+ }
return;
}
@@ -94,7 +110,11 @@ + (void) removeKeyFromIndexer:(MMKV *)kv
if (index != NULL && [index containsObject:key]) {
[index removeObject:key];
- [kv setObject:index forKey:arrayIndexKey];
+ if (!index || [index count] == 0) {
+ [kv removeValueForKey:arrayIndexKey];
+ } else {
+ [kv setObject:index forKey:arrayIndexKey];
+ }
return;
}

View File

@ -0,0 +1,44 @@
diff --git a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
index eacd030..f4b1b96 100644
--- a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
+++ b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
@@ -96,6 +96,9 @@ NSString *serviceName = nil;
- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
+ // this value is shared by main app and extensions, so, is the best to be used here
+ serviceName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"];
+
if(serviceName == nil){
serviceName = [[NSBundle mainBundle] bundleIdentifier];
}
@@ -107,6 +110,9 @@ NSString *serviceName = nil;
[searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount];
[searchDictionary setObject:serviceName forKey:(id)kSecAttrService];
+ NSString *keychainGroup = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"KeychainGroup"];
+ [searchDictionary setObject:keychainGroup forKey:(id)kSecAttrAccessGroup];
+
return searchDictionary;
}
@@ -208,11 +214,14 @@ NSString *serviceName = nil;
- (void)handleAppUninstallation
{
- // if (![[NSUserDefaults standardUserDefaults] boolForKey:@"RnSksIsAppInstalled"]) {
- // [self clearSecureKeyStore];
- //[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"IsAppInstalled"];
- [[NSUserDefaults standardUserDefaults] synchronize];
- // }
+ // use app group user defaults to prevent clear when it's share extension
+ NSString *suiteName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"];
+ NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
+ if (![userDefaults boolForKey:@"RnSksIsAppInstalled"]) {
+ [self clearSecureKeyStore];
+ [userDefaults setBool:YES forKey:@"RnSksIsAppInstalled"];
+ [userDefaults synchronize];
+ }
}
- (void) setServiceName:(NSString *)_serviceName

View File

@ -102,7 +102,7 @@ index ab869cf..08ce7ce 100644
ignoreErrFailedForThisURL = url; ignoreErrFailedForThisURL = url;
} }
diff --git a/node_modules/react-native-webview/apple/RNCWebView.m b/node_modules/react-native-webview/apple/RNCWebView.m diff --git a/node_modules/react-native-webview/apple/RNCWebView.m b/node_modules/react-native-webview/apple/RNCWebView.m
index 02b4238..04bad05 100644 index 02b4238..e0635ed 100644
--- a/node_modules/react-native-webview/apple/RNCWebView.m --- a/node_modules/react-native-webview/apple/RNCWebView.m
+++ b/node_modules/react-native-webview/apple/RNCWebView.m +++ b/node_modules/react-native-webview/apple/RNCWebView.m
@@ -17,6 +17,9 @@ @@ -17,6 +17,9 @@
@ -115,7 +115,7 @@ index 02b4238..04bad05 100644
static NSTimer *keyboardTimer; static NSTimer *keyboardTimer;
static NSString *const HistoryShimName = @"ReactNativeHistoryShim"; static NSString *const HistoryShimName = @"ReactNativeHistoryShim";
static NSString *const MessageHandlerName = @"ReactNativeWebView"; static NSString *const MessageHandlerName = @"ReactNativeWebView";
@@ -737,6 +740,68 @@ + (void)setCustomCertificatesForHost:(nullable NSDictionary*)certificates { @@ -737,6 +740,68 @@ static NSDictionary* customCertificatesForHost;
customCertificatesForHost = certificates; customCertificatesForHost = certificates;
} }
@ -184,7 +184,7 @@ index 02b4238..04bad05 100644
- (void) webView:(WKWebView *)webView - (void) webView:(WKWebView *)webView
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable))completionHandler completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable))completionHandler
@@ -746,7 +811,34 @@ - (void) webView:(WKWebView *)webView @@ -746,7 +811,32 @@ static NSDictionary* customCertificatesForHost;
host = webView.URL.host; host = webView.URL.host;
} }
if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) { if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
@ -196,17 +196,15 @@ index 02b4238..04bad05 100644
+ SecureStorage *secureStorage = [[SecureStorage alloc] init]; + SecureStorage *secureStorage = [[SecureStorage alloc] init];
+ +
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31 + // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
+ [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"] callback:^(NSArray *response) { + NSString *key = [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"]];
+ // Error happened
+ if ([response objectAtIndex:0] != [NSNull null]) {
+ return;
+ }
+ NSString *key = [response objectAtIndex:1];
+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
+ +
+ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host]; + if (key == NULL) {
+ }]; + return;
+ }
+
+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
+ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host];
+ +
+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+ +

View File

@ -23,7 +23,7 @@ index 602d51d..920d975 100644
public String getName() { public String getName() {
return "RNFetchBlob"; return "RNFetchBlob";
diff --git a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m diff --git a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
index cdbe6b1..bee6228 100644 index cdbe6b1..1699c6c 100644
--- a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m --- a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
+++ b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m +++ b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
@@ -15,6 +15,9 @@ @@ -15,6 +15,9 @@
@ -36,12 +36,18 @@ index cdbe6b1..bee6228 100644
typedef NS_ENUM(NSUInteger, ResponseFormat) { typedef NS_ENUM(NSUInteger, ResponseFormat) {
UTF8, UTF8,
@@ -450,16 +453,109 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen @@ -450,16 +453,107 @@ typedef NS_ENUM(NSUInteger, ResponseFormat) {
} }
} }
-
-- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
+-(NSURLCredential *)getUrlCredential:(NSURLAuthenticationChallenge *)challenge path:(NSString *)path password:(NSString *)password +-(NSURLCredential *)getUrlCredential:(NSURLAuthenticationChallenge *)challenge path:(NSString *)path password:(NSString *)password
+{ {
- if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- } else {
- completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+ NSString *authMethod = [[challenge protectionSpace] authenticationMethod]; + NSString *authMethod = [[challenge protectionSpace] authenticationMethod];
+ SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; + SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
+ +
@ -75,7 +81,7 @@ index cdbe6b1..bee6228 100644
+ NSDictionary* firstItem = nil; + NSDictionary* firstItem = nil;
+ if ((status == errSecSuccess) && ([items count]>0)) { + if ((status == errSecSuccess) && ([items count]>0)) {
+ firstItem = items[0]; + firstItem = items[0];
+ } }
+ +
+ SecIdentityRef identity = (SecIdentityRef)CFBridgingRetain(firstItem[(id)kSecImportItemIdentity]); + SecIdentityRef identity = (SecIdentityRef)CFBridgingRetain(firstItem[(id)kSecImportItemIdentity]);
+ SecCertificateRef certificate = NULL; + SecCertificateRef certificate = NULL;
@ -89,17 +95,12 @@ index cdbe6b1..bee6228 100644
+ +
+ return [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceNone]; + return [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceNone];
+ } + }
+
-- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+} +}
+ +
+- (NSString *)stringToHex:(NSString *)string +- (NSString *)stringToHex:(NSString *)string
{ +{
- if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- } else {
- completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+ char *utf8 = (char *)[string UTF8String]; + char *utf8 = (char *)[string UTF8String];
+ NSMutableString *hex = [NSMutableString string]; + NSMutableString *hex = [NSMutableString string];
+ while (*utf8) [hex appendFormat:@"%02X", *utf8++ & 0x00FF]; + while (*utf8) [hex appendFormat:@"%02X", *utf8++ & 0x00FF];
@ -116,17 +117,15 @@ index cdbe6b1..bee6228 100644
+ SecureStorage *secureStorage = [[SecureStorage alloc] init]; + SecureStorage *secureStorage = [[SecureStorage alloc] init];
+ +
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31 + // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
+ [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"] callback:^(NSArray *response) { + NSString *key = [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"]];
+ // Error happened
+ if ([response objectAtIndex:0] != [NSNull null]) {
+ return;
}
+ NSString *key = [response objectAtIndex:1];
+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
+ +
+ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host]; + if (key == NULL) {
+ }]; + return;
+ }
+
+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
+ clientSSL = [mmkv getObjectOfClass:[NSDictionary class] forKey:host];
+ +
+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+ +

View File

@ -14424,10 +14424,10 @@ react-native-mime-types@2.3.0:
dependencies: dependencies:
mime-db "~1.37.0" mime-db "~1.37.0"
react-native-mmkv-storage@0.3.5: react-native-mmkv-storage@0.6.12:
version "0.3.5" version "0.6.12"
resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.3.5.tgz#9c2f064c0efdaf960e9646c68dc6ae7f11640fa5" resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.6.12.tgz#e9e052e26c3dea6818211919d2586cb260ee8e24"
integrity sha512-xp0E55Qdi81k8CTeq3PUrXGwT2tMmfNfmYvZ7Emq9qWpvg3ko1/M6B1kXEXOgEou/hgqB503TGcsR/mpN5HSMA== integrity sha512-9gJlnGSAJkeWanNE24GPqEn4NMiNFVBpPsTZAQ5YDEKEX/gG7bGdHCTNkXc6L826QxLEPKjpPmf9ZqgPM2G8TQ==
react-native-modal@11.10.0: react-native-modal@11.10.0:
version "11.10.0" version "11.10.0"
@ -14632,7 +14632,7 @@ react-native-webview@10.3.2:
react-native@RocketChat/react-native#0.64.2: react-native@RocketChat/react-native#0.64.2:
version "0.64.2" version "0.64.2"
resolved "https://codeload.github.com/RocketChat/react-native/tar.gz/6c015e1ec93d2f9a79a7033a7e160d76cb4df4aa" resolved "https://codeload.github.com/RocketChat/react-native/tar.gz/eefc7ead3d343d5514118d79ff5e0ac7fc1761ec"
dependencies: dependencies:
"@jest/create-cache-key-function" "^27.0.2" "@jest/create-cache-key-function" "^27.0.2"
"@react-native-community/cli" "^5.0.1-alpha.1" "@react-native-community/cli" "^5.0.1-alpha.1"