Chore: Migrate Utils Folder to Typescript (#3544)
Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com>
This commit is contained in:
parent
f7418791a2
commit
a2b92f5e70
|
@ -1,7 +1,8 @@
|
|||
import React from 'react';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
|
||||
import { isAndroid } from '../../utils/deviceInfo';
|
||||
import Touch from '../../utils/touch';
|
||||
|
||||
// Taken from https://github.com/rgommezz/react-native-scroll-bottom-sheet#touchables
|
||||
export const Button = isAndroid ? Touch : TouchableOpacity;
|
||||
export const Button: typeof React.Component = isAndroid ? Touch : TouchableOpacity;
|
||||
|
|
|
@ -5,6 +5,7 @@ import Touchable from 'react-native-platform-touchable';
|
|||
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
||||
|
||||
import { avatarURL } from '../../utils/avatar';
|
||||
import { SubscriptionType } from '../../definitions/ISubscription';
|
||||
import Emoji from '../markdown/Emoji';
|
||||
import { IAvatar } from './interfaces';
|
||||
|
||||
|
@ -27,8 +28,8 @@ const Avatar = React.memo(
|
|||
text,
|
||||
size = 25,
|
||||
borderRadius = 4,
|
||||
type = 'd'
|
||||
}: Partial<IAvatar>) => {
|
||||
type = SubscriptionType.DIRECT
|
||||
}: IAvatar) => {
|
||||
if ((!text && !avatar && !emoji && !rid) || !server) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -7,17 +7,17 @@ import { getUserSelector } from '../../selectors/login';
|
|||
import Avatar from './Avatar';
|
||||
import { IAvatar } from './interfaces';
|
||||
|
||||
class AvatarContainer extends React.Component<Partial<IAvatar>, any> {
|
||||
class AvatarContainer extends React.Component<IAvatar, any> {
|
||||
private mounted: boolean;
|
||||
|
||||
private subscription!: any;
|
||||
private subscription: any;
|
||||
|
||||
static defaultProps = {
|
||||
text: '',
|
||||
type: 'd'
|
||||
};
|
||||
|
||||
constructor(props: Partial<IAvatar>) {
|
||||
constructor(props: IAvatar) {
|
||||
super(props);
|
||||
this.mounted = false;
|
||||
this.state = { avatarETag: '' };
|
||||
|
@ -55,7 +55,7 @@ class AvatarContainer extends React.Component<Partial<IAvatar>, any> {
|
|||
try {
|
||||
if (this.isDirect) {
|
||||
const { text } = this.props;
|
||||
const [user] = await usersCollection.query(Q.where('username', text!)).fetch();
|
||||
const [user] = await usersCollection.query(Q.where('username', text)).fetch();
|
||||
record = user;
|
||||
} else {
|
||||
const { rid } = this.props;
|
||||
|
@ -82,7 +82,7 @@ class AvatarContainer extends React.Component<Partial<IAvatar>, any> {
|
|||
render() {
|
||||
const { avatarETag } = this.state;
|
||||
const { serverVersion } = this.props;
|
||||
return <Avatar avatarETag={avatarETag} serverVersion={serverVersion} {...this.props} />;
|
||||
return <Avatar {...this.props} avatarETag={avatarETag} serverVersion={serverVersion} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
export interface IAvatar {
|
||||
server: string;
|
||||
style: any;
|
||||
server?: string;
|
||||
style?: any;
|
||||
text: string;
|
||||
avatar: string;
|
||||
emoji: string;
|
||||
size: number;
|
||||
borderRadius: number;
|
||||
type: string;
|
||||
children: JSX.Element;
|
||||
user: {
|
||||
id: string;
|
||||
token: string;
|
||||
avatar?: string;
|
||||
emoji?: string;
|
||||
size?: number;
|
||||
borderRadius?: number;
|
||||
type?: string;
|
||||
children?: JSX.Element;
|
||||
user?: {
|
||||
id?: string;
|
||||
token?: string;
|
||||
};
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
getCustomEmoji(): any;
|
||||
avatarETag: string;
|
||||
isStatic: boolean | string;
|
||||
rid: string;
|
||||
blockUnauthenticatedAccess: boolean;
|
||||
serverVersion: string;
|
||||
theme?: string;
|
||||
onPress?: () => void;
|
||||
getCustomEmoji?: () => any;
|
||||
avatarETag?: string;
|
||||
isStatic?: boolean | string;
|
||||
rid?: string;
|
||||
blockUnauthenticatedAccess?: boolean;
|
||||
serverVersion?: string;
|
||||
}
|
||||
|
|
|
@ -305,8 +305,6 @@ const MessageActions = React.memo(
|
|||
};
|
||||
|
||||
const handleDelete = (message: any) => {
|
||||
// TODO - migrate this function for ts when fix the lint erros
|
||||
// @ts-ignore
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
|
||||
confirmationText: I18n.t('Delete'),
|
||||
|
|
|
@ -7,28 +7,28 @@ import Touch from '../../../utils/touch';
|
|||
import { CustomIcon } from '../../../lib/Icons';
|
||||
|
||||
interface IPasscodeButton {
|
||||
text: string;
|
||||
icon: string;
|
||||
text?: string;
|
||||
icon?: string;
|
||||
theme: string;
|
||||
disabled: boolean;
|
||||
onPress: Function;
|
||||
disabled?: boolean;
|
||||
onPress?: Function;
|
||||
}
|
||||
|
||||
const Button = React.memo(({ text, disabled, theme, onPress, icon }: Partial<IPasscodeButton>) => {
|
||||
const press = () => onPress && onPress(text!);
|
||||
const Button = React.memo(({ text, disabled, theme, onPress, icon }: IPasscodeButton) => {
|
||||
const press = () => onPress && onPress(text);
|
||||
|
||||
return (
|
||||
<Touch
|
||||
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
|
||||
underlayColor={themes[theme!].passcodeButtonActive}
|
||||
rippleColor={themes[theme!].passcodeButtonActive}
|
||||
underlayColor={themes[theme].passcodeButtonActive}
|
||||
rippleColor={themes[theme].passcodeButtonActive}
|
||||
enabled={!disabled}
|
||||
theme={theme}
|
||||
onPress={press}>
|
||||
{icon ? (
|
||||
<CustomIcon name={icon} size={36} color={themes[theme!].passcodePrimary} />
|
||||
<CustomIcon name={icon} size={36} color={themes[theme].passcodePrimary} />
|
||||
) : (
|
||||
<Text style={[styles.buttonText, { color: themes[theme!].passcodePrimary }]}>{text}</Text>
|
||||
<Text style={[styles.buttonText, { color: themes[theme].passcodePrimary }]}>{text}</Text>
|
||||
)}
|
||||
</Touch>
|
||||
);
|
||||
|
|
|
@ -84,7 +84,7 @@ export interface IMessageContent {
|
|||
export interface IMessageDiscussion {
|
||||
msg: string;
|
||||
dcount: number;
|
||||
dlm: string;
|
||||
dlm: Date;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export interface ICommand {
|
||||
event: {
|
||||
input: string;
|
||||
modifierFlags: number;
|
||||
};
|
||||
}
|
|
@ -10,8 +10,8 @@ export interface IServer {
|
|||
version: string;
|
||||
lastLocalAuthenticatedSession: Date;
|
||||
autoLock: boolean;
|
||||
autoLockTime: number | null;
|
||||
biometry: boolean | null;
|
||||
autoLockTime?: number;
|
||||
biometry?: boolean;
|
||||
uniqueID: string;
|
||||
enterpriseModules: string;
|
||||
E2E_Enable: boolean;
|
||||
|
|
|
@ -79,6 +79,8 @@ export interface ISubscription {
|
|||
avatarETag?: string;
|
||||
teamId?: string;
|
||||
teamMain?: boolean;
|
||||
search?: boolean;
|
||||
username?: string;
|
||||
// https://nozbe.github.io/WatermelonDB/Relation.html#relation-api
|
||||
messages: Relation<TMessageModel>;
|
||||
threads: Relation<TThreadModel>;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export type TThemeMode = 'automatic' | 'light' | 'dark';
|
||||
|
||||
export type TDarkLevel = 'black' | 'dark';
|
||||
|
||||
export interface IThemePreference {
|
||||
currentTheme: TThemeMode;
|
||||
darkLevel: TDarkLevel;
|
||||
}
|
|
@ -13,3 +13,4 @@ declare module 'react-native-mime-types';
|
|||
declare module 'react-native-restart';
|
||||
declare module 'react-native-prompt-android';
|
||||
declare module 'react-native-jitsi-meet';
|
||||
declare module 'rn-root-view';
|
||||
|
|
|
@ -30,6 +30,8 @@ import InAppNotification from './containers/InAppNotification';
|
|||
import { ActionSheetProvider } from './containers/ActionSheet';
|
||||
import debounce from './utils/debounce';
|
||||
import { isFDroidBuild } from './constants/environment';
|
||||
import { IThemePreference } from './definitions/ITheme';
|
||||
import { ICommand } from './definitions/ICommand';
|
||||
|
||||
RNScreens.enableScreens();
|
||||
|
||||
|
@ -42,10 +44,7 @@ interface IDimensions {
|
|||
|
||||
interface IState {
|
||||
theme: string;
|
||||
themePreferences: {
|
||||
currentTheme: 'automatic' | 'light';
|
||||
darkLevel: string;
|
||||
};
|
||||
themePreferences: IThemePreference;
|
||||
width: number;
|
||||
height: number;
|
||||
scale: number;
|
||||
|
@ -175,7 +174,7 @@ export default class Root extends React.Component<{}, IState> {
|
|||
setTheme = (newTheme = {}) => {
|
||||
// change theme state
|
||||
this.setState(
|
||||
prevState => newThemeState(prevState, newTheme),
|
||||
prevState => newThemeState(prevState, newTheme as IThemePreference),
|
||||
() => {
|
||||
const { themePreferences } = this.state;
|
||||
// subscribe to Appearance changes
|
||||
|
@ -191,7 +190,7 @@ export default class Root extends React.Component<{}, IState> {
|
|||
initTablet = () => {
|
||||
const { width } = this.state;
|
||||
this.setMasterDetail(width);
|
||||
this.onKeyCommands = KeyCommandsEmitter.addListener('onKeyCommand', (command: unknown) => {
|
||||
this.onKeyCommands = KeyCommandsEmitter.addListener('onKeyCommand', (command: ICommand) => {
|
||||
EventEmitter.emit(KEY_COMMAND, { event: command });
|
||||
});
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ import { defaultHeader, getActiveRouteName, navigationTheme, themedHeader } from
|
|||
import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat';
|
||||
import { ThemeContext } from './theme';
|
||||
import { localAuthenticate } from './utils/localAuthentication';
|
||||
import { IThemePreference } from './definitions/ITheme';
|
||||
import ScreenLockedView from './views/ScreenLockedView';
|
||||
// Outside Stack
|
||||
import WithoutServersView from './views/WithoutServersView';
|
||||
|
@ -36,10 +37,7 @@ interface IDimensions {
|
|||
|
||||
interface IState {
|
||||
theme: string;
|
||||
themePreferences: {
|
||||
currentTheme: 'automatic' | 'light';
|
||||
darkLevel: string;
|
||||
};
|
||||
themePreferences: IThemePreference;
|
||||
root: any;
|
||||
width: number;
|
||||
height: number;
|
||||
|
@ -135,7 +133,7 @@ class Root extends React.Component<{}, IState> {
|
|||
setTheme = (newTheme = {}) => {
|
||||
// change theme state
|
||||
this.setState(
|
||||
prevState => newThemeState(prevState, newTheme),
|
||||
prevState => newThemeState(prevState, newTheme as IThemePreference),
|
||||
() => {
|
||||
const { themePreferences } = this.state;
|
||||
// subscribe to Appearance changes
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import React from 'react';
|
||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
||||
|
||||
import { IThemePreference } from './definitions/ITheme';
|
||||
|
||||
interface IThemeContextProps {
|
||||
theme: string;
|
||||
themePreferences?: {
|
||||
currentTheme: 'automatic' | 'light';
|
||||
darkLevel: string;
|
||||
};
|
||||
themePreferences?: IThemePreference;
|
||||
setTheme?: (newTheme?: {}) => void;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { isIOS } from './deviceInfo';
|
|||
|
||||
const { AppGroup } = NativeModules;
|
||||
|
||||
const appGroup = {
|
||||
const appGroup: { path: string } = {
|
||||
path: isIOS ? AppGroup.path : ''
|
||||
};
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import { compareServerVersion, methods } from '../lib/utils';
|
||||
import { SubscriptionType } from '../definitions/ISubscription';
|
||||
import { IAvatar } from '../containers/Avatar/interfaces';
|
||||
|
||||
const formatUrl = (url, size, query) => `${url}?format=png&size=${size}${query}`;
|
||||
const formatUrl = (url: string, size: number, query: string) => `${url}?format=png&size=${size}${query}`;
|
||||
|
||||
export const avatarURL = ({
|
||||
type,
|
||||
|
@ -13,9 +15,9 @@ export const avatarURL = ({
|
|||
rid,
|
||||
blockUnauthenticatedAccess,
|
||||
serverVersion
|
||||
}) => {
|
||||
}: IAvatar): string => {
|
||||
let room;
|
||||
if (type === 'd') {
|
||||
if (type === SubscriptionType.DIRECT) {
|
||||
room = text;
|
||||
} else if (rid && !compareServerVersion(serverVersion, '3.6.0', methods.lowerThan)) {
|
||||
room = `room/${rid}`;
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-disable no-bitwise */
|
||||
// https://github.com/beatgammit/base64-js/blob/master/index.js
|
||||
|
||||
const lookup = [];
|
||||
const revLookup = [];
|
||||
const lookup: string[] = [];
|
||||
const revLookup: number[] = [];
|
||||
const Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
|
||||
|
||||
const code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
|
@ -16,7 +16,7 @@ for (let i = 0, len = code.length; i < len; i += 1) {
|
|||
revLookup['-'.charCodeAt(0)] = 62;
|
||||
revLookup['_'.charCodeAt(0)] = 63;
|
||||
|
||||
const getLens = b64 => {
|
||||
const getLens = (b64: string) => {
|
||||
const len = b64.length;
|
||||
|
||||
// We're encoding some strings not multiple of 4, so, disable this check
|
||||
|
@ -37,16 +37,17 @@ const getLens = b64 => {
|
|||
};
|
||||
|
||||
// base64 is 4/3 + up to two characters of the original data
|
||||
export const byteLength = b64 => {
|
||||
export const byteLength = (b64: string) => {
|
||||
const lens = getLens(b64);
|
||||
const validLen = lens[0];
|
||||
const placeHoldersLen = lens[1];
|
||||
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
||||
};
|
||||
|
||||
const _byteLength = (b64, validLen, placeHoldersLen) => ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
||||
const _byteLength = (b64: string, validLen: number, placeHoldersLen: number) =>
|
||||
((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
||||
|
||||
export const toByteArray = b64 => {
|
||||
export const toByteArray = (b64: string) => {
|
||||
let tmp;
|
||||
const lens = getLens(b64);
|
||||
const validLen = lens[0];
|
||||
|
@ -92,10 +93,10 @@ export const toByteArray = b64 => {
|
|||
return arr;
|
||||
};
|
||||
|
||||
const tripletToBase64 = num =>
|
||||
const tripletToBase64 = (num: number) =>
|
||||
lookup[(num >> 18) & 0x3f] + lookup[(num >> 12) & 0x3f] + lookup[(num >> 6) & 0x3f] + lookup[num & 0x3f];
|
||||
|
||||
const encodeChunk = (uint8, start, end) => {
|
||||
const encodeChunk = (uint8: number[] | Uint8Array, start: number, end: number) => {
|
||||
let tmp;
|
||||
const output = [];
|
||||
for (let i = start; i < end; i += 3) {
|
||||
|
@ -105,7 +106,7 @@ const encodeChunk = (uint8, start, end) => {
|
|||
return output.join('');
|
||||
};
|
||||
|
||||
export const fromByteArray = uint8 => {
|
||||
export const fromByteArray = (uint8: number[] | Uint8Array) => {
|
||||
let tmp;
|
||||
const len = uint8.length;
|
||||
const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
|
|
@ -1,20 +0,0 @@
|
|||
export default function debounce(func, wait, immediate) {
|
||||
let timeout;
|
||||
function _debounce(...args) {
|
||||
const context = this;
|
||||
const later = function __debounce() {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
func.apply(context, args);
|
||||
}
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
func.apply(context, args);
|
||||
}
|
||||
}
|
||||
_debounce.stop = () => clearTimeout(timeout);
|
||||
return _debounce;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
export default function debounce(func: Function, wait?: number, immediate?: boolean) {
|
||||
let timeout: number | null;
|
||||
function _debounce(...args: any[]) {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const context = this;
|
||||
const later = function __debounce() {
|
||||
timeout = null;
|
||||
if (!immediate) {
|
||||
func.apply(context, args);
|
||||
}
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout!);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
func.apply(context, args);
|
||||
}
|
||||
}
|
||||
_debounce.stop = () => clearTimeout(timeout!);
|
||||
return _debounce;
|
||||
}
|
|
@ -9,7 +9,7 @@ export const getBundleId = DeviceInfo.getBundleId();
|
|||
export const getDeviceModel = DeviceInfo.getModel();
|
||||
|
||||
// Theme is supported by system on iOS 13+ or Android 10+
|
||||
export const supportSystemTheme = () => {
|
||||
export const supportSystemTheme = (): boolean => {
|
||||
const systemVersion = parseInt(DeviceInfo.getSystemVersion(), 10);
|
||||
return systemVersion >= (isIOS ? 13 : 10);
|
||||
};
|
|
@ -1,11 +1,25 @@
|
|||
import { ICommand } from '../definitions/ICommand';
|
||||
import log from './log';
|
||||
|
||||
type TEventEmitterEmmitArgs =
|
||||
| { rid: string }
|
||||
| { message: string }
|
||||
| { method: string }
|
||||
| { invalid: boolean }
|
||||
| { force: boolean }
|
||||
| { hasBiometry: boolean }
|
||||
| { event: string | ICommand }
|
||||
| { cancel: () => void }
|
||||
| { submit: (param: string) => void };
|
||||
|
||||
class EventEmitter {
|
||||
private events: { [key: string]: any };
|
||||
|
||||
constructor() {
|
||||
this.events = {};
|
||||
}
|
||||
|
||||
addEventListener(event, listener) {
|
||||
addEventListener(event: string, listener: Function) {
|
||||
if (typeof this.events[event] !== 'object') {
|
||||
this.events[event] = [];
|
||||
}
|
||||
|
@ -13,7 +27,7 @@ class EventEmitter {
|
|||
return listener;
|
||||
}
|
||||
|
||||
removeListener(event, listener) {
|
||||
removeListener(event: string, listener: Function) {
|
||||
if (typeof this.events[event] === 'object') {
|
||||
const idx = this.events[event].indexOf(listener);
|
||||
if (idx > -1) {
|
||||
|
@ -25,9 +39,9 @@ class EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
emit(event, ...args) {
|
||||
emit(event: string, ...args: TEventEmitterEmmitArgs[]) {
|
||||
if (typeof this.events[event] === 'object') {
|
||||
this.events[event].forEach(listener => {
|
||||
this.events[event].forEach((listener: Function) => {
|
||||
try {
|
||||
listener.apply(this, args);
|
||||
} catch (e) {
|
|
@ -4,15 +4,20 @@ import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
|||
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
||||
interface CustomHeaders {
|
||||
'User-Agent': string;
|
||||
Authorization?: string;
|
||||
}
|
||||
|
||||
// this form is required by Rocket.Chat's parser in "app/statistics/server/lib/UAParserCustom.js"
|
||||
export const headers = {
|
||||
export const headers: CustomHeaders = {
|
||||
'User-Agent': `RC Mobile; ${
|
||||
Platform.OS
|
||||
} ${DeviceInfo.getSystemVersion()}; v${DeviceInfo.getVersion()} (${DeviceInfo.getBuildNumber()})`
|
||||
};
|
||||
|
||||
let _basicAuth;
|
||||
export const setBasicAuth = basicAuth => {
|
||||
export const setBasicAuth = (basicAuth: string): void => {
|
||||
_basicAuth = basicAuth;
|
||||
if (basicAuth) {
|
||||
RocketChatSettings.customHeaders = { ...headers, Authorization: `Basic ${_basicAuth}` };
|
||||
|
@ -24,12 +29,15 @@ export const BASIC_AUTH_KEY = 'BASIC_AUTH_KEY';
|
|||
|
||||
RocketChatSettings.customHeaders = headers;
|
||||
|
||||
export default (url, options = {}) => {
|
||||
export default (url: string, options: { headers?: Headers; signal?: AbortSignal } = {}): Promise<Response> => {
|
||||
let customOptions = { ...options, headers: RocketChatSettings.customHeaders };
|
||||
if (options && options.headers) {
|
||||
customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } };
|
||||
}
|
||||
// TODO: Refactor when migrate rocketchat.js
|
||||
// @ts-ignore
|
||||
if (RocketChat.controller) {
|
||||
// @ts-ignore
|
||||
const { signal } = RocketChat.controller;
|
||||
customOptions = { ...customOptions, signal };
|
||||
}
|
|
@ -1,19 +1,25 @@
|
|||
import { IFileUpload } from './interfaces';
|
||||
|
||||
class Upload {
|
||||
public xhr: XMLHttpRequest;
|
||||
|
||||
public formData: FormData;
|
||||
|
||||
constructor() {
|
||||
this.xhr = new XMLHttpRequest();
|
||||
this.formData = new FormData();
|
||||
}
|
||||
|
||||
then = callback => {
|
||||
then = (callback: (param: { respInfo: XMLHttpRequest }) => XMLHttpRequest) => {
|
||||
this.xhr.onload = () => callback({ respInfo: this.xhr });
|
||||
this.xhr.send(this.formData);
|
||||
};
|
||||
|
||||
catch = callback => {
|
||||
catch = (callback: ((this: XMLHttpRequest, ev: ProgressEvent<EventTarget>) => any) | null) => {
|
||||
this.xhr.onerror = callback;
|
||||
};
|
||||
|
||||
uploadProgress = callback => {
|
||||
uploadProgress = (callback: (param: number, arg1: number) => any) => {
|
||||
this.xhr.upload.onprogress = ({ total, loaded }) => callback(loaded, total);
|
||||
};
|
||||
|
||||
|
@ -24,7 +30,7 @@ class Upload {
|
|||
}
|
||||
|
||||
class FileUpload {
|
||||
fetch = (method, url, headers, data) => {
|
||||
fetch = (method: string, url: string, headers: { [x: string]: string }, data: IFileUpload[]) => {
|
||||
const upload = new Upload();
|
||||
upload.xhr.open(method, url);
|
||||
|
||||
|
@ -35,6 +41,7 @@ class FileUpload {
|
|||
data.forEach(item => {
|
||||
if (item.uri) {
|
||||
upload.formData.append(item.name, {
|
||||
// @ts-ignore
|
||||
uri: item.uri,
|
||||
type: item.type,
|
||||
name: item.filename
|
|
@ -1,7 +1,11 @@
|
|||
import RNFetchBlob from 'rn-fetch-blob';
|
||||
|
||||
import { IFileUpload } from './interfaces';
|
||||
|
||||
type TMethods = 'POST' | 'GET' | 'DELETE' | 'PUT' | 'post' | 'get' | 'delete' | 'put';
|
||||
|
||||
class FileUpload {
|
||||
fetch = (method, url, headers, data) => {
|
||||
fetch = (method: TMethods, url: string, headers: { [key: string]: string }, data: IFileUpload[]) => {
|
||||
const formData = data.map(item => {
|
||||
if (item.uri) {
|
||||
return {
|
|
@ -0,0 +1,7 @@
|
|||
export interface IFileUpload {
|
||||
name: string;
|
||||
uri?: string;
|
||||
type: string;
|
||||
filename: string;
|
||||
data: any;
|
||||
}
|
|
@ -1,7 +1,17 @@
|
|||
import { ChatsStackParamList } from '../stacks/types';
|
||||
import Navigation from '../lib/Navigation';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { ISubscription, SubscriptionType } from '../definitions/ISubscription';
|
||||
|
||||
const navigate = ({ item, isMasterDetail, ...props }) => {
|
||||
const navigate = ({
|
||||
item,
|
||||
isMasterDetail,
|
||||
...props
|
||||
}: {
|
||||
item: IItem;
|
||||
isMasterDetail: boolean;
|
||||
navigationMethod?: () => ChatsStackParamList;
|
||||
}) => {
|
||||
let navigationMethod = props.navigationMethod ?? Navigation.navigate;
|
||||
|
||||
if (isMasterDetail) {
|
||||
|
@ -20,7 +30,22 @@ const navigate = ({ item, isMasterDetail, ...props }) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const goRoom = async ({ item = {}, isMasterDetail = false, ...props }) => {
|
||||
interface IItem extends Partial<ISubscription> {
|
||||
rid: string;
|
||||
name: string;
|
||||
t: SubscriptionType;
|
||||
}
|
||||
|
||||
export const goRoom = async ({
|
||||
item,
|
||||
isMasterDetail = false,
|
||||
...props
|
||||
}: {
|
||||
item: IItem;
|
||||
isMasterDetail: boolean;
|
||||
navigationMethod?: any;
|
||||
jumpToMessageId?: string;
|
||||
}): Promise<void> => {
|
||||
if (item.t === 'd' && item.search) {
|
||||
// if user is using the search we need first to join/create room
|
||||
try {
|
||||
|
@ -30,8 +55,8 @@ export const goRoom = async ({ item = {}, isMasterDetail = false, ...props }) =>
|
|||
return navigate({
|
||||
item: {
|
||||
rid: result.room._id,
|
||||
name: username,
|
||||
t: 'd'
|
||||
name: username!,
|
||||
t: SubscriptionType.DIRECT
|
||||
},
|
||||
isMasterDetail,
|
||||
...props
|
|
@ -1,25 +0,0 @@
|
|||
import { Alert } from 'react-native';
|
||||
|
||||
import I18n from '../i18n';
|
||||
|
||||
export const showErrorAlert = (message, title, onPress = () => {}) =>
|
||||
Alert.alert(title, message, [{ text: 'OK', onPress }], { cancelable: true });
|
||||
|
||||
export const showConfirmationAlert = ({ title, message, confirmationText, dismissText = I18n.t('Cancel'), onPress, onCancel }) =>
|
||||
Alert.alert(
|
||||
title || I18n.t('Are_you_sure_question_mark'),
|
||||
message,
|
||||
[
|
||||
{
|
||||
text: dismissText,
|
||||
onPress: onCancel,
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: confirmationText,
|
||||
style: 'destructive',
|
||||
onPress
|
||||
}
|
||||
],
|
||||
{ cancelable: false }
|
||||
);
|
|
@ -0,0 +1,41 @@
|
|||
import { Alert } from 'react-native';
|
||||
|
||||
import I18n from '../i18n';
|
||||
|
||||
export const showErrorAlert = (message: string, title?: string, onPress = () => {}): void =>
|
||||
Alert.alert(title!, message, [{ text: 'OK', onPress }], { cancelable: true });
|
||||
|
||||
interface IShowConfirmationAlert {
|
||||
title?: string;
|
||||
message: string;
|
||||
confirmationText: string;
|
||||
dismissText?: string;
|
||||
onPress: () => void;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
export const showConfirmationAlert = ({
|
||||
title,
|
||||
message,
|
||||
confirmationText,
|
||||
dismissText = I18n.t('Cancel'),
|
||||
onPress,
|
||||
onCancel
|
||||
}: IShowConfirmationAlert): void =>
|
||||
Alert.alert(
|
||||
title || I18n.t('Are_you_sure_question_mark'),
|
||||
message,
|
||||
[
|
||||
{
|
||||
text: dismissText,
|
||||
onPress: onCancel,
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: confirmationText,
|
||||
style: 'destructive',
|
||||
onPress
|
||||
}
|
||||
],
|
||||
{ cancelable: false }
|
||||
);
|
|
@ -1,16 +1,21 @@
|
|||
import RocketChat from '../lib/rocketchat';
|
||||
import reduxStore from '../lib/createStore';
|
||||
import { ISubscription } from '../definitions/ISubscription';
|
||||
|
||||
const canPostReadOnly = async ({ rid }) => {
|
||||
const canPostReadOnly = async ({ rid }: { rid: string }) => {
|
||||
// TODO: this is not reactive. If this permission changes, the component won't be updated
|
||||
const postReadOnlyPermission = reduxStore.getState().permissions['post-readonly'];
|
||||
const permission = await RocketChat.hasPermission([postReadOnlyPermission], rid);
|
||||
return permission[0];
|
||||
};
|
||||
|
||||
const isMuted = (room, user) => room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username);
|
||||
const isMuted = (room: ISubscription, user: { username: string }) =>
|
||||
room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username);
|
||||
|
||||
export const isReadOnly = async (room, user) => {
|
||||
export const isReadOnly = async (
|
||||
room: ISubscription,
|
||||
user: { id?: string; username: string; token?: string }
|
||||
): Promise<boolean> => {
|
||||
if (room.archived) {
|
||||
return true;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
export default function isValidEmail(email) {
|
||||
export default function isValidEmail(email: string): boolean {
|
||||
/* eslint-disable no-useless-escape */
|
||||
const reg =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
@ -16,16 +16,17 @@ import {
|
|||
} from '../constants/localAuthentication';
|
||||
import I18n from '../i18n';
|
||||
import { setLocalAuthenticated } from '../actions/login';
|
||||
import { TServerModel } from '../definitions/IServer';
|
||||
import EventEmitter from './events';
|
||||
import { isIOS } from './deviceInfo';
|
||||
|
||||
export const saveLastLocalAuthenticationSession = async (server, serverRecord) => {
|
||||
export const saveLastLocalAuthenticationSession = async (server: string, serverRecord?: TServerModel): Promise<void> => {
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.get('servers');
|
||||
await serversDB.action(async () => {
|
||||
await serversDB.write(async () => {
|
||||
try {
|
||||
if (!serverRecord) {
|
||||
serverRecord = await serversCollection.find(server);
|
||||
serverRecord = (await serversCollection.find(server)) as TServerModel;
|
||||
}
|
||||
await serverRecord.update(record => {
|
||||
record.lastLocalAuthenticatedSession = new Date();
|
||||
|
@ -36,31 +37,31 @@ export const saveLastLocalAuthenticationSession = async (server, serverRecord) =
|
|||
});
|
||||
};
|
||||
|
||||
export const resetAttempts = () => AsyncStorage.multiRemove([LOCKED_OUT_TIMER_KEY, ATTEMPTS_KEY]);
|
||||
export const resetAttempts = (): Promise<void> => AsyncStorage.multiRemove([LOCKED_OUT_TIMER_KEY, ATTEMPTS_KEY]);
|
||||
|
||||
const openModal = hasBiometry =>
|
||||
new Promise(resolve => {
|
||||
const openModal = (hasBiometry: boolean) =>
|
||||
new Promise<void>(resolve => {
|
||||
EventEmitter.emit(LOCAL_AUTHENTICATE_EMITTER, {
|
||||
submit: () => resolve(),
|
||||
hasBiometry
|
||||
});
|
||||
});
|
||||
|
||||
const openChangePasscodeModal = ({ force }) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const openChangePasscodeModal = ({ force }: { force: boolean }) =>
|
||||
new Promise<string>((resolve, reject) => {
|
||||
EventEmitter.emit(CHANGE_PASSCODE_EMITTER, {
|
||||
submit: passcode => resolve(passcode),
|
||||
submit: (passcode: string) => resolve(passcode),
|
||||
cancel: () => reject(),
|
||||
force
|
||||
});
|
||||
});
|
||||
|
||||
export const changePasscode = async ({ force = false }) => {
|
||||
export const changePasscode = async ({ force = false }: { force: boolean }): Promise<void> => {
|
||||
const passcode = await openChangePasscodeModal({ force });
|
||||
await UserPreferences.setStringAsync(PASSCODE_KEY, sha256(passcode));
|
||||
};
|
||||
|
||||
export const biometryAuth = force =>
|
||||
export const biometryAuth = (force?: boolean): Promise<LocalAuthentication.LocalAuthenticationResult> =>
|
||||
LocalAuthentication.authenticateAsync({
|
||||
disableDeviceFallback: true,
|
||||
cancelLabel: force ? I18n.t('Dont_activate') : I18n.t('Local_authentication_biometry_fallback'),
|
||||
|
@ -71,11 +72,11 @@ export const biometryAuth = force =>
|
|||
* It'll help us to get the permission to use FaceID
|
||||
* and enable/disable the biometry when user put their first passcode
|
||||
*/
|
||||
const checkBiometry = async serverRecord => {
|
||||
const checkBiometry = async (serverRecord: TServerModel) => {
|
||||
const serversDB = database.servers;
|
||||
|
||||
const result = await biometryAuth(true);
|
||||
await serversDB.action(async () => {
|
||||
await serversDB.write(async () => {
|
||||
try {
|
||||
await serverRecord.update(record => {
|
||||
record.biometry = !!result?.success;
|
||||
|
@ -86,7 +87,13 @@ const checkBiometry = async serverRecord => {
|
|||
});
|
||||
};
|
||||
|
||||
export const checkHasPasscode = async ({ force = true, serverRecord }) => {
|
||||
export const checkHasPasscode = async ({
|
||||
force = true,
|
||||
serverRecord
|
||||
}: {
|
||||
force?: boolean;
|
||||
serverRecord: TServerModel;
|
||||
}): Promise<{ newPasscode?: boolean } | void> => {
|
||||
const storedPasscode = await UserPreferences.getStringAsync(PASSCODE_KEY);
|
||||
if (!storedPasscode) {
|
||||
await changePasscode({ force });
|
||||
|
@ -96,13 +103,13 @@ export const checkHasPasscode = async ({ force = true, serverRecord }) => {
|
|||
return Promise.resolve();
|
||||
};
|
||||
|
||||
export const localAuthenticate = async server => {
|
||||
export const localAuthenticate = async (server: string): Promise<void> => {
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.get('servers');
|
||||
|
||||
let serverRecord;
|
||||
let serverRecord: TServerModel;
|
||||
try {
|
||||
serverRecord = await serversCollection.find(server);
|
||||
serverRecord = (await serversCollection.find(server)) as TServerModel;
|
||||
} catch (error) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
@ -125,7 +132,7 @@ export const localAuthenticate = async server => {
|
|||
const diffToLastSession = moment().diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds');
|
||||
|
||||
// if last authenticated session is older than configured auto lock time, authentication is required
|
||||
if (diffToLastSession >= serverRecord?.autoLockTime) {
|
||||
if (diffToLastSession >= serverRecord.autoLockTime!) {
|
||||
// set isLocalAuthenticated to false
|
||||
store.dispatch(setLocalAuthenticated(false));
|
||||
|
||||
|
@ -150,7 +157,7 @@ export const localAuthenticate = async server => {
|
|||
}
|
||||
};
|
||||
|
||||
export const supportedBiometryLabel = async () => {
|
||||
export const supportedBiometryLabel = async (): Promise<string | null> => {
|
||||
try {
|
||||
const enrolled = await LocalAuthentication.isEnrolledAsync();
|
||||
|
|
@ -4,13 +4,13 @@ import { isFDroidBuild } from '../../constants/environment';
|
|||
import events from './events';
|
||||
|
||||
const analytics = firebaseAnalytics || '';
|
||||
let bugsnag = '';
|
||||
let crashlytics;
|
||||
let bugsnag: any = '';
|
||||
let crashlytics: any;
|
||||
let reportCrashErrors = true;
|
||||
let reportAnalyticsEvents = true;
|
||||
|
||||
export const getReportCrashErrorsValue = () => reportCrashErrors;
|
||||
export const getReportAnalyticsEventsValue = () => reportAnalyticsEvents;
|
||||
export const getReportCrashErrorsValue = (): boolean => reportCrashErrors;
|
||||
export const getReportAnalyticsEventsValue = (): boolean => reportAnalyticsEvents;
|
||||
|
||||
if (!isFDroidBuild) {
|
||||
bugsnag = require('@bugsnag/react-native').default;
|
||||
|
@ -18,7 +18,7 @@ if (!isFDroidBuild) {
|
|||
onBreadcrumb() {
|
||||
return reportAnalyticsEvents;
|
||||
},
|
||||
onError(error) {
|
||||
onError(error: { breadcrumbs: string[] }) {
|
||||
if (!reportAnalyticsEvents) {
|
||||
error.breadcrumbs = [];
|
||||
}
|
||||
|
@ -34,13 +34,13 @@ export { events };
|
|||
|
||||
let metadata = {};
|
||||
|
||||
export const logServerVersion = serverVersion => {
|
||||
export const logServerVersion = (serverVersion: string): void => {
|
||||
metadata = {
|
||||
serverVersion
|
||||
};
|
||||
};
|
||||
|
||||
export const logEvent = (eventName, payload) => {
|
||||
export const logEvent = (eventName: string, payload?: { [key: string]: any }): void => {
|
||||
try {
|
||||
if (!isFDroidBuild) {
|
||||
analytics().logEvent(eventName, payload);
|
||||
|
@ -51,26 +51,26 @@ export const logEvent = (eventName, payload) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const setCurrentScreen = currentScreen => {
|
||||
export const setCurrentScreen = (currentScreen: string): void => {
|
||||
if (!isFDroidBuild) {
|
||||
analytics().setCurrentScreen(currentScreen);
|
||||
bugsnag.leaveBreadcrumb(currentScreen, { type: 'navigation' });
|
||||
}
|
||||
};
|
||||
|
||||
export const toggleCrashErrorsReport = value => {
|
||||
export const toggleCrashErrorsReport = (value: boolean): boolean => {
|
||||
crashlytics().setCrashlyticsCollectionEnabled(value);
|
||||
return (reportCrashErrors = value);
|
||||
};
|
||||
|
||||
export const toggleAnalyticsEventsReport = value => {
|
||||
export const toggleAnalyticsEventsReport = (value: boolean): boolean => {
|
||||
analytics().setAnalyticsCollectionEnabled(value);
|
||||
return (reportAnalyticsEvents = value);
|
||||
};
|
||||
|
||||
export default e => {
|
||||
export default (e: any): void => {
|
||||
if (e instanceof Error && bugsnag && e.message !== 'Aborted' && !__DEV__) {
|
||||
bugsnag.notify(e, event => {
|
||||
bugsnag.notify(e, (event: { addMetadata: (arg0: string, arg1: {}) => void }) => {
|
||||
event.addMetadata('details', { ...metadata });
|
||||
});
|
||||
if (!isFDroidBuild) {
|
|
@ -1,4 +1,11 @@
|
|||
export const canUploadFile = (file, allowList, maxFileSize, permissionToUploadFile) => {
|
||||
import { IAttachment } from '../views/ShareView/interfaces';
|
||||
|
||||
export const canUploadFile = (
|
||||
file: IAttachment,
|
||||
allowList: string,
|
||||
maxFileSize: number,
|
||||
permissionToUploadFile: boolean
|
||||
): { success: boolean; error?: string } => {
|
||||
if (!(file && file.path)) {
|
||||
return { success: true };
|
||||
}
|
||||
|
@ -13,11 +20,11 @@ export const canUploadFile = (file, allowList, maxFileSize, permissionToUploadFi
|
|||
return { success: true };
|
||||
}
|
||||
const allowedMime = allowList.split(',');
|
||||
if (allowedMime.includes(file.mime)) {
|
||||
if (allowedMime.includes(file.mime!)) {
|
||||
return { success: true };
|
||||
}
|
||||
const wildCardGlob = '/*';
|
||||
const wildCards = allowedMime.filter(item => item.indexOf(wildCardGlob) > 0);
|
||||
const wildCards = allowedMime.filter((item: string) => item.indexOf(wildCardGlob) > 0);
|
||||
if (file.mime && wildCards.includes(file.mime.replace(/(\/.*)$/, wildCardGlob))) {
|
||||
return { success: true };
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
const localeKeys = {
|
||||
const localeKeys: { [key: string]: string } = {
|
||||
en: 'en',
|
||||
ru: 'ru',
|
||||
'pt-BR': 'pt-br',
|
||||
|
@ -13,4 +13,4 @@ const localeKeys = {
|
|||
'zh-TW': 'zh-tw'
|
||||
};
|
||||
|
||||
export const toMomentLocale = locale => localeKeys[locale];
|
||||
export const toMomentLocale = (locale: string): string => localeKeys[locale];
|
|
@ -1,12 +1,14 @@
|
|||
import { Animated, Easing } from 'react-native';
|
||||
import { HeaderStyleInterpolators, TransitionPresets } from '@react-navigation/stack';
|
||||
import { HeaderStyleInterpolators, TransitionPreset, TransitionPresets } from '@react-navigation/stack';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { StackCardStyleInterpolator, TransitionSpec } from '@react-navigation/stack/lib/typescript/src/types';
|
||||
|
||||
import { isAndroid } from '../deviceInfo';
|
||||
import conditional from './conditional';
|
||||
|
||||
const { multiply } = Animated;
|
||||
|
||||
const forFadeFromCenter = ({ current, closing }) => {
|
||||
const forFadeFromCenter: StackCardStyleInterpolator = ({ current, closing }) => {
|
||||
const opacity = conditional(
|
||||
closing,
|
||||
current.progress,
|
||||
|
@ -23,7 +25,7 @@ const forFadeFromCenter = ({ current, closing }) => {
|
|||
};
|
||||
};
|
||||
|
||||
const FadeIn = {
|
||||
const FadeIn: TransitionSpec = {
|
||||
animation: 'timing',
|
||||
config: {
|
||||
duration: 250,
|
||||
|
@ -31,7 +33,7 @@ const FadeIn = {
|
|||
}
|
||||
};
|
||||
|
||||
const FadeOut = {
|
||||
const FadeOut: TransitionSpec = {
|
||||
animation: 'timing',
|
||||
config: {
|
||||
duration: 150,
|
||||
|
@ -48,7 +50,7 @@ export const FadeFromCenterModal = {
|
|||
cardStyleInterpolator: forFadeFromCenter
|
||||
};
|
||||
|
||||
const forStackAndroid = ({ current, inverted, layouts: { screen } }) => {
|
||||
const forStackAndroid: StackCardStyleInterpolator = ({ current, inverted, layouts: { screen } }) => {
|
||||
const translateX = multiply(
|
||||
current.progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
|
@ -70,7 +72,7 @@ const forStackAndroid = ({ current, inverted, layouts: { screen } }) => {
|
|||
};
|
||||
};
|
||||
|
||||
const StackAndroid = {
|
||||
const StackAndroid: TransitionPreset = {
|
||||
gestureDirection: 'horizontal',
|
||||
transitionSpec: {
|
||||
open: FadeIn,
|
|
@ -10,7 +10,11 @@ const { add, multiply } = Animated;
|
|||
* @param main Animated Node to use if the condition is `true`
|
||||
* @param fallback Animated Node to use if the condition is `false`
|
||||
*/
|
||||
export default function conditional(condition, main, fallback) {
|
||||
export default function conditional(
|
||||
condition: Animated.AnimatedInterpolation,
|
||||
main: Animated.Animated,
|
||||
fallback: Animated.Animated
|
||||
): Animated.AnimatedAddition {
|
||||
// To implement this behavior, we multiply the main node with the condition.
|
||||
// So if condition is 0, result will be 0, and if condition is 1, result will be main node.
|
||||
// Then we multiple reverse of the condition (0 if condition is 1) with the fallback.
|
|
@ -14,7 +14,7 @@ const scheme = {
|
|||
brave: 'brave:'
|
||||
};
|
||||
|
||||
const appSchemeURL = (url, browser) => {
|
||||
const appSchemeURL = (url: string, browser: string): string => {
|
||||
let schemeUrl = url;
|
||||
const parsedUrl = parse(url, true);
|
||||
const { protocol } = parsedUrl;
|
||||
|
@ -35,7 +35,7 @@ const appSchemeURL = (url, browser) => {
|
|||
return schemeUrl;
|
||||
};
|
||||
|
||||
const openLink = async (url, theme = 'light') => {
|
||||
const openLink = async (url: string, theme = 'light'): Promise<void> => {
|
||||
try {
|
||||
const browser = await UserPreferences.getStringAsync(DEFAULT_BROWSER_KEY);
|
||||
|
||||
|
@ -43,11 +43,12 @@ const openLink = async (url, theme = 'light') => {
|
|||
await WebBrowser.openBrowserAsync(url, {
|
||||
toolbarColor: themes[theme].headerBackground,
|
||||
controlsColor: themes[theme].headerTintColor,
|
||||
collapseToolbar: true,
|
||||
// https://github.com/expo/expo/pull/4923
|
||||
enableBarCollapsing: true,
|
||||
showTitle: true
|
||||
});
|
||||
} else {
|
||||
const schemeUrl = appSchemeURL(url, browser.replace(':', ''));
|
||||
const schemeUrl = appSchemeURL(url, browser!.replace(':', ''));
|
||||
await Linking.openURL(schemeUrl);
|
||||
}
|
||||
} catch {
|
|
@ -1,4 +1,4 @@
|
|||
export default function random(length) {
|
||||
export default function random(length: number): string {
|
||||
let text = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
for (let i = 0; i < length; i += 1) {
|
|
@ -15,7 +15,7 @@ const reviewDelay = 2000;
|
|||
const numberOfDays = 7;
|
||||
const numberOfPositiveEvent = 5;
|
||||
|
||||
const daysBetween = (date1, date2) => {
|
||||
const daysBetween = (date1: Date, date2: Date): number => {
|
||||
const one_day = 1000 * 60 * 60 * 24;
|
||||
const date1_ms = date1.getTime();
|
||||
const date2_ms = date2.getTime();
|
||||
|
@ -32,7 +32,7 @@ const onCancelPress = () => {
|
|||
}
|
||||
};
|
||||
|
||||
export const onReviewPress = async () => {
|
||||
export const onReviewPress = async (): Promise<void> => {
|
||||
logEvent(events.SE_REVIEW_THIS_APP);
|
||||
await onCancelPress();
|
||||
try {
|
|
@ -1,53 +0,0 @@
|
|||
import moment from 'moment';
|
||||
|
||||
import { themes } from '../constants/colors';
|
||||
import I18n from '../i18n';
|
||||
|
||||
export const isBlocked = room => {
|
||||
if (room) {
|
||||
const { t, blocked, blocker } = room;
|
||||
if (t === 'd' && (blocked || blocker)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const capitalize = s => {
|
||||
if (typeof s !== 'string') {
|
||||
return '';
|
||||
}
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
};
|
||||
|
||||
export const formatDate = date =>
|
||||
moment(date).calendar(null, {
|
||||
lastDay: `[${I18n.t('Yesterday')}]`,
|
||||
sameDay: 'LT',
|
||||
lastWeek: 'dddd',
|
||||
sameElse: 'L'
|
||||
});
|
||||
|
||||
export const formatDateThreads = date =>
|
||||
moment(date).calendar(null, {
|
||||
sameDay: 'LT',
|
||||
lastDay: `[${I18n.t('Yesterday')}] LT`,
|
||||
lastWeek: 'dddd LT',
|
||||
sameElse: 'LL'
|
||||
});
|
||||
|
||||
export const getBadgeColor = ({ subscription, messageId, theme }) => {
|
||||
if (subscription?.tunreadUser?.includes(messageId)) {
|
||||
return themes[theme].mentionMeColor;
|
||||
}
|
||||
if (subscription?.tunreadGroup?.includes(messageId)) {
|
||||
return themes[theme].mentionGroupColor;
|
||||
}
|
||||
if (subscription?.tunread?.includes(messageId)) {
|
||||
return themes[theme].tunreadColor;
|
||||
}
|
||||
};
|
||||
|
||||
export const makeThreadName = messageRecord => messageRecord.msg || messageRecord?.attachments[0]?.title;
|
||||
|
||||
export const isTeamRoom = ({ teamId, joined }) => teamId && joined;
|
|
@ -0,0 +1,65 @@
|
|||
import moment from 'moment';
|
||||
|
||||
import { themes } from '../constants/colors';
|
||||
import I18n from '../i18n';
|
||||
import { IAttachment } from '../definitions/IAttachment';
|
||||
import { ISubscription, SubscriptionType } from '../definitions/ISubscription';
|
||||
|
||||
export const isBlocked = (room: ISubscription): boolean => {
|
||||
if (room) {
|
||||
const { t, blocked, blocker } = room;
|
||||
if (t === SubscriptionType.DIRECT && (blocked || blocker)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const capitalize = (s: string): string => {
|
||||
if (typeof s !== 'string') {
|
||||
return '';
|
||||
}
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
};
|
||||
|
||||
export const formatDate = (date: Date): string =>
|
||||
moment(date).calendar(null, {
|
||||
lastDay: `[${I18n.t('Yesterday')}]`,
|
||||
sameDay: 'LT',
|
||||
lastWeek: 'dddd',
|
||||
sameElse: 'L'
|
||||
});
|
||||
|
||||
export const formatDateThreads = (date: Date): string =>
|
||||
moment(date).calendar(null, {
|
||||
sameDay: 'LT',
|
||||
lastDay: `[${I18n.t('Yesterday')}] LT`,
|
||||
lastWeek: 'dddd LT',
|
||||
sameElse: 'LL'
|
||||
});
|
||||
|
||||
export const getBadgeColor = ({
|
||||
subscription,
|
||||
messageId,
|
||||
theme
|
||||
}: {
|
||||
// TODO: Refactor when migrate model folder
|
||||
subscription: any;
|
||||
messageId: string;
|
||||
theme: string;
|
||||
}): string | undefined => {
|
||||
if (subscription?.tunreadUser?.includes(messageId)) {
|
||||
return themes[theme].mentionMeColor;
|
||||
}
|
||||
if (subscription?.tunreadGroup?.includes(messageId)) {
|
||||
return themes[theme].mentionGroupColor;
|
||||
}
|
||||
if (subscription?.tunread?.includes(messageId)) {
|
||||
return themes[theme].tunreadColor;
|
||||
}
|
||||
};
|
||||
|
||||
export const makeThreadName = (messageRecord: { id?: string; msg?: string; attachments?: IAttachment[] }): string | undefined =>
|
||||
messageRecord.msg || messageRecord.attachments![0].title;
|
||||
|
||||
export const isTeamRoom = ({ teamId, joined }: { teamId: string; joined: boolean }): boolean => !!teamId && joined;
|
|
@ -3,7 +3,7 @@
|
|||
url = 'https://open.rocket.chat/method'
|
||||
hostname = 'open.rocket.chat'
|
||||
*/
|
||||
export const extractHostname = url => {
|
||||
export const extractHostname = (url: string): string => {
|
||||
let hostname;
|
||||
|
||||
if (url.indexOf('//') > -1) {
|
|
@ -3,7 +3,7 @@
|
|||
/* eslint-disable object-curly-spacing */
|
||||
/* eslint-disable comma-spacing */
|
||||
/* eslint-disable key-spacing */
|
||||
const ascii = {
|
||||
const ascii: { [key: string]: string } = {
|
||||
'*\\0/*': '🙆',
|
||||
'*\\O/*': '🙆',
|
||||
'-___-': '😑',
|
|
@ -3,7 +3,7 @@
|
|||
/* eslint-disable object-curly-spacing */
|
||||
/* eslint-disable comma-spacing */
|
||||
/* eslint-disable key-spacing */
|
||||
const emojis = {
|
||||
const emojis: { [key: string]: string } = {
|
||||
':england:': '🏴',
|
||||
':scotland:': '🏴',
|
||||
':wales:': '🏴',
|
|
@ -2,11 +2,11 @@ import emojis from './emojis';
|
|||
import ascii, { asciiRegexp } from './ascii';
|
||||
|
||||
const shortnamePattern = new RegExp(/:[-+_a-z0-9]+:/, 'gi');
|
||||
const replaceShortNameWithUnicode = shortname => emojis[shortname] || shortname;
|
||||
const replaceShortNameWithUnicode = (shortname: string) => emojis[shortname] || shortname;
|
||||
const regAscii = new RegExp(`((\\s|^)${asciiRegexp}(?=\\s|$|[!,.?]))`, 'gi');
|
||||
|
||||
const unescapeHTML = string => {
|
||||
const unescaped = {
|
||||
const unescapeHTML = (string: string) => {
|
||||
const unescaped: { [key: string]: string } = {
|
||||
'&': '&',
|
||||
'&': '&',
|
||||
'&': '&',
|
||||
|
@ -27,7 +27,7 @@ const unescapeHTML = string => {
|
|||
return string.replace(/&(?:amp|#38|#x26|lt|#60|#x3C|gt|#62|#x3E|apos|#39|#x27|quot|#34|#x22);/gi, match => unescaped[match]);
|
||||
};
|
||||
|
||||
const shortnameToUnicode = str => {
|
||||
const shortnameToUnicode = (str: string): string => {
|
||||
str = str.replace(shortnamePattern, replaceShortNameWithUnicode);
|
||||
|
||||
str = str.replace(regAscii, (entire, m1, m2, m3) => {
|
|
@ -9,13 +9,18 @@ import { extractHostname } from './server';
|
|||
const { SSLPinning } = NativeModules;
|
||||
const { documentDirectory } = FileSystem;
|
||||
|
||||
const extractFileScheme = path => path.replace('file://', ''); // file:// isn't allowed by obj-C
|
||||
const extractFileScheme = (path: string) => path.replace('file://', ''); // file:// isn't allowed by obj-C
|
||||
|
||||
const getPath = name => `${documentDirectory}/${name}`;
|
||||
const getPath = (name: string) => `${documentDirectory}/${name}`;
|
||||
|
||||
const persistCertificate = async (name, password) => {
|
||||
interface ICertificate {
|
||||
path: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const persistCertificate = async (name: string, password: string) => {
|
||||
const certificatePath = getPath(name);
|
||||
const certificate = {
|
||||
const certificate: ICertificate = {
|
||||
path: extractFileScheme(certificatePath),
|
||||
password
|
||||
};
|
||||
|
@ -29,6 +34,7 @@ const RCSSLPinning = Platform.select({
|
|||
new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const res = await DocumentPicker.pick({
|
||||
// @ts-ignore
|
||||
type: ['com.rsa.pkcs-12']
|
||||
});
|
||||
const { uri, name } = res;
|
||||
|
@ -42,7 +48,7 @@ const RCSSLPinning = Platform.select({
|
|||
try {
|
||||
const certificatePath = getPath(name);
|
||||
await FileSystem.copyAsync({ from: uri, to: certificatePath });
|
||||
await persistCertificate(name, password);
|
||||
await persistCertificate(name, password!);
|
||||
resolve(name);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
|
@ -56,10 +62,10 @@ const RCSSLPinning = Platform.select({
|
|||
reject(e);
|
||||
}
|
||||
}),
|
||||
setCertificate: async (name, server) => {
|
||||
setCertificate: async (name: string, server: string) => {
|
||||
if (name) {
|
||||
let certificate = await UserPreferences.getMapAsync(name);
|
||||
if (!certificate.path.match(extractFileScheme(documentDirectory))) {
|
||||
let certificate = (await UserPreferences.getMapAsync(name)) as ICertificate;
|
||||
if (!certificate.path.match(extractFileScheme(documentDirectory!))) {
|
||||
certificate = await persistCertificate(name, certificate.password);
|
||||
}
|
||||
await UserPreferences.setMapAsync(extractHostname(server), certificate);
|
|
@ -2,12 +2,13 @@ import { Appearance } from 'react-native-appearance';
|
|||
import changeNavigationBarColor from 'react-native-navigation-bar-color';
|
||||
import setRootViewColor from 'rn-root-view';
|
||||
|
||||
import { IThemePreference, TThemeMode } from '../definitions/ITheme';
|
||||
import { themes } from '../constants/colors';
|
||||
import { isAndroid } from './deviceInfo';
|
||||
|
||||
let themeListener;
|
||||
let themeListener: { remove: () => void } | null;
|
||||
|
||||
export const defaultTheme = () => {
|
||||
export const defaultTheme = (): TThemeMode => {
|
||||
const systemTheme = Appearance.getColorScheme();
|
||||
if (systemTheme && systemTheme !== 'no-preference') {
|
||||
return systemTheme;
|
||||
|
@ -15,7 +16,7 @@ export const defaultTheme = () => {
|
|||
return 'light';
|
||||
};
|
||||
|
||||
export const getTheme = themePreferences => {
|
||||
export const getTheme = (themePreferences: IThemePreference): string => {
|
||||
const { darkLevel, currentTheme } = themePreferences;
|
||||
let theme = currentTheme;
|
||||
if (currentTheme === 'automatic') {
|
||||
|
@ -24,7 +25,7 @@ export const getTheme = themePreferences => {
|
|||
return theme === 'dark' ? darkLevel : 'light';
|
||||
};
|
||||
|
||||
export const newThemeState = (prevState, newTheme) => {
|
||||
export const newThemeState = (prevState: { themePreferences: IThemePreference }, newTheme: IThemePreference) => {
|
||||
// new theme preferences
|
||||
const themePreferences = {
|
||||
...prevState.themePreferences,
|
||||
|
@ -35,12 +36,13 @@ export const newThemeState = (prevState, newTheme) => {
|
|||
return { themePreferences, theme: getTheme(themePreferences) };
|
||||
};
|
||||
|
||||
export const setNativeTheme = async themePreferences => {
|
||||
export const setNativeTheme = async (themePreferences: IThemePreference): Promise<void> => {
|
||||
const theme = getTheme(themePreferences);
|
||||
if (isAndroid) {
|
||||
const iconsLight = theme === 'light';
|
||||
try {
|
||||
await changeNavigationBarColor(themes[theme].navbarBackground, iconsLight);
|
||||
// The late param as default is true @ react-native-navigation-bar-color/src/index.js line 8
|
||||
await changeNavigationBarColor(themes[theme].navbarBackground, iconsLight, true);
|
||||
} catch (error) {
|
||||
// Do nothing
|
||||
}
|
||||
|
@ -55,7 +57,7 @@ export const unsubscribeTheme = () => {
|
|||
}
|
||||
};
|
||||
|
||||
export const subscribeTheme = (themePreferences, setTheme) => {
|
||||
export const subscribeTheme = (themePreferences: IThemePreference, setTheme: () => void): void => {
|
||||
const { currentTheme } = themePreferences;
|
||||
if (!themeListener && currentTheme === 'automatic') {
|
||||
// not use listener params because we use getTheme
|
|
@ -1,26 +0,0 @@
|
|||
export default function throttle(fn, threshhold = 250, scope) {
|
||||
let last;
|
||||
let deferTimer;
|
||||
|
||||
const _throttle = (...args) => {
|
||||
const context = scope || this;
|
||||
|
||||
const now = +new Date();
|
||||
|
||||
if (last && now < last + threshhold) {
|
||||
// hold on to it
|
||||
clearTimeout(deferTimer);
|
||||
deferTimer = setTimeout(() => {
|
||||
last = now;
|
||||
fn.apply(context, args);
|
||||
}, threshhold);
|
||||
} else {
|
||||
last = now;
|
||||
fn.apply(context, args);
|
||||
}
|
||||
};
|
||||
|
||||
_throttle.stop = () => clearTimeout(deferTimer);
|
||||
|
||||
return _throttle;
|
||||
}
|
|
@ -1,19 +1,27 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { RectButton } from 'react-native-gesture-handler';
|
||||
import { RectButton, RectButtonProps } from 'react-native-gesture-handler';
|
||||
|
||||
import { themes } from '../constants/colors';
|
||||
|
||||
class Touch extends React.Component {
|
||||
setNativeProps(props) {
|
||||
interface ITouchProps extends RectButtonProps {
|
||||
children: React.ReactNode;
|
||||
theme: string;
|
||||
accessibilityLabel?: string;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
class Touch extends React.Component<ITouchProps> {
|
||||
private ref: any;
|
||||
|
||||
setNativeProps(props: ITouchProps): void {
|
||||
this.ref.setNativeProps(props);
|
||||
}
|
||||
|
||||
getRef = ref => {
|
||||
getRef = (ref: RectButton): void => {
|
||||
this.ref = ref;
|
||||
};
|
||||
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
const { children, onPress, theme, underlayColor, ...props } = this.props;
|
||||
|
||||
return (
|
||||
|
@ -30,11 +38,4 @@ class Touch extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
Touch.propTypes = {
|
||||
children: PropTypes.node,
|
||||
onPress: PropTypes.func,
|
||||
theme: PropTypes.string,
|
||||
underlayColor: PropTypes.string
|
||||
};
|
||||
|
||||
export default Touch;
|
|
@ -3,13 +3,18 @@ import { settings } from '@rocket.chat/sdk';
|
|||
import { TWO_FACTOR } from '../containers/TwoFactor';
|
||||
import EventEmitter from './events';
|
||||
|
||||
export const twoFactor = ({ method, invalid }) =>
|
||||
interface ITwoFactor {
|
||||
method: string;
|
||||
invalid: boolean;
|
||||
}
|
||||
|
||||
export const twoFactor = ({ method, invalid }: ITwoFactor): Promise<{ twoFactorCode: string; twoFactorMethod: string }> =>
|
||||
new Promise((resolve, reject) => {
|
||||
EventEmitter.emit(TWO_FACTOR, {
|
||||
method,
|
||||
invalid,
|
||||
cancel: () => reject(),
|
||||
submit: code => {
|
||||
submit: (code: string) => {
|
||||
settings.customHeaders = {
|
||||
...settings.customHeaders,
|
||||
'x-2fa-code': code,
|
|
@ -1,4 +1,4 @@
|
|||
export const isValidURL = url => {
|
||||
export const isValidURL = (url: string): boolean => {
|
||||
const pattern = new RegExp(
|
||||
'^(https?:\\/\\/)?' + // protocol
|
||||
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
|
||||
|
@ -12,4 +12,4 @@ export const isValidURL = url => {
|
|||
};
|
||||
|
||||
// Use useSsl: false only if server url starts with http://
|
||||
export const useSsl = url => !/http:\/\//.test(url);
|
||||
export const useSsl = (url: string): boolean => !/http:\/\//.test(url);
|
|
@ -32,8 +32,6 @@ const SelectChannel = ({
|
|||
}, 300);
|
||||
|
||||
const getAvatar = (item: any) =>
|
||||
// TODO: remove this ts-ignore when migrate the file: app/utils/avatar.js
|
||||
// @ts-ignore
|
||||
avatarURL({
|
||||
text: RocketChat.getRoomAvatar(item),
|
||||
type: item.t,
|
||||
|
|
|
@ -12,6 +12,7 @@ import { MultiSelect } from '../../containers/UIKit/MultiSelect';
|
|||
import { themes } from '../../constants/colors';
|
||||
import styles from './styles';
|
||||
import { ICreateDiscussionViewSelectUsers } from './interfaces';
|
||||
import { SubscriptionType } from '../../definitions/ISubscription';
|
||||
|
||||
interface IUser {
|
||||
name: string;
|
||||
|
@ -62,11 +63,9 @@ const SelectUsers = ({
|
|||
}, 300);
|
||||
|
||||
const getAvatar = (item: any) =>
|
||||
// TODO: remove this ts-ignore when migrate the file: app/utils/avatar.js
|
||||
// @ts-ignore
|
||||
avatarURL({
|
||||
text: RocketChat.getRoomAvatar(item),
|
||||
type: 'd',
|
||||
type: SubscriptionType.DIRECT,
|
||||
user: { id: userId, token },
|
||||
server,
|
||||
avatarETag: item.avatarETag,
|
||||
|
|
|
@ -2,6 +2,7 @@ import { RouteProp } from '@react-navigation/core';
|
|||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
|
||||
import { NewMessageStackParamList } from '../../stacks/types';
|
||||
import { SubscriptionType } from '../../definitions/ISubscription';
|
||||
|
||||
export interface ICreateChannelViewProps {
|
||||
navigation: StackNavigationProp<NewMessageStackParamList, 'CreateDiscussionView'>;
|
||||
|
@ -15,7 +16,7 @@ export interface ICreateChannelViewProps {
|
|||
loading: boolean;
|
||||
result: {
|
||||
rid: string;
|
||||
t: string;
|
||||
t: SubscriptionType;
|
||||
prid: string;
|
||||
};
|
||||
failure: boolean;
|
||||
|
|
|
@ -75,8 +75,6 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
|
|||
if (!newPassword.trim()) {
|
||||
return;
|
||||
}
|
||||
// TODO: Remove ts-ignore when migrate the showConfirmationAlert
|
||||
// @ts-ignore
|
||||
showConfirmationAlert({
|
||||
title: I18n.t('Are_you_sure_question_mark'),
|
||||
message: I18n.t('E2E_encryption_change_password_message'),
|
||||
|
@ -98,8 +96,6 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
|
|||
};
|
||||
|
||||
resetOwnKey = () => {
|
||||
// TODO: Remove ts-ignore when migrate the showConfirmationAlert
|
||||
// @ts-ignore
|
||||
showConfirmationAlert({
|
||||
title: I18n.t('Are_you_sure_question_mark'),
|
||||
message: I18n.t('E2E_encryption_reset_message'),
|
||||
|
|
|
@ -269,11 +269,10 @@ class NewServerView extends React.Component<INewServerView, IState> {
|
|||
uriToPath = (uri: string) => uri.replace('file://', '');
|
||||
|
||||
handleRemove = () => {
|
||||
// TODO: Remove ts-ignore when migrate the showConfirmationAlert
|
||||
// @ts-ignore
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('You_will_unset_a_certificate_for_this_server'),
|
||||
confirmationText: I18n.t('Remove'),
|
||||
// @ts-ignore
|
||||
onPress: this.setState({ certificate: null }) // We not need delete file from DocumentPicker because it is a temp file
|
||||
});
|
||||
};
|
||||
|
|
|
@ -424,7 +424,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
|||
|
||||
logoutOtherLocations = () => {
|
||||
logEvent(events.PL_OTHER_LOCATIONS);
|
||||
// @ts-ignore
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('You_will_be_logged_out_from_other_locations'),
|
||||
confirmationText: I18n.t('Logout'),
|
||||
|
|
|
@ -26,9 +26,9 @@ export interface IParams {
|
|||
}
|
||||
|
||||
export interface IAvatarButton {
|
||||
key: React.Key;
|
||||
key: string;
|
||||
child: React.ReactNode;
|
||||
onPress: Function;
|
||||
onPress: () => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import { Switch } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { StackNavigationOptions } from '@react-navigation/stack';
|
||||
import Model from '@nozbe/watermelondb/Model';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import I18n from '../i18n';
|
||||
|
@ -15,15 +14,10 @@ import { changePasscode, checkHasPasscode, supportedBiometryLabel } from '../uti
|
|||
import { DEFAULT_AUTO_LOCK } from '../constants/localAuthentication';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import { events, logEvent } from '../utils/log';
|
||||
import { TServerModel } from '../definitions/IServer';
|
||||
|
||||
const DEFAULT_BIOMETRY = false;
|
||||
|
||||
interface IServerRecords extends Model {
|
||||
autoLock?: boolean;
|
||||
autoLockTime?: number;
|
||||
biometry?: boolean;
|
||||
}
|
||||
|
||||
interface IItem {
|
||||
title: string;
|
||||
value: number;
|
||||
|
@ -38,14 +32,14 @@ interface IScreenLockConfigViewProps {
|
|||
}
|
||||
|
||||
interface IScreenLockConfigViewState {
|
||||
autoLock?: boolean;
|
||||
autoLock: boolean;
|
||||
autoLockTime?: number | null;
|
||||
biometry?: boolean;
|
||||
biometryLabel: null;
|
||||
biometryLabel: string | null;
|
||||
}
|
||||
|
||||
class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, IScreenLockConfigViewState> {
|
||||
private serverRecord?: IServerRecords;
|
||||
private serverRecord?: TServerModel;
|
||||
|
||||
private observable?: Subscription;
|
||||
|
||||
|
@ -98,7 +92,7 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
|
|||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.get('servers');
|
||||
try {
|
||||
this.serverRecord = await serversCollection.find(server);
|
||||
this.serverRecord = (await serversCollection.find(server)) as TServerModel;
|
||||
this.setState({
|
||||
autoLock: this.serverRecord?.autoLock,
|
||||
autoLockTime: this.serverRecord?.autoLockTime === null ? DEFAULT_AUTO_LOCK : this.serverRecord?.autoLockTime,
|
||||
|
@ -151,7 +145,7 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
|
|||
const { autoLock } = this.state;
|
||||
if (autoLock) {
|
||||
try {
|
||||
await checkHasPasscode({ force: false, serverRecord: this.serverRecord });
|
||||
await checkHasPasscode({ force: false, serverRecord: this.serverRecord! });
|
||||
} catch {
|
||||
this.toggleAutoLock();
|
||||
}
|
||||
|
|
|
@ -88,7 +88,6 @@ class SettingsView extends React.Component<ISettingsViewProps, any> {
|
|||
|
||||
handleLogout = () => {
|
||||
logEvent(events.SE_LOG_OUT);
|
||||
// @ts-ignore
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('You_will_be_logged_out_of_this_application'),
|
||||
confirmationText: I18n.t('Logout'),
|
||||
|
@ -98,7 +97,6 @@ class SettingsView extends React.Component<ISettingsViewProps, any> {
|
|||
|
||||
handleClearCache = () => {
|
||||
logEvent(events.SE_CLEAR_LOCAL_SERVER_CACHE);
|
||||
/* @ts-ignore */
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('This_will_clear_all_your_offline_data'),
|
||||
confirmationText: I18n.t('Clear'),
|
||||
|
|
|
@ -25,12 +25,12 @@ import { sanitizeLikeString } from '../../lib/database/utils';
|
|||
import styles from './styles';
|
||||
import ShareListHeader from './Header';
|
||||
|
||||
interface IFile {
|
||||
interface IDataFromShare {
|
||||
value: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface IAttachment {
|
||||
interface IFileToShare {
|
||||
filename: string;
|
||||
description: string;
|
||||
size: number;
|
||||
|
@ -61,7 +61,7 @@ interface IState {
|
|||
searchResults: IChat[];
|
||||
chats: IChat[];
|
||||
serversCount: number;
|
||||
attachments: IAttachment[];
|
||||
attachments: IFileToShare[];
|
||||
text: string;
|
||||
loading: boolean;
|
||||
serverInfo: IServerInfo;
|
||||
|
@ -121,7 +121,7 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
|
|||
async componentDidMount() {
|
||||
const { server } = this.props;
|
||||
try {
|
||||
const data = (await ShareExtension.data()) as IFile[];
|
||||
const data = (await ShareExtension.data()) as IDataFromShare[];
|
||||
if (isAndroid) {
|
||||
await this.askForPermission(data);
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
|
|||
size: file.size,
|
||||
mime: mime.lookup(file.uri),
|
||||
path: file.uri
|
||||
})) as IAttachment[];
|
||||
})) as IFileToShare[];
|
||||
const text = data.filter(item => item.type === 'text').reduce((acc, item) => `${item.value}\n${acc}`, '');
|
||||
this.setState({
|
||||
text,
|
||||
|
@ -294,7 +294,7 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
askForPermission = async (data: IFile[]) => {
|
||||
askForPermission = async (data: IDataFromShare[]) => {
|
||||
const mediaIndex = data.findIndex(item => item.type === 'media');
|
||||
if (mediaIndex !== -1) {
|
||||
const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE, permission);
|
||||
|
|
|
@ -39,7 +39,7 @@ interface IShareViewState {
|
|||
room: ISubscription;
|
||||
thread: any; // change
|
||||
maxFileSize: number;
|
||||
mediaAllowList: number;
|
||||
mediaAllowList: string;
|
||||
}
|
||||
|
||||
interface IShareViewProps {
|
||||
|
@ -52,7 +52,7 @@ interface IShareViewProps {
|
|||
token: string;
|
||||
};
|
||||
server: string;
|
||||
FileUpload_MediaTypeWhiteList?: number;
|
||||
FileUpload_MediaTypeWhiteList?: string;
|
||||
FileUpload_MaxFileSize?: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,17 +11,18 @@ import { supportSystemTheme } from '../utils/deviceInfo';
|
|||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import UserPreferences from '../lib/userPreferences';
|
||||
import { events, logEvent } from '../utils/log';
|
||||
import { IThemePreference, TThemeMode, TDarkLevel } from '../definitions/ITheme';
|
||||
|
||||
const THEME_GROUP = 'THEME_GROUP';
|
||||
const DARK_GROUP = 'DARK_GROUP';
|
||||
|
||||
const SYSTEM_THEME = {
|
||||
const SYSTEM_THEME: ITheme = {
|
||||
label: 'Automatic',
|
||||
value: 'automatic',
|
||||
group: THEME_GROUP
|
||||
};
|
||||
|
||||
const THEMES = [
|
||||
const THEMES: ITheme[] = [
|
||||
{
|
||||
label: 'Light',
|
||||
value: 'light',
|
||||
|
@ -53,15 +54,10 @@ const darkGroup = THEMES.filter(item => item.group === DARK_GROUP);
|
|||
|
||||
interface ITheme {
|
||||
label: string;
|
||||
value: string;
|
||||
value: TThemeMode | TDarkLevel;
|
||||
group: string;
|
||||
}
|
||||
|
||||
interface IThemePreference {
|
||||
currentTheme?: string;
|
||||
darkLevel?: string;
|
||||
}
|
||||
|
||||
interface IThemeViewProps {
|
||||
theme: string;
|
||||
themePreferences: IThemePreference;
|
||||
|
@ -89,19 +85,19 @@ class ThemeView extends React.Component<IThemeViewProps> {
|
|||
const { themePreferences } = this.props;
|
||||
const { darkLevel, currentTheme } = themePreferences;
|
||||
const { value, group } = item;
|
||||
let changes: IThemePreference = {};
|
||||
let changes: Partial<IThemePreference> = {};
|
||||
if (group === THEME_GROUP && currentTheme !== value) {
|
||||
logEvent(events.THEME_SET_THEME_GROUP, { theme_group: value });
|
||||
changes = { currentTheme: value };
|
||||
changes = { currentTheme: value as TThemeMode };
|
||||
}
|
||||
if (group === DARK_GROUP && darkLevel !== value) {
|
||||
logEvent(events.THEME_SET_DARK_LEVEL, { dark_level: value });
|
||||
changes = { darkLevel: value };
|
||||
changes = { darkLevel: value as TDarkLevel };
|
||||
}
|
||||
this.setTheme(changes);
|
||||
};
|
||||
|
||||
setTheme = async (theme: IThemePreference) => {
|
||||
setTheme = async (theme: Partial<IThemePreference>) => {
|
||||
const { setTheme, themePreferences } = this.props;
|
||||
const newTheme = { ...themePreferences, ...theme };
|
||||
setTheme(newTheme);
|
||||
|
|
Loading…
Reference in New Issue