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));
|
||||
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();
|
||||
|
||||
initStoryshots({
|
||||
|
|
|
@ -12,7 +12,6 @@ import com.facebook.soloader.SoLoader;
|
|||
import com.reactnativecommunity.viewpager.RNCViewPagerPackage;
|
||||
import com.facebook.react.bridge.JSIModulePackage;
|
||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
|
||||
|
||||
import org.unimodules.adapters.react.ModuleRegistryAdapter;
|
||||
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
|
||||
|
||||
|
@ -54,7 +53,7 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
|
||||
@Override
|
||||
protected JSIModulePackage getJSIModulePackage() {
|
||||
return new ReanimatedJSIModulePackage(); // <- add
|
||||
return new ReanimatedJSIModulePackage();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -53,16 +53,8 @@ public class Ejson {
|
|||
String alias = Utils.toHex("com.MMKV.default");
|
||||
|
||||
// Retrieve container password
|
||||
secureKeystore.getSecureKey(alias, new RNCallback() {
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
String password = secureKeystore.getSecureKey(alias);
|
||||
mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
|
||||
}
|
||||
|
||||
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 { biometryAuth, resetAttempts } from '../../utils/localAuthentication';
|
||||
import { getDiff, getLockedUntil } from './utils';
|
||||
import UserPreferences from '../../lib/userPreferences';
|
||||
import { useUserPreferences } from '../../lib/userPreferences';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
interface IPasscodePasscodeEnter {
|
||||
|
@ -23,16 +23,11 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeE
|
|||
const ref = useRef(null);
|
||||
let attempts: any = 0;
|
||||
let lockedUntil: any = false;
|
||||
const [passcode, setPasscode] = useState(null);
|
||||
const [passcode] = useUserPreferences(PASSCODE_KEY);
|
||||
const [status, setStatus] = useState(null);
|
||||
const { getItem: getAttempts, setItem: setAttempts } = useAsyncStorage(ATTEMPTS_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 () => {
|
||||
if (hasBiometry && status === TYPE.ENTER) {
|
||||
const result = await biometryAuth();
|
||||
|
@ -56,7 +51,6 @@ const PasscodeEnter = ({ theme, hasBiometry, finishProcess }: IPasscodePasscodeE
|
|||
} else {
|
||||
setStatus(TYPE.ENTER);
|
||||
}
|
||||
await fetchPasscode();
|
||||
biometry();
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
|
||||
import { ProfileStackParamList } from '../../stacks/types';
|
||||
import { ProfileStackParamList } from '../stacks/types';
|
||||
|
||||
export interface IUser {
|
||||
id: string;
|
||||
|
@ -57,6 +57,14 @@ export interface IAvatar {
|
|||
service?: any;
|
||||
}
|
||||
|
||||
export interface IAvatarSuggestion {
|
||||
[service: string]: {
|
||||
url: string;
|
||||
blob: string;
|
||||
contentType: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IProfileViewState {
|
||||
saving: boolean;
|
||||
name: string;
|
||||
|
@ -66,13 +74,7 @@ export interface IProfileViewState {
|
|||
currentPassword: string | null;
|
||||
avatarUrl: string | null;
|
||||
avatar: IAvatar;
|
||||
avatarSuggestions: {
|
||||
[service: string]: {
|
||||
url: string;
|
||||
blob: string;
|
||||
contentType: string;
|
||||
};
|
||||
};
|
||||
avatarSuggestions: IAvatarSuggestion;
|
||||
customFields: {
|
||||
[key: string | number]: string;
|
||||
};
|
|
@ -4,7 +4,7 @@ import { IMessage } from './IMessage';
|
|||
import { IRocketChatRecord } from './IRocketChatRecord';
|
||||
import { IServedBy } from './IServedBy';
|
||||
import { IVisitor, SubscriptionType } from './ISubscription';
|
||||
import { IUser } from './IUser';
|
||||
import { IUser, TNotifications } from './IUser';
|
||||
|
||||
interface IRequestTranscript {
|
||||
email: string;
|
||||
|
@ -202,3 +202,13 @@ export interface IServerRoom extends IRocketChatRecord {
|
|||
livechatData?: any;
|
||||
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;
|
||||
};
|
||||
}
|
||||
type TNotifications = 'default' | 'all' | 'mentions' | 'nothing';
|
||||
export type TNotifications = 'default' | 'all' | 'mentions' | 'nothing';
|
||||
|
||||
export interface INotificationPreferences {
|
||||
id: string;
|
||||
|
@ -111,6 +111,7 @@ export interface INotificationPreferences {
|
|||
desktopNotifications: TNotifications;
|
||||
pushNotifications: TNotifications;
|
||||
emailNotificationMode?: 'mentions' | 'nothing';
|
||||
language?: string;
|
||||
}
|
||||
|
||||
export interface IUserPreferences {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { IMessage } from '../../IMessage';
|
||||
import type { IServerRoom } from '../../IRoom';
|
||||
import type { IRoomNotifications, IServerRoom } from '../../IRoom';
|
||||
import type { IUser } from '../../IUser';
|
||||
|
||||
export type RoomsEndpoints = {
|
||||
|
@ -41,4 +41,7 @@ export type RoomsEndpoints = {
|
|||
'rooms.favorite': {
|
||||
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 { IUser } from '../../IUser';
|
||||
import { INotificationPreferences, IUserPreferences, IUserRegistered } from '../../IUser';
|
||||
|
@ -26,7 +27,7 @@ export type UsersEndpoints = {
|
|||
};
|
||||
};
|
||||
'users.setPreferences': {
|
||||
POST: (params: { userId: IUser['_id']; data: Partial<INotificationPreferences> }) => {
|
||||
POST: (params: { userId?: IUser['_id']; data: Partial<INotificationPreferences> }) => {
|
||||
user: IUserPreferences;
|
||||
success: boolean;
|
||||
};
|
||||
|
@ -37,4 +38,21 @@ export type UsersEndpoints = {
|
|||
'users.setStatus': {
|
||||
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 { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context';
|
||||
|
||||
import { defaultTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme';
|
||||
import UserPreferences from './lib/userPreferences';
|
||||
import { getTheme, initialTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme';
|
||||
import EventEmitter from './utils/events';
|
||||
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
|
||||
import { deepLinkingOpen } from './actions/deepLinking';
|
||||
|
@ -17,9 +16,9 @@ import store from './lib/createStore';
|
|||
import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './utils/log';
|
||||
import { ThemeContext } from './theme';
|
||||
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 { isTablet, supportSystemTheme } from './utils/deviceInfo';
|
||||
import { isTablet } from './utils/deviceInfo';
|
||||
import { KEY_COMMAND } from './commands';
|
||||
import AppContainer from './AppContainer';
|
||||
import TwoFactor from './containers/TwoFactor';
|
||||
|
@ -33,6 +32,7 @@ import { isFDroidBuild } from './constants/environment';
|
|||
import { IThemePreference } from './definitions/ITheme';
|
||||
import { ICommand } from './definitions/ICommand';
|
||||
import { initStore } from './lib/auxStore';
|
||||
import { themes } from './constants/colors';
|
||||
|
||||
RNScreens.enableScreens();
|
||||
initStore(store);
|
||||
|
@ -88,12 +88,10 @@ export default class Root extends React.Component<{}, IState> {
|
|||
this.initCrashReport();
|
||||
}
|
||||
const { width, height, scale, fontScale } = Dimensions.get('window');
|
||||
const theme = initialTheme();
|
||||
this.state = {
|
||||
theme: defaultTheme(),
|
||||
themePreferences: {
|
||||
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
|
||||
darkLevel: 'black'
|
||||
},
|
||||
theme: getTheme(theme),
|
||||
themePreferences: theme,
|
||||
width,
|
||||
height,
|
||||
scale,
|
||||
|
@ -128,7 +126,6 @@ export default class Root extends React.Component<{}, IState> {
|
|||
}
|
||||
|
||||
init = async () => {
|
||||
UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then((theme: any) => this.setTheme(theme));
|
||||
store.dispatch(appInitLocalSettings());
|
||||
|
||||
// Open app from push notification
|
||||
|
@ -209,7 +206,9 @@ export default class Root extends React.Component<{}, IState> {
|
|||
render() {
|
||||
const { themePreferences, theme, width, height, scale, fontScale } = this.state;
|
||||
return (
|
||||
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
||||
<SafeAreaProvider
|
||||
initialMetrics={initialWindowMetrics}
|
||||
style={{ backgroundColor: themes[this.state.theme].backgroundColor }}>
|
||||
<AppearanceProvider>
|
||||
<Provider store={store}>
|
||||
<ThemeContext.Provider
|
||||
|
|
|
@ -118,8 +118,8 @@ class Encryption {
|
|||
// Persist keys on UserPreferences
|
||||
persistKeys = async (server: string, publicKey: string, privateKey: string) => {
|
||||
this.privateKey = await SimpleCrypto.RSA.importKey(EJSON.parse(privateKey));
|
||||
await UserPreferences.setStringAsync(`${server}-${E2E_PUBLIC_KEY}`, EJSON.stringify(publicKey));
|
||||
await UserPreferences.setStringAsync(`${server}-${E2E_PRIVATE_KEY}`, privateKey);
|
||||
UserPreferences.setString(`${server}-${E2E_PUBLIC_KEY}`, EJSON.stringify(publicKey));
|
||||
UserPreferences.setString(`${server}-${E2E_PRIVATE_KEY}`, privateKey);
|
||||
};
|
||||
|
||||
// Could not obtain public-private keypair from server.
|
||||
|
@ -182,9 +182,9 @@ class Encryption {
|
|||
};
|
||||
|
||||
// Create a random password to local created keys
|
||||
createRandomPassword = async (server: string) => {
|
||||
createRandomPassword = (server: string) => {
|
||||
const password = randomPassword();
|
||||
await UserPreferences.setStringAsync(`${server}-${E2E_RANDOM_PASSWORD_KEY}`, password);
|
||||
UserPreferences.setString(`${server}-${E2E_RANDOM_PASSWORD_KEY}`, password);
|
||||
return password;
|
||||
};
|
||||
|
||||
|
@ -194,7 +194,7 @@ class Encryption {
|
|||
|
||||
// Encode the private key
|
||||
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
|
||||
await RocketChat.e2eSetUserPublicAndPrivateKeys(EJSON.stringify(publicKey), encodedPrivateKey);
|
||||
|
|
|
@ -14,23 +14,23 @@ import UserPreferences from '../userPreferences';
|
|||
import { ICertificate, IRocketChat } from '../../definitions';
|
||||
import sdk from '../rocketchat/services/sdk';
|
||||
|
||||
async function removeServerKeys({ server, userId }: { server: string; userId?: string | null }) {
|
||||
await UserPreferences.removeItem(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||
function removeServerKeys({ server, userId }: { server: string; userId?: string | null }) {
|
||||
UserPreferences.removeItem(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||
if (userId) {
|
||||
await UserPreferences.removeItem(`${RocketChat.TOKEN_KEY}-${userId}`);
|
||||
UserPreferences.removeItem(`${RocketChat.TOKEN_KEY}-${userId}`);
|
||||
}
|
||||
await UserPreferences.removeItem(`${BASIC_AUTH_KEY}-${server}`);
|
||||
await UserPreferences.removeItem(`${server}-${E2E_PUBLIC_KEY}`);
|
||||
await UserPreferences.removeItem(`${server}-${E2E_PRIVATE_KEY}`);
|
||||
await UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
||||
UserPreferences.removeItem(`${BASIC_AUTH_KEY}-${server}`);
|
||||
UserPreferences.removeItem(`${server}-${E2E_PUBLIC_KEY}`);
|
||||
UserPreferences.removeItem(`${server}-${E2E_PRIVATE_KEY}`);
|
||||
UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
||||
}
|
||||
|
||||
async function removeSharedCredentials({ server }: { server: string }) {
|
||||
// clear certificate for server - SSL Pinning
|
||||
try {
|
||||
const certificate = (await UserPreferences.getMapAsync(extractHostname(server))) as ICertificate | null;
|
||||
const certificate = UserPreferences.getMap(extractHostname(server)) as ICertificate | null;
|
||||
if (certificate?.path) {
|
||||
await UserPreferences.removeItem(extractHostname(server));
|
||||
UserPreferences.removeItem(extractHostname(server));
|
||||
await FileSystem.deleteAsync(certificate.path);
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -42,7 +42,7 @@ async function removeServerData({ server }: { server: string }) {
|
|||
try {
|
||||
const batch: Model[] = [];
|
||||
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');
|
||||
if (userId) {
|
||||
|
@ -55,14 +55,14 @@ async function removeServerData({ server }: { server: string }) {
|
|||
|
||||
await serversDB.write(() => serversDB.batch(...batch));
|
||||
await removeSharedCredentials({ server });
|
||||
await removeServerKeys({ server, userId });
|
||||
removeServerKeys({ server, userId });
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function removeCurrentServer() {
|
||||
await UserPreferences.removeItem(RocketChat.CURRENT_SERVER);
|
||||
function removeCurrentServer() {
|
||||
UserPreferences.removeItem(RocketChat.CURRENT_SERVER);
|
||||
}
|
||||
|
||||
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> {
|
||||
try {
|
||||
const userId = await UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||
const userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||
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) });
|
||||
await sdk.login({ resume });
|
||||
|
|
|
@ -115,8 +115,8 @@ const RocketChat = {
|
|||
database.setShareDB(server);
|
||||
|
||||
try {
|
||||
const certificate = await UserPreferences.getStringAsync(`${RocketChat.CERTIFICATE_KEY}-${server}`);
|
||||
await SSLPinning.setCertificate(certificate, server);
|
||||
const certificate = UserPreferences.getString(`${RocketChat.CERTIFICATE_KEY}-${server}`);
|
||||
SSLPinning.setCertificate(certificate, server);
|
||||
} catch {
|
||||
// Do nothing
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ const RocketChat = {
|
|||
reduxStore.dispatch(shareSetSettings(this.parseSettings(parsed)));
|
||||
|
||||
// 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');
|
||||
let user = null;
|
||||
if (userId) {
|
||||
|
@ -461,14 +461,13 @@ const RocketChat = {
|
|||
}
|
||||
return JSON.parse(allowAnalyticsEvents);
|
||||
},
|
||||
async getSortPreferences() {
|
||||
const prefs = await UserPreferences.getMapAsync(SORT_PREFS_KEY);
|
||||
return prefs;
|
||||
getSortPreferences() {
|
||||
return UserPreferences.getMap(SORT_PREFS_KEY);
|
||||
},
|
||||
async saveSortPreference(param) {
|
||||
let prefs = await RocketChat.getSortPreferences();
|
||||
saveSortPreference(param) {
|
||||
let prefs = RocketChat.getSortPreferences();
|
||||
prefs = { ...prefs, ...param };
|
||||
return UserPreferences.setMapAsync(SORT_PREFS_KEY, prefs);
|
||||
return UserPreferences.setMap(SORT_PREFS_KEY, prefs);
|
||||
},
|
||||
getLoginServices,
|
||||
determineAuthType,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import sdk from './sdk';
|
||||
import { TEAM_TYPE } from '../../../definitions/ITeam';
|
||||
import roomTypeToApiType, { RoomTypes } from '../methods/roomTypeToApiType';
|
||||
import { SubscriptionType, INotificationPreferences } from '../../../definitions';
|
||||
import { SubscriptionType, INotificationPreferences, IRoomNotifications } from '../../../definitions';
|
||||
import { ISpotlight } from '../../../definitions/ISpotlight';
|
||||
import { IAvatarSuggestion, IParams } from '../../../definitions/IProfileViewInterfaces';
|
||||
|
||||
export const createChannel = ({
|
||||
name,
|
||||
|
@ -289,10 +290,8 @@ export const getChannelInfo = (roomId: string) =>
|
|||
// RC 0.48.0
|
||||
sdk.get('channels.info', { roomId });
|
||||
|
||||
export const getUserPreferences = (userId: string): any =>
|
||||
export const getUserPreferences = (userId: string) =>
|
||||
// RC 0.62.0
|
||||
// TODO: missing definitions from server
|
||||
// @ts-ignore
|
||||
sdk.get('users.getPreferences', { userId });
|
||||
|
||||
export const getRoomInfo = (roomId: string) =>
|
||||
|
@ -545,22 +544,16 @@ export const saveRoomSettings = (
|
|||
// RC 0.55.0
|
||||
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
|
||||
// TODO: missing definitions from server
|
||||
// @ts-ignore
|
||||
sdk.post('users.updateOwnBasicInfo', { data, customFields });
|
||||
|
||||
export const saveUserPreferences = (data: any): any =>
|
||||
export const saveUserPreferences = (data: Partial<INotificationPreferences>) =>
|
||||
// RC 0.62.0
|
||||
// TODO: missing definitions from server
|
||||
// @ts-ignore
|
||||
sdk.post('users.setPreferences', { data });
|
||||
|
||||
export const saveNotificationSettings = (roomId: string, notifications: any): any =>
|
||||
export const saveNotificationSettings = (roomId: string, notifications: IRoomNotifications) =>
|
||||
// RC 0.63.0
|
||||
// TODO: missing definitions from server
|
||||
// @ts-ignore
|
||||
sdk.post('rooms.saveNotification', { roomId, notifications });
|
||||
|
||||
export const getSingleMessage = (msgId: string) =>
|
||||
|
@ -573,14 +566,12 @@ export const getRoomRoles = (roomId: string, type: SubscriptionType): any =>
|
|||
// @ts-ignore
|
||||
sdk.get(`${roomTypeToApiType(type)}.roles`, { roomId });
|
||||
|
||||
export const getAvatarSuggestion = () =>
|
||||
export const getAvatarSuggestion = (): Promise<IAvatarSuggestion> =>
|
||||
// RC 0.51.0
|
||||
sdk.methodCallWrapper('getAvatarSuggestion');
|
||||
|
||||
export const resetAvatar = (userId: string): any =>
|
||||
export const resetAvatar = (userId: string) =>
|
||||
// RC 0.55.0
|
||||
// TODO: missing definitions from server
|
||||
// @ts-ignore
|
||||
sdk.post('users.resetAvatar', { userId });
|
||||
|
||||
export const setAvatarFromService = ({
|
||||
|
@ -591,14 +582,12 @@ export const setAvatarFromService = ({
|
|||
data: any;
|
||||
contentType?: string;
|
||||
service?: string | null;
|
||||
}) =>
|
||||
}): Promise<void> =>
|
||||
// RC 0.51.0
|
||||
sdk.methodCallWrapper('setAvatarFromService', data, contentType, service);
|
||||
|
||||
export const getUsernameSuggestion = (): any =>
|
||||
export const getUsernameSuggestion = () =>
|
||||
// RC 0.65.0
|
||||
// TODO: missing definitions from server
|
||||
// @ts-ignore
|
||||
sdk.get('users.getUsernameSuggestion');
|
||||
|
||||
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()
|
||||
// MODES.MULTI_PROCESS = ACCESSIBLE BY APP GROUP (iOS)
|
||||
|
@ -6,52 +6,51 @@ const MMKV = new MMKVStorage.Loader()
|
|||
.withEncryption()
|
||||
.initialize();
|
||||
|
||||
export const useUserPreferences = create(MMKV);
|
||||
|
||||
class UserPreferences {
|
||||
private mmkv: MMKVStorage.API;
|
||||
constructor() {
|
||||
this.mmkv = MMKV;
|
||||
}
|
||||
|
||||
async getStringAsync(key: string) {
|
||||
getString(key: string): string | null {
|
||||
try {
|
||||
const value = await this.mmkv.getStringAsync(key);
|
||||
return value;
|
||||
return this.mmkv.getString(key) || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
setStringAsync(key: string, value: string) {
|
||||
return this.mmkv.setStringAsync(key, value);
|
||||
setString(key: string, value: string): boolean | undefined {
|
||||
return this.mmkv.setString(key, value);
|
||||
}
|
||||
|
||||
async getBoolAsync(key: string) {
|
||||
getBool(key: string): boolean | null {
|
||||
try {
|
||||
const value = await this.mmkv.getBoolAsync(key);
|
||||
return value;
|
||||
return this.mmkv.getBool(key) || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
setBoolAsync(key: string, value: boolean) {
|
||||
return this.mmkv.setBoolAsync(key, value);
|
||||
setBool(key: string, value: boolean): boolean | undefined {
|
||||
return this.mmkv.setBool(key, value);
|
||||
}
|
||||
|
||||
async getMapAsync(key: string) {
|
||||
getMap(key: string): object | null {
|
||||
try {
|
||||
const value = await this.mmkv.getMapAsync(key);
|
||||
return value;
|
||||
return this.mmkv.getMap(key) || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
setMapAsync(key: string, value: object) {
|
||||
return this.mmkv.setMapAsync(key, value);
|
||||
setMap(key: string, value: object): boolean | undefined {
|
||||
return this.mmkv.setMap(key, value);
|
||||
}
|
||||
|
||||
removeItem(key: string) {
|
||||
removeItem(key: string): boolean | undefined {
|
||||
return this.mmkv.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,8 +158,8 @@ const handleOpen = function* handleOpen({ params }) {
|
|||
}
|
||||
|
||||
const [server, user] = yield all([
|
||||
UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER),
|
||||
UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${host}`)
|
||||
UserPreferences.getString(RocketChat.CURRENT_SERVER),
|
||||
UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${host}`)
|
||||
]);
|
||||
|
||||
// TODO: needs better test
|
||||
|
|
|
@ -39,7 +39,7 @@ const handleEncryptionInit = function* handleEncryptionInit() {
|
|||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
const storedRandomPassword = yield UserPreferences.getStringAsync(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
||||
const storedRandomPassword = UserPreferences.getString(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
||||
|
||||
if (storedRandomPassword) {
|
||||
yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD));
|
||||
}
|
||||
|
||||
// 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
|
||||
if (storedPublicKey) {
|
||||
storedPublicKey = EJSON.parse(storedPublicKey);
|
||||
|
|
|
@ -14,7 +14,7 @@ import { appReady, appStart } from '../actions/app';
|
|||
import { RootEnum } from '../definitions';
|
||||
|
||||
export const initLocalSettings = function* initLocalSettings() {
|
||||
const sortPreferences = yield RocketChat.getSortPreferences();
|
||||
const sortPreferences = RocketChat.getSortPreferences();
|
||||
yield put(setAllPreferences(sortPreferences));
|
||||
};
|
||||
|
||||
|
@ -22,19 +22,19 @@ const BIOMETRY_MIGRATION_KEY = 'kBiometryMigration';
|
|||
|
||||
const restore = function* restore() {
|
||||
try {
|
||||
const server = yield UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER);
|
||||
let userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||
const server = UserPreferences.getString(RocketChat.CURRENT_SERVER);
|
||||
let userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||
|
||||
// Migration biometry setting from WatermelonDB to MMKV
|
||||
// TODO: remove it after a few versions
|
||||
const hasMigratedBiometry = yield UserPreferences.getBoolAsync(BIOMETRY_MIGRATION_KEY);
|
||||
const hasMigratedBiometry = UserPreferences.getBool(BIOMETRY_MIGRATION_KEY);
|
||||
if (!hasMigratedBiometry) {
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.get('servers');
|
||||
const servers = yield serversCollection.query().fetch();
|
||||
const isBiometryEnabled = servers.some(server => !!server.biometry);
|
||||
yield UserPreferences.setBoolAsync(BIOMETRY_ENABLED_KEY, isBiometryEnabled);
|
||||
yield UserPreferences.setBoolAsync(BIOMETRY_MIGRATION_KEY, true);
|
||||
UserPreferences.setBool(BIOMETRY_ENABLED_KEY, isBiometryEnabled);
|
||||
UserPreferences.setBool(BIOMETRY_MIGRATION_KEY, true);
|
||||
}
|
||||
|
||||
if (!server) {
|
||||
|
@ -48,7 +48,7 @@ const restore = function* restore() {
|
|||
if (servers.length > 0) {
|
||||
for (let i = 0; i < servers.length; i += 1) {
|
||||
const newServer = servers[i].id;
|
||||
userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${newServer}`);
|
||||
userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${newServer}`);
|
||||
if (userId) {
|
||||
return yield put(selectServerRequest(newServer));
|
||||
}
|
||||
|
|
|
@ -161,8 +161,8 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
|
|||
}
|
||||
});
|
||||
|
||||
yield UserPreferences.setStringAsync(`${RocketChat.TOKEN_KEY}-${server}`, user.id);
|
||||
yield UserPreferences.setStringAsync(`${RocketChat.TOKEN_KEY}-${user.id}`, user.token);
|
||||
UserPreferences.setString(`${RocketChat.TOKEN_KEY}-${server}`, user.id);
|
||||
UserPreferences.setString(`${RocketChat.TOKEN_KEY}-${user.id}`, user.token);
|
||||
yield put(setUser(user));
|
||||
EventEmitter.emit('connected');
|
||||
|
||||
|
@ -200,7 +200,7 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
|
|||
if (servers.length > 0) {
|
||||
for (let i = 0; i < servers.length; i += 1) {
|
||||
const newServer = servers[i].id;
|
||||
const token = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${newServer}`);
|
||||
const token = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${newServer}`);
|
||||
if (token) {
|
||||
yield put(selectServerRequest(newServer));
|
||||
return;
|
||||
|
|
|
@ -69,15 +69,14 @@ const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
|
|||
const handleSelectServer = function* handleSelectServer({ server, version, fetchVersion }) {
|
||||
try {
|
||||
// SSL Pinning - Read certificate alias and set it to be used by network requests
|
||||
const certificate = yield UserPreferences.getStringAsync(`${RocketChat.CERTIFICATE_KEY}-${server}`);
|
||||
yield SSLPinning.setCertificate(certificate, server);
|
||||
|
||||
const certificate = UserPreferences.getString(`${RocketChat.CERTIFICATE_KEY}-${server}`);
|
||||
SSLPinning.setCertificate(certificate, server);
|
||||
yield put(inquiryReset());
|
||||
yield put(encryptionStop());
|
||||
yield put(clearActiveUsers());
|
||||
const serversDB = database.servers;
|
||||
yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server);
|
||||
const userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||
UserPreferences.setString(RocketChat.CURRENT_SERVER, server);
|
||||
const userId = UserPreferences.getString(`${RocketChat.TOKEN_KEY}-${server}`);
|
||||
const userCollections = serversDB.get('users');
|
||||
let user = null;
|
||||
if (userId) {
|
||||
|
@ -97,14 +96,14 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
|
|||
};
|
||||
} catch {
|
||||
// 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) {
|
||||
user = { token };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const basicAuth = yield UserPreferences.getStringAsync(`${BASIC_AUTH_KEY}-${server}`);
|
||||
const basicAuth = UserPreferences.getString(`${BASIC_AUTH_KEY}-${server}`);
|
||||
setBasicAuth(basicAuth);
|
||||
|
||||
// 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 }) {
|
||||
try {
|
||||
// SSL Pinning - Read certificate alias and set it to be used by network requests
|
||||
const certificate = yield UserPreferences.getStringAsync(`${RocketChat.CERTIFICATE_KEY}-${server}`);
|
||||
yield SSLPinning.setCertificate(certificate, server);
|
||||
const certificate = UserPreferences.getString(`${RocketChat.CERTIFICATE_KEY}-${server}`);
|
||||
SSLPinning.setCertificate(certificate, server);
|
||||
|
||||
const serverInfo = yield getServerInfo({ server });
|
||||
const serversDB = database.servers;
|
||||
|
|
|
@ -5,14 +5,13 @@ import { AppearanceProvider } from 'react-native-appearance';
|
|||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
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 Navigation from './lib/ShareNavigation';
|
||||
import store from './lib/createStore';
|
||||
import { initStore } from './lib/auxStore';
|
||||
import { supportSystemTheme } from './utils/deviceInfo';
|
||||
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 { localAuthenticate } from './utils/localAuthentication';
|
||||
import { IThemePreference } from './definitions/ITheme';
|
||||
|
@ -94,12 +93,10 @@ class Root extends React.Component<{}, IState> {
|
|||
constructor(props: any) {
|
||||
super(props);
|
||||
const { width, height, scale, fontScale } = Dimensions.get('screen');
|
||||
const theme = initialTheme();
|
||||
this.state = {
|
||||
theme: defaultTheme(),
|
||||
themePreferences: {
|
||||
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
|
||||
darkLevel: 'black'
|
||||
},
|
||||
theme: getTheme(theme),
|
||||
themePreferences: theme,
|
||||
root: '',
|
||||
width,
|
||||
height,
|
||||
|
@ -115,9 +112,7 @@ class Root extends React.Component<{}, IState> {
|
|||
}
|
||||
|
||||
init = async () => {
|
||||
UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then((theme: any) => this.setTheme(theme));
|
||||
|
||||
const currentServer = await UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER);
|
||||
const currentServer = UserPreferences.getString(RocketChat.CURRENT_SERVER);
|
||||
|
||||
if (currentServer) {
|
||||
await localAuthenticate(currentServer);
|
||||
|
|
|
@ -69,7 +69,7 @@ const openChangePasscodeModal = ({ force }: { force: boolean }) =>
|
|||
|
||||
export const changePasscode = async ({ force = false }: { force: boolean }): Promise<void> => {
|
||||
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> =>
|
||||
|
@ -86,12 +86,12 @@ export const biometryAuth = (force?: boolean): Promise<LocalAuthentication.Local
|
|||
const checkBiometry = async () => {
|
||||
const result = await biometryAuth(true);
|
||||
const isBiometryEnabled = !!result?.success;
|
||||
await UserPreferences.setBoolAsync(BIOMETRY_ENABLED_KEY, isBiometryEnabled);
|
||||
UserPreferences.setBool(BIOMETRY_ENABLED_KEY, isBiometryEnabled);
|
||||
return isBiometryEnabled;
|
||||
};
|
||||
|
||||
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) {
|
||||
await changePasscode({ force });
|
||||
await checkBiometry();
|
||||
|
@ -137,7 +137,7 @@ export const localAuthenticate = async (server: string): Promise<void> => {
|
|||
store.dispatch(setLocalAuthenticated(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 (hasBiometry) {
|
||||
|
|
|
@ -37,7 +37,7 @@ const appSchemeURL = (url: string, browser: string): string => {
|
|||
|
||||
const openLink = async (url: string, theme = 'light'): Promise<void> => {
|
||||
try {
|
||||
const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY);
|
||||
const browser = UserPreferences.getString(DEFAULT_BROWSER_KEY);
|
||||
|
||||
if (browser === 'inApp') {
|
||||
await WebBrowser.openBrowserAsync(url, {
|
||||
|
|
|
@ -14,13 +14,13 @@ const extractFileScheme = (path: string) => path.replace('file://', ''); // file
|
|||
|
||||
const getPath = (name: string) => `${documentDirectory}/${name}`;
|
||||
|
||||
const persistCertificate = async (name: string, password: string) => {
|
||||
const persistCertificate = (name: string, password: string) => {
|
||||
const certificatePath = getPath(name);
|
||||
const certificate: ICertificate = {
|
||||
path: extractFileScheme(certificatePath),
|
||||
password
|
||||
};
|
||||
await UserPreferences.setMapAsync(name, certificate);
|
||||
UserPreferences.setMap(name, certificate);
|
||||
return certificate;
|
||||
};
|
||||
|
||||
|
@ -44,7 +44,7 @@ const RCSSLPinning = Platform.select({
|
|||
try {
|
||||
const certificatePath = getPath(name);
|
||||
await FileSystem.copyAsync({ from: uri, to: certificatePath });
|
||||
await persistCertificate(name, password!);
|
||||
persistCertificate(name, password!);
|
||||
resolve(name);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
|
@ -58,13 +58,13 @@ const RCSSLPinning = Platform.select({
|
|||
reject(e);
|
||||
}
|
||||
}),
|
||||
setCertificate: async (name: string, server: string) => {
|
||||
setCertificate: (name: string, server: string) => {
|
||||
if (name) {
|
||||
let certificate = (await UserPreferences.getMapAsync(name)) as ICertificate;
|
||||
let certificate = UserPreferences.getMap(name) as ICertificate;
|
||||
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 { themes } from '../constants/colors';
|
||||
import { isAndroid } from './deviceInfo';
|
||||
import UserPreferences from '../lib/userPreferences';
|
||||
import { THEME_PREFERENCES_KEY } from '../lib/rocketchat';
|
||||
|
||||
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 => {
|
||||
const systemTheme = Appearance.getColorScheme();
|
||||
if (systemTheme && systemTheme !== 'no-preference') {
|
||||
|
|
|
@ -73,9 +73,9 @@ class DefaultBrowserView extends React.Component<IDefaultBrowserViewProps, IDefa
|
|||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
componentDidMount() {
|
||||
this.mounted = true;
|
||||
const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY);
|
||||
const browser = UserPreferences.getString(DEFAULT_BROWSER_KEY);
|
||||
this.setState({ browser });
|
||||
}
|
||||
|
||||
|
@ -104,11 +104,11 @@ class DefaultBrowserView extends React.Component<IDefaultBrowserViewProps, IDefa
|
|||
return browser === value;
|
||||
};
|
||||
|
||||
changeDefaultBrowser = async (newBrowser: TValue) => {
|
||||
changeDefaultBrowser = (newBrowser: TValue) => {
|
||||
logEvent(events.DB_CHANGE_DEFAULT_BROWSER, { browser: newBrowser });
|
||||
try {
|
||||
const browser = newBrowser || 'systemDefault:';
|
||||
await UserPreferences.setStringAsync(DEFAULT_BROWSER_KEY, browser);
|
||||
UserPreferences.setString(DEFAULT_BROWSER_KEY, browser);
|
||||
this.setState({ browser });
|
||||
} catch {
|
||||
logEvent(events.DB_CHANGE_DEFAULT_BROWSER_F);
|
||||
|
|
|
@ -17,7 +17,7 @@ import I18n from '../i18n';
|
|||
import RocketChat from '../lib/rocketchat';
|
||||
import { SettingsStackParamList } from '../stacks/types';
|
||||
import { useTheme } from '../theme';
|
||||
import log, { events, logEvent } from '../utils/log';
|
||||
import { events, logEvent } from '../utils/log';
|
||||
|
||||
interface IDisplayPrefsView {
|
||||
navigation: StackNavigationProp<SettingsStackParamList, 'DisplayPrefsView'>;
|
||||
|
@ -45,53 +45,49 @@ const DisplayPrefsView = (props: IDisplayPrefsView): JSX.Element => {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const setSortPreference = async (param: Partial<IPreferences>) => {
|
||||
try {
|
||||
dispatch(setPreference(param));
|
||||
await RocketChat.saveSortPreference(param);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
const setSortPreference = (param: Partial<IPreferences>) => {
|
||||
dispatch(setPreference(param));
|
||||
RocketChat.saveSortPreference(param);
|
||||
};
|
||||
|
||||
const sortByName = async () => {
|
||||
const sortByName = () => {
|
||||
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);
|
||||
await setSortPreference({ sortBy: SortBy.Activity });
|
||||
setSortPreference({ sortBy: SortBy.Activity });
|
||||
};
|
||||
|
||||
const toggleGroupByType = async () => {
|
||||
const toggleGroupByType = () => {
|
||||
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);
|
||||
await setSortPreference({ showFavorites: !showFavorites });
|
||||
setSortPreference({ showFavorites: !showFavorites });
|
||||
};
|
||||
|
||||
const toggleUnread = async () => {
|
||||
const toggleUnread = () => {
|
||||
logEvent(events.DP_GROUP_CHANNELS_BY_UNREAD);
|
||||
await setSortPreference({ showUnread: !showUnread });
|
||||
setSortPreference({ showUnread: !showUnread });
|
||||
};
|
||||
|
||||
const toggleAvatar = async () => {
|
||||
const toggleAvatar = () => {
|
||||
logEvent(events.DP_TOGGLE_AVATAR);
|
||||
await setSortPreference({ showAvatar: !showAvatar });
|
||||
setSortPreference({ showAvatar: !showAvatar });
|
||||
};
|
||||
|
||||
const displayExpanded = async () => {
|
||||
const displayExpanded = () => {
|
||||
logEvent(events.DP_DISPLAY_EXPANDED);
|
||||
await setSortPreference({ displayMode: DisplayMode.Expanded });
|
||||
setSortPreference({ displayMode: DisplayMode.Expanded });
|
||||
};
|
||||
|
||||
const displayCondensed = async () => {
|
||||
const displayCondensed = () => {
|
||||
logEvent(events.DP_DISPLAY_CONDENSED);
|
||||
await setSortPreference({ displayMode: DisplayMode.Condensed });
|
||||
setSortPreference({ displayMode: DisplayMode.Condensed });
|
||||
};
|
||||
|
||||
const renderCheckBox = (value: boolean) => (
|
||||
|
|
|
@ -81,11 +81,11 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
|
|||
this.mounted = true;
|
||||
}
|
||||
|
||||
init = async () => {
|
||||
init = () => {
|
||||
const { server } = this.props;
|
||||
try {
|
||||
// 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) {
|
||||
this.setState({ password: password! });
|
||||
} else {
|
||||
|
@ -97,11 +97,11 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
|
|||
}
|
||||
};
|
||||
|
||||
onSaved = async () => {
|
||||
onSaved = () => {
|
||||
logEvent(events.E2E_SAVE_PW_SAVED);
|
||||
const { navigation, server, dispatch } = this.props;
|
||||
// Remove stored password
|
||||
await UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
||||
UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
||||
// Hide encryption banner
|
||||
dispatch(encryptionSetBanner());
|
||||
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 }));
|
||||
};
|
||||
|
||||
submit = async ({ fromServerHistory = false, username }: ISubmitParams = {}) => {
|
||||
submit = ({ fromServerHistory = false, username }: ISubmitParams = {}) => {
|
||||
logEvent(events.NS_CONNECT_TO_WORKSPACE);
|
||||
const { text, certificate } = this.state;
|
||||
const { dispatch } = this.props;
|
||||
|
@ -193,10 +193,12 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
|
|||
const server = this.completeUrl(text);
|
||||
|
||||
// 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
|
||||
await this.basicAuth(server, text);
|
||||
this.basicAuth(server, text);
|
||||
|
||||
if (fromServerHistory) {
|
||||
dispatch(serverRequest(server, username, true));
|
||||
|
@ -213,12 +215,12 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
|
|||
dispatch(serverRequest('https://open.rocket.chat'));
|
||||
};
|
||||
|
||||
basicAuth = async (server: string, text: string) => {
|
||||
basicAuth = (server: string, text: string) => {
|
||||
try {
|
||||
const parsedUrl = parse(text, true);
|
||||
if (parsedUrl.auth.length) {
|
||||
const credentials = Base64.encode(parsedUrl.auth);
|
||||
await UserPreferences.setStringAsync(`${BASIC_AUTH_KEY}-${server}`, credentials);
|
||||
UserPreferences.setString(`${BASIC_AUTH_KEY}-${server}`, credentials);
|
||||
setBasicAuth(credentials);
|
||||
}
|
||||
} catch {
|
||||
|
|
|
@ -18,6 +18,7 @@ import log, { events, logEvent } from '../../utils/log';
|
|||
import sharedStyles from '../Styles';
|
||||
import { OPTIONS } from './options';
|
||||
import { ChatsStackParamList } from '../../stacks/types';
|
||||
import { IRoomNotifications } from '../../definitions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
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
|
||||
logEvent(events[`NP_${key.toUpperCase()}`]);
|
||||
const { room } = this.state;
|
||||
|
|
|
@ -31,7 +31,15 @@ import { withTheme } from '../../theme';
|
|||
import { getUserSelector } from '../../selectors/login';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
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> {
|
||||
private name: any;
|
||||
|
|
|
@ -34,7 +34,7 @@ import log, { events, logEvent } from '../../utils/log';
|
|||
import { MessageTypeValues } from '../../utils/messageTypes';
|
||||
import random from '../../utils/random';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import { IAvatar } from '../ProfileView/interfaces';
|
||||
import { IAvatar } from '../../definitions/IProfileViewInterfaces';
|
||||
import sharedStyles from '../Styles';
|
||||
import styles from './styles';
|
||||
import SwitchContainer from './SwitchContainer';
|
||||
|
|
|
@ -135,7 +135,7 @@ class ServerDropdown extends Component<IServerDropdownProps, IServerDropdownStat
|
|||
this.close();
|
||||
if (currentServer !== 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) {
|
||||
goRoom({ item: {}, isMasterDetail });
|
||||
}
|
||||
|
|
|
@ -339,7 +339,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
|||
this.getSubscriptions();
|
||||
}
|
||||
// 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
|
||||
// @ts-ignore
|
||||
this.setState({ item: { rid: rooms[0] } });
|
||||
|
|
|
@ -92,7 +92,7 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
|
|||
const { server } = this.props;
|
||||
const serversDB = database.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 {
|
||||
this.serverRecord = await serversCollection.find(server);
|
||||
this.setState({
|
||||
|
@ -147,9 +147,9 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
|
|||
logEvent(events.SLC_TOGGLE_BIOMETRY);
|
||||
this.setState(
|
||||
({ biometry }) => ({ biometry: !biometry }),
|
||||
async () => {
|
||||
() => {
|
||||
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);
|
||||
};
|
||||
|
||||
setTheme = async (theme: Partial<IThemePreference>) => {
|
||||
setTheme = (theme: Partial<IThemePreference>) => {
|
||||
const { setTheme, themePreferences } = this.props;
|
||||
const newTheme = { ...themePreferences, ...theme };
|
||||
setTheme(newTheme);
|
||||
await UserPreferences.setMapAsync(THEME_PREFERENCES_KEY, newTheme);
|
||||
UserPreferences.setMap(THEME_PREFERENCES_KEY, newTheme);
|
||||
};
|
||||
|
||||
renderIcon = () => {
|
||||
|
|
|
@ -15,6 +15,7 @@ import { getUserSelector } from '../../selectors/login';
|
|||
import sharedStyles from '../Styles';
|
||||
import { OPTIONS } from './options';
|
||||
import { ProfileStackParamList } from '../../stacks/types';
|
||||
import { INotificationPreferences } from '../../definitions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
pickerText: {
|
||||
|
@ -26,11 +27,7 @@ const styles = StyleSheet.create({
|
|||
type TKey = 'desktopNotifications' | 'pushNotifications' | 'emailNotificationMode';
|
||||
|
||||
interface IUserNotificationPreferencesViewState {
|
||||
preferences: {
|
||||
desktopNotifications?: string;
|
||||
pushNotifications?: string;
|
||||
emailNotificationMode?: string;
|
||||
};
|
||||
preferences: INotificationPreferences;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
|
@ -53,7 +50,7 @@ class UserNotificationPreferencesView extends React.Component<
|
|||
constructor(props: IUserNotificationPreferencesViewProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
preferences: {},
|
||||
preferences: {} as INotificationPreferences,
|
||||
loading: false
|
||||
};
|
||||
}
|
||||
|
@ -62,8 +59,10 @@ class UserNotificationPreferencesView extends React.Component<
|
|||
const { user } = this.props;
|
||||
const { id } = user;
|
||||
const result = await RocketChat.getUserPreferences(id);
|
||||
const { preferences } = result;
|
||||
this.setState({ preferences, loading: true });
|
||||
if (result.success) {
|
||||
const { preferences } = result;
|
||||
this.setState({ preferences, loading: true });
|
||||
}
|
||||
}
|
||||
|
||||
findDefaultOption = (key: TKey) => {
|
||||
|
|
|
@ -177,9 +177,9 @@ PODS:
|
|||
- libwebp/mux (1.1.0):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.1.0)
|
||||
- MMKV (1.2.1):
|
||||
- MMKVCore (~> 1.2.1)
|
||||
- MMKVCore (1.2.1)
|
||||
- MMKV (1.2.10):
|
||||
- MMKVCore (~> 1.2.10)
|
||||
- MMKVCore (1.2.12)
|
||||
- nanopb (1.30905.0):
|
||||
- nanopb/decode (= 1.30905.0)
|
||||
- nanopb/encode (= 1.30905.0)
|
||||
|
@ -410,9 +410,9 @@ PODS:
|
|||
- react-native-jitsi-meet (3.6.0):
|
||||
- JitsiMeetSDK (= 3.6.0)
|
||||
- React
|
||||
- react-native-mmkv-storage (0.3.5):
|
||||
- MMKV (= 1.2.1)
|
||||
- React
|
||||
- react-native-mmkv-storage (0.6.12):
|
||||
- MMKV (= 1.2.10)
|
||||
- React-Core
|
||||
- react-native-netinfo (6.0.0):
|
||||
- React-Core
|
||||
- react-native-notifications (2.1.7):
|
||||
|
@ -950,7 +950,7 @@ SPEC CHECKSUMS:
|
|||
EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7
|
||||
EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5
|
||||
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
|
||||
FBReactNativeSpec: 8a1012a62d7a3d667376b413656d780b8e4680ce
|
||||
FBReactNativeSpec: 110d69378fce79af38271c39894b59fec7890221
|
||||
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
||||
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
|
||||
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
|
||||
|
@ -974,8 +974,8 @@ SPEC CHECKSUMS:
|
|||
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
|
||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
|
||||
MMKV: 67253edee25a34edf332f91d73fa94a9e038b971
|
||||
MMKVCore: fe398984acac1fa33f92795d1b5fd0a334c944af
|
||||
MMKV: 76033b9ace2006623308910a3afcc0e25eba3140
|
||||
MMKVCore: df0565f6b58463604731a68ba6cd89bc0b2d1d55
|
||||
nanopb: c43f40fadfe79e8b8db116583945847910cbabc9
|
||||
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
|
||||
PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75
|
||||
|
@ -997,7 +997,7 @@ SPEC CHECKSUMS:
|
|||
react-native-cookies: 2cb6ef472da68610dfcf0eaee68464c244943abd
|
||||
react-native-document-picker: f1b5398801b332c77bc62ae0eae2116f49bdff26
|
||||
react-native-jitsi-meet: 3e3ac5d0445091154119f94342efd55c8b1124ce
|
||||
react-native-mmkv-storage: 48729fe90e850ef2fdc9d3714b7030c7c51d82b0
|
||||
react-native-mmkv-storage: 88bcb10bbe85a8122061d17d03abcc64a02fe1c9
|
||||
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
|
||||
react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227
|
||||
react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d
|
||||
|
|
|
@ -25,10 +25,8 @@ class Storage {
|
|||
|
||||
// get mmkv instance password from keychain
|
||||
var key: Data?
|
||||
secureStorage.getSecureKey(instanceID.toHex()) { (response) -> () in
|
||||
if let password = response?[1] as? String {
|
||||
key = password.data(using: .utf8)
|
||||
}
|
||||
if let password: String = secureStorage.getSecureKey(instanceID.toHex()) {
|
||||
key = password.data(using: .utf8)
|
||||
}
|
||||
|
||||
guard let cryptKey = key else {
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
"react-native-keycommands": "2.0.3",
|
||||
"react-native-localize": "2.1.1",
|
||||
"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-navigation-bar-color": "2.0.1",
|
||||
"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;
|
||||
}
|
||||
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
|
||||
+++ b/node_modules/react-native-webview/apple/RNCWebView.m
|
||||
@@ -17,6 +17,9 @@
|
||||
|
@ -115,7 +115,7 @@ index 02b4238..04bad05 100644
|
|||
static NSTimer *keyboardTimer;
|
||||
static NSString *const HistoryShimName = @"ReactNativeHistoryShim";
|
||||
static NSString *const MessageHandlerName = @"ReactNativeWebView";
|
||||
@@ -737,6 +740,68 @@ + (void)setCustomCertificatesForHost:(nullable NSDictionary*)certificates {
|
||||
@@ -737,6 +740,68 @@ static NSDictionary* customCertificatesForHost;
|
||||
customCertificatesForHost = certificates;
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ index 02b4238..04bad05 100644
|
|||
- (void) webView:(WKWebView *)webView
|
||||
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
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;
|
||||
}
|
||||
if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
|
||||
|
@ -196,17 +196,15 @@ index 02b4238..04bad05 100644
|
|||
+ SecureStorage *secureStorage = [[SecureStorage alloc] init];
|
||||
+
|
||||
+ // 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) {
|
||||
+ // 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];
|
||||
+ NSString *key = [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"]];
|
||||
+
|
||||
+ 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];
|
||||
+
|
||||
|
|
|
@ -23,7 +23,7 @@ index 602d51d..920d975 100644
|
|||
public String getName() {
|
||||
return "RNFetchBlob";
|
||||
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
|
||||
+++ b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
|
||||
@@ -15,6 +15,9 @@
|
||||
|
@ -36,12 +36,18 @@ index cdbe6b1..bee6228 100644
|
|||
|
||||
typedef NS_ENUM(NSUInteger, ResponseFormat) {
|
||||
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
|
||||
+{
|
||||
{
|
||||
- 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];
|
||||
+ SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
|
||||
+
|
||||
|
@ -75,7 +81,7 @@ index cdbe6b1..bee6228 100644
|
|||
+ NSDictionary* firstItem = nil;
|
||||
+ if ((status == errSecSuccess) && ([items count]>0)) {
|
||||
+ firstItem = items[0];
|
||||
+ }
|
||||
}
|
||||
+
|
||||
+ SecIdentityRef identity = (SecIdentityRef)CFBridgingRetain(firstItem[(id)kSecImportItemIdentity]);
|
||||
+ SecCertificateRef certificate = NULL;
|
||||
|
@ -89,17 +95,12 @@ index cdbe6b1..bee6228 100644
|
|||
+
|
||||
+ 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];
|
||||
+}
|
||||
+
|
||||
+- (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];
|
||||
+ NSMutableString *hex = [NSMutableString string];
|
||||
+ while (*utf8) [hex appendFormat:@"%02X", *utf8++ & 0x00FF];
|
||||
|
@ -116,17 +117,15 @@ index cdbe6b1..bee6228 100644
|
|||
+ SecureStorage *secureStorage = [[SecureStorage alloc] init];
|
||||
+
|
||||
+ // 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) {
|
||||
+ // 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];
|
||||
+ NSString *key = [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"]];
|
||||
+
|
||||
+ 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];
|
||||
+
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -14424,10 +14424,10 @@ react-native-mime-types@2.3.0:
|
|||
dependencies:
|
||||
mime-db "~1.37.0"
|
||||
|
||||
react-native-mmkv-storage@0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.3.5.tgz#9c2f064c0efdaf960e9646c68dc6ae7f11640fa5"
|
||||
integrity sha512-xp0E55Qdi81k8CTeq3PUrXGwT2tMmfNfmYvZ7Emq9qWpvg3ko1/M6B1kXEXOgEou/hgqB503TGcsR/mpN5HSMA==
|
||||
react-native-mmkv-storage@0.6.12:
|
||||
version "0.6.12"
|
||||
resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.6.12.tgz#e9e052e26c3dea6818211919d2586cb260ee8e24"
|
||||
integrity sha512-9gJlnGSAJkeWanNE24GPqEn4NMiNFVBpPsTZAQ5YDEKEX/gG7bGdHCTNkXc6L826QxLEPKjpPmf9ZqgPM2G8TQ==
|
||||
|
||||
react-native-modal@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:
|
||||
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:
|
||||
"@jest/create-cache-key-function" "^27.0.2"
|
||||
"@react-native-community/cli" "^5.0.1-alpha.1"
|
||||
|
|
Loading…
Reference in New Issue