Merge branch 'develop' into fix.e2efetchkeys-startup
This commit is contained in:
commit
9a9edc7f94
|
@ -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({
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 }) => {};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 =>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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, {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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') {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) => (
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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 });
|
||||||
}
|
}
|
||||||
|
|
|
@ -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] } });
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 = () => {
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
@ -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];
|
||||||
+
|
+
|
||||||
|
|
|
@ -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];
|
||||||
+
|
+
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue