Merge branch 'develop' into new.add-discusions-roomactionsview
This commit is contained in:
commit
e010d804d5
|
@ -17,14 +17,15 @@ module.exports = {
|
||||||
legacyDecorators: true
|
legacyDecorators: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: ['react', 'jsx-a11y', 'import', 'react-native', '@babel'],
|
plugins: ['react', 'jsx-a11y', 'import', 'react-native', '@babel', 'jest'],
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
commonjs: true,
|
commonjs: true,
|
||||||
es6: true,
|
es6: true,
|
||||||
node: true,
|
node: true,
|
||||||
jquery: true,
|
jquery: true,
|
||||||
mocha: true
|
mocha: true,
|
||||||
|
'jest/globals': true
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'import/extensions': [
|
'import/extensions': [
|
||||||
|
|
|
@ -1419,6 +1419,7 @@ Array [
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
|
accessibilityLabel="Use"
|
||||||
accessible={true}
|
accessible={true}
|
||||||
focusable={true}
|
focusable={true}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
@ -1581,6 +1582,7 @@ Array [
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
|
accessibilityLabel="Use"
|
||||||
accessible={true}
|
accessible={true}
|
||||||
focusable={true}
|
focusable={true}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
@ -41244,6 +41246,7 @@ exports[`Storyshots Message Show a button as attachment 1`] = `
|
||||||
Test Button
|
Test Button
|
||||||
</Text>
|
</Text>
|
||||||
<View
|
<View
|
||||||
|
accessibilityLabel="Text button"
|
||||||
accessible={true}
|
accessible={true}
|
||||||
focusable={true}
|
focusable={true}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
|
|
@ -144,13 +144,15 @@ android {
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode VERSIONCODE as Integer
|
versionCode VERSIONCODE as Integer
|
||||||
versionName "4.22.0"
|
versionName "4.24.0"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
if (!isFoss) {
|
if (!isFoss) {
|
||||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||||
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below!
|
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below!
|
||||||
}
|
}
|
||||||
resValue "string", "rn_config_reader_custom_package", "chat.rocket.reactnative"
|
resValue "string", "rn_config_reader_custom_package", "chat.rocket.reactnative"
|
||||||
|
testBuildType System.getProperty('testBuildType', 'debug')
|
||||||
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
@ -203,6 +205,10 @@ android {
|
||||||
dimension = "app"
|
dimension = "app"
|
||||||
buildConfigField "boolean", "IS_OFFICIAL", "false"
|
buildConfigField "boolean", "IS_OFFICIAL", "false"
|
||||||
}
|
}
|
||||||
|
e2e {
|
||||||
|
dimension = "app"
|
||||||
|
buildConfigField "boolean", "IS_OFFICIAL", "false"
|
||||||
|
}
|
||||||
foss {
|
foss {
|
||||||
dimension = "type"
|
dimension = "type"
|
||||||
buildConfigField "boolean", "FDROID_BUILD", "true"
|
buildConfigField "boolean", "FDROID_BUILD", "true"
|
||||||
|
@ -230,6 +236,16 @@ android {
|
||||||
java.srcDirs = ['src/main/java', 'src/play/java']
|
java.srcDirs = ['src/main/java', 'src/play/java']
|
||||||
manifest.srcFile 'src/play/AndroidManifest.xml'
|
manifest.srcFile 'src/play/AndroidManifest.xml'
|
||||||
}
|
}
|
||||||
|
e2ePlayDebug {
|
||||||
|
java.srcDirs = ['src/main/java', 'src/play/java']
|
||||||
|
res.srcDirs = ['src/experimental/res']
|
||||||
|
manifest.srcFile 'src/play/AndroidManifest.xml'
|
||||||
|
}
|
||||||
|
e2ePlayRelease {
|
||||||
|
java.srcDirs = ['src/main/java', 'src/play/java']
|
||||||
|
res.srcDirs = ['src/experimental/res']
|
||||||
|
manifest.srcFile 'src/play/AndroidManifest.xml'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
|
@ -294,6 +310,8 @@ dependencies {
|
||||||
implementation "com.tencent:mmkv-static:1.2.1"
|
implementation "com.tencent:mmkv-static:1.2.1"
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
|
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
|
||||||
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.9.0"
|
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.9.0"
|
||||||
|
androidTestImplementation('com.wix:detox:+') { transitive = true }
|
||||||
|
androidTestImplementation 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run this once to be able to run the application with BUCK
|
// Run this once to be able to run the application with BUCK
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Replace "com.example" here and below with your app's package name from the top of MainActivity.java
|
||||||
|
package chat.rocket.reactnative;
|
||||||
|
|
||||||
|
import com.wix.detox.Detox;
|
||||||
|
import com.wix.detox.config.DetoxConfig;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.filters.LargeTest;
|
||||||
|
import androidx.test.rule.ActivityTestRule;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@LargeTest
|
||||||
|
public class DetoxTest {
|
||||||
|
@Rule
|
||||||
|
// Replace 'MainActivity' with the value of android:name entry in
|
||||||
|
// <activity> in AndroidManifest.xml
|
||||||
|
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void runDetoxTests() {
|
||||||
|
DetoxConfig detoxConfig = new DetoxConfig();
|
||||||
|
detoxConfig.idlePolicyConfig.masterTimeoutSec = 90;
|
||||||
|
detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60;
|
||||||
|
detoxConfig.rnContextLoadTimeoutSec = (chat.rocket.reactnative.BuildConfig.DEBUG ? 180 : 60);
|
||||||
|
|
||||||
|
Detox.runTests(mActivityRule, detoxConfig);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<base-config cleartextTrafficPermitted="true">
|
||||||
|
<trust-anchors>
|
||||||
|
<certificates src="system" />
|
||||||
|
<certificates src="user"
|
||||||
|
tools:ignore="AcceptsUserCertificates" />
|
||||||
|
</trust-anchors>
|
||||||
|
</base-config>
|
||||||
|
</network-security-config>
|
|
@ -11,9 +11,12 @@ import com.facebook.react.bridge.ReactMethod;
|
||||||
import com.facebook.react.bridge.Promise;
|
import com.facebook.react.bridge.Promise;
|
||||||
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.security.KeyStore;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import javax.net.ssl.X509ExtendedKeyManager;
|
import javax.net.ssl.X509ExtendedKeyManager;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
@ -21,11 +24,12 @@ import javax.net.ssl.X509TrustManager;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import java.lang.InterruptedException;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import javax.net.ssl.KeyManager;
|
import javax.net.ssl.KeyManager;
|
||||||
import android.security.KeyChain;
|
import android.security.KeyChain;
|
||||||
import android.security.KeyChainAliasCallback;
|
import android.security.KeyChainAliasCallback;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import com.RNFetchBlob.RNFetchBlob;
|
import com.RNFetchBlob.RNFetchBlob;
|
||||||
|
@ -52,8 +56,9 @@ public class SSLPinningModule extends ReactContextBaseJavaModule implements KeyC
|
||||||
public void apply(OkHttpClient.Builder builder) {
|
public void apply(OkHttpClient.Builder builder) {
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
SSLSocketFactory sslSocketFactory = getSSLFactory(alias);
|
SSLSocketFactory sslSocketFactory = getSSLFactory(alias);
|
||||||
|
X509TrustManager trustManager = getTrustManagerFactory();
|
||||||
if (sslSocketFactory != null) {
|
if (sslSocketFactory != null) {
|
||||||
builder.sslSocketFactory(sslSocketFactory);
|
builder.sslSocketFactory(sslSocketFactory, trustManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,8 +73,9 @@ public class SSLPinningModule extends ReactContextBaseJavaModule implements KeyC
|
||||||
|
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
SSLSocketFactory sslSocketFactory = getSSLFactory(alias);
|
SSLSocketFactory sslSocketFactory = getSSLFactory(alias);
|
||||||
|
X509TrustManager trustManager = getTrustManagerFactory();
|
||||||
if (sslSocketFactory != null) {
|
if (sslSocketFactory != null) {
|
||||||
builder.sslSocketFactory(sslSocketFactory);
|
builder.sslSocketFactory(sslSocketFactory, trustManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,25 +168,9 @@ public class SSLPinningModule extends ReactContextBaseJavaModule implements KeyC
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final TrustManager[] trustAllCerts = new TrustManager[] {
|
final X509TrustManager trustManager = getTrustManagerFactory();
|
||||||
new X509TrustManager() {
|
|
||||||
@Override
|
|
||||||
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return certChain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final SSLContext sslContext = SSLContext.getInstance("TLS");
|
final SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
sslContext.init(new KeyManager[]{keyManager}, trustAllCerts, new java.security.SecureRandom());
|
sslContext.init(new KeyManager[]{keyManager}, new TrustManager[]{trustManager}, new java.security.SecureRandom());
|
||||||
SSLContext.setDefault(sslContext);
|
SSLContext.setDefault(sslContext);
|
||||||
|
|
||||||
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||||
|
@ -190,4 +180,19 @@ public class SSLPinningModule extends ReactContextBaseJavaModule implements KeyC
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static X509TrustManager getTrustManagerFactory() {
|
||||||
|
try {
|
||||||
|
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
trustManagerFactory.init((KeyStore) null);
|
||||||
|
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
|
||||||
|
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
|
||||||
|
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
|
||||||
|
}
|
||||||
|
final X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
|
||||||
|
return trustManager;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,10 @@ allprojects {
|
||||||
url("$rootDir/../node_modules/jsc-android/dist")
|
url("$rootDir/../node_modules/jsc-android/dist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maven {
|
||||||
|
url "$rootDir/../node_modules/detox/Detox-android"
|
||||||
|
}
|
||||||
|
|
||||||
maven {
|
maven {
|
||||||
url jitsi_url
|
url jitsi_url
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { NavigationContainer } from '@react-navigation/native';
|
||||||
import { createStackNavigator } from '@react-navigation/stack';
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { SetUsernameStackParamList, StackParamList } from './navigationTypes';
|
||||||
import Navigation from './lib/Navigation';
|
import Navigation from './lib/Navigation';
|
||||||
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
||||||
import { ROOT_INSIDE, ROOT_LOADING, ROOT_OUTSIDE, ROOT_SET_USERNAME } from './actions/app';
|
import { ROOT_INSIDE, ROOT_LOADING, ROOT_OUTSIDE, ROOT_SET_USERNAME } from './actions/app';
|
||||||
|
@ -17,7 +18,7 @@ import { ThemeContext } from './theme';
|
||||||
import { setCurrentScreen } from './utils/log';
|
import { setCurrentScreen } from './utils/log';
|
||||||
|
|
||||||
// SetUsernameStack
|
// SetUsernameStack
|
||||||
const SetUsername = createStackNavigator();
|
const SetUsername = createStackNavigator<SetUsernameStackParamList>();
|
||||||
const SetUsernameStack = () => (
|
const SetUsernameStack = () => (
|
||||||
<SetUsername.Navigator screenOptions={defaultHeader}>
|
<SetUsername.Navigator screenOptions={defaultHeader}>
|
||||||
<SetUsername.Screen name='SetUsernameView' component={SetUsernameView} />
|
<SetUsername.Screen name='SetUsernameView' component={SetUsernameView} />
|
||||||
|
@ -25,7 +26,7 @@ const SetUsernameStack = () => (
|
||||||
);
|
);
|
||||||
|
|
||||||
// App
|
// App
|
||||||
const Stack = createStackNavigator();
|
const Stack = createStackNavigator<StackParamList>();
|
||||||
const App = React.memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => {
|
const App = React.memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => {
|
||||||
if (!root) {
|
if (!root) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -2,8 +2,8 @@ const REQUEST = 'REQUEST';
|
||||||
const SUCCESS = 'SUCCESS';
|
const SUCCESS = 'SUCCESS';
|
||||||
const FAILURE = 'FAILURE';
|
const FAILURE = 'FAILURE';
|
||||||
const defaultTypes = [REQUEST, SUCCESS, FAILURE];
|
const defaultTypes = [REQUEST, SUCCESS, FAILURE];
|
||||||
function createRequestTypes(base, types = defaultTypes) {
|
function createRequestTypes(base = {}, types = defaultTypes): Record<any, any> {
|
||||||
const res = {};
|
const res: Record<any, any> = {};
|
||||||
types.forEach(type => (res[type] = `${base}_${type}`));
|
types.forEach(type => (res[type] = `${base}_${type}`));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
import { SET_ACTIVE_USERS } from './actionsTypes';
|
|
||||||
|
|
||||||
export function setActiveUsers(activeUsers) {
|
|
||||||
return {
|
|
||||||
type: SET_ACTIVE_USERS,
|
|
||||||
activeUsers
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { IActiveUsers } from '../reducers/activeUsers';
|
||||||
|
import { SET_ACTIVE_USERS } from './actionsTypes';
|
||||||
|
|
||||||
|
export interface ISetActiveUsers extends Action {
|
||||||
|
activeUsers: IActiveUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionActiveUsers = ISetActiveUsers;
|
||||||
|
|
||||||
|
export const setActiveUsers = (activeUsers: IActiveUsers): ISetActiveUsers => ({
|
||||||
|
type: SET_ACTIVE_USERS,
|
||||||
|
activeUsers
|
||||||
|
});
|
|
@ -1,28 +0,0 @@
|
||||||
import * as types from './actionsTypes';
|
|
||||||
|
|
||||||
export function addUser(user) {
|
|
||||||
return {
|
|
||||||
type: types.SELECTED_USERS.ADD_USER,
|
|
||||||
user
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeUser(user) {
|
|
||||||
return {
|
|
||||||
type: types.SELECTED_USERS.REMOVE_USER,
|
|
||||||
user
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reset() {
|
|
||||||
return {
|
|
||||||
type: types.SELECTED_USERS.RESET
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setLoading(loading) {
|
|
||||||
return {
|
|
||||||
type: types.SELECTED_USERS.SET_LOADING,
|
|
||||||
loading
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { ISelectedUser } from '../reducers/selectedUsers';
|
||||||
|
import * as types from './actionsTypes';
|
||||||
|
|
||||||
|
type TUser = {
|
||||||
|
user: ISelectedUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TAction = Action & TUser;
|
||||||
|
|
||||||
|
interface ISetLoading extends Action {
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionSelectedUsers = TAction & ISetLoading;
|
||||||
|
|
||||||
|
export function addUser(user: ISelectedUser): TAction {
|
||||||
|
return {
|
||||||
|
type: types.SELECTED_USERS.ADD_USER,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeUser(user: ISelectedUser): TAction {
|
||||||
|
return {
|
||||||
|
type: types.SELECTED_USERS.REMOVE_USER,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reset(): Action {
|
||||||
|
return {
|
||||||
|
type: types.SELECTED_USERS.RESET
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLoading(loading: boolean): ISetLoading {
|
||||||
|
return {
|
||||||
|
type: types.SELECTED_USERS.SET_LOADING,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,2 +0,0 @@
|
||||||
export const DISPLAY_MODE_CONDENSED = 'condensed';
|
|
||||||
export const DISPLAY_MODE_EXPANDED = 'expanded';
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
export enum DisplayMode {
|
||||||
|
Condensed = 'condensed',
|
||||||
|
Expanded = 'expanded'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SortBy {
|
||||||
|
Alphabetical = 'alphabetical',
|
||||||
|
Activity = 'activity'
|
||||||
|
}
|
|
@ -124,7 +124,11 @@ const ActionSheet = React.memo(
|
||||||
|
|
||||||
const renderFooter = () =>
|
const renderFooter = () =>
|
||||||
data?.hasCancel ? (
|
data?.hasCancel ? (
|
||||||
<Button onPress={hide} style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]} theme={theme}>
|
<Button
|
||||||
|
onPress={hide}
|
||||||
|
style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||||
|
theme={theme}
|
||||||
|
accessibilityLabel={I18n.t('Cancel')}>
|
||||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text>
|
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import React from 'react';
|
||||||
import { TouchableOpacity } from 'react-native';
|
import { TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
import { isAndroid } from '../../utils/deviceInfo';
|
import { isAndroid } from '../../utils/deviceInfo';
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
|
|
||||||
// Taken from https://github.com/rgommezz/react-native-scroll-bottom-sheet#touchables
|
// 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;
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const useActionSheet = () => useContext(context);
|
||||||
|
|
||||||
const { Provider, Consumer } = context;
|
const { Provider, Consumer } = context;
|
||||||
|
|
||||||
export const withActionSheet = <P extends object>(Component: React.ComponentType<P>) =>
|
export const withActionSheet = (Component: any): any =>
|
||||||
forwardRef((props: any, ref: ForwardedRef<any>) => (
|
forwardRef((props: any, ref: ForwardedRef<any>) => (
|
||||||
<Consumer>{(contexts: any) => <Component {...props} {...contexts} ref={ref} />}</Consumer>
|
<Consumer>{(contexts: any) => <Component {...props} {...contexts} ref={ref} />}</Consumer>
|
||||||
));
|
));
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Touchable from 'react-native-platform-touchable';
|
||||||
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
||||||
|
|
||||||
import { avatarURL } from '../../utils/avatar';
|
import { avatarURL } from '../../utils/avatar';
|
||||||
|
import { SubscriptionType } from '../../definitions/ISubscription';
|
||||||
import Emoji from '../markdown/Emoji';
|
import Emoji from '../markdown/Emoji';
|
||||||
import { IAvatar } from './interfaces';
|
import { IAvatar } from './interfaces';
|
||||||
|
|
||||||
|
@ -27,8 +28,8 @@ const Avatar = React.memo(
|
||||||
text,
|
text,
|
||||||
size = 25,
|
size = 25,
|
||||||
borderRadius = 4,
|
borderRadius = 4,
|
||||||
type = 'd'
|
type = SubscriptionType.DIRECT
|
||||||
}: Partial<IAvatar>) => {
|
}: IAvatar) => {
|
||||||
if ((!text && !avatar && !emoji && !rid) || !server) {
|
if ((!text && !avatar && !emoji && !rid) || !server) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,17 @@ import { getUserSelector } from '../../selectors/login';
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import { IAvatar } from './interfaces';
|
import { IAvatar } from './interfaces';
|
||||||
|
|
||||||
class AvatarContainer extends React.Component<Partial<IAvatar>, any> {
|
class AvatarContainer extends React.Component<IAvatar, any> {
|
||||||
private mounted: boolean;
|
private mounted: boolean;
|
||||||
|
|
||||||
private subscription!: any;
|
private subscription: any;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
text: '',
|
text: '',
|
||||||
type: 'd'
|
type: 'd'
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: Partial<IAvatar>) {
|
constructor(props: IAvatar) {
|
||||||
super(props);
|
super(props);
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
this.state = { avatarETag: '' };
|
this.state = { avatarETag: '' };
|
||||||
|
@ -55,7 +55,7 @@ class AvatarContainer extends React.Component<Partial<IAvatar>, any> {
|
||||||
try {
|
try {
|
||||||
if (this.isDirect) {
|
if (this.isDirect) {
|
||||||
const { text } = this.props;
|
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;
|
record = user;
|
||||||
} else {
|
} else {
|
||||||
const { rid } = this.props;
|
const { rid } = this.props;
|
||||||
|
@ -82,7 +82,7 @@ class AvatarContainer extends React.Component<Partial<IAvatar>, any> {
|
||||||
render() {
|
render() {
|
||||||
const { avatarETag } = this.state;
|
const { avatarETag } = this.state;
|
||||||
const { serverVersion } = this.props;
|
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 {
|
export interface IAvatar {
|
||||||
server: string;
|
server?: string;
|
||||||
style: any;
|
style?: any;
|
||||||
text: string;
|
text: string;
|
||||||
avatar: string;
|
avatar?: string;
|
||||||
emoji: string;
|
emoji?: string;
|
||||||
size: number;
|
size?: number;
|
||||||
borderRadius: number;
|
borderRadius?: number;
|
||||||
type: string;
|
type?: string;
|
||||||
children: JSX.Element;
|
children?: JSX.Element;
|
||||||
user: {
|
user?: {
|
||||||
id: string;
|
id?: string;
|
||||||
token: string;
|
token?: string;
|
||||||
};
|
};
|
||||||
theme: string;
|
theme?: string;
|
||||||
onPress(): void;
|
onPress?: () => void;
|
||||||
getCustomEmoji(): any;
|
getCustomEmoji?: () => any;
|
||||||
avatarETag: string;
|
avatarETag?: string;
|
||||||
isStatic: boolean | string;
|
isStatic?: boolean | string;
|
||||||
rid: string;
|
rid?: string;
|
||||||
blockUnauthenticatedAccess: boolean;
|
blockUnauthenticatedAccess?: boolean;
|
||||||
serverVersion: string;
|
serverVersion?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ export default class Button extends React.PureComponent<Partial<IButtonProps>, a
|
||||||
disabled && styles.disabled,
|
disabled && styles.disabled,
|
||||||
style
|
style
|
||||||
]}
|
]}
|
||||||
|
accessibilityLabel={title}
|
||||||
{...otherProps}>
|
{...otherProps}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<ActivityIndicator color={textColor} />
|
<ActivityIndicator color={textColor} />
|
||||||
|
|
|
@ -31,7 +31,7 @@ interface IEmojiPickerProps {
|
||||||
customEmojis?: any;
|
customEmojis?: any;
|
||||||
style: object;
|
style: object;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
onEmojiSelected?: Function;
|
onEmojiSelected?: ((emoji: any) => void) | ((keyboardId: any, params?: any) => void);
|
||||||
tabEmojiStyle?: object;
|
tabEmojiStyle?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,4 +201,5 @@ const mapStateToProps = (state: any) => ({
|
||||||
customEmojis: state.customEmojis
|
customEmojis: state.customEmojis
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(EmojiPicker));
|
// TODO - remove this as any, at the new PR to fix the HOC erros
|
||||||
|
export default connect(mapStateToProps)(withTheme(EmojiPicker)) as any;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { StyleSheet, View } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
interface IHeaderButtonContainer {
|
interface IHeaderButtonContainer {
|
||||||
children: JSX.Element;
|
children: React.ReactNode;
|
||||||
left?: boolean;
|
left?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -423,4 +423,4 @@ const mapStateToProps = (state: any) => ({
|
||||||
services: state.login.services
|
services: state.login.services
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(LoginServices));
|
export default connect(mapStateToProps)(withTheme(LoginServices)) as any;
|
||||||
|
|
|
@ -305,8 +305,6 @@ const MessageActions = React.memo(
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = (message: any) => {
|
const handleDelete = (message: any) => {
|
||||||
// TODO - migrate this function for ts when fix the lint erros
|
|
||||||
// @ts-ignore
|
|
||||||
showConfirmationAlert({
|
showConfirmationAlert({
|
||||||
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
|
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
|
||||||
confirmationText: I18n.t('Delete'),
|
confirmationText: I18n.t('Delete'),
|
||||||
|
|
|
@ -13,7 +13,7 @@ interface IMessageBoxEmojiKeyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiKeyboard, any> {
|
export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiKeyboard, any> {
|
||||||
private readonly baseUrl: any;
|
private readonly baseUrl: string;
|
||||||
|
|
||||||
constructor(props: IMessageBoxEmojiKeyboard) {
|
constructor(props: IMessageBoxEmojiKeyboard) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { events, logEvent } from '../../utils/log';
|
||||||
|
|
||||||
interface IMessageBoxRecordAudioProps {
|
interface IMessageBoxRecordAudioProps {
|
||||||
theme: string;
|
theme: string;
|
||||||
|
permissionToUpload: boolean;
|
||||||
recordingCallback: Function;
|
recordingCallback: Function;
|
||||||
onFinish: Function;
|
onFinish: Function;
|
||||||
}
|
}
|
||||||
|
@ -192,9 +193,11 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme, permissionToUpload } = this.props;
|
||||||
const { isRecording, isRecorderActive } = this.state;
|
const { isRecording, isRecorderActive } = this.state;
|
||||||
|
if (!permissionToUpload) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (!isRecording && !isRecorderActive) {
|
if (!isRecording && !isRecorderActive) {
|
||||||
return (
|
return (
|
||||||
<BorderlessButton
|
<BorderlessButton
|
||||||
|
|
|
@ -109,6 +109,8 @@ interface IMessageBoxProps {
|
||||||
sharing: boolean;
|
sharing: boolean;
|
||||||
isActionsEnabled: boolean;
|
isActionsEnabled: boolean;
|
||||||
usedCannedResponse: string;
|
usedCannedResponse: string;
|
||||||
|
uploadFilePermission: string[];
|
||||||
|
serverVersion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageBoxState {
|
interface IMessageBoxState {
|
||||||
|
@ -124,6 +126,7 @@ interface IMessageBoxState {
|
||||||
};
|
};
|
||||||
tshow: boolean;
|
tshow: boolean;
|
||||||
mentionLoading: boolean;
|
mentionLoading: boolean;
|
||||||
|
permissionToUpload: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
@ -179,41 +182,13 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
showCommandPreview: false,
|
showCommandPreview: false,
|
||||||
command: {},
|
command: {},
|
||||||
tshow: false,
|
tshow: false,
|
||||||
mentionLoading: false
|
mentionLoading: false,
|
||||||
|
permissionToUpload: true
|
||||||
};
|
};
|
||||||
this.text = '';
|
this.text = '';
|
||||||
this.selection = { start: 0, end: 0 };
|
this.selection = { start: 0, end: 0 };
|
||||||
this.focused = false;
|
this.focused = false;
|
||||||
|
|
||||||
// MessageBox Actions
|
|
||||||
this.options = [
|
|
||||||
{
|
|
||||||
title: I18n.t('Take_a_photo'),
|
|
||||||
icon: 'camera-photo',
|
|
||||||
onPress: this.takePhoto
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: I18n.t('Take_a_video'),
|
|
||||||
icon: 'camera',
|
|
||||||
onPress: this.takeVideo
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: I18n.t('Choose_from_library'),
|
|
||||||
icon: 'image',
|
|
||||||
onPress: this.chooseFromLibrary
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: I18n.t('Choose_file'),
|
|
||||||
icon: 'attach',
|
|
||||||
onPress: this.chooseFile
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: I18n.t('Create_Discussion'),
|
|
||||||
icon: 'discussions',
|
|
||||||
onPress: this.createDiscussion
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const libPickerLabels = {
|
const libPickerLabels = {
|
||||||
cropperChooseText: I18n.t('Choose'),
|
cropperChooseText: I18n.t('Choose'),
|
||||||
cropperCancelText: I18n.t('Cancel'),
|
cropperCancelText: I18n.t('Cancel'),
|
||||||
|
@ -277,6 +252,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
this.onChangeText(usedCannedResponse);
|
this.onChangeText(usedCannedResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setOptions();
|
||||||
|
|
||||||
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
||||||
// didFocus
|
// didFocus
|
||||||
// We should wait pushed views be dismissed
|
// We should wait pushed views be dismissed
|
||||||
|
@ -321,10 +298,20 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: any, nextState: any) {
|
shouldComponentUpdate(nextProps: IMessageBoxProps, nextState: IMessageBoxState) {
|
||||||
const { showEmojiKeyboard, showSend, recording, mentions, commandPreview, tshow, mentionLoading, trackingType } = this.state;
|
const {
|
||||||
|
showEmojiKeyboard,
|
||||||
|
showSend,
|
||||||
|
recording,
|
||||||
|
mentions,
|
||||||
|
commandPreview,
|
||||||
|
tshow,
|
||||||
|
mentionLoading,
|
||||||
|
trackingType,
|
||||||
|
permissionToUpload
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
const { roomType, replying, editing, isFocused, message, theme, usedCannedResponse } = this.props;
|
const { roomType, replying, editing, isFocused, message, theme, usedCannedResponse, uploadFilePermission } = this.props;
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -358,6 +345,9 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
if (nextState.tshow !== tshow) {
|
if (nextState.tshow !== tshow) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (nextState.permissionToUpload !== permissionToUpload) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!dequal(nextState.mentions, mentions)) {
|
if (!dequal(nextState.mentions, mentions)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -367,12 +357,22 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
if (!dequal(nextProps.message?.id, message?.id)) {
|
if (!dequal(nextProps.message?.id, message?.id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!dequal(nextProps.uploadFilePermission, uploadFilePermission)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (nextProps.usedCannedResponse !== usedCannedResponse) {
|
if (nextProps.usedCannedResponse !== usedCannedResponse) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: IMessageBoxProps) {
|
||||||
|
const { uploadFilePermission } = this.props;
|
||||||
|
if (!dequal(prevProps.uploadFilePermission, uploadFilePermission)) {
|
||||||
|
this.setOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
console.countReset(`${this.constructor.name}.render calls`);
|
console.countReset(`${this.constructor.name}.render calls`);
|
||||||
if (this.onChangeText && this.onChangeText.stop) {
|
if (this.onChangeText && this.onChangeText.stop) {
|
||||||
|
@ -404,6 +404,19 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOptions = async () => {
|
||||||
|
const { uploadFilePermission, rid } = this.props;
|
||||||
|
|
||||||
|
// Servers older than 4.2
|
||||||
|
if (!uploadFilePermission) {
|
||||||
|
this.setState({ permissionToUpload: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionToUpload = await RocketChat.hasPermission([uploadFilePermission], rid);
|
||||||
|
this.setState({ permissionToUpload: permissionToUpload[0] });
|
||||||
|
};
|
||||||
|
|
||||||
onChangeText: any = (text: string): void => {
|
onChangeText: any = (text: string): void => {
|
||||||
const isTextEmpty = text.length === 0;
|
const isTextEmpty = text.length === 0;
|
||||||
this.setShowSend(!isTextEmpty);
|
this.setShowSend(!isTextEmpty);
|
||||||
|
@ -666,8 +679,9 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
canUploadFile = (file: any) => {
|
canUploadFile = (file: any) => {
|
||||||
|
const { permissionToUpload } = this.state;
|
||||||
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
|
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
|
||||||
const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize);
|
const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize, permissionToUpload);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -766,8 +780,41 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
showMessageBoxActions = () => {
|
showMessageBoxActions = () => {
|
||||||
logEvent(events.ROOM_SHOW_BOX_ACTIONS);
|
logEvent(events.ROOM_SHOW_BOX_ACTIONS);
|
||||||
|
const { permissionToUpload } = this.state;
|
||||||
const { showActionSheet } = this.props;
|
const { showActionSheet } = this.props;
|
||||||
showActionSheet({ options: this.options });
|
|
||||||
|
const options = [];
|
||||||
|
if (permissionToUpload) {
|
||||||
|
options.push(
|
||||||
|
{
|
||||||
|
title: I18n.t('Take_a_photo'),
|
||||||
|
icon: 'camera-photo',
|
||||||
|
onPress: this.takePhoto
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: I18n.t('Take_a_video'),
|
||||||
|
icon: 'camera',
|
||||||
|
onPress: this.takeVideo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: I18n.t('Choose_from_library'),
|
||||||
|
icon: 'image',
|
||||||
|
onPress: this.chooseFromLibrary
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: I18n.t('Choose_file'),
|
||||||
|
icon: 'attach',
|
||||||
|
onPress: this.chooseFile
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.push({
|
||||||
|
title: I18n.t('Create_Discussion'),
|
||||||
|
icon: 'discussions',
|
||||||
|
onPress: this.createDiscussion
|
||||||
|
});
|
||||||
|
showActionSheet({ options });
|
||||||
};
|
};
|
||||||
|
|
||||||
editCancel = () => {
|
editCancel = () => {
|
||||||
|
@ -968,8 +1015,17 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderContent = () => {
|
renderContent = () => {
|
||||||
const { recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview, mentionLoading } =
|
const {
|
||||||
this.state;
|
recording,
|
||||||
|
showEmojiKeyboard,
|
||||||
|
showSend,
|
||||||
|
mentions,
|
||||||
|
trackingType,
|
||||||
|
commandPreview,
|
||||||
|
showCommandPreview,
|
||||||
|
mentionLoading,
|
||||||
|
permissionToUpload
|
||||||
|
} = this.state;
|
||||||
const {
|
const {
|
||||||
editing,
|
editing,
|
||||||
message,
|
message,
|
||||||
|
@ -995,7 +1051,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
const recordAudio =
|
const recordAudio =
|
||||||
showSend || !Message_AudioRecorderEnabled ? null : (
|
showSend || !Message_AudioRecorderEnabled ? null : (
|
||||||
<RecordAudio theme={theme} recordingCallback={this.recordingCallback} onFinish={this.finishAudioMessage} />
|
<RecordAudio
|
||||||
|
theme={theme}
|
||||||
|
recordingCallback={this.recordingCallback}
|
||||||
|
onFinish={this.finishAudioMessage}
|
||||||
|
permissionToUpload={permissionToUpload}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const commandsPreviewAndMentions = !recording ? (
|
const commandsPreviewAndMentions = !recording ? (
|
||||||
|
@ -1117,11 +1178,12 @@ const mapStateToProps = (state: any) => ({
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
|
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
|
||||||
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize,
|
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize,
|
||||||
Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled
|
Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled,
|
||||||
|
uploadFilePermission: state.permissions['mobile-upload-file']
|
||||||
});
|
});
|
||||||
|
|
||||||
const dispatchToProps = {
|
const dispatchToProps = {
|
||||||
typing: (rid: any, status: any) => userTypingAction(rid, status)
|
typing: (rid: any, status: any) => userTypingAction(rid, status)
|
||||||
};
|
};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox));
|
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox)) as any;
|
||||||
|
|
|
@ -7,28 +7,28 @@ import Touch from '../../../utils/touch';
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
|
|
||||||
interface IPasscodeButton {
|
interface IPasscodeButton {
|
||||||
text: string;
|
text?: string;
|
||||||
icon: string;
|
icon?: string;
|
||||||
theme: string;
|
theme: string;
|
||||||
disabled: boolean;
|
disabled?: boolean;
|
||||||
onPress: Function;
|
onPress?: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Button = React.memo(({ text, disabled, theme, onPress, icon }: Partial<IPasscodeButton>) => {
|
const Button = React.memo(({ text, disabled, theme, onPress, icon }: IPasscodeButton) => {
|
||||||
const press = () => onPress && onPress(text!);
|
const press = () => onPress && onPress(text);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Touch
|
<Touch
|
||||||
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
|
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
|
||||||
underlayColor={themes[theme!].passcodeButtonActive}
|
underlayColor={themes[theme].passcodeButtonActive}
|
||||||
rippleColor={themes[theme!].passcodeButtonActive}
|
rippleColor={themes[theme].passcodeButtonActive}
|
||||||
enabled={!disabled}
|
enabled={!disabled}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
onPress={press}>
|
onPress={press}>
|
||||||
{icon ? (
|
{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>
|
</Touch>
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,7 +20,7 @@ interface IPasscodeBase {
|
||||||
previousPasscode?: string;
|
previousPasscode?: string;
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
showBiometry?: string;
|
showBiometry?: boolean;
|
||||||
onEndProcess: Function;
|
onEndProcess: Function;
|
||||||
onError?: Function;
|
onError?: Function;
|
||||||
onBiometryPress?(): void;
|
onBiometryPress?(): void;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import I18n from '../../i18n';
|
||||||
|
|
||||||
interface IPasscodePasscodeEnter {
|
interface IPasscodePasscodeEnter {
|
||||||
theme: string;
|
theme: string;
|
||||||
hasBiometry: string;
|
hasBiometry: boolean;
|
||||||
finishProcess: Function;
|
finishProcess: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, TextInputProps, View } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from '../presentation/TextInput';
|
||||||
|
@ -45,7 +45,7 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ISearchBox {
|
interface ISearchBox {
|
||||||
onChangeText: () => void;
|
onChangeText: TextInputProps['onChangeText'];
|
||||||
onSubmitEditing: () => void;
|
onSubmitEditing: () => void;
|
||||||
hasCancel: boolean;
|
hasCancel: boolean;
|
||||||
onCancelPress: Function;
|
onCancelPress: Function;
|
||||||
|
|
|
@ -8,6 +8,7 @@ interface IStatus {
|
||||||
status: string;
|
status: string;
|
||||||
size: number;
|
size: number;
|
||||||
style?: StyleProp<TextStyle>;
|
style?: StyleProp<TextStyle>;
|
||||||
|
testID?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: IStatus) => {
|
const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: IStatus) => {
|
||||||
|
|
|
@ -43,7 +43,11 @@ const Content = React.memo(
|
||||||
content = <Text style={[styles.text, { color: themes[props.theme].bodyText }]}>{I18n.t('Sent_an_attachment')}</Text>;
|
content = <Text style={[styles.text, { color: themes[props.theme].bodyText }]}>{I18n.t('Sent_an_attachment')}</Text>;
|
||||||
} else if (props.isEncrypted) {
|
} else if (props.isEncrypted) {
|
||||||
content = (
|
content = (
|
||||||
<Text style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}>{I18n.t('Encrypted_message')}</Text>
|
<Text
|
||||||
|
style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}
|
||||||
|
accessibilityLabel={I18n.t('Encrypted_message')}>
|
||||||
|
{I18n.t('Encrypted_message')}
|
||||||
|
</Text>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
|
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { themes } from '../../constants/colors';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
import { fileDownloadAndPreview } from '../../utils/fileDownload';
|
import { fileDownloadAndPreview } from '../../utils/fileDownload';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
|
import { IAttachment } from '../../definitions/IAttachment';
|
||||||
import RCActivityIndicator from '../ActivityIndicator';
|
import RCActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -90,43 +91,26 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IMessageReplyAttachment {
|
|
||||||
author_name: string;
|
|
||||||
message_link: string;
|
|
||||||
ts: string;
|
|
||||||
text: string;
|
|
||||||
title: string;
|
|
||||||
short: boolean;
|
|
||||||
value: string;
|
|
||||||
title_link: string;
|
|
||||||
author_link: string;
|
|
||||||
type: string;
|
|
||||||
color: string;
|
|
||||||
description: string;
|
|
||||||
fields: IMessageReplyAttachment[];
|
|
||||||
thumb_url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IMessageTitle {
|
interface IMessageTitle {
|
||||||
attachment: Partial<IMessageReplyAttachment>;
|
attachment: IAttachment;
|
||||||
timeFormat: string;
|
timeFormat: string;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageDescription {
|
interface IMessageDescription {
|
||||||
attachment: Partial<IMessageReplyAttachment>;
|
attachment: IAttachment;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: Function;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageFields {
|
interface IMessageFields {
|
||||||
attachment: Partial<IMessageReplyAttachment>;
|
attachment: IAttachment;
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageReply {
|
interface IMessageReply {
|
||||||
attachment: IMessageReplyAttachment;
|
attachment: IAttachment;
|
||||||
timeFormat: string;
|
timeFormat: string;
|
||||||
index: number;
|
index: number;
|
||||||
theme: string;
|
theme: string;
|
||||||
|
@ -198,7 +182,7 @@ const Fields = React.memo(
|
||||||
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
|
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
|
||||||
{/* @ts-ignore*/}
|
{/* @ts-ignore*/}
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={field.value}
|
msg={field.value!}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { fileDownload } from '../../utils/fileDownload';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import { LISTENER } from '../Toast';
|
import { LISTENER } from '../Toast';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
import { IAttachment } from '../../definitions/IAttachment';
|
||||||
import RCActivityIndicator from '../ActivityIndicator';
|
import RCActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
|
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
|
||||||
|
@ -30,14 +31,7 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IMessageVideo {
|
interface IMessageVideo {
|
||||||
file: {
|
file: IAttachment;
|
||||||
title: string;
|
|
||||||
title_link: string;
|
|
||||||
type: string;
|
|
||||||
video_type: string;
|
|
||||||
video_url: string;
|
|
||||||
description: string;
|
|
||||||
};
|
|
||||||
showAttachment: Function;
|
showAttachment: Function;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: Function;
|
||||||
theme: string;
|
theme: string;
|
||||||
|
|
|
@ -84,7 +84,7 @@ export interface IMessageContent {
|
||||||
export interface IMessageDiscussion {
|
export interface IMessageDiscussion {
|
||||||
msg: string;
|
msg: string;
|
||||||
dcount: number;
|
dcount: number;
|
||||||
dlm: string;
|
dlm: Date;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
export interface IAttachment {
|
||||||
|
ts: Date;
|
||||||
|
title: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
title_link?: string;
|
||||||
|
image_url?: string;
|
||||||
|
image_type?: string;
|
||||||
|
video_url?: string;
|
||||||
|
video_type?: string;
|
||||||
|
title_link_download?: boolean;
|
||||||
|
fields?: IAttachment[];
|
||||||
|
image_dimensions?: { width?: number; height?: number };
|
||||||
|
image_preview?: string;
|
||||||
|
image_size?: number;
|
||||||
|
author_name?: string;
|
||||||
|
author_icon?: string;
|
||||||
|
message_link?: string;
|
||||||
|
text?: string;
|
||||||
|
short?: boolean;
|
||||||
|
value?: string;
|
||||||
|
author_link?: string;
|
||||||
|
color?: string;
|
||||||
|
thumb_url?: string;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface ICommand {
|
||||||
|
event: {
|
||||||
|
input: string;
|
||||||
|
modifierFlags: number;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface ICustomEmoji {
|
||||||
|
name?: string;
|
||||||
|
aliases?: string;
|
||||||
|
extension: string;
|
||||||
|
_updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TCustomEmojiModel = ICustomEmoji & Model;
|
|
@ -0,0 +1,10 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface IFrequentlyUsedEmoji {
|
||||||
|
content?: string;
|
||||||
|
extension?: string;
|
||||||
|
isCustom: boolean;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TFrequentlyUsedEmoji = IFrequentlyUsedEmoji & Model;
|
|
@ -0,0 +1,18 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface ILoggedUser {
|
||||||
|
id: string;
|
||||||
|
token: string;
|
||||||
|
username: string;
|
||||||
|
name: string;
|
||||||
|
language?: string;
|
||||||
|
status: string;
|
||||||
|
statusText?: string;
|
||||||
|
roles: string[];
|
||||||
|
avatarETag?: string;
|
||||||
|
showMessageInMainThread: boolean;
|
||||||
|
isFromWebView: boolean;
|
||||||
|
enableMessageParserEarlyAdoption?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TLoggedUser = ILoggedUser & Model;
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface IMention {
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
username: string;
|
||||||
|
type: string;
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import { IAttachment } from './IAttachment';
|
||||||
|
import { IReaction } from './IReaction';
|
||||||
|
import { SubscriptionType } from './ISubscription';
|
||||||
|
|
||||||
|
export interface IUserMessage {
|
||||||
|
_id: string;
|
||||||
|
username?: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserMention extends IUserMessage {
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserChannel {
|
||||||
|
[index: number]: string | number;
|
||||||
|
name: string;
|
||||||
|
_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEditedBy {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TOnLinkPress = (link: string) => void;
|
||||||
|
|
||||||
|
export interface ITranslations {
|
||||||
|
_id: string;
|
||||||
|
language: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ILastMessage {
|
||||||
|
_id: string;
|
||||||
|
rid: string;
|
||||||
|
tshow: boolean;
|
||||||
|
tmid: string;
|
||||||
|
msg: string;
|
||||||
|
ts: Date;
|
||||||
|
u: IUserMessage;
|
||||||
|
_updatedAt: Date;
|
||||||
|
urls: string[];
|
||||||
|
mentions: IUserMention[];
|
||||||
|
channels: IUserChannel[];
|
||||||
|
md: MarkdownAST;
|
||||||
|
attachments: IAttachment[];
|
||||||
|
reactions: IReaction[];
|
||||||
|
unread: boolean;
|
||||||
|
status: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IMessage {
|
||||||
|
msg?: string;
|
||||||
|
t?: SubscriptionType;
|
||||||
|
ts: Date;
|
||||||
|
u: IUserMessage;
|
||||||
|
alias: string;
|
||||||
|
parseUrls: boolean;
|
||||||
|
groupable?: boolean;
|
||||||
|
avatar?: string;
|
||||||
|
emoji?: string;
|
||||||
|
attachments?: IAttachment[];
|
||||||
|
urls?: string[];
|
||||||
|
_updatedAt: Date;
|
||||||
|
status?: number;
|
||||||
|
pinned?: boolean;
|
||||||
|
starred?: boolean;
|
||||||
|
editedBy?: IEditedBy;
|
||||||
|
reactions?: IReaction[];
|
||||||
|
role?: string;
|
||||||
|
drid?: string;
|
||||||
|
dcount?: number;
|
||||||
|
dlm?: Date;
|
||||||
|
tmid?: string;
|
||||||
|
tcount?: number;
|
||||||
|
tlm?: Date;
|
||||||
|
replies?: string[];
|
||||||
|
mentions?: IUserMention[];
|
||||||
|
channels?: IUserChannel[];
|
||||||
|
unread?: boolean;
|
||||||
|
autoTranslate?: boolean;
|
||||||
|
translations?: ITranslations[];
|
||||||
|
tmsg?: string;
|
||||||
|
blocks?: any;
|
||||||
|
e2e?: string;
|
||||||
|
tshow?: boolean;
|
||||||
|
md?: MarkdownAST;
|
||||||
|
subscription: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TMessageModel = IMessage & Model;
|
|
@ -0,0 +1,12 @@
|
||||||
|
export interface INotification {
|
||||||
|
message: string;
|
||||||
|
style: string;
|
||||||
|
ejson: string;
|
||||||
|
collapse_key: string;
|
||||||
|
notId: string;
|
||||||
|
msgcnt: string;
|
||||||
|
title: string;
|
||||||
|
from: string;
|
||||||
|
image: string;
|
||||||
|
soundname: string;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface IPermission {
|
||||||
|
id: string;
|
||||||
|
roles: string[];
|
||||||
|
_updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TPermissionModel = IPermission & Model;
|
|
@ -0,0 +1,5 @@
|
||||||
|
export interface IReaction {
|
||||||
|
_id: string;
|
||||||
|
emoji: string;
|
||||||
|
usernames: string[];
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface IRole {
|
||||||
|
id: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TRoleModel = IRole & Model;
|
|
@ -0,0 +1,20 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
import { IServedBy } from './IServedBy';
|
||||||
|
|
||||||
|
export interface IRoom {
|
||||||
|
id: string;
|
||||||
|
customFields: string[];
|
||||||
|
broadcast: boolean;
|
||||||
|
encrypted: boolean;
|
||||||
|
ro: boolean;
|
||||||
|
v?: string[];
|
||||||
|
servedBy?: IServedBy;
|
||||||
|
departmentId?: string;
|
||||||
|
livechatData?: any;
|
||||||
|
tags?: string[];
|
||||||
|
e2eKeyId?: string;
|
||||||
|
avatarETag?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TRoomModel = IRoom & Model;
|
|
@ -0,0 +1,5 @@
|
||||||
|
export interface IServedBy {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
ts: Date;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface IServer {
|
||||||
|
name: string;
|
||||||
|
iconURL: string;
|
||||||
|
useRealName: boolean;
|
||||||
|
FileUpload_MediaTypeWhiteList: string;
|
||||||
|
FileUpload_MaxFileSize: number;
|
||||||
|
roomsUpdatedAt: Date;
|
||||||
|
version: string;
|
||||||
|
lastLocalAuthenticatedSession: Date;
|
||||||
|
autoLock: boolean;
|
||||||
|
autoLockTime?: number;
|
||||||
|
biometry?: boolean;
|
||||||
|
uniqueID: string;
|
||||||
|
enterpriseModules: string;
|
||||||
|
E2E_Enable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TServerModel = IServer & Model;
|
|
@ -0,0 +1,10 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface IServerHistory {
|
||||||
|
id: string;
|
||||||
|
url: string;
|
||||||
|
username: string;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TServerHistory = IServerHistory & Model;
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface ISettings {
|
||||||
|
id: string;
|
||||||
|
valueAsString?: string;
|
||||||
|
valueAsBoolean?: boolean;
|
||||||
|
valueAsNumber?: number;
|
||||||
|
valueAsArray?: string[];
|
||||||
|
_updatedAt?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSettingsModel = ISettings & Model;
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface ISlashCommand {
|
||||||
|
id: string;
|
||||||
|
params?: string;
|
||||||
|
description?: string;
|
||||||
|
clientOnly?: boolean;
|
||||||
|
providesPreview?: boolean;
|
||||||
|
appId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSlashCommandModel = ISlashCommand & Model;
|
|
@ -0,0 +1,91 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
import Relation from '@nozbe/watermelondb/Relation';
|
||||||
|
|
||||||
|
import { ILastMessage, TMessageModel } from './IMessage';
|
||||||
|
import { IServedBy } from './IServedBy';
|
||||||
|
import { TThreadModel } from './IThread';
|
||||||
|
import { TThreadMessageModel } from './IThreadMessage';
|
||||||
|
import { TUploadModel } from './IUpload';
|
||||||
|
|
||||||
|
export enum SubscriptionType {
|
||||||
|
GROUP = 'p',
|
||||||
|
DIRECT = 'd',
|
||||||
|
CHANNEL = 'c',
|
||||||
|
OMNICHANNEL = 'l',
|
||||||
|
THREAD = 'thread'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVisitor {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
token: string;
|
||||||
|
status: string;
|
||||||
|
lastMessageTs: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISubscription {
|
||||||
|
_id: string; // _id belongs watermelonDB
|
||||||
|
id: string; // id from server
|
||||||
|
f: boolean;
|
||||||
|
t: SubscriptionType;
|
||||||
|
ts: Date;
|
||||||
|
ls: Date;
|
||||||
|
name: string;
|
||||||
|
fname?: string;
|
||||||
|
rid: string; // the same as id
|
||||||
|
open: boolean;
|
||||||
|
alert: boolean;
|
||||||
|
roles?: string[];
|
||||||
|
unread: number;
|
||||||
|
userMentions: number;
|
||||||
|
groupMentions: number;
|
||||||
|
tunread?: string[];
|
||||||
|
tunreadUser?: string[];
|
||||||
|
tunreadGroup?: string[];
|
||||||
|
roomUpdatedAt: Date;
|
||||||
|
ro: boolean;
|
||||||
|
lastOpen?: Date;
|
||||||
|
description?: string;
|
||||||
|
announcement?: string;
|
||||||
|
bannerClosed?: boolean;
|
||||||
|
topic?: string;
|
||||||
|
blocked?: boolean;
|
||||||
|
blocker?: boolean;
|
||||||
|
reactWhenReadOnly?: boolean;
|
||||||
|
archived: boolean;
|
||||||
|
joinCodeRequired?: boolean;
|
||||||
|
muted?: string[];
|
||||||
|
ignored?: string[];
|
||||||
|
broadcast?: boolean;
|
||||||
|
prid?: string;
|
||||||
|
draftMessage?: string;
|
||||||
|
lastThreadSync?: Date;
|
||||||
|
jitsiTimeout?: number;
|
||||||
|
autoTranslate?: boolean;
|
||||||
|
autoTranslateLanguage: string;
|
||||||
|
lastMessage?: ILastMessage;
|
||||||
|
hideUnreadStatus?: boolean;
|
||||||
|
sysMes?: string[] | boolean;
|
||||||
|
uids?: string[];
|
||||||
|
usernames?: string[];
|
||||||
|
visitor?: IVisitor;
|
||||||
|
departmentId?: string;
|
||||||
|
servedBy?: IServedBy;
|
||||||
|
livechatData?: any;
|
||||||
|
tags?: string[];
|
||||||
|
E2EKey?: string;
|
||||||
|
encrypted?: boolean;
|
||||||
|
e2eKeyId?: string;
|
||||||
|
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>;
|
||||||
|
threadMessages: Relation<TThreadMessageModel>;
|
||||||
|
uploads: Relation<TUploadModel>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSubscriptionModel = ISubscription & Model;
|
|
@ -1,5 +1,5 @@
|
||||||
// https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts
|
// https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts
|
||||||
export const TEAM_TYPE = {
|
exports.TEAM_TYPE = {
|
||||||
PUBLIC: 0,
|
PUBLIC: 0,
|
||||||
PRIVATE: 1
|
PRIVATE: 1
|
||||||
};
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
export type TThemeMode = 'automatic' | 'light' | 'dark';
|
||||||
|
|
||||||
|
export type TDarkLevel = 'black' | 'dark';
|
||||||
|
|
||||||
|
export interface IThemePreference {
|
||||||
|
currentTheme: TThemeMode;
|
||||||
|
darkLevel: TDarkLevel;
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import { IAttachment } from './IAttachment';
|
||||||
|
import { IEditedBy, IUserChannel, IUserMention, IUserMessage } from './IMessage';
|
||||||
|
import { IReaction } from './IReaction';
|
||||||
|
import { SubscriptionType } from './ISubscription';
|
||||||
|
|
||||||
|
export interface IUrl {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
image: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFileThread {
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IThreadResult {
|
||||||
|
_id: string;
|
||||||
|
rid: string;
|
||||||
|
ts: string;
|
||||||
|
msg: string;
|
||||||
|
file?: IFileThread;
|
||||||
|
files?: IFileThread[];
|
||||||
|
groupable?: boolean;
|
||||||
|
attachments?: IAttachment[];
|
||||||
|
md?: MarkdownAST;
|
||||||
|
u: IUserMessage;
|
||||||
|
_updatedAt: string;
|
||||||
|
urls: IUrl[];
|
||||||
|
mentions: IUserMention[];
|
||||||
|
channels: IUserChannel[];
|
||||||
|
replies: string[];
|
||||||
|
tcount: number;
|
||||||
|
tlm: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IThread {
|
||||||
|
id: string;
|
||||||
|
msg?: string;
|
||||||
|
t?: SubscriptionType;
|
||||||
|
rid: string;
|
||||||
|
_updatedAt: Date;
|
||||||
|
ts: Date;
|
||||||
|
u: IUserMessage;
|
||||||
|
alias?: string;
|
||||||
|
parseUrls?: boolean;
|
||||||
|
groupable?: boolean;
|
||||||
|
avatar?: string;
|
||||||
|
emoji?: string;
|
||||||
|
attachments?: IAttachment[];
|
||||||
|
urls?: IUrl[];
|
||||||
|
status?: number;
|
||||||
|
pinned?: boolean;
|
||||||
|
starred?: boolean;
|
||||||
|
editedBy?: IEditedBy;
|
||||||
|
reactions?: IReaction[];
|
||||||
|
role?: string;
|
||||||
|
drid?: string;
|
||||||
|
dcount?: number;
|
||||||
|
dlm?: number;
|
||||||
|
tmid?: string;
|
||||||
|
tcount?: number;
|
||||||
|
tlm?: Date;
|
||||||
|
replies?: string[];
|
||||||
|
mentions?: IUserMention[];
|
||||||
|
channels?: IUserChannel[];
|
||||||
|
unread?: boolean;
|
||||||
|
autoTranslate?: boolean;
|
||||||
|
translations?: any;
|
||||||
|
e2e?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TThreadModel = IThread & Model;
|
|
@ -0,0 +1,44 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
import { IAttachment } from './IAttachment';
|
||||||
|
import { IEditedBy, ITranslations, IUserChannel, IUserMention, IUserMessage } from './IMessage';
|
||||||
|
import { IReaction } from './IReaction';
|
||||||
|
import { SubscriptionType } from './ISubscription';
|
||||||
|
|
||||||
|
export interface IThreadMessage {
|
||||||
|
msg?: string;
|
||||||
|
t?: SubscriptionType;
|
||||||
|
rid: string;
|
||||||
|
ts: Date;
|
||||||
|
u: IUserMessage;
|
||||||
|
alias?: string;
|
||||||
|
parseUrls?: boolean;
|
||||||
|
groupable?: boolean;
|
||||||
|
avatar?: string;
|
||||||
|
emoji?: string;
|
||||||
|
attachments?: IAttachment[];
|
||||||
|
urls?: string[];
|
||||||
|
_updatedAt?: Date;
|
||||||
|
status?: number;
|
||||||
|
pinned?: boolean;
|
||||||
|
starred?: boolean;
|
||||||
|
editedBy?: IEditedBy;
|
||||||
|
reactions?: IReaction[];
|
||||||
|
role?: string;
|
||||||
|
drid?: string;
|
||||||
|
dcount?: number;
|
||||||
|
dlm?: Date;
|
||||||
|
tmid?: string;
|
||||||
|
tcount?: number;
|
||||||
|
tlm?: Date;
|
||||||
|
replies?: string[];
|
||||||
|
mentions?: IUserMention[];
|
||||||
|
channels?: IUserChannel[];
|
||||||
|
unread?: boolean;
|
||||||
|
autoTranslate?: boolean;
|
||||||
|
translations?: ITranslations[];
|
||||||
|
e2e?: string;
|
||||||
|
subscription?: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TThreadMessageModel = IThreadMessage & Model;
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface IUpload {
|
||||||
|
id: string;
|
||||||
|
path?: string;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
size: number;
|
||||||
|
type?: string;
|
||||||
|
store?: string;
|
||||||
|
progress: number;
|
||||||
|
error: boolean;
|
||||||
|
subscription: { id: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TUploadModel = IUpload & Model;
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface IUrl {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
image: string;
|
||||||
|
url: string;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface IUser {
|
||||||
|
_id: string;
|
||||||
|
name?: string;
|
||||||
|
username: string;
|
||||||
|
avatarETag?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TUserModel = IUser & Model;
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { RouteProp } from '@react-navigation/native';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
export * from './IAttachment';
|
||||||
|
export * from './IMessage';
|
||||||
|
export * from './INotification';
|
||||||
|
export * from './IRoom';
|
||||||
|
export * from './IServer';
|
||||||
|
export * from './ISubscription';
|
||||||
|
|
||||||
|
export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> {
|
||||||
|
navigation: StackNavigationProp<T, S>;
|
||||||
|
route: RouteProp<T, S>;
|
||||||
|
dispatch: Dispatch;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from './redux';
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { TActionSelectedUsers } from '../../actions/selectedUsers';
|
||||||
|
import { TActionActiveUsers } from '../../actions/activeUsers';
|
||||||
|
// REDUCERS
|
||||||
|
import { IActiveUsers } from '../../reducers/activeUsers';
|
||||||
|
import { ISelectedUsers } from '../../reducers/selectedUsers';
|
||||||
|
|
||||||
|
export interface IApplicationState {
|
||||||
|
settings: any;
|
||||||
|
login: any;
|
||||||
|
meteor: any;
|
||||||
|
server: any;
|
||||||
|
selectedUsers: ISelectedUsers;
|
||||||
|
createChannel: any;
|
||||||
|
app: any;
|
||||||
|
room: any;
|
||||||
|
rooms: any;
|
||||||
|
sortPreferences: any;
|
||||||
|
share: any;
|
||||||
|
customEmojis: any;
|
||||||
|
activeUsers: IActiveUsers;
|
||||||
|
usersTyping: any;
|
||||||
|
inviteLinks: any;
|
||||||
|
createDiscussion: any;
|
||||||
|
inquiry: any;
|
||||||
|
enterpriseModules: any;
|
||||||
|
encryption: any;
|
||||||
|
permissions: any;
|
||||||
|
roles: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TApplicationActions = TActionActiveUsers & TActionSelectedUsers;
|
|
@ -22,7 +22,7 @@ export interface IDimensionsContextProps {
|
||||||
|
|
||||||
export const DimensionsContext = React.createContext<Partial<IDimensionsContextProps>>(Dimensions.get('window'));
|
export const DimensionsContext = React.createContext<Partial<IDimensionsContextProps>>(Dimensions.get('window'));
|
||||||
|
|
||||||
export function withDimensions(Component: any) {
|
export function withDimensions(Component: any): any {
|
||||||
const DimensionsComponent = (props: any) => (
|
const DimensionsComponent = (props: any) => (
|
||||||
<DimensionsContext.Consumer>{contexts => <Component {...props} {...contexts} />}</DimensionsContext.Consumer>
|
<DimensionsContext.Consumer>{contexts => <Component {...props} {...contexts} />}</DimensionsContext.Consumer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { inquiryQueueAdd, inquiryQueueRemove, inquiryQueueUpdate, inquiryRequest
|
||||||
const removeListener = listener => listener.stop();
|
const removeListener = listener => listener.stop();
|
||||||
|
|
||||||
let connectedListener;
|
let connectedListener;
|
||||||
let disconnectedListener;
|
|
||||||
let queueListener;
|
let queueListener;
|
||||||
|
|
||||||
const streamTopic = 'stream-livechat-inquiry-queue-observer';
|
const streamTopic = 'stream-livechat-inquiry-queue-observer';
|
||||||
|
@ -48,10 +47,6 @@ export default function subscribeInquiry() {
|
||||||
connectedListener.then(removeListener);
|
connectedListener.then(removeListener);
|
||||||
connectedListener = false;
|
connectedListener = false;
|
||||||
}
|
}
|
||||||
if (disconnectedListener) {
|
|
||||||
disconnectedListener.then(removeListener);
|
|
||||||
disconnectedListener = false;
|
|
||||||
}
|
|
||||||
if (queueListener) {
|
if (queueListener) {
|
||||||
queueListener.then(removeListener);
|
queueListener.then(removeListener);
|
||||||
queueListener = false;
|
queueListener = false;
|
||||||
|
@ -59,7 +54,6 @@ export default function subscribeInquiry() {
|
||||||
};
|
};
|
||||||
|
|
||||||
connectedListener = RocketChat.onStreamData('connected', handleConnection);
|
connectedListener = RocketChat.onStreamData('connected', handleConnection);
|
||||||
disconnectedListener = RocketChat.onStreamData('close', handleConnection);
|
|
||||||
queueListener = RocketChat.onStreamData(streamTopic, handleQueueMessageReceived);
|
queueListener = RocketChat.onStreamData(streamTopic, handleQueueMessageReceived);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -161,4 +161,5 @@ const mapStateToProps = state => ({
|
||||||
showAvatar: state.sortPreferences.showAvatar,
|
showAvatar: state.sortPreferences.showAvatar,
|
||||||
displayMode: state.sortPreferences.displayMode
|
displayMode: state.sortPreferences.displayMode
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withDimensions(withTheme(QueueListView)));
|
export default connect(mapStateToProps)(withDimensions(withTheme(QueueListView)));
|
||||||
|
|
|
@ -13,3 +13,4 @@ declare module 'react-native-mime-types';
|
||||||
declare module 'react-native-restart';
|
declare module 'react-native-restart';
|
||||||
declare module 'react-native-prompt-android';
|
declare module 'react-native-prompt-android';
|
||||||
declare module 'react-native-jitsi-meet';
|
declare module 'react-native-jitsi-meet';
|
||||||
|
declare module 'rn-root-view';
|
||||||
|
|
|
@ -328,7 +328,6 @@
|
||||||
"N_users": "{{n}} مستخدمين",
|
"N_users": "{{n}} مستخدمين",
|
||||||
"N_channels": "{{n}} القنوات",
|
"N_channels": "{{n}} القنوات",
|
||||||
"Name": "اسم",
|
"Name": "اسم",
|
||||||
"Navigation_history": "تاريخ التصفح",
|
|
||||||
"Never": "أبداً",
|
"Never": "أبداً",
|
||||||
"New_Message": "رسالة جديدة",
|
"New_Message": "رسالة جديدة",
|
||||||
"New_Password": "كلمة مرور جديدة",
|
"New_Password": "كلمة مرور جديدة",
|
||||||
|
|
|
@ -330,7 +330,6 @@
|
||||||
"N_users": "{{n}} Benutzer",
|
"N_users": "{{n}} Benutzer",
|
||||||
"N_channels": "{{n}} Kanäle",
|
"N_channels": "{{n}} Kanäle",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Navigation_history": "Navigations-Verlauf",
|
|
||||||
"Never": "Niemals",
|
"Never": "Niemals",
|
||||||
"New_Message": "Neue Nachricht",
|
"New_Message": "Neue Nachricht",
|
||||||
"New_Password": "Neues Kennwort",
|
"New_Password": "Neues Kennwort",
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"error-save-video": "Error while saving video",
|
"error-save-video": "Error while saving video",
|
||||||
"error-field-unavailable": "{{field}} is already in use :(",
|
"error-field-unavailable": "{{field}} is already in use :(",
|
||||||
"error-file-too-large": "File is too large",
|
"error-file-too-large": "File is too large",
|
||||||
|
"error-not-permission-to-upload-file": "You don't have permission to upload files",
|
||||||
"error-importer-not-defined": "The importer was not defined correctly, it is missing the Import class.",
|
"error-importer-not-defined": "The importer was not defined correctly, it is missing the Import class.",
|
||||||
"error-input-is-not-a-valid-field": "{{input}} is not a valid {{field}}",
|
"error-input-is-not-a-valid-field": "{{input}} is not a valid {{field}}",
|
||||||
"error-invalid-actionlink": "Invalid action link",
|
"error-invalid-actionlink": "Invalid action link",
|
||||||
|
@ -330,7 +331,6 @@
|
||||||
"N_users": "{{n}} users",
|
"N_users": "{{n}} users",
|
||||||
"N_channels": "{{n}} channels",
|
"N_channels": "{{n}} channels",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Navigation_history": "Navigation history",
|
|
||||||
"Never": "Never",
|
"Never": "Never",
|
||||||
"New_Message": "New Message",
|
"New_Message": "New Message",
|
||||||
"New_Password": "New Password",
|
"New_Password": "New Password",
|
||||||
|
|
|
@ -330,7 +330,6 @@
|
||||||
"N_users": "{{n}} utilisateurs",
|
"N_users": "{{n}} utilisateurs",
|
||||||
"N_channels": "{{n}} canaux",
|
"N_channels": "{{n}} canaux",
|
||||||
"Name": "Nom",
|
"Name": "Nom",
|
||||||
"Navigation_history": "Historique de navigation",
|
|
||||||
"Never": "Jamais",
|
"Never": "Jamais",
|
||||||
"New_Message": "Nouveau message",
|
"New_Message": "Nouveau message",
|
||||||
"New_Password": "Nouveau mot de passe",
|
"New_Password": "Nouveau mot de passe",
|
||||||
|
@ -782,5 +781,8 @@
|
||||||
"No_canned_responses": "Pas de réponses standardisées",
|
"No_canned_responses": "Pas de réponses standardisées",
|
||||||
"Send_email_confirmation": "Envoyer un e-mail de confirmation",
|
"Send_email_confirmation": "Envoyer un e-mail de confirmation",
|
||||||
"sending_email_confirmation": "envoi d'e-mail de confirmation",
|
"sending_email_confirmation": "envoi d'e-mail de confirmation",
|
||||||
"Enable_Message_Parser": "Activer le parseur de messages"
|
"Enable_Message_Parser": "Activer le parseur de messages",
|
||||||
|
"Unsupported_format": "Format non supporté",
|
||||||
|
"Downloaded_file": "Fichier téléchargé",
|
||||||
|
"Error_Download_file": "Erreur lors du téléchargement du fichier"
|
||||||
}
|
}
|
|
@ -322,7 +322,6 @@
|
||||||
"N_people_reacted": "{{n}} persone hanno reagito",
|
"N_people_reacted": "{{n}} persone hanno reagito",
|
||||||
"N_users": "{{n}} utenti",
|
"N_users": "{{n}} utenti",
|
||||||
"Name": "Nome",
|
"Name": "Nome",
|
||||||
"Navigation_history": "Cronologia di navigazione",
|
|
||||||
"Never": "Mai",
|
"Never": "Mai",
|
||||||
"New_Message": "Nuovo messaggio",
|
"New_Message": "Nuovo messaggio",
|
||||||
"New_Password": "Nuova password",
|
"New_Password": "Nuova password",
|
||||||
|
|
|
@ -330,7 +330,6 @@
|
||||||
"N_users": "{{n}} gebruikers",
|
"N_users": "{{n}} gebruikers",
|
||||||
"N_channels": "{{n}} kanalen",
|
"N_channels": "{{n}} kanalen",
|
||||||
"Name": "Naam",
|
"Name": "Naam",
|
||||||
"Navigation_history": "Navigatie geschiedenis",
|
|
||||||
"Never": "Nooit",
|
"Never": "Nooit",
|
||||||
"New_Message": "Nieuw bericht",
|
"New_Message": "Nieuw bericht",
|
||||||
"New_Password": "Nieuw wachtwoord",
|
"New_Password": "Nieuw wachtwoord",
|
||||||
|
@ -782,5 +781,8 @@
|
||||||
"No_canned_responses": "Geen standaardantwoorden",
|
"No_canned_responses": "Geen standaardantwoorden",
|
||||||
"Send_email_confirmation": "Stuur e-mailbevestiging",
|
"Send_email_confirmation": "Stuur e-mailbevestiging",
|
||||||
"sending_email_confirmation": "e-mailbevestiging aan het verzenden",
|
"sending_email_confirmation": "e-mailbevestiging aan het verzenden",
|
||||||
"Enable_Message_Parser": "Berichtparser inschakelen"
|
"Enable_Message_Parser": "Berichtparser inschakelen",
|
||||||
|
"Unsupported_format": "Niet ondersteund formaat",
|
||||||
|
"Downloaded_file": "Gedownload bestand",
|
||||||
|
"Error_Download_file": "Fout tijdens het downloaden van bestand"
|
||||||
}
|
}
|
|
@ -309,7 +309,6 @@
|
||||||
"N_users": "{{n}} usuários",
|
"N_users": "{{n}} usuários",
|
||||||
"N_channels": "{{n}} canais",
|
"N_channels": "{{n}} canais",
|
||||||
"Name": "Nome",
|
"Name": "Nome",
|
||||||
"Navigation_history": "Histórico de navegação",
|
|
||||||
"Never": "Nunca",
|
"Never": "Nunca",
|
||||||
"New_Message": "Nova Mensagem",
|
"New_Message": "Nova Mensagem",
|
||||||
"New_Password": "Nova Senha",
|
"New_Password": "Nova Senha",
|
||||||
|
|
|
@ -329,7 +329,6 @@
|
||||||
"N_users": "{{n}} utilizadores",
|
"N_users": "{{n}} utilizadores",
|
||||||
"N_channels": "{{n}} canais",
|
"N_channels": "{{n}} canais",
|
||||||
"Name": "Nome",
|
"Name": "Nome",
|
||||||
"Navigation_history": "Histórico de navegação",
|
|
||||||
"Never": "Nunca",
|
"Never": "Nunca",
|
||||||
"New_Message": "Nova Mensagem",
|
"New_Message": "Nova Mensagem",
|
||||||
"New_Password": "Nova Palavra-passe",
|
"New_Password": "Nova Palavra-passe",
|
||||||
|
|
|
@ -330,7 +330,6 @@
|
||||||
"N_users": "{{n}} пользователи",
|
"N_users": "{{n}} пользователи",
|
||||||
"N_channels": "{{n}} каналов",
|
"N_channels": "{{n}} каналов",
|
||||||
"Name": "Имя",
|
"Name": "Имя",
|
||||||
"Navigation_history": "История навигации",
|
|
||||||
"Never": "Никогда",
|
"Never": "Никогда",
|
||||||
"New_Message": "Новое сообщение",
|
"New_Message": "Новое сообщение",
|
||||||
"New_Password": "Новый пароль",
|
"New_Password": "Новый пароль",
|
||||||
|
@ -782,5 +781,8 @@
|
||||||
"No_canned_responses": "Нет заготовленных ответов",
|
"No_canned_responses": "Нет заготовленных ответов",
|
||||||
"Send_email_confirmation": "Отправить электронное письмо с подтверждением",
|
"Send_email_confirmation": "Отправить электронное письмо с подтверждением",
|
||||||
"sending_email_confirmation": "отправка подтверждения по электронной почте",
|
"sending_email_confirmation": "отправка подтверждения по электронной почте",
|
||||||
"Enable_Message_Parser": "Включить парсер сообщений"
|
"Enable_Message_Parser": "Включить парсер сообщений",
|
||||||
|
"Unsupported_format": "Неподдерживаемый формат",
|
||||||
|
"Downloaded_file": "Скачанный файл",
|
||||||
|
"Error_Download_file": "Ошибка при скачивании файла"
|
||||||
}
|
}
|
|
@ -323,7 +323,6 @@
|
||||||
"N_people_reacted": "{{n}} kişi tepki verdi",
|
"N_people_reacted": "{{n}} kişi tepki verdi",
|
||||||
"N_users": "{{n}} kullanıcı",
|
"N_users": "{{n}} kullanıcı",
|
||||||
"Name": "İsim",
|
"Name": "İsim",
|
||||||
"Navigation_history": "Gezinti geçmişi",
|
|
||||||
"Never": "Asla",
|
"Never": "Asla",
|
||||||
"New_Message": "Yeni İleti",
|
"New_Message": "Yeni İleti",
|
||||||
"New_Password": "Yeni Şifre",
|
"New_Password": "Yeni Şifre",
|
||||||
|
|
|
@ -320,7 +320,6 @@
|
||||||
"N_people_reacted": "{{n}} 人回复",
|
"N_people_reacted": "{{n}} 人回复",
|
||||||
"N_users": "{{n}} 位用户",
|
"N_users": "{{n}} 位用户",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
"Navigation_history": "浏览历史记录",
|
|
||||||
"Never": "从不",
|
"Never": "从不",
|
||||||
"New_Message": "新信息",
|
"New_Message": "新信息",
|
||||||
"New_Password": "新密码",
|
"New_Password": "新密码",
|
||||||
|
|
|
@ -321,7 +321,6 @@
|
||||||
"N_people_reacted": "{{n}} 人回复",
|
"N_people_reacted": "{{n}} 人回复",
|
||||||
"N_users": "{{n}} 位使用者",
|
"N_users": "{{n}} 位使用者",
|
||||||
"Name": "名稱",
|
"Name": "名稱",
|
||||||
"Navigation_history": "瀏覽歷史記錄",
|
|
||||||
"Never": "從不",
|
"Never": "從不",
|
||||||
"New_Message": "新訊息",
|
"New_Message": "新訊息",
|
||||||
"New_Password": "新密碼",
|
"New_Password": "新密碼",
|
||||||
|
|
|
@ -30,6 +30,8 @@ import InAppNotification from './containers/InAppNotification';
|
||||||
import { ActionSheetProvider } from './containers/ActionSheet';
|
import { ActionSheetProvider } from './containers/ActionSheet';
|
||||||
import debounce from './utils/debounce';
|
import debounce from './utils/debounce';
|
||||||
import { isFDroidBuild } from './constants/environment';
|
import { isFDroidBuild } from './constants/environment';
|
||||||
|
import { IThemePreference } from './definitions/ITheme';
|
||||||
|
import { ICommand } from './definitions/ICommand';
|
||||||
|
|
||||||
RNScreens.enableScreens();
|
RNScreens.enableScreens();
|
||||||
|
|
||||||
|
@ -42,10 +44,7 @@ interface IDimensions {
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
theme: string;
|
theme: string;
|
||||||
themePreferences: {
|
themePreferences: IThemePreference;
|
||||||
currentTheme: 'automatic' | 'light';
|
|
||||||
darkLevel: string;
|
|
||||||
};
|
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
scale: number;
|
scale: number;
|
||||||
|
@ -175,7 +174,7 @@ export default class Root extends React.Component<{}, IState> {
|
||||||
setTheme = (newTheme = {}) => {
|
setTheme = (newTheme = {}) => {
|
||||||
// change theme state
|
// change theme state
|
||||||
this.setState(
|
this.setState(
|
||||||
prevState => newThemeState(prevState, newTheme),
|
prevState => newThemeState(prevState, newTheme as IThemePreference),
|
||||||
() => {
|
() => {
|
||||||
const { themePreferences } = this.state;
|
const { themePreferences } = this.state;
|
||||||
// subscribe to Appearance changes
|
// subscribe to Appearance changes
|
||||||
|
@ -191,7 +190,7 @@ export default class Root extends React.Component<{}, IState> {
|
||||||
initTablet = () => {
|
initTablet = () => {
|
||||||
const { width } = this.state;
|
const { width } = this.state;
|
||||||
this.setMasterDetail(width);
|
this.setMasterDetail(width);
|
||||||
this.onKeyCommands = KeyCommandsEmitter.addListener('onKeyCommand', (command: unknown) => {
|
this.onKeyCommands = KeyCommandsEmitter.addListener('onKeyCommand', (command: ICommand) => {
|
||||||
EventEmitter.emit(KEY_COMMAND, { event: command });
|
EventEmitter.emit(KEY_COMMAND, { event: command });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,7 +55,8 @@ const PERMISSIONS = [
|
||||||
'convert-team',
|
'convert-team',
|
||||||
'edit-omnichannel-contact',
|
'edit-omnichannel-contact',
|
||||||
'edit-livechat-room-customfields',
|
'edit-livechat-room-customfields',
|
||||||
'view-canned-responses'
|
'view-canned-responses',
|
||||||
|
'mobile-upload-file'
|
||||||
];
|
];
|
||||||
|
|
||||||
export async function setPermissions() {
|
export async function setPermissions() {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import messagesStatus from '../../../constants/messagesStatus';
|
||||||
import log from '../../../utils/log';
|
import log from '../../../utils/log';
|
||||||
import random from '../../../utils/random';
|
import random from '../../../utils/random';
|
||||||
import store from '../../createStore';
|
import store from '../../createStore';
|
||||||
import { roomsRequest } from '../../../actions/rooms';
|
|
||||||
import { handlePayloadUserInteraction } from '../actions';
|
import { handlePayloadUserInteraction } from '../actions';
|
||||||
import buildMessage from '../helpers/buildMessage';
|
import buildMessage from '../helpers/buildMessage';
|
||||||
import RocketChat from '../../rocketchat';
|
import RocketChat from '../../rocketchat';
|
||||||
|
@ -21,8 +20,6 @@ import { E2E_MESSAGE_TYPE } from '../../encryption/constants';
|
||||||
|
|
||||||
const removeListener = listener => listener.stop();
|
const removeListener = listener => listener.stop();
|
||||||
|
|
||||||
let connectedListener;
|
|
||||||
let disconnectedListener;
|
|
||||||
let streamListener;
|
let streamListener;
|
||||||
let subServer;
|
let subServer;
|
||||||
let queue = {};
|
let queue = {};
|
||||||
|
@ -255,10 +252,6 @@ const debouncedUpdate = subscription => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function subscribeRooms() {
|
export default function subscribeRooms() {
|
||||||
const handleConnection = () => {
|
|
||||||
store.dispatch(roomsRequest());
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStreamMessageReceived = protectedFunction(async ddpMessage => {
|
const handleStreamMessageReceived = protectedFunction(async ddpMessage => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
|
|
||||||
|
@ -388,14 +381,6 @@ export default function subscribeRooms() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const stop = () => {
|
const stop = () => {
|
||||||
if (connectedListener) {
|
|
||||||
connectedListener.then(removeListener);
|
|
||||||
connectedListener = false;
|
|
||||||
}
|
|
||||||
if (disconnectedListener) {
|
|
||||||
disconnectedListener.then(removeListener);
|
|
||||||
disconnectedListener = false;
|
|
||||||
}
|
|
||||||
if (streamListener) {
|
if (streamListener) {
|
||||||
streamListener.then(removeListener);
|
streamListener.then(removeListener);
|
||||||
streamListener = false;
|
streamListener = false;
|
||||||
|
@ -407,8 +392,6 @@ export default function subscribeRooms() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
connectedListener = this.sdk.onStreamData('connected', handleConnection);
|
|
||||||
// disconnectedListener = this.sdk.onStreamData('close', handleConnection);
|
|
||||||
streamListener = this.sdk.onStreamData('stream-notify-user', handleStreamMessageReceived);
|
streamListener = this.sdk.onStreamData('stream-notify-user', handleStreamMessageReceived);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { selectServerFailure } from '../actions/server';
|
||||||
import { useSsl } from '../utils/url';
|
import { useSsl } from '../utils/url';
|
||||||
import EventEmitter from '../utils/events';
|
import EventEmitter from '../utils/events';
|
||||||
import { updatePermission } from '../actions/permissions';
|
import { updatePermission } from '../actions/permissions';
|
||||||
import { TEAM_TYPE } from '../definition/ITeam';
|
import { TEAM_TYPE } from '../definitions/ITeam';
|
||||||
import { updateSettings } from '../actions/settings';
|
import { updateSettings } from '../actions/settings';
|
||||||
import { compareServerVersion, methods } from './utils';
|
import { compareServerVersion, methods } from './utils';
|
||||||
import reduxStore from './createStore';
|
import reduxStore from './createStore';
|
||||||
|
@ -239,37 +239,34 @@ const RocketChat = {
|
||||||
this.code = null;
|
this.code = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
|
// The app can't reconnect if reopen interval is 5s while in development
|
||||||
|
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server), reopen: __DEV__ ? 20000 : 5000 });
|
||||||
this.getSettings();
|
this.getSettings();
|
||||||
|
|
||||||
const sdkConnect = () =>
|
|
||||||
this.sdk
|
this.sdk
|
||||||
.connect()
|
.connect()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const { server: currentServer } = reduxStore.getState().server;
|
console.log('connected');
|
||||||
if (user && user.token && server === currentServer) {
|
|
||||||
reduxStore.dispatch(loginRequest({ resume: user.token }, logoutOnError));
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log('connect error', err);
|
console.log('connect error', err);
|
||||||
|
|
||||||
// when `connect` raises an error, we try again in 10 seconds
|
|
||||||
this.connectTimeout = setTimeout(() => {
|
|
||||||
if (this.sdk?.client?.host === server) {
|
|
||||||
sdkConnect();
|
|
||||||
}
|
|
||||||
}, 10000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
sdkConnect();
|
|
||||||
|
|
||||||
this.connectingListener = this.sdk.onStreamData('connecting', () => {
|
this.connectingListener = this.sdk.onStreamData('connecting', () => {
|
||||||
reduxStore.dispatch(connectRequest());
|
reduxStore.dispatch(connectRequest());
|
||||||
});
|
});
|
||||||
|
|
||||||
this.connectedListener = this.sdk.onStreamData('connected', () => {
|
this.connectedListener = this.sdk.onStreamData('connected', () => {
|
||||||
|
const { connected } = reduxStore.getState().meteor;
|
||||||
|
if (connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
reduxStore.dispatch(connectSuccess());
|
reduxStore.dispatch(connectSuccess());
|
||||||
|
const { server: currentServer } = reduxStore.getState().server;
|
||||||
|
const { user } = reduxStore.getState().login;
|
||||||
|
if (user?.token && server === currentServer) {
|
||||||
|
reduxStore.dispatch(loginRequest({ resume: user.token }, logoutOnError));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.closeListener = this.sdk.onStreamData('close', () => {
|
this.closeListener = this.sdk.onStreamData('close', () => {
|
||||||
|
@ -848,17 +845,21 @@ const RocketChat = {
|
||||||
// RC 3.13.0
|
// RC 3.13.0
|
||||||
return this.post('teams.removeRoom', { roomId, teamId });
|
return this.post('teams.removeRoom', { roomId, teamId });
|
||||||
},
|
},
|
||||||
leaveTeam({ teamName, rooms }) {
|
leaveTeam({ teamId, rooms }) {
|
||||||
// RC 3.13.0
|
// RC 3.13.0
|
||||||
return this.post('teams.leave', { teamName, rooms });
|
return this.post('teams.leave', {
|
||||||
|
teamId,
|
||||||
|
// RC 4.2.0
|
||||||
|
...(rooms?.length && { rooms })
|
||||||
|
});
|
||||||
},
|
},
|
||||||
removeTeamMember({ teamId, teamName, userId, rooms }) {
|
removeTeamMember({ teamId, userId, rooms }) {
|
||||||
// RC 3.13.0
|
// RC 3.13.0
|
||||||
return this.post('teams.removeMember', {
|
return this.post('teams.removeMember', {
|
||||||
teamId,
|
teamId,
|
||||||
teamName,
|
|
||||||
userId,
|
userId,
|
||||||
rooms
|
// RC 4.2.0
|
||||||
|
...(rooms?.length && { rooms })
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateTeamRoom({ roomId, isDefault }) {
|
updateTeamRoom({ roomId, isDefault }) {
|
||||||
|
@ -1151,10 +1152,6 @@ const RocketChat = {
|
||||||
// RC 0.36.0
|
// RC 0.36.0
|
||||||
return this.methodCallWrapper('livechat:transfer', transferData);
|
return this.methodCallWrapper('livechat:transfer', transferData);
|
||||||
},
|
},
|
||||||
getPagesLivechat(rid, offset) {
|
|
||||||
// RC 2.3.0
|
|
||||||
return this.sdk.get(`livechat/visitors.pagesVisited/${rid}?count=50&offset=${offset}`);
|
|
||||||
},
|
|
||||||
getDepartmentInfo(departmentId) {
|
getDepartmentInfo(departmentId) {
|
||||||
// RC 2.2.0
|
// RC 2.2.0
|
||||||
return this.sdk.get(`livechat/department/${departmentId}?includeAgents=false`);
|
return this.sdk.get(`livechat/department/${departmentId}?includeAgents=false`);
|
||||||
|
@ -1533,16 +1530,7 @@ const RocketChat = {
|
||||||
return this.sdk.get(`${this.roomTypeToApiType(type)}.files`, {
|
return this.sdk.get(`${this.roomTypeToApiType(type)}.files`, {
|
||||||
roomId,
|
roomId,
|
||||||
offset,
|
offset,
|
||||||
sort: { uploadedAt: -1 },
|
sort: { uploadedAt: -1 }
|
||||||
fields: {
|
|
||||||
name: 1,
|
|
||||||
description: 1,
|
|
||||||
size: 1,
|
|
||||||
type: 1,
|
|
||||||
uploadedAt: 1,
|
|
||||||
url: 1,
|
|
||||||
userId: 1
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getMessages(roomId, type, query, offset) {
|
getMessages(roomId, type, query, offset) {
|
||||||
|
|
|
@ -7,11 +7,12 @@ const MMKV = new MMKVStorage.Loader()
|
||||||
.initialize();
|
.initialize();
|
||||||
|
|
||||||
class UserPreferences {
|
class UserPreferences {
|
||||||
|
private mmkv: MMKVStorage.API;
|
||||||
constructor() {
|
constructor() {
|
||||||
this.mmkv = MMKV;
|
this.mmkv = MMKV;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStringAsync(key) {
|
async getStringAsync(key: string) {
|
||||||
try {
|
try {
|
||||||
const value = await this.mmkv.getStringAsync(key);
|
const value = await this.mmkv.getStringAsync(key);
|
||||||
return value;
|
return value;
|
||||||
|
@ -20,11 +21,11 @@ class UserPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setStringAsync(key, value) {
|
setStringAsync(key: string, value: string) {
|
||||||
return this.mmkv.setStringAsync(key, value);
|
return this.mmkv.setStringAsync(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBoolAsync(key) {
|
async getBoolAsync(key: string) {
|
||||||
try {
|
try {
|
||||||
const value = await this.mmkv.getBoolAsync(key);
|
const value = await this.mmkv.getBoolAsync(key);
|
||||||
return value;
|
return value;
|
||||||
|
@ -33,11 +34,11 @@ class UserPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setBoolAsync(key, value) {
|
setBoolAsync(key: string, value: boolean) {
|
||||||
return this.mmkv.setBoolAsync(key, value);
|
return this.mmkv.setBoolAsync(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMapAsync(key) {
|
async getMapAsync(key: string) {
|
||||||
try {
|
try {
|
||||||
const value = await this.mmkv.getMapAsync(key);
|
const value = await this.mmkv.getMapAsync(key);
|
||||||
return value;
|
return value;
|
||||||
|
@ -46,11 +47,11 @@ class UserPreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setMapAsync(key, value) {
|
setMapAsync(key: string, value: object) {
|
||||||
return this.mmkv.setMapAsync(key, value);
|
return this.mmkv.setMapAsync(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeItem(key) {
|
removeItem(key: string) {
|
||||||
return this.mmkv.removeItem(key);
|
return this.mmkv.removeItem(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { NavigatorScreenParams } from '@react-navigation/core';
|
||||||
|
|
||||||
|
import { ISubscription } from './definitions/ISubscription';
|
||||||
|
import { IServer } from './definitions/IServer';
|
||||||
|
import { IAttachment } from './definitions/IAttachment';
|
||||||
|
import { MasterDetailInsideStackParamList } from './stacks/MasterDetailStack/types';
|
||||||
|
import { OutsideParamList, InsideStackParamList } from './stacks/types';
|
||||||
|
|
||||||
|
export type SetUsernameStackParamList = {
|
||||||
|
SetUsernameView: {
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StackParamList = {
|
||||||
|
AuthLoading: undefined;
|
||||||
|
OutsideStack: NavigatorScreenParams<OutsideParamList>;
|
||||||
|
InsideStack: NavigatorScreenParams<InsideStackParamList>;
|
||||||
|
MasterDetailStack: NavigatorScreenParams<MasterDetailInsideStackParamList>;
|
||||||
|
SetUsernameStack: NavigatorScreenParams<SetUsernameStackParamList>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShareInsideStackParamList = {
|
||||||
|
ShareListView: undefined;
|
||||||
|
ShareView: {
|
||||||
|
attachments: IAttachment[];
|
||||||
|
isShareView?: boolean;
|
||||||
|
isShareExtension: boolean;
|
||||||
|
serverInfo: IServer;
|
||||||
|
text: string;
|
||||||
|
room: ISubscription;
|
||||||
|
thread: any; // TODO: Change
|
||||||
|
};
|
||||||
|
SelectServerView: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShareOutsideStackParamList = {
|
||||||
|
WithoutServersView: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShareAppStackParamList = {
|
||||||
|
AuthLoading?: undefined;
|
||||||
|
OutsideStack?: NavigatorScreenParams<ShareOutsideStackParamList>;
|
||||||
|
InsideStack?: NavigatorScreenParams<ShareInsideStackParamList>;
|
||||||
|
};
|
|
@ -1,50 +0,0 @@
|
||||||
import EJSON from 'ejson';
|
|
||||||
|
|
||||||
import store from '../../lib/createStore';
|
|
||||||
import { deepLinkingOpen } from '../../actions/deepLinking';
|
|
||||||
import { isFDroidBuild } from '../../constants/environment';
|
|
||||||
import PushNotification from './push';
|
|
||||||
|
|
||||||
export const onNotification = notification => {
|
|
||||||
if (notification) {
|
|
||||||
const data = notification.getData();
|
|
||||||
if (data) {
|
|
||||||
try {
|
|
||||||
const { rid, name, sender, type, host, messageType, messageId } = EJSON.parse(data.ejson);
|
|
||||||
|
|
||||||
const types = {
|
|
||||||
c: 'channel',
|
|
||||||
d: 'direct',
|
|
||||||
p: 'group',
|
|
||||||
l: 'channels'
|
|
||||||
};
|
|
||||||
let roomName = type === 'd' ? sender.username : name;
|
|
||||||
if (type === 'l') {
|
|
||||||
roomName = sender.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
host,
|
|
||||||
rid,
|
|
||||||
messageId,
|
|
||||||
path: `${types[type]}/${roomName}`,
|
|
||||||
isCall: messageType === 'jitsi_call_started'
|
|
||||||
};
|
|
||||||
store.dispatch(deepLinkingOpen(params));
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDeviceToken = () => PushNotification.getDeviceToken();
|
|
||||||
export const setBadgeCount = count => PushNotification.setBadgeCount(count);
|
|
||||||
export const initializePushNotifications = () => {
|
|
||||||
if (!isFDroidBuild) {
|
|
||||||
setBadgeCount();
|
|
||||||
return PushNotification.configure({
|
|
||||||
onNotification
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import EJSON from 'ejson';
|
||||||
|
|
||||||
|
import store from '../../lib/createStore';
|
||||||
|
import { deepLinkingOpen } from '../../actions/deepLinking';
|
||||||
|
import { isFDroidBuild } from '../../constants/environment';
|
||||||
|
import PushNotification from './push';
|
||||||
|
import { INotification, SubscriptionType } from '../../definitions';
|
||||||
|
|
||||||
|
interface IEjson {
|
||||||
|
rid: string;
|
||||||
|
name: string;
|
||||||
|
sender: { username: string; name: string };
|
||||||
|
type: string;
|
||||||
|
host: string;
|
||||||
|
messageType: string;
|
||||||
|
messageId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const onNotification = (notification: INotification): void => {
|
||||||
|
if (notification) {
|
||||||
|
try {
|
||||||
|
const { rid, name, sender, type, host, messageType, messageId }: IEjson = EJSON.parse(notification.ejson);
|
||||||
|
|
||||||
|
const types: Record<string, string> = {
|
||||||
|
c: 'channel',
|
||||||
|
d: 'direct',
|
||||||
|
p: 'group',
|
||||||
|
l: 'channels'
|
||||||
|
};
|
||||||
|
let roomName = type === SubscriptionType.DIRECT ? sender.username : name;
|
||||||
|
if (type === SubscriptionType.OMNICHANNEL) {
|
||||||
|
roomName = sender.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
host,
|
||||||
|
rid,
|
||||||
|
messageId,
|
||||||
|
path: `${types[type]}/${roomName}`,
|
||||||
|
isCall: messageType === 'jitsi_call_started'
|
||||||
|
};
|
||||||
|
// TODO REDUX MIGRATION TO TS
|
||||||
|
store.dispatch(deepLinkingOpen(params));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDeviceToken = (): string => PushNotification.getDeviceToken();
|
||||||
|
export const setBadgeCount = (count?: number): void => PushNotification.setBadgeCount(count);
|
||||||
|
export const initializePushNotifications = (): Promise<INotification> | undefined => {
|
||||||
|
if (!isFDroidBuild) {
|
||||||
|
setBadgeCount();
|
||||||
|
return PushNotification.configure(onNotification);
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,32 +0,0 @@
|
||||||
import { NotificationsAndroid, PendingNotifications } from 'react-native-notifications';
|
|
||||||
|
|
||||||
class PushNotification {
|
|
||||||
constructor() {
|
|
||||||
this.onRegister = null;
|
|
||||||
this.onNotification = null;
|
|
||||||
this.deviceToken = null;
|
|
||||||
|
|
||||||
NotificationsAndroid.setRegistrationTokenUpdateListener(deviceToken => {
|
|
||||||
this.deviceToken = deviceToken;
|
|
||||||
});
|
|
||||||
|
|
||||||
NotificationsAndroid.setNotificationOpenedListener(notification => {
|
|
||||||
this.onNotification(notification);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getDeviceToken() {
|
|
||||||
return this.deviceToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBadgeCount = () => {};
|
|
||||||
|
|
||||||
configure(params) {
|
|
||||||
this.onRegister = params.onRegister;
|
|
||||||
this.onNotification = params.onNotification;
|
|
||||||
NotificationsAndroid.refreshToken();
|
|
||||||
return PendingNotifications.getInitialNotification();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new PushNotification();
|
|
|
@ -1,61 +0,0 @@
|
||||||
import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications';
|
|
||||||
|
|
||||||
import reduxStore from '../../lib/createStore';
|
|
||||||
import I18n from '../../i18n';
|
|
||||||
|
|
||||||
const replyAction = new NotificationAction({
|
|
||||||
activationMode: 'background',
|
|
||||||
title: I18n.t('Reply'),
|
|
||||||
textInput: {
|
|
||||||
buttonTitle: I18n.t('Reply'),
|
|
||||||
placeholder: I18n.t('Type_message')
|
|
||||||
},
|
|
||||||
identifier: 'REPLY_ACTION'
|
|
||||||
});
|
|
||||||
|
|
||||||
class PushNotification {
|
|
||||||
constructor() {
|
|
||||||
this.onRegister = null;
|
|
||||||
this.onNotification = null;
|
|
||||||
this.deviceToken = null;
|
|
||||||
|
|
||||||
NotificationsIOS.addEventListener('remoteNotificationsRegistered', deviceToken => {
|
|
||||||
this.deviceToken = deviceToken;
|
|
||||||
});
|
|
||||||
|
|
||||||
NotificationsIOS.addEventListener('notificationOpened', (notification, completion) => {
|
|
||||||
const { background } = reduxStore.getState().app;
|
|
||||||
if (background) {
|
|
||||||
this.onNotification(notification);
|
|
||||||
}
|
|
||||||
completion();
|
|
||||||
});
|
|
||||||
|
|
||||||
const actions = [];
|
|
||||||
actions.push(
|
|
||||||
new NotificationCategory({
|
|
||||||
identifier: 'MESSAGE',
|
|
||||||
actions: [replyAction]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
NotificationsIOS.requestPermissions(actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDeviceToken() {
|
|
||||||
return this.deviceToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBadgeCount = (count = 0) => {
|
|
||||||
NotificationsIOS.setBadgesCount(count);
|
|
||||||
};
|
|
||||||
|
|
||||||
async configure(params) {
|
|
||||||
this.onRegister = params.onRegister;
|
|
||||||
this.onNotification = params.onNotification;
|
|
||||||
|
|
||||||
const initial = await NotificationsIOS.getInitialNotification();
|
|
||||||
// NotificationsIOS.consumeBackgroundQueue();
|
|
||||||
return Promise.resolve(initial);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default new PushNotification();
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
// @ts-ignore
|
||||||
|
// TODO BUMP LIB VERSION
|
||||||
|
import NotificationsIOS, { NotificationAction, NotificationCategory, Notification } from 'react-native-notifications';
|
||||||
|
|
||||||
|
import reduxStore from '../../lib/createStore';
|
||||||
|
import I18n from '../../i18n';
|
||||||
|
import { INotification } from '../../definitions/INotification';
|
||||||
|
|
||||||
|
class PushNotification {
|
||||||
|
onNotification: (notification: Notification) => void;
|
||||||
|
deviceToken: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.onNotification = () => {};
|
||||||
|
this.deviceToken = '';
|
||||||
|
|
||||||
|
NotificationsIOS.addEventListener('remoteNotificationsRegistered', (deviceToken: string) => {
|
||||||
|
this.deviceToken = deviceToken;
|
||||||
|
});
|
||||||
|
|
||||||
|
NotificationsIOS.addEventListener('notificationOpened', (notification: Notification, completion: () => void) => {
|
||||||
|
// TODO REDUX MIGRATION TO TS
|
||||||
|
const { background } = reduxStore.getState().app;
|
||||||
|
if (background) {
|
||||||
|
this.onNotification(notification?.getData());
|
||||||
|
}
|
||||||
|
completion();
|
||||||
|
});
|
||||||
|
|
||||||
|
const actions = [
|
||||||
|
new NotificationCategory({
|
||||||
|
identifier: 'MESSAGE',
|
||||||
|
actions: [
|
||||||
|
new NotificationAction({
|
||||||
|
activationMode: 'background',
|
||||||
|
title: I18n.t('Reply'),
|
||||||
|
textInput: {
|
||||||
|
buttonTitle: I18n.t('Reply'),
|
||||||
|
placeholder: I18n.t('Type_message')
|
||||||
|
},
|
||||||
|
identifier: 'REPLY_ACTION'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
];
|
||||||
|
NotificationsIOS.requestPermissions(actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeviceToken() {
|
||||||
|
return this.deviceToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBadgeCount = (count = 0) => {
|
||||||
|
NotificationsIOS.setBadgesCount(count);
|
||||||
|
};
|
||||||
|
|
||||||
|
async configure(onNotification: (notification: INotification) => void) {
|
||||||
|
this.onNotification = onNotification;
|
||||||
|
const initial = await NotificationsIOS.getInitialNotification();
|
||||||
|
return Promise.resolve(initial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default new PushNotification();
|
|
@ -0,0 +1,36 @@
|
||||||
|
// @ts-ignore
|
||||||
|
// TODO BUMP LIB VERSION
|
||||||
|
import { NotificationsAndroid, PendingNotifications, Notification } from 'react-native-notifications';
|
||||||
|
|
||||||
|
import { INotification } from '../../definitions/INotification';
|
||||||
|
|
||||||
|
class PushNotification {
|
||||||
|
onNotification: (notification: Notification) => void;
|
||||||
|
deviceToken: string;
|
||||||
|
constructor() {
|
||||||
|
this.onNotification = () => {};
|
||||||
|
this.deviceToken = '';
|
||||||
|
|
||||||
|
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken: string) => {
|
||||||
|
this.deviceToken = deviceToken;
|
||||||
|
});
|
||||||
|
|
||||||
|
NotificationsAndroid.setNotificationOpenedListener((notification: Notification) => {
|
||||||
|
this.onNotification(notification?.getData());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeviceToken() {
|
||||||
|
return this.deviceToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBadgeCount = (_?: number) => {};
|
||||||
|
|
||||||
|
configure(onNotification: (notification: INotification) => void) {
|
||||||
|
this.onNotification = onNotification;
|
||||||
|
NotificationsAndroid.refreshToken();
|
||||||
|
return PendingNotifications.getInitialNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new PushNotification();
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
|
@ -10,7 +10,7 @@ import { themes } from '../../constants/colors';
|
||||||
export { ROW_HEIGHT };
|
export { ROW_HEIGHT };
|
||||||
|
|
||||||
interface IDirectoryItemLabel {
|
interface IDirectoryItemLabel {
|
||||||
text: string;
|
text?: string;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,9 +21,9 @@ interface IDirectoryItem {
|
||||||
type: string;
|
type: string;
|
||||||
onPress(): void;
|
onPress(): void;
|
||||||
testID: string;
|
testID: string;
|
||||||
style: any;
|
style?: ViewStyle;
|
||||||
rightLabel: string;
|
rightLabel?: string;
|
||||||
rid: string;
|
rid?: string;
|
||||||
theme: string;
|
theme: string;
|
||||||
teamMain?: boolean;
|
teamMain?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ const DirectoryItemLabel = React.memo(({ text, theme }: IDirectoryItemLabel) =>
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <Text style={[styles.directoryItemLabel, { color: themes[theme!].auxiliaryText }]}>{text}</Text>;
|
return <Text style={[styles.directoryItemLabel, { color: themes[theme].auxiliaryText }]}>{text}</Text>;
|
||||||
});
|
});
|
||||||
|
|
||||||
const DirectoryItem = ({
|
const DirectoryItem = ({
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { KeyboardAwareScrollView, KeyboardAwareScrollViewProps } from '@codler/r
|
||||||
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||||
|
|
||||||
interface IKeyboardViewProps extends KeyboardAwareScrollViewProps {
|
interface IKeyboardViewProps extends KeyboardAwareScrollViewProps {
|
||||||
keyboardVerticalOffset: number;
|
keyboardVerticalOffset?: number;
|
||||||
scrollEnabled?: boolean;
|
scrollEnabled?: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { RectButton } from 'react-native-gesture-handler';
|
||||||
import { isRTL } from '../../i18n';
|
import { isRTL } from '../../i18n';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { DISPLAY_MODE_CONDENSED } from '../../constants/constantDisplayMode';
|
import { DisplayMode } from '../../constants/constantDisplayMode';
|
||||||
import styles, { ACTION_WIDTH, LONG_SWIPE, ROW_HEIGHT_CONDENSED } from './styles';
|
import styles, { ACTION_WIDTH, LONG_SWIPE, ROW_HEIGHT_CONDENSED } from './styles';
|
||||||
|
|
||||||
interface ILeftActions {
|
interface ILeftActions {
|
||||||
|
@ -40,7 +40,7 @@ export const LeftActions = React.memo(({ theme, transX, isRead, width, onToggleR
|
||||||
reverse
|
reverse
|
||||||
);
|
);
|
||||||
|
|
||||||
const isCondensed = displayMode === DISPLAY_MODE_CONDENSED;
|
const isCondensed = displayMode === DisplayMode.Condensed;
|
||||||
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -87,7 +87,7 @@ export const RightActions = React.memo(
|
||||||
reverse
|
reverse
|
||||||
);
|
);
|
||||||
|
|
||||||
const isCondensed = displayMode === DISPLAY_MODE_CONDENSED;
|
const isCondensed = displayMode === DisplayMode.Condensed;
|
||||||
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
import { DISPLAY_MODE_CONDENSED, DISPLAY_MODE_EXPANDED } from '../../constants/constantDisplayMode';
|
import { DisplayMode } from '../../constants/constantDisplayMode';
|
||||||
import TypeIcon from './TypeIcon';
|
import TypeIcon from './TypeIcon';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
@ -22,11 +22,11 @@ const IconOrAvatar = ({
|
||||||
}) => {
|
}) => {
|
||||||
if (showAvatar) {
|
if (showAvatar) {
|
||||||
return (
|
return (
|
||||||
<Avatar text={avatar} size={displayMode === DISPLAY_MODE_CONDENSED ? 36 : 48} type={type} style={styles.avatar} rid={rid} />
|
<Avatar text={avatar} size={displayMode === DisplayMode.Condensed ? 36 : 48} type={type} style={styles.avatar} rid={rid} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displayMode === DISPLAY_MODE_EXPANDED && showLastMessage) {
|
if (displayMode === DisplayMode.Expanded && showLastMessage) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.typeIcon}>
|
<View style={styles.typeIcon}>
|
||||||
<TypeIcon
|
<TypeIcon
|
||||||
|
|
|
@ -11,7 +11,7 @@ import UpdatedAt from './UpdatedAt';
|
||||||
import Touchable from './Touchable';
|
import Touchable from './Touchable';
|
||||||
import Tag from './Tag';
|
import Tag from './Tag';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { DISPLAY_MODE_EXPANDED } from '../../constants/constantDisplayMode';
|
import { DisplayMode } from '../../constants/constantDisplayMode';
|
||||||
|
|
||||||
interface IRoomItem {
|
interface IRoomItem {
|
||||||
rid: string;
|
rid: string;
|
||||||
|
@ -132,7 +132,7 @@ const RoomItem = ({
|
||||||
displayMode={displayMode}
|
displayMode={displayMode}
|
||||||
showAvatar={showAvatar}
|
showAvatar={showAvatar}
|
||||||
showLastMessage={showLastMessage}>
|
showLastMessage={showLastMessage}>
|
||||||
{showLastMessage && displayMode === DISPLAY_MODE_EXPANDED ? (
|
{showLastMessage && displayMode === DisplayMode.Expanded ? (
|
||||||
<>
|
<>
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
{showAvatar ? (
|
{showAvatar ? (
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { DISPLAY_MODE_CONDENSED } from '../../constants/constantDisplayMode';
|
import { DisplayMode } from '../../constants/constantDisplayMode';
|
||||||
import IconOrAvatar from './IconOrAvatar';
|
import IconOrAvatar from './IconOrAvatar';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ interface IWrapper {
|
||||||
|
|
||||||
const Wrapper = ({ accessibilityLabel, theme, children, displayMode, ...props }: IWrapper) => (
|
const Wrapper = ({ accessibilityLabel, theme, children, displayMode, ...props }: IWrapper) => (
|
||||||
<View
|
<View
|
||||||
style={[styles.container, displayMode === DISPLAY_MODE_CONDENSED && styles.containerCondensed]}
|
style={[styles.container, displayMode === DisplayMode.Condensed && styles.containerCondensed]}
|
||||||
accessibilityLabel={accessibilityLabel}>
|
accessibilityLabel={accessibilityLabel}>
|
||||||
<IconOrAvatar theme={theme} displayMode={displayMode} {...props} />
|
<IconOrAvatar theme={theme} displayMode={displayMode} {...props} />
|
||||||
<View
|
<View
|
||||||
|
@ -34,7 +34,7 @@ const Wrapper = ({ accessibilityLabel, theme, children, displayMode, ...props }:
|
||||||
{
|
{
|
||||||
borderColor: themes[theme].separatorColor
|
borderColor: themes[theme].separatorColor
|
||||||
},
|
},
|
||||||
displayMode === DISPLAY_MODE_CONDENSED && styles.condensedPaddingVertical
|
displayMode === DisplayMode.Condensed && styles.condensedPaddingVertical
|
||||||
]}>
|
]}>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -46,7 +46,7 @@ interface IUserItem {
|
||||||
testID: string;
|
testID: string;
|
||||||
onLongPress?: () => void;
|
onLongPress?: () => void;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
icon: string;
|
icon?: string | null;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue