Merge 4.27.0 into master (#4135)
This commit is contained in:
parent
3b20ea6596
commit
601d94444b
|
@ -3,7 +3,7 @@ defaults: &defaults
|
||||||
|
|
||||||
macos: &macos
|
macos: &macos
|
||||||
macos:
|
macos:
|
||||||
xcode: "12.5.0"
|
xcode: "13.3.0"
|
||||||
resource_class: large
|
resource_class: large
|
||||||
|
|
||||||
bash-env: &bash-env
|
bash-env: &bash-env
|
||||||
|
@ -339,7 +339,7 @@ jobs:
|
||||||
lint-testunit:
|
lint-testunit:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:15
|
- image: cimg/node:16.14
|
||||||
resource_class: large
|
resource_class: large
|
||||||
environment:
|
environment:
|
||||||
CODECOV_TOKEN: caa771ab-3d45-4756-8e2a-e1f25996fef6
|
CODECOV_TOKEN: caa771ab-3d45-4756-8e2a-e1f25996fef6
|
||||||
|
@ -372,7 +372,7 @@ jobs:
|
||||||
android-build-experimental:
|
android-build-experimental:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-29-node
|
- image: cimg/android:2022.03.1-node
|
||||||
environment:
|
environment:
|
||||||
<<: *android-env
|
<<: *android-env
|
||||||
<<: *bash-env
|
<<: *bash-env
|
||||||
|
@ -383,7 +383,7 @@ jobs:
|
||||||
android-build-official:
|
android-build-official:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-29-node
|
- image: cimg/android:2022.03.1-node
|
||||||
environment:
|
environment:
|
||||||
<<: *android-env
|
<<: *android-env
|
||||||
<<: *bash-env
|
<<: *bash-env
|
||||||
|
@ -394,7 +394,7 @@ jobs:
|
||||||
android-internal-app-sharing-experimental:
|
android-internal-app-sharing-experimental:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-28-node
|
- image: cimg/android:2022.03.1-node
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- upload-to-internal-app-sharing
|
- upload-to-internal-app-sharing
|
||||||
|
@ -402,7 +402,7 @@ jobs:
|
||||||
android-google-play-beta-experimental:
|
android-google-play-beta-experimental:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-29-node
|
- image: cimg/android:2022.03.1-node
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- upload-to-google-play-beta:
|
- upload-to-google-play-beta:
|
||||||
|
@ -411,14 +411,14 @@ jobs:
|
||||||
android-google-play-production-experimental:
|
android-google-play-production-experimental:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-29-node
|
- image: cimg/android:2022.03.1-node
|
||||||
steps:
|
steps:
|
||||||
- upload-to-google-play-production
|
- upload-to-google-play-production
|
||||||
|
|
||||||
android-google-play-beta-official:
|
android-google-play-beta-official:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-29-node
|
- image: cimg/android:2022.03.1-node
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- upload-to-google-play-beta:
|
- upload-to-google-play-beta:
|
||||||
|
|
10
.eslintrc.js
10
.eslintrc.js
|
@ -17,7 +17,7 @@ module.exports = {
|
||||||
legacyDecorators: true
|
legacyDecorators: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: ['react', 'jsx-a11y', 'import', 'react-native', '@babel', 'jest'],
|
plugins: ['react', 'jsx-a11y', 'import', 'react-native', '@babel', 'jest', 'react-hooks'],
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
commonjs: true,
|
commonjs: true,
|
||||||
|
@ -148,7 +148,9 @@ module.exports = {
|
||||||
'no-async-promise-executor': [0],
|
'no-async-promise-executor': [0],
|
||||||
'max-classes-per-file': [0],
|
'max-classes-per-file': [0],
|
||||||
'no-multiple-empty-lines': [0],
|
'no-multiple-empty-lines': [0],
|
||||||
'no-sequences': 'off'
|
'no-sequences': 'off',
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn'
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
__DEV__: true
|
__DEV__: true
|
||||||
|
@ -237,7 +239,9 @@ module.exports = {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'new-cap': 'off',
|
'new-cap': 'off',
|
||||||
'lines-between-class-members': 'off'
|
'lines-between-class-members': 'off',
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn'
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
JSX: true
|
JSX: true
|
||||||
|
|
|
@ -144,7 +144,7 @@ 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.26.2"
|
versionName "4.27.0"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
if (!isFoss) {
|
if (!isFoss) {
|
||||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||||
|
@ -277,14 +277,17 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
addUnimodulesDependencies()
|
addUnimodulesDependencies()
|
||||||
implementation project(':@react-native-community_viewpager')
|
implementation project(':@react-native-community_viewpager')
|
||||||
playImplementation project(':reactnativenotifications')
|
playImplementation project(':react-native-notifications')
|
||||||
|
playImplementation 'com.google.firebase:firebase-core:16.0.0'
|
||||||
playImplementation project(':@react-native-firebase_app')
|
playImplementation project(':@react-native-firebase_app')
|
||||||
playImplementation project(':@react-native-firebase_analytics')
|
playImplementation project(':@react-native-firebase_analytics')
|
||||||
playImplementation project(':@react-native-firebase_crashlytics')
|
playImplementation project(':@react-native-firebase_crashlytics')
|
||||||
|
implementation(project(':react-native-jitsi-meet')) { // https://github.com/skrafft/react-native-jitsi-meet#side-note
|
||||||
|
exclude group: 'com.facebook.react',module:'react-native-svg'
|
||||||
|
}
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
//noinspection GradleDynamicVersion
|
//noinspection GradleDynamicVersion
|
||||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||||
playImplementation "com.google.firebase:firebase-messaging:18.0.0"
|
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||||
exclude group:'com.facebook.fbjni'
|
exclude group:'com.facebook.fbjni'
|
||||||
|
|
Binary file not shown.
|
@ -3,7 +3,6 @@ package chat.rocket.reactnative;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
import com.wix.reactnativenotifications.RNNotificationsPackage;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -17,8 +16,7 @@ public class AdditionalModules {
|
||||||
return Arrays.<ReactPackage>asList(
|
return Arrays.<ReactPackage>asList(
|
||||||
new ReactNativeFirebaseAnalyticsPackage(),
|
new ReactNativeFirebaseAnalyticsPackage(),
|
||||||
new ReactNativeFirebaseAppPackage(),
|
new ReactNativeFirebaseAppPackage(),
|
||||||
new ReactNativeFirebaseCrashlyticsPackage(),
|
new ReactNativeFirebaseCrashlyticsPackage()
|
||||||
new RNNotificationsPackage(application)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@ apply from: '../node_modules/react-native-unimodules/gradle.groovy'
|
||||||
includeUnimodulesProjects()
|
includeUnimodulesProjects()
|
||||||
|
|
||||||
rootProject.name = 'RocketChatRN'
|
rootProject.name = 'RocketChatRN'
|
||||||
include ':reactnativenotifications'
|
|
||||||
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app')
|
|
||||||
include ':@react-native-community_viewpager'
|
include ':@react-native-community_viewpager'
|
||||||
project(':@react-native-community_viewpager').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/viewpager/android')
|
project(':@react-native-community_viewpager').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/viewpager/android')
|
||||||
include ':@react-native-firebase_app'
|
include ':@react-native-firebase_app'
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React, { useContext, memo, useEffect } from 'react';
|
||||||
import { NavigationContainer } from '@react-navigation/native';
|
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 './definitions/navigationTypes';
|
import { SetUsernameStackParamList, StackParamList } from './definitions/navigationTypes';
|
||||||
import Navigation from './lib/Navigation';
|
import Navigation from './lib/navigation/appNavigation';
|
||||||
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
||||||
import { RootEnum } from './definitions';
|
import { RootEnum } from './definitions';
|
||||||
// Stacks
|
// Stacks
|
||||||
|
@ -27,20 +27,22 @@ const SetUsernameStack = () => (
|
||||||
|
|
||||||
// App
|
// App
|
||||||
const Stack = createStackNavigator<StackParamList>();
|
const Stack = createStackNavigator<StackParamList>();
|
||||||
const App = React.memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => {
|
const App = memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => {
|
||||||
if (!root) {
|
const { theme } = useContext(ThemeContext);
|
||||||
return null;
|
useEffect(() => {
|
||||||
}
|
if (root) {
|
||||||
|
|
||||||
const { theme } = React.useContext(ThemeContext);
|
|
||||||
const navTheme = navigationTheme(theme);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const state = Navigation.navigationRef.current?.getRootState();
|
const state = Navigation.navigationRef.current?.getRootState();
|
||||||
const currentRouteName = getActiveRouteName(state);
|
const currentRouteName = getActiveRouteName(state);
|
||||||
Navigation.routeNameRef.current = currentRouteName;
|
Navigation.routeNameRef.current = currentRouteName;
|
||||||
setCurrentScreen(currentRouteName);
|
setCurrentScreen(currentRouteName);
|
||||||
}, []);
|
}
|
||||||
|
}, [root]);
|
||||||
|
|
||||||
|
if (!root) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const navTheme = navigationTheme(theme);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationContainer
|
<NavigationContainer
|
||||||
|
|
|
@ -25,6 +25,7 @@ interface ILoginFailure extends Action {
|
||||||
|
|
||||||
interface ILogout extends Action {
|
interface ILogout extends Action {
|
||||||
forcedByServer: boolean;
|
forcedByServer: boolean;
|
||||||
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISetUser extends Action {
|
interface ISetUser extends Action {
|
||||||
|
@ -79,10 +80,11 @@ export function loginFailure(err: Record<string, any>): ILoginFailure {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logout(forcedByServer = false): ILogout {
|
export function logout(forcedByServer = false, message = ''): ILogout {
|
||||||
return {
|
return {
|
||||||
type: types.LOGOUT,
|
type: types.LOGOUT,
|
||||||
forcedByServer
|
forcedByServer,
|
||||||
|
message
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
|
|
||||||
import { MESSAGES } from './actionsTypes';
|
import { MESSAGES } from './actionsTypes';
|
||||||
|
import { IMessage } from '../definitions';
|
||||||
type IMessage = Record<string, string>;
|
|
||||||
|
|
||||||
interface IReplyBroadcast extends Action {
|
interface IReplyBroadcast extends Action {
|
||||||
message: IMessage;
|
message: IMessage;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Animated, { Easing, Extrapolate, interpolateNode, Value } from 'react-nat
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
|
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { useDimensions, useOrientation } from '../../dimensions';
|
import { useDimensions, useOrientation } from '../../dimensions';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
export const Handle = React.memo(() => {
|
export const Handle = React.memo(() => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react';
|
||||||
|
|
||||||
import ActionSheet from './ActionSheet';
|
import ActionSheet from './ActionSheet';
|
||||||
|
|
||||||
export type TActionSheetOptionsItem = { title: string; icon: string; onPress: () => void };
|
export type TActionSheetOptionsItem = { title: string; icon: string; onPress: () => void; danger?: boolean };
|
||||||
|
|
||||||
export type TActionSheetOptions = {
|
export type TActionSheetOptions = {
|
||||||
options: TActionSheetOptionsItem[];
|
options: TActionSheetOptionsItem[];
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { ActivityIndicator, ActivityIndicatorProps, StyleSheet } from 'react-native';
|
import { ActivityIndicator, ActivityIndicatorProps, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
|
|
||||||
interface IActivityIndicator extends ActivityIndicatorProps {
|
interface IActivityIndicator extends ActivityIndicatorProps {
|
||||||
absolute?: boolean;
|
absolute?: boolean;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { getReadableVersion } from '../utils/deviceInfo';
|
import { getReadableVersion } from '../utils/deviceInfo';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
import { TSupportedThemes } from '../theme';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -20,7 +21,7 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const AppVersion = React.memo(({ theme }: { theme: string }) => (
|
const AppVersion = React.memo(({ theme }: { theme: TSupportedThemes }) => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>
|
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>
|
||||||
{I18n.t('Version_no', { version: '' })}
|
{I18n.t('Version_no', { version: '' })}
|
||||||
|
|
|
@ -28,14 +28,15 @@ const Avatar = React.memo(
|
||||||
text,
|
text,
|
||||||
size = 25,
|
size = 25,
|
||||||
borderRadius = 4,
|
borderRadius = 4,
|
||||||
type = SubscriptionType.DIRECT
|
type = SubscriptionType.DIRECT,
|
||||||
|
externalProviderUrl
|
||||||
}: IAvatar) => {
|
}: IAvatar) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
if ((!text && !avatar && !emoji && !rid) || !server) {
|
if ((!text && !avatar && !emoji && !rid) || !server) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
const avatarStyle = {
|
const avatarStyle = {
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
|
@ -67,7 +68,8 @@ const Avatar = React.memo(
|
||||||
avatarETag,
|
avatarETag,
|
||||||
serverVersion,
|
serverVersion,
|
||||||
rid,
|
rid,
|
||||||
blockUnauthenticatedAccess
|
blockUnauthenticatedAccess,
|
||||||
|
externalProviderUrl
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,10 @@ class AvatarContainer extends React.Component<IAvatar, any> {
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: IAvatar, nextState: { avatarETag: string }) {
|
shouldComponentUpdate(nextProps: IAvatar, nextState: { avatarETag: string }) {
|
||||||
const { avatarETag } = this.state;
|
const { avatarETag } = this.state;
|
||||||
const { text, type } = this.props;
|
const { text, type, size, externalProviderUrl } = this.props;
|
||||||
|
if (nextProps.externalProviderUrl !== externalProviderUrl) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (nextState.avatarETag !== avatarETag) {
|
if (nextState.avatarETag !== avatarETag) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +45,10 @@ class AvatarContainer extends React.Component<IAvatar, any> {
|
||||||
if (nextProps.type !== type) {
|
if (nextProps.type !== type) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (nextProps.size !== size) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +107,7 @@ const mapStateToProps = (state: IApplicationState) => ({
|
||||||
blockUnauthenticatedAccess:
|
blockUnauthenticatedAccess:
|
||||||
(state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess as boolean) ??
|
(state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess as boolean) ??
|
||||||
state.settings.Accounts_AvatarBlockUnauthenticatedAccess ??
|
state.settings.Accounts_AvatarBlockUnauthenticatedAccess ??
|
||||||
true
|
true,
|
||||||
|
externalProviderUrl: state.settings.Accounts_AvatarExternalProviderUrl as string
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps)(AvatarContainer);
|
export default connect(mapStateToProps)(AvatarContainer);
|
||||||
|
|
|
@ -23,4 +23,5 @@ export interface IAvatar {
|
||||||
rid?: string;
|
rid?: string;
|
||||||
blockUnauthenticatedAccess?: boolean;
|
blockUnauthenticatedAccess?: boolean;
|
||||||
serverVersion: string | null;
|
serverVersion: string | null;
|
||||||
|
externalProviderUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { ActivityIndicator, ImageBackground, StyleSheet, Text, View } from 'reac
|
||||||
|
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
|
|
||||||
interface IBackgroundContainer {
|
interface IBackgroundContainer {
|
||||||
text?: string;
|
text?: string;
|
||||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
||||||
import { StyleSheet, Text } from 'react-native';
|
import { StyleSheet, Text } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { TSupportedThemes } from '../../theme';
|
||||||
|
import { themes } from '../../lib/constants';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import ActivityIndicator from '../ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ interface IButtonProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
backgroundColor: string;
|
backgroundColor: string;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
color: string;
|
color: string;
|
||||||
fontSize: any;
|
fontSize: any;
|
||||||
style: any;
|
style: any;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import { CustomIcon } from '../lib/Icons';
|
import { CustomIcon } from '../lib/Icons';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text, View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
import Touch from '../../utils/touch';
|
||||||
|
import Avatar from '../Avatar';
|
||||||
|
import RoomTypeIcon from '../RoomTypeIcon';
|
||||||
|
import styles, { ROW_HEIGHT } from './styles';
|
||||||
|
import { themes } from '../../lib/constants';
|
||||||
|
import { TSupportedThemes, useTheme } from '../../theme';
|
||||||
|
|
||||||
|
export { ROW_HEIGHT };
|
||||||
|
|
||||||
|
interface IDirectoryItemLabel {
|
||||||
|
text?: string;
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDirectoryItem {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
avatar: string;
|
||||||
|
type: string;
|
||||||
|
onPress(): void;
|
||||||
|
testID: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
rightLabel?: string;
|
||||||
|
rid?: string;
|
||||||
|
teamMain?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DirectoryItemLabel = React.memo(({ text, theme }: IDirectoryItemLabel) => {
|
||||||
|
if (!text) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return <Text style={[styles.directoryItemLabel, { color: themes[theme].auxiliaryText }]}>{text}</Text>;
|
||||||
|
});
|
||||||
|
|
||||||
|
const DirectoryItem = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
avatar,
|
||||||
|
onPress,
|
||||||
|
testID,
|
||||||
|
style,
|
||||||
|
rightLabel,
|
||||||
|
type,
|
||||||
|
rid,
|
||||||
|
teamMain
|
||||||
|
}: IDirectoryItem): React.ReactElement => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
|
<Touch onPress={onPress} style={{ backgroundColor: themes[theme].backgroundColor }} testID={testID} theme={theme}>
|
||||||
|
<View style={[styles.directoryItemContainer, styles.directoryItemButton, style]}>
|
||||||
|
<Avatar text={avatar} size={30} type={type} rid={rid} style={styles.directoryItemAvatar} />
|
||||||
|
<View style={styles.directoryItemTextContainer}>
|
||||||
|
<View style={styles.directoryItemTextTitle}>
|
||||||
|
<RoomTypeIcon type={type} teamMain={teamMain} />
|
||||||
|
<Text style={[styles.directoryItemName, { color: themes[theme].titleText }]} numberOfLines={1}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{description ? (
|
||||||
|
<Text style={[styles.directoryItemUsername, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
|
||||||
|
{description}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
<DirectoryItemLabel text={rightLabel} theme={theme} />
|
||||||
|
</View>
|
||||||
|
</Touch>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DirectoryItem;
|
|
@ -8,6 +8,7 @@ const CustomEmoji = React.memo(
|
||||||
<FastImage
|
<FastImage
|
||||||
style={style}
|
style={style}
|
||||||
source={{
|
source={{
|
||||||
|
// @ts-ignore
|
||||||
uri: `${baseUrl}/emoji-custom/${encodeURIComponent(emoji.content || emoji.name)}.${emoji.extension}`,
|
uri: `${baseUrl}/emoji-custom/${encodeURIComponent(emoji.content || emoji.name)}.${emoji.extension}`,
|
||||||
priority: FastImage.priority.high
|
priority: FastImage.priority.high
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -56,6 +56,7 @@ class EmojiCategory extends React.Component<Partial<IEmojiCategory>> {
|
||||||
contentContainerStyle={{ marginHorizontal }}
|
contentContainerStyle={{ marginHorizontal }}
|
||||||
// rerender FlatList in case of width changes
|
// rerender FlatList in case of width changes
|
||||||
key={`emoji-category-${width}`}
|
key={`emoji-category-${width}`}
|
||||||
|
// @ts-ignore
|
||||||
keyExtractor={item => (item && item.isCustom && item.content) || item}
|
keyExtractor={item => (item && item.isCustom && item.content) || item}
|
||||||
data={emojis}
|
data={emojis}
|
||||||
extraData={this.props}
|
extraData={this.props}
|
||||||
|
|
|
@ -2,14 +2,15 @@ import React from 'react';
|
||||||
import { Text, TouchableOpacity, View } from 'react-native';
|
import { Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
|
import { TSupportedThemes } from '../../theme';
|
||||||
|
|
||||||
interface ITabBarProps {
|
interface ITabBarProps {
|
||||||
goToPage: Function;
|
goToPage: Function;
|
||||||
activeTab: number;
|
activeTab: number;
|
||||||
tabs: [];
|
tabs: [];
|
||||||
tabEmojiStyle: object;
|
tabEmojiStyle: object;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TabBar extends React.Component<Partial<ITabBarProps>> {
|
export default class TabBar extends React.Component<Partial<ITabBarProps>> {
|
||||||
|
|
|
@ -15,8 +15,8 @@ import { emojisByCategory } from '../../emojis';
|
||||||
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
||||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { withTheme } from '../../theme';
|
import { TSupportedThemes, withTheme } from '../../theme';
|
||||||
import { IEmoji } from '../../definitions/IEmoji';
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const scrollProps = {
|
const scrollProps = {
|
||||||
|
@ -30,7 +30,7 @@ interface IEmojiPickerProps {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
customEmojis?: any;
|
customEmojis?: any;
|
||||||
style: object;
|
style: object;
|
||||||
theme?: string;
|
theme: TSupportedThemes;
|
||||||
onEmojiSelected?: ((emoji: any) => void) | ((keyboardId: any, params?: any) => void);
|
onEmojiSelected?: ((emoji: any) => void) | ((keyboardId: any, params?: any) => void);
|
||||||
tabEmojiStyle?: object;
|
tabEmojiStyle?: object;
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
|
||||||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||||
let freqEmojiRecord: any;
|
let freqEmojiRecord: any;
|
||||||
try {
|
try {
|
||||||
|
// @ts-ignore
|
||||||
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -185,7 +186,7 @@ class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
|
||||||
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} theme={theme} />}
|
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} theme={theme} />}
|
||||||
/* @ts-ignore*/
|
/* @ts-ignore*/
|
||||||
contentProps={scrollProps}
|
contentProps={scrollProps}
|
||||||
style={{ backgroundColor: themes[theme!].focusedBackground }}>
|
style={{ backgroundColor: themes[theme].focusedBackground }}>
|
||||||
{categories.tabs.map((tab, i) =>
|
{categories.tabs.map((tab, i) =>
|
||||||
i === 0 && frequentlyUsed.length === 0
|
i === 0 && frequentlyUsed.length === 0
|
||||||
? null // when no frequentlyUsed don't show the tab
|
? null // when no frequentlyUsed don't show the tab
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, ScrollViewProps, StyleSheet, View } from 'react-native';
|
import { ScrollView, ScrollViewProps, StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||||
import KeyboardView from '../presentation/KeyboardView';
|
import KeyboardView from './KeyboardView';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import StatusBar from './StatusBar';
|
import StatusBar from './StatusBar';
|
||||||
import AppVersion from './AppVersion';
|
import AppVersion from './AppVersion';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { StyleSheet, View } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
import { themedHeader } from '../../utils/navigation';
|
||||||
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
|
|
@ -4,7 +4,7 @@ import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
|
|
||||||
interface IHeaderButtonItem {
|
interface IHeaderButtonItem {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import UnreadBadge from '../../presentation/UnreadBadge';
|
import UnreadBadge from '../UnreadBadge';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
badgeContainer: {
|
badgeContainer: {
|
||||||
|
|
|
@ -8,11 +8,11 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import Avatar from '../Avatar';
|
import Avatar from '../Avatar';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { ROW_HEIGHT } from '../../presentation/RoomItem';
|
import { ROW_HEIGHT } from '../RoomItem';
|
||||||
import { goRoom } from '../../utils/goRoom';
|
import { goRoom } from '../../utils/goRoom';
|
||||||
import Navigation from '../../lib/Navigation';
|
import Navigation from '../../lib/navigation/appNavigation';
|
||||||
import { useOrientation } from '../../dimensions';
|
import { useOrientation } from '../../dimensions';
|
||||||
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
|
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { dequal } from 'dequal';
|
||||||
|
|
||||||
import NotifierComponent, { INotifierComponent } from './NotifierComponent';
|
import NotifierComponent, { INotifierComponent } from './NotifierComponent';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import Navigation from '../../lib/Navigation';
|
import Navigation from '../../lib/navigation/appNavigation';
|
||||||
import { getActiveRoute } from '../../utils/navigation';
|
import { getActiveRoute } from '../../utils/navigation';
|
||||||
import { IApplicationState } from '../../definitions';
|
import { IApplicationState } from '../../definitions';
|
||||||
import { IRoom } from '../../reducers/room';
|
import { IRoom } from '../../reducers/room';
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { KeyboardAwareScrollView, KeyboardAwareScrollViewProps } from '@codler/react-native-keyboard-aware-scroll-view';
|
||||||
|
|
||||||
|
import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||||
|
|
||||||
|
interface IKeyboardViewProps extends KeyboardAwareScrollViewProps {
|
||||||
|
keyboardVerticalOffset?: number;
|
||||||
|
scrollEnabled?: boolean;
|
||||||
|
children: React.ReactElement[] | React.ReactElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KeyboardView = ({ style, contentContainerStyle, scrollEnabled, keyboardVerticalOffset, children }: IKeyboardViewProps) => (
|
||||||
|
<KeyboardAwareScrollView
|
||||||
|
{...scrollPersistTaps}
|
||||||
|
style={style}
|
||||||
|
contentContainerStyle={contentContainerStyle}
|
||||||
|
scrollEnabled={scrollEnabled}
|
||||||
|
alwaysBounceVertical={false}
|
||||||
|
extraHeight={keyboardVerticalOffset}>
|
||||||
|
{children}
|
||||||
|
</KeyboardAwareScrollView>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default KeyboardView;
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
|
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { PADDING_HORIZONTAL } from './constants';
|
import { PADDING_HORIZONTAL } from './constants';
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
|
import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { ICON_SIZE } from './constants';
|
import { ICON_SIZE } from './constants';
|
||||||
|
|
||||||
interface IListIcon {
|
interface IListIcon {
|
||||||
name: string;
|
name: string;
|
||||||
color?: string;
|
color?: string | null;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
testID?: string;
|
testID?: string;
|
||||||
|
size?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -20,12 +21,12 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListIcon = React.memo(({ name, color, style, testID }: IListIcon) => {
|
const ListIcon = React.memo(({ name, color, style, testID, size }: IListIcon) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.icon, style]}>
|
<View style={[styles.icon, style]}>
|
||||||
<CustomIcon name={name} color={color ?? themes[theme].auxiliaryText} size={ICON_SIZE} testID={testID} />
|
<CustomIcon name={name} color={color ?? themes[theme].auxiliaryText} size={size ?? ICON_SIZE} testID={testID} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
|
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { PADDING_HORIZONTAL } from './constants';
|
import { PADDING_HORIZONTAL } from './constants';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { I18nManager, StyleSheet, Text, View } from 'react-native';
|
import { I18nManager, StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { useTheme } from '../../theme';
|
import { TSupportedThemes, useTheme } from '../../theme';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { Icon } from '.';
|
import { Icon } from '.';
|
||||||
import { BASE_HEIGHT, ICON_SIZE, PADDING_HORIZONTAL } from './constants';
|
import { BASE_HEIGHT, ICON_SIZE, PADDING_HORIZONTAL } from './constants';
|
||||||
|
@ -59,13 +59,15 @@ interface IListItemContent {
|
||||||
left?: () => JSX.Element | null;
|
left?: () => JSX.Element | null;
|
||||||
right?: () => JSX.Element | null;
|
right?: () => JSX.Element | null;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
testID?: string;
|
testID?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
translateTitle?: boolean;
|
translateTitle?: boolean;
|
||||||
translateSubtitle?: boolean;
|
translateSubtitle?: boolean;
|
||||||
showActionIndicator?: boolean;
|
showActionIndicator?: boolean;
|
||||||
alert?: boolean;
|
alert?: boolean;
|
||||||
|
heightContainer?: number;
|
||||||
|
styleTitle?: StyleProp<TextStyle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Content = React.memo(
|
const Content = React.memo(
|
||||||
|
@ -81,17 +83,21 @@ const Content = React.memo(
|
||||||
translateTitle = true,
|
translateTitle = true,
|
||||||
translateSubtitle = true,
|
translateSubtitle = true,
|
||||||
showActionIndicator = false,
|
showActionIndicator = false,
|
||||||
theme
|
theme,
|
||||||
|
heightContainer,
|
||||||
|
styleTitle
|
||||||
}: IListItemContent) => {
|
}: IListItemContent) => {
|
||||||
const { fontScale } = useDimensions();
|
const { fontScale } = useDimensions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, disabled && styles.disabled, { height: BASE_HEIGHT * fontScale }]} testID={testID}>
|
<View
|
||||||
|
style={[styles.container, disabled && styles.disabled, { height: (heightContainer || BASE_HEIGHT) * fontScale }]}
|
||||||
|
testID={testID}>
|
||||||
{left ? <View style={styles.leftContainer}>{left()}</View> : null}
|
{left ? <View style={styles.leftContainer}>{left()}</View> : null}
|
||||||
<View style={styles.textContainer}>
|
<View style={styles.textContainer}>
|
||||||
<View style={styles.textAlertContainer}>
|
<View style={styles.textAlertContainer}>
|
||||||
<Text style={[styles.title, { color: color || themes[theme].titleText }]} numberOfLines={1}>
|
<Text style={[styles.title, styleTitle, { color: color || themes[theme].titleText }]} numberOfLines={1}>
|
||||||
{translateTitle ? I18n.t(title) : title}
|
{translateTitle && title ? I18n.t(title) : title}
|
||||||
</Text>
|
</Text>
|
||||||
{alert ? (
|
{alert ? (
|
||||||
<CustomIcon style={[styles.alertIcon, { color: themes[theme].dangerColor }]} size={ICON_SIZE} name='info' />
|
<CustomIcon style={[styles.alertIcon, { color: themes[theme].dangerColor }]} size={ICON_SIZE} name='info' />
|
||||||
|
@ -121,7 +127,7 @@ interface IListButtonPress extends IListItemButton {
|
||||||
interface IListItemButton {
|
interface IListItemButton {
|
||||||
title?: string;
|
title?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
underlayColor?: string;
|
underlayColor?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, View, ViewStyle } from 'react-native';
|
import { StyleSheet, View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Animated, Modal, StyleSheet, View } from 'react-native';
|
import { Animated, Modal, StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
import { withTheme } from '../theme';
|
import { TSupportedThemes, withTheme } from '../theme';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -19,7 +19,7 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
interface ILoadingProps {
|
interface ILoadingProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
theme?: string;
|
theme?: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ILoadingState {
|
interface ILoadingState {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Animated, Easing, Linking, StyleSheet, Text, View } from 'react-native';
|
import { Animated, Easing, Linking, StyleSheet, Text, View, ViewStyle } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
import * as AppleAuthentication from 'expo-apple-authentication';
|
import * as AppleAuthentication from 'expo-apple-authentication';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
import { withTheme } from '../theme';
|
import { TSupportedThemes, withTheme } from '../theme';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import OrSeparator from './OrSeparator';
|
import OrSeparator from './OrSeparator';
|
||||||
import Touch from '../utils/touch';
|
import Touch from '../utils/touch';
|
||||||
|
@ -100,7 +100,7 @@ interface ILoginServicesProps {
|
||||||
CAS_enabled: boolean;
|
CAS_enabled: boolean;
|
||||||
CAS_login_url: string;
|
CAS_login_url: string;
|
||||||
separator: boolean;
|
separator: boolean;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ILoginServicesState {
|
interface ILoginServicesState {
|
||||||
|
@ -410,7 +410,7 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, ILoginServi
|
||||||
const { servicesHeight } = this.state;
|
const { servicesHeight } = this.state;
|
||||||
const { services, separator } = this.props;
|
const { services, separator } = this.props;
|
||||||
const { length } = Object.values(services);
|
const { length } = Object.values(services);
|
||||||
const style = {
|
const style: Animated.AnimatedProps<ViewStyle> = {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
height: servicesHeight
|
height: servicesHeight
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { FlatList, StyleSheet, Text, View } from 'react-native';
|
import { FlatList, StyleSheet, Text, View } from 'react-native';
|
||||||
|
|
||||||
import { withTheme } from '../../theme';
|
import { TSupportedThemes, useTheme } from '../../theme';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
||||||
|
@ -10,27 +10,31 @@ import database from '../../lib/database';
|
||||||
import { Button } from '../ActionSheet';
|
import { Button } from '../ActionSheet';
|
||||||
import { useDimensions } from '../../dimensions';
|
import { useDimensions } from '../../dimensions';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { IEmoji } from '../../definitions/IEmoji';
|
|
||||||
import { TFrequentlyUsedEmojiModel } from '../../definitions/IFrequentlyUsedEmoji';
|
import { TFrequentlyUsedEmojiModel } from '../../definitions/IFrequentlyUsedEmoji';
|
||||||
|
import { TAnyMessageModel } from '../../definitions';
|
||||||
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IHeader {
|
type TItem = TFrequentlyUsedEmojiModel | string;
|
||||||
handleReaction: Function;
|
|
||||||
|
export interface IHeader {
|
||||||
|
handleReaction: (emoji: TItem, message: TAnyMessageModel) => void;
|
||||||
server: string;
|
server: string;
|
||||||
message: object;
|
message: TAnyMessageModel;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
theme?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TOnReaction = ({ emoji }: { emoji: TItem }) => void;
|
||||||
|
|
||||||
interface THeaderItem {
|
interface THeaderItem {
|
||||||
item: IEmoji;
|
item: TItem;
|
||||||
onReaction: Function;
|
onReaction: TOnReaction;
|
||||||
server: string;
|
server: string;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface THeaderFooter {
|
interface THeaderFooter {
|
||||||
onReaction: any;
|
onReaction: TOnReaction;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HEADER_HEIGHT = 36;
|
export const HEADER_HEIGHT = 36;
|
||||||
|
@ -62,25 +66,32 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const keyExtractor = (item: any) => item?.id || item;
|
const keyExtractor = (item: TItem) => {
|
||||||
|
const emojiModel = item as TFrequentlyUsedEmojiModel;
|
||||||
|
return (emojiModel.id ? emojiModel.content : item) as string;
|
||||||
|
};
|
||||||
|
|
||||||
const DEFAULT_EMOJIS = ['clap', '+1', 'heart_eyes', 'grinning', 'thinking_face', 'smiley'];
|
const DEFAULT_EMOJIS = ['clap', '+1', 'heart_eyes', 'grinning', 'thinking_face', 'smiley'];
|
||||||
|
|
||||||
const HeaderItem = React.memo(({ item, onReaction, server, theme }: THeaderItem) => (
|
const HeaderItem = ({ item, onReaction, server, theme }: THeaderItem) => {
|
||||||
|
const emojiModel = item as TFrequentlyUsedEmojiModel;
|
||||||
|
const emoji = (emojiModel.id ? emojiModel.content : item) as string;
|
||||||
|
return (
|
||||||
<Button
|
<Button
|
||||||
testID={`message-actions-emoji-${item.content || item}`}
|
testID={`message-actions-emoji-${emoji}`}
|
||||||
onPress={() => onReaction({ emoji: `:${item.content || item}:` })}
|
onPress={() => onReaction({ emoji: `:${emoji}:` })}
|
||||||
style={[styles.headerItem, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
style={[styles.headerItem, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||||
theme={theme}>
|
theme={theme}>
|
||||||
{item?.isCustom ? (
|
{emojiModel?.isCustom ? (
|
||||||
<CustomEmoji style={styles.customEmoji} emoji={item} baseUrl={server} />
|
<CustomEmoji style={styles.customEmoji} emoji={emojiModel as IEmoji} baseUrl={server} />
|
||||||
) : (
|
) : (
|
||||||
<Text style={styles.headerIcon}>{shortnameToUnicode(`:${item.content || item}:`)}</Text>
|
<Text style={styles.headerIcon}>{shortnameToUnicode(`:${emoji}:`)}</Text>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
));
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const HeaderFooter = React.memo(({ onReaction, theme }: THeaderFooter) => (
|
const HeaderFooter = ({ onReaction, theme }: THeaderFooter) => (
|
||||||
<Button
|
<Button
|
||||||
testID='add-reaction'
|
testID='add-reaction'
|
||||||
onPress={onReaction}
|
onPress={onReaction}
|
||||||
|
@ -88,17 +99,19 @@ const HeaderFooter = React.memo(({ onReaction, theme }: THeaderFooter) => (
|
||||||
theme={theme}>
|
theme={theme}>
|
||||||
<CustomIcon name='reaction-add' size={24} color={themes[theme].bodyText} />
|
<CustomIcon name='reaction-add' size={24} color={themes[theme].bodyText} />
|
||||||
</Button>
|
</Button>
|
||||||
));
|
);
|
||||||
|
|
||||||
const Header = React.memo(({ handleReaction, server, message, isMasterDetail, theme }: IHeader) => {
|
const Header = React.memo(({ handleReaction, server, message, isMasterDetail }: IHeader) => {
|
||||||
const [items, setItems] = useState<(TFrequentlyUsedEmojiModel | string)[]>([]);
|
const [items, setItems] = useState<TItem[]>([]);
|
||||||
const { width, height }: any = useDimensions();
|
const { width, height } = useDimensions();
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
// TODO: create custom hook to re-render based on screen size
|
||||||
const setEmojis = async () => {
|
const setEmojis = async () => {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||||
let freqEmojis: (TFrequentlyUsedEmojiModel | string)[] = await freqEmojiCollection.query().fetch();
|
let freqEmojis: TItem[] = await freqEmojiCollection.query().fetch();
|
||||||
|
|
||||||
const isLandscape = width > height;
|
const isLandscape = width > height;
|
||||||
const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
||||||
|
@ -115,22 +128,21 @@ const Header = React.memo(({ handleReaction, server, message, isMasterDetail, th
|
||||||
setEmojis();
|
setEmojis();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onReaction = ({ emoji }: { emoji: IEmoji }) => handleReaction(emoji, message);
|
const onReaction: TOnReaction = ({ emoji }) => handleReaction(emoji, message);
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = ({ item }: { item: TItem }) => (
|
||||||
({ item }) => <HeaderItem item={item} onReaction={onReaction} server={server} theme={theme!} />,
|
<HeaderItem item={item} onReaction={onReaction} server={server} theme={theme} />
|
||||||
[]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderFooter = useCallback(() => <HeaderFooter onReaction={onReaction} theme={theme!} />, []);
|
const renderFooter = () => <HeaderFooter onReaction={onReaction} theme={theme} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { backgroundColor: themes[theme!].focusedBackground }]}>
|
<View style={[styles.container, { backgroundColor: themes[theme].focusedBackground }]}>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={items}
|
data={items}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
ListFooterComponent={renderFooter}
|
ListFooterComponent={renderFooter}
|
||||||
style={{ backgroundColor: themes[theme!].focusedBackground }}
|
style={{ backgroundColor: themes[theme].focusedBackground }}
|
||||||
keyExtractor={keyExtractor}
|
keyExtractor={keyExtractor}
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
scrollEnabled={false}
|
scrollEnabled={false}
|
||||||
|
@ -140,4 +152,4 @@ const Header = React.memo(({ handleReaction, server, message, isMasterDetail, th
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withTheme(Header);
|
export default Header;
|
||||||
|
|
|
@ -8,38 +8,38 @@ import RocketChat from '../../lib/rocketchat';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import log, { logEvent } from '../../utils/log';
|
import log, { logEvent } from '../../utils/log';
|
||||||
import Navigation from '../../lib/Navigation';
|
import Navigation from '../../lib/navigation/appNavigation';
|
||||||
import { getMessageTranslation } from '../message/utils';
|
import { getMessageTranslation } from '../message/utils';
|
||||||
import { LISTENER } from '../Toast';
|
import { LISTENER } from '../Toast';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import { showConfirmationAlert } from '../../utils/info';
|
import { showConfirmationAlert } from '../../utils/info';
|
||||||
import { useActionSheet } from '../ActionSheet';
|
import { TActionSheetOptionsItem, useActionSheet } from '../ActionSheet';
|
||||||
import Header, { HEADER_HEIGHT } from './Header';
|
import Header, { HEADER_HEIGHT, IHeader } from './Header';
|
||||||
import events from '../../utils/log/events';
|
import events from '../../utils/log/events';
|
||||||
import { ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
import { IApplicationState, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
||||||
|
|
||||||
export interface IMessageActions {
|
export interface IMessageActions {
|
||||||
room: TSubscriptionModel;
|
room: TSubscriptionModel;
|
||||||
tmid?: string;
|
tmid?: string;
|
||||||
user: Pick<ILoggedUser, 'id'>;
|
user: Pick<ILoggedUser, 'id'>;
|
||||||
editInit: Function;
|
editInit: (message: TAnyMessageModel) => void;
|
||||||
reactionInit: Function;
|
reactionInit: (message: TAnyMessageModel) => void;
|
||||||
onReactionPress: Function;
|
onReactionPress: (shortname: string, messageId: string) => void;
|
||||||
replyInit: Function;
|
replyInit: (message: TAnyMessageModel, mention: boolean) => void;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
Message_AllowDeleting: boolean;
|
Message_AllowDeleting?: boolean;
|
||||||
Message_AllowDeleting_BlockDeleteInMinutes: number;
|
Message_AllowDeleting_BlockDeleteInMinutes?: number;
|
||||||
Message_AllowEditing: boolean;
|
Message_AllowEditing?: boolean;
|
||||||
Message_AllowEditing_BlockEditInMinutes: number;
|
Message_AllowEditing_BlockEditInMinutes?: number;
|
||||||
Message_AllowPinning: boolean;
|
Message_AllowPinning?: boolean;
|
||||||
Message_AllowStarring: boolean;
|
Message_AllowStarring?: boolean;
|
||||||
Message_Read_Receipt_Store_Users: boolean;
|
Message_Read_Receipt_Store_Users?: boolean;
|
||||||
server: string;
|
server: string;
|
||||||
editMessagePermission: [];
|
editMessagePermission?: string[];
|
||||||
deleteMessagePermission: [];
|
deleteMessagePermission?: string[];
|
||||||
forceDeleteMessagePermission: [];
|
forceDeleteMessagePermission?: string[];
|
||||||
pinMessagePermission: [];
|
pinMessagePermission?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const MessageActions = React.memo(
|
const MessageActions = React.memo(
|
||||||
|
@ -69,9 +69,14 @@ const MessageActions = React.memo(
|
||||||
pinMessagePermission
|
pinMessagePermission
|
||||||
}: IMessageActions,
|
}: IMessageActions,
|
||||||
ref
|
ref
|
||||||
): any => {
|
) => {
|
||||||
let permissions: any = {};
|
let permissions = {
|
||||||
const { showActionSheet, hideActionSheet }: any = useActionSheet();
|
hasEditPermission: false,
|
||||||
|
hasDeletePermission: false,
|
||||||
|
hasForceDeletePermission: false,
|
||||||
|
hasPinPermission: false
|
||||||
|
};
|
||||||
|
const { showActionSheet, hideActionSheet } = useActionSheet();
|
||||||
|
|
||||||
const getPermissions = async () => {
|
const getPermissions = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -88,9 +93,9 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isOwn = (message: any) => message.u && message.u._id === user.id;
|
const isOwn = (message: TAnyMessageModel) => message.u && message.u._id === user.id;
|
||||||
|
|
||||||
const allowEdit = (message: any) => {
|
const allowEdit = (message: TAnyMessageModel) => {
|
||||||
if (isReadOnly) {
|
if (isReadOnly) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +110,7 @@ const MessageActions = React.memo(
|
||||||
if (message.ts != null) {
|
if (message.ts != null) {
|
||||||
msgTs = moment(message.ts);
|
msgTs = moment(message.ts);
|
||||||
}
|
}
|
||||||
let currentTsDiff: any;
|
let currentTsDiff = 0;
|
||||||
if (msgTs != null) {
|
if (msgTs != null) {
|
||||||
currentTsDiff = moment().diff(msgTs, 'minutes');
|
currentTsDiff = moment().diff(msgTs, 'minutes');
|
||||||
}
|
}
|
||||||
|
@ -114,7 +119,7 @@ const MessageActions = React.memo(
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const allowDelete = (message: any) => {
|
const allowDelete = (message: TAnyMessageModel) => {
|
||||||
if (isReadOnly) {
|
if (isReadOnly) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -136,7 +141,7 @@ const MessageActions = React.memo(
|
||||||
if (message.ts != null) {
|
if (message.ts != null) {
|
||||||
msgTs = moment(message.ts);
|
msgTs = moment(message.ts);
|
||||||
}
|
}
|
||||||
let currentTsDiff: any;
|
let currentTsDiff = 0;
|
||||||
if (msgTs != null) {
|
if (msgTs != null) {
|
||||||
currentTsDiff = moment().diff(msgTs, 'minutes');
|
currentTsDiff = moment().diff(msgTs, 'minutes');
|
||||||
}
|
}
|
||||||
|
@ -145,19 +150,19 @@ const MessageActions = React.memo(
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPermalink = (message: any) => RocketChat.getPermalinkMessage(message);
|
const getPermalink = (message: TAnyMessageModel) => RocketChat.getPermalinkMessage(message);
|
||||||
|
|
||||||
const handleReply = (message: any) => {
|
const handleReply = (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_REPLY);
|
logEvent(events.ROOM_MSG_ACTION_REPLY);
|
||||||
replyInit(message, true);
|
replyInit(message, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = (message: any) => {
|
const handleEdit = (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_EDIT);
|
logEvent(events.ROOM_MSG_ACTION_EDIT);
|
||||||
editInit(message);
|
editInit(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateDiscussion = (message: any) => {
|
const handleCreateDiscussion = (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_DISCUSSION);
|
logEvent(events.ROOM_MSG_ACTION_DISCUSSION);
|
||||||
const params = { message, channel: room, showCloseModal: true };
|
const params = { message, channel: room, showCloseModal: true };
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
|
@ -167,7 +172,7 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUnread = async (message: any) => {
|
const handleUnread = async (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_UNREAD);
|
logEvent(events.ROOM_MSG_ACTION_UNREAD);
|
||||||
const { id: messageId, ts } = message;
|
const { id: messageId, ts } = message;
|
||||||
const { rid } = room;
|
const { rid } = room;
|
||||||
|
@ -179,7 +184,7 @@ const MessageActions = React.memo(
|
||||||
const subRecord = await subCollection.find(rid);
|
const subRecord = await subCollection.find(rid);
|
||||||
await db.write(async () => {
|
await db.write(async () => {
|
||||||
try {
|
try {
|
||||||
await subRecord.update(sub => (sub.lastOpen = ts));
|
await subRecord.update(sub => (sub.lastOpen = ts as Date)); // TODO: reevaluate IMessage
|
||||||
} catch {
|
} catch {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -192,42 +197,44 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePermalink = async (message: any) => {
|
const handlePermalink = async (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_PERMALINK);
|
logEvent(events.ROOM_MSG_ACTION_PERMALINK);
|
||||||
try {
|
try {
|
||||||
const permalink: any = await getPermalink(message);
|
const permalink = await getPermalink(message);
|
||||||
Clipboard.setString(permalink);
|
Clipboard.setString(permalink ?? '');
|
||||||
EventEmitter.emit(LISTENER, { message: I18n.t('Permalink_copied_to_clipboard') });
|
EventEmitter.emit(LISTENER, { message: I18n.t('Permalink_copied_to_clipboard') });
|
||||||
} catch {
|
} catch {
|
||||||
logEvent(events.ROOM_MSG_ACTION_PERMALINK_F);
|
logEvent(events.ROOM_MSG_ACTION_PERMALINK_F);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopy = async (message: any) => {
|
const handleCopy = async (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_COPY);
|
logEvent(events.ROOM_MSG_ACTION_COPY);
|
||||||
await Clipboard.setString(message?.attachments?.[0]?.description || message.msg);
|
await Clipboard.setString((message?.attachments?.[0]?.description || message.msg) ?? '');
|
||||||
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShare = async (message: any) => {
|
const handleShare = async (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_SHARE);
|
logEvent(events.ROOM_MSG_ACTION_SHARE);
|
||||||
try {
|
try {
|
||||||
const permalink: any = await getPermalink(message);
|
const permalink = await getPermalink(message);
|
||||||
|
if (permalink) {
|
||||||
Share.share({ message: permalink });
|
Share.share({ message: permalink });
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
logEvent(events.ROOM_MSG_ACTION_SHARE_F);
|
logEvent(events.ROOM_MSG_ACTION_SHARE_F);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleQuote = (message: any) => {
|
const handleQuote = (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_QUOTE);
|
logEvent(events.ROOM_MSG_ACTION_QUOTE);
|
||||||
replyInit(message, false);
|
replyInit(message, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStar = async (message: any) => {
|
const handleStar = async (message: TAnyMessageModel) => {
|
||||||
logEvent(message.starred ? events.ROOM_MSG_ACTION_UNSTAR : events.ROOM_MSG_ACTION_STAR);
|
logEvent(message.starred ? events.ROOM_MSG_ACTION_UNSTAR : events.ROOM_MSG_ACTION_STAR);
|
||||||
try {
|
try {
|
||||||
await RocketChat.toggleStarMessage(message.id, message.starred);
|
await RocketChat.toggleStarMessage(message.id, message.starred as boolean); // TODO: reevaluate `message.starred` type on IMessage
|
||||||
EventEmitter.emit(LISTENER, { message: message.starred ? I18n.t('Message_unstarred') : I18n.t('Message_starred') });
|
EventEmitter.emit(LISTENER, { message: message.starred ? I18n.t('Message_unstarred') : I18n.t('Message_starred') });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logEvent(events.ROOM_MSG_ACTION_STAR_F);
|
logEvent(events.ROOM_MSG_ACTION_STAR_F);
|
||||||
|
@ -235,20 +242,21 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePin = async (message: any) => {
|
const handlePin = async (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_PIN);
|
logEvent(events.ROOM_MSG_ACTION_PIN);
|
||||||
try {
|
try {
|
||||||
await RocketChat.togglePinMessage(message.id, message.pinned);
|
await RocketChat.togglePinMessage(message.id, message.pinned as boolean); // TODO: reevaluate `message.pinned` type on IMessage
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logEvent(events.ROOM_MSG_ACTION_PIN_F);
|
logEvent(events.ROOM_MSG_ACTION_PIN_F);
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReaction = (shortname: any, message: any) => {
|
const handleReaction: IHeader['handleReaction'] = (shortname, message) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_REACTION);
|
logEvent(events.ROOM_MSG_ACTION_REACTION);
|
||||||
if (shortname) {
|
if (shortname) {
|
||||||
onReactionPress(shortname, message.id);
|
// TODO: evaluate unification with IEmoji
|
||||||
|
onReactionPress(shortname as any, message.id);
|
||||||
} else {
|
} else {
|
||||||
reactionInit(message);
|
reactionInit(message);
|
||||||
}
|
}
|
||||||
|
@ -256,7 +264,7 @@ const MessageActions = React.memo(
|
||||||
hideActionSheet();
|
hideActionSheet();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReadReceipt = (message: any) => {
|
const handleReadReceipt = (message: TAnyMessageModel) => {
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
Navigation.navigate('ModalStackNavigator', { screen: 'ReadReceiptsView', params: { messageId: message.id } });
|
Navigation.navigate('ModalStackNavigator', { screen: 'ReadReceiptsView', params: { messageId: message.id } });
|
||||||
} else {
|
} else {
|
||||||
|
@ -291,7 +299,7 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReport = async (message: any) => {
|
const handleReport = async (message: TAnyMessageModel) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_REPORT);
|
logEvent(events.ROOM_MSG_ACTION_REPORT);
|
||||||
try {
|
try {
|
||||||
await RocketChat.reportMessage(message.id);
|
await RocketChat.reportMessage(message.id);
|
||||||
|
@ -302,14 +310,14 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = (message: any) => {
|
const handleDelete = (message: TAnyMessageModel) => {
|
||||||
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'),
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
try {
|
try {
|
||||||
logEvent(events.ROOM_MSG_ACTION_DELETE);
|
logEvent(events.ROOM_MSG_ACTION_DELETE);
|
||||||
await RocketChat.deleteMessage(message.id, message.subscription.id);
|
await RocketChat.deleteMessage(message.id, message.subscription ? message.subscription.id : '');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logEvent(events.ROOM_MSG_ACTION_DELETE_F);
|
logEvent(events.ROOM_MSG_ACTION_DELETE_F);
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -319,7 +327,7 @@ const MessageActions = React.memo(
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOptions = (message: TAnyMessageModel) => {
|
const getOptions = (message: TAnyMessageModel) => {
|
||||||
let options: any = [];
|
let options: TActionSheetOptionsItem[] = [];
|
||||||
|
|
||||||
// Reply
|
// Reply
|
||||||
if (!isReadOnly) {
|
if (!isReadOnly) {
|
||||||
|
@ -463,16 +471,15 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
const mapStateToProps = (state: any) => ({
|
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Message_AllowDeleting: state.settings.Message_AllowDeleting,
|
Message_AllowDeleting: state.settings.Message_AllowDeleting as boolean,
|
||||||
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
|
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes as number,
|
||||||
Message_AllowEditing: state.settings.Message_AllowEditing,
|
Message_AllowEditing: state.settings.Message_AllowEditing as boolean,
|
||||||
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes,
|
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes as number,
|
||||||
Message_AllowPinning: state.settings.Message_AllowPinning,
|
Message_AllowPinning: state.settings.Message_AllowPinning as boolean,
|
||||||
Message_AllowStarring: state.settings.Message_AllowStarring,
|
Message_AllowStarring: state.settings.Message_AllowStarring as boolean,
|
||||||
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users,
|
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users as boolean,
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
editMessagePermission: state.permissions['edit-message'],
|
editMessagePermission: state.permissions['edit-message'],
|
||||||
deleteMessagePermission: state.permissions['delete-message'],
|
deleteMessagePermission: state.permissions['delete-message'],
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import { TouchableOpacity } from 'react-native';
|
import { TouchableOpacity } from 'react-native';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
|
||||||
|
|
||||||
import styles from '../styles';
|
import { themes } from '../../../lib/constants';
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
import { themes } from '../../../constants/colors';
|
import { useTheme } from '../../../theme';
|
||||||
import MessageboxContext from '../Context';
|
|
||||||
import ActivityIndicator from '../../ActivityIndicator';
|
import ActivityIndicator from '../../ActivityIndicator';
|
||||||
|
import MessageboxContext from '../Context';
|
||||||
|
import styles from '../styles';
|
||||||
|
|
||||||
interface IMessageBoxCommandsPreviewItem {
|
interface IMessageBoxCommandsPreviewItem {
|
||||||
item: {
|
item: {
|
||||||
|
@ -14,13 +15,13 @@ interface IMessageBoxCommandsPreviewItem {
|
||||||
id: string;
|
id: string;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
theme?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Item = ({ item, theme }: IMessageBoxCommandsPreviewItem) => {
|
const Item = ({ item }: IMessageBoxCommandsPreviewItem) => {
|
||||||
const context = useContext(MessageboxContext);
|
const context = useContext(MessageboxContext);
|
||||||
const { onPressCommandPreview } = context;
|
const { onPressCommandPreview } = context;
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
@ -37,7 +38,7 @@ const Item = ({ item, theme }: IMessageBoxCommandsPreviewItem) => {
|
||||||
{loading ? <ActivityIndicator /> : null}
|
{loading ? <ActivityIndicator /> : null}
|
||||||
</FastImage>
|
</FastImage>
|
||||||
) : (
|
) : (
|
||||||
<CustomIcon name='attach' size={36} color={themes[theme!].actionTintColor} />
|
<CustomIcon name='attach' size={36} color={themes[theme].actionTintColor} />
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
|
import { dequal } from 'dequal';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FlatList } from 'react-native';
|
import { FlatList } from 'react-native';
|
||||||
import { dequal } from 'dequal';
|
|
||||||
|
|
||||||
import Item from './Item';
|
import { themes } from '../../../lib/constants';
|
||||||
import styles from '../styles';
|
|
||||||
import { themes } from '../../../constants/colors';
|
|
||||||
import { withTheme } from '../../../theme';
|
|
||||||
import { IPreviewItem } from '../../../definitions';
|
import { IPreviewItem } from '../../../definitions';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import styles from '../styles';
|
||||||
|
import Item from './Item';
|
||||||
|
|
||||||
interface IMessageBoxCommandsPreview {
|
interface IMessageBoxCommandsPreview {
|
||||||
commandPreview: IPreviewItem[];
|
commandPreview: IPreviewItem[];
|
||||||
showCommandPreview: boolean;
|
showCommandPreview: boolean;
|
||||||
theme?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CommandsPreview = React.memo(
|
const CommandsPreview = React.memo(
|
||||||
({ theme, commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => {
|
({ commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
if (!showCommandPreview) {
|
if (!showCommandPreview) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<FlatList
|
||||||
testID='commandbox-container'
|
testID='commandbox-container'
|
||||||
style={[styles.mentionList, { backgroundColor: themes[theme!].messageboxBackground }]}
|
style={[styles.mentionList, { backgroundColor: themes[theme].messageboxBackground }]}
|
||||||
data={commandPreview}
|
data={commandPreview}
|
||||||
renderItem={({ item }) => <Item item={item} theme={theme} />}
|
renderItem={({ item }) => <Item item={item} />}
|
||||||
keyExtractor={(item: any) => item.id}
|
keyExtractor={(item: any) => item.id}
|
||||||
keyboardShouldPersistTaps='always'
|
keyboardShouldPersistTaps='always'
|
||||||
horizontal
|
horizontal
|
||||||
|
@ -33,9 +35,6 @@ const CommandsPreview = React.memo(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(prevProps, nextProps) => {
|
(prevProps, nextProps) => {
|
||||||
if (prevProps.theme !== nextProps.theme) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (prevProps.showCommandPreview !== nextProps.showCommandPreview) {
|
if (prevProps.showCommandPreview !== nextProps.showCommandPreview) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -46,4 +45,4 @@ const CommandsPreview = React.memo(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withTheme(CommandsPreview);
|
export default CommandsPreview;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
// @ts-ignore
|
const MessageboxContext = React.createContext<any>(null);
|
||||||
const MessageboxContext = React.createContext<any>();
|
|
||||||
export default MessageboxContext;
|
export default MessageboxContext;
|
||||||
|
|
|
@ -2,14 +2,15 @@ import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { KeyboardRegistry } from 'react-native-ui-lib/keyboard';
|
import { KeyboardRegistry } from 'react-native-ui-lib/keyboard';
|
||||||
|
|
||||||
import { store } from '../../lib/auxStore';
|
import { store } from '../../lib/store/auxStore';
|
||||||
import EmojiPicker from '../EmojiPicker';
|
import EmojiPicker from '../EmojiPicker';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { withTheme } from '../../theme';
|
import { TSupportedThemes, withTheme } from '../../theme';
|
||||||
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessageBoxEmojiKeyboard {
|
interface IMessageBoxEmojiKeyboard {
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiKeyboard, any> {
|
export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiKeyboard, any> {
|
||||||
|
@ -21,7 +22,7 @@ export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiK
|
||||||
this.baseUrl = state.share.server.server || state.server.server;
|
this.baseUrl = state.share.server.server || state.server.server;
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmojiSelected = (emoji: any) => {
|
onEmojiSelected = (emoji: IEmoji) => {
|
||||||
KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji });
|
KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import React from 'react';
|
||||||
import { CancelEditingButton, ToggleEmojiButton } from './buttons';
|
import { CancelEditingButton, ToggleEmojiButton } from './buttons';
|
||||||
|
|
||||||
interface IMessageBoxLeftButtons {
|
interface IMessageBoxLeftButtons {
|
||||||
theme: string;
|
|
||||||
showEmojiKeyboard: boolean;
|
showEmojiKeyboard: boolean;
|
||||||
openEmoji(): void;
|
openEmoji(): void;
|
||||||
closeEmoji(): void;
|
closeEmoji(): void;
|
||||||
|
@ -11,13 +10,11 @@ interface IMessageBoxLeftButtons {
|
||||||
editCancel(): void;
|
editCancel(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LeftButtons = React.memo(
|
const LeftButtons = React.memo(({ showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji }: IMessageBoxLeftButtons) => {
|
||||||
({ theme, showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji }: IMessageBoxLeftButtons) => {
|
|
||||||
if (editing) {
|
if (editing) {
|
||||||
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
return <CancelEditingButton onPress={editCancel} />;
|
||||||
}
|
}
|
||||||
return <ToggleEmojiButton show={showEmojiKeyboard} open={openEmoji} close={closeEmoji} theme={theme} />;
|
return <ToggleEmojiButton show={showEmojiKeyboard} open={openEmoji} close={closeEmoji} />;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
export default LeftButtons;
|
export default LeftButtons;
|
||||||
|
|
|
@ -5,23 +5,20 @@ import { ActionsButton, CancelEditingButton } from './buttons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IMessageBoxLeftButtons {
|
interface IMessageBoxLeftButtons {
|
||||||
theme: string;
|
|
||||||
showMessageBoxActions(): void;
|
showMessageBoxActions(): void;
|
||||||
editing: boolean;
|
editing: boolean;
|
||||||
editCancel(): void;
|
editCancel(): void;
|
||||||
isActionsEnabled: boolean;
|
isActionsEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LeftButtons = React.memo(
|
const LeftButtons = React.memo(({ showMessageBoxActions, editing, editCancel, isActionsEnabled }: IMessageBoxLeftButtons) => {
|
||||||
({ theme, showMessageBoxActions, editing, editCancel, isActionsEnabled }: IMessageBoxLeftButtons) => {
|
|
||||||
if (editing) {
|
if (editing) {
|
||||||
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
return <CancelEditingButton onPress={editCancel} />;
|
||||||
}
|
}
|
||||||
if (isActionsEnabled) {
|
if (isActionsEnabled) {
|
||||||
return <ActionsButton onPress={showMessageBoxActions} theme={theme} />;
|
return <ActionsButton onPress={showMessageBoxActions} />;
|
||||||
}
|
}
|
||||||
return <View style={styles.buttonsWhitespace} />;
|
return <View style={styles.buttonsWhitespace} />;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
export default LeftButtons;
|
export default LeftButtons;
|
||||||
|
|
|
@ -3,17 +3,19 @@ import { Text, TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../lib/constants';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
interface IMessageBoxFixedMentionItem {
|
interface IMessageBoxFixedMentionItem {
|
||||||
item: {
|
item: {
|
||||||
username: string;
|
username: string;
|
||||||
};
|
};
|
||||||
onPress: Function;
|
onPress: Function;
|
||||||
theme: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FixedMentionItem = ({ item, onPress, theme }: IMessageBoxFixedMentionItem) => (
|
const FixedMentionItem = ({ item, onPress }: IMessageBoxFixedMentionItem) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.mentionItem,
|
styles.mentionItem,
|
||||||
|
@ -29,5 +31,6 @@ const FixedMentionItem = ({ item, onPress, theme }: IMessageBoxFixedMentionItem)
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default FixedMentionItem;
|
export default FixedMentionItem;
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { Text } from 'react-native';
|
import { Text } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
|
||||||
import styles from '../styles';
|
|
||||||
import MessageboxContext from '../Context';
|
|
||||||
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
|
||||||
import { IEmoji } from '../../../definitions/IEmoji';
|
import { IEmoji } from '../../../definitions/IEmoji';
|
||||||
|
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
||||||
|
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||||
|
import MessageboxContext from '../Context';
|
||||||
|
import styles from '../styles';
|
||||||
|
|
||||||
interface IMessageBoxMentionEmoji {
|
interface IMessageBoxMentionEmoji {
|
||||||
item: IEmoji;
|
item: IEmoji;
|
||||||
|
@ -22,8 +21,4 @@ const MentionEmoji = ({ item }: IMessageBoxMentionEmoji) => {
|
||||||
return <Text style={styles.mentionItemEmoji}>{shortnameToUnicode(`:${item}:`)}</Text>;
|
return <Text style={styles.mentionItemEmoji}>{shortnameToUnicode(`:${item}:`)}</Text>;
|
||||||
};
|
};
|
||||||
|
|
||||||
MentionEmoji.propTypes = {
|
|
||||||
item: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MentionEmoji;
|
export default MentionEmoji;
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { View, Text, ActivityIndicator, TouchableOpacity } from 'react-native';
|
import { ActivityIndicator, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
import { themes } from '../../../lib/constants';
|
||||||
import styles from '../styles';
|
|
||||||
import sharedStyles from '../../../views/Styles';
|
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
import { themes } from '../../../constants/colors';
|
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import sharedStyles from '../../../views/Styles';
|
||||||
import MessageboxContext from '../Context';
|
import MessageboxContext from '../Context';
|
||||||
|
import styles from '../styles';
|
||||||
|
import { MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
||||||
|
|
||||||
const MentionHeaderList = ({ trackingType, hasMentions, theme, loading }) => {
|
interface IMentionHeaderList {
|
||||||
|
trackingType: string;
|
||||||
|
hasMentions: boolean;
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MentionHeaderList = ({ trackingType, hasMentions, loading }: IMentionHeaderList) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
const context = useContext(MessageboxContext);
|
const context = useContext(MessageboxContext);
|
||||||
const { onPressNoMatchCanned } = context;
|
const { onPressNoMatchCanned } = context;
|
||||||
|
|
||||||
|
@ -39,11 +46,4 @@ const MentionHeaderList = ({ trackingType, hasMentions, theme, loading }) => {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
MentionHeaderList.propTypes = {
|
|
||||||
trackingType: PropTypes.string,
|
|
||||||
hasMentions: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
loading: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MentionHeaderList;
|
export default MentionHeaderList;
|
|
@ -1,14 +1,15 @@
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { Text, TouchableOpacity } from 'react-native';
|
import { Text, TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
import styles from '../styles';
|
import { themes } from '../../../lib/constants';
|
||||||
|
import { IEmoji } from '../../../definitions/IEmoji';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
import Avatar from '../../Avatar';
|
import Avatar from '../../Avatar';
|
||||||
|
import { MENTIONS_TRACKING_TYPE_CANNED, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_EMOJIS } from '../constants';
|
||||||
import MessageboxContext from '../Context';
|
import MessageboxContext from '../Context';
|
||||||
|
import styles from '../styles';
|
||||||
import FixedMentionItem from './FixedMentionItem';
|
import FixedMentionItem from './FixedMentionItem';
|
||||||
import MentionEmoji from './MentionEmoji';
|
import MentionEmoji from './MentionEmoji';
|
||||||
import { MENTIONS_TRACKING_TYPE_EMOJIS, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
|
||||||
import { themes } from '../../../constants/colors';
|
|
||||||
import { IEmoji } from '../../../definitions/IEmoji';
|
|
||||||
|
|
||||||
interface IMessageBoxMentionItem {
|
interface IMessageBoxMentionItem {
|
||||||
item: {
|
item: {
|
||||||
|
@ -21,11 +22,48 @@ interface IMessageBoxMentionItem {
|
||||||
text: string;
|
text: string;
|
||||||
} & IEmoji;
|
} & IEmoji;
|
||||||
trackingType: string;
|
trackingType: string;
|
||||||
theme: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
|
const MentionItemContent = React.memo(({ trackingType, item }: IMessageBoxMentionItem) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
switch (trackingType) {
|
||||||
|
case MENTIONS_TRACKING_TYPE_EMOJIS:
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MentionEmoji item={item} />
|
||||||
|
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>:{item.name || item}:</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case MENTIONS_TRACKING_TYPE_COMMANDS:
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text style={[styles.slash, { backgroundColor: themes[theme].borderColor, color: themes[theme].tintColor }]}>/</Text>
|
||||||
|
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.id}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case MENTIONS_TRACKING_TYPE_CANNED:
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text style={[styles.cannedItem, { color: themes[theme].titleText }]}>!{item.shortcut}</Text>
|
||||||
|
<Text numberOfLines={1} style={[styles.cannedMentionText, { color: themes[theme].auxiliaryTintColor }]}>
|
||||||
|
{item.text}
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Avatar style={styles.avatar} text={item.username || item.name} size={30} type={item.t} />
|
||||||
|
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.username || item.name || item}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const MentionItem = ({ item, trackingType }: IMessageBoxMentionItem) => {
|
||||||
const context = useContext(MessageboxContext);
|
const context = useContext(MessageboxContext);
|
||||||
|
const { theme } = useTheme();
|
||||||
const { onPressMention } = context;
|
const { onPressMention } = context;
|
||||||
|
|
||||||
const defineTestID = (type: string) => {
|
const defineTestID = (type: string) => {
|
||||||
|
@ -44,43 +82,7 @@ const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
|
||||||
const testID = defineTestID(trackingType);
|
const testID = defineTestID(trackingType);
|
||||||
|
|
||||||
if (item.username === 'all' || item.username === 'here') {
|
if (item.username === 'all' || item.username === 'here') {
|
||||||
return <FixedMentionItem item={item} onPress={onPressMention} theme={theme} />;
|
return <FixedMentionItem item={item} onPress={onPressMention} />;
|
||||||
}
|
|
||||||
|
|
||||||
let content = (
|
|
||||||
<>
|
|
||||||
<Avatar style={styles.avatar} text={item.username || item.name} size={30} type={item.t} />
|
|
||||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.username || item.name || item}</Text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (trackingType === MENTIONS_TRACKING_TYPE_EMOJIS) {
|
|
||||||
content = (
|
|
||||||
<>
|
|
||||||
<MentionEmoji item={item} />
|
|
||||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>:{item.name || item}:</Text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) {
|
|
||||||
content = (
|
|
||||||
<>
|
|
||||||
<Text style={[styles.slash, { backgroundColor: themes[theme].borderColor, color: themes[theme].tintColor }]}>/</Text>
|
|
||||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.id}</Text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackingType === MENTIONS_TRACKING_TYPE_CANNED) {
|
|
||||||
content = (
|
|
||||||
<>
|
|
||||||
<Text style={[styles.cannedItem, { color: themes[theme].titleText }]}>!{item.shortcut}</Text>
|
|
||||||
<Text numberOfLines={1} style={[styles.cannedMentionText, { color: themes[theme].auxiliaryTintColor }]}>
|
|
||||||
{item.text}
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -94,7 +96,7 @@ const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
|
||||||
]}
|
]}
|
||||||
onPress={() => onPressMention(item)}
|
onPress={() => onPressMention(item)}
|
||||||
testID={testID}>
|
testID={testID}>
|
||||||
{content}
|
<MentionItemContent item={item} trackingType={trackingType} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,30 +5,33 @@ import { dequal } from 'dequal';
|
||||||
import MentionHeaderList from './MentionHeaderList';
|
import MentionHeaderList from './MentionHeaderList';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import MentionItem from './MentionItem';
|
import MentionItem from './MentionItem';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../lib/constants';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
interface IMessageBoxMentions {
|
interface IMessageBoxMentions {
|
||||||
mentions: any[];
|
mentions: any[];
|
||||||
trackingType: string;
|
trackingType: string;
|
||||||
theme: string;
|
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Mentions = React.memo(
|
const Mentions = React.memo(
|
||||||
({ mentions, trackingType, theme, loading }: IMessageBoxMentions) => {
|
({ mentions, trackingType, loading }: IMessageBoxMentions) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
if (!trackingType) {
|
if (!trackingType) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View testID='messagebox-container'>
|
<View testID='messagebox-container'>
|
||||||
<FlatList
|
<FlatList
|
||||||
style={[styles.mentionList, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
style={[styles.mentionList, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||||
ListHeaderComponent={() => (
|
ListHeaderComponent={() => (
|
||||||
<MentionHeaderList trackingType={trackingType} hasMentions={mentions.length > 0} theme={theme} loading={loading} />
|
<MentionHeaderList trackingType={trackingType} hasMentions={mentions.length > 0} loading={loading} />
|
||||||
)}
|
)}
|
||||||
data={mentions}
|
data={mentions}
|
||||||
extraData={mentions}
|
extraData={mentions}
|
||||||
renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} theme={theme} />}
|
renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} />}
|
||||||
keyExtractor={item => item.rid || item.name || item.command || item.shortcut || item}
|
keyExtractor={item => item.rid || item.name || item.command || item.shortcut || item}
|
||||||
keyboardShouldPersistTaps='always'
|
keyboardShouldPersistTaps='always'
|
||||||
/>
|
/>
|
||||||
|
@ -39,9 +42,6 @@ const Mentions = React.memo(
|
||||||
if (prevProps.loading !== nextProps.loading) {
|
if (prevProps.loading !== nextProps.loading) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (prevProps.theme !== nextProps.theme) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (prevProps.trackingType !== nextProps.trackingType) {
|
if (prevProps.trackingType !== nextProps.trackingType) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,13 @@ import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { events, logEvent } from '../../utils/log';
|
import { events, logEvent } from '../../utils/log';
|
||||||
|
import { TSupportedThemes } from '../../theme';
|
||||||
|
|
||||||
interface IMessageBoxRecordAudioProps {
|
interface IMessageBoxRecordAudioProps {
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
permissionToUpload: boolean;
|
permissionToUpload: boolean;
|
||||||
recordingCallback: Function;
|
recordingCallback: Function;
|
||||||
onFinish: Function;
|
onFinish: Function;
|
||||||
|
@ -47,23 +48,17 @@ const RECORDING_MODE = {
|
||||||
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX
|
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatTime = function (seconds: any) {
|
const formatTime = function (time: number) {
|
||||||
let minutes: any = Math.floor(seconds / 60);
|
const minutes = Math.floor(time / 60);
|
||||||
seconds %= 60;
|
const seconds = time % 60;
|
||||||
if (minutes < 10) {
|
const min = minutes < 10 ? `0${minutes}` : minutes;
|
||||||
minutes = `0${minutes}`;
|
const sec = seconds < 10 ? `0${seconds}` : seconds;
|
||||||
}
|
return `${min}:${sec}`;
|
||||||
if (seconds < 10) {
|
|
||||||
seconds = `0${seconds}`;
|
|
||||||
}
|
|
||||||
return `${minutes}:${seconds}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAudioProps, any> {
|
export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAudioProps, any> {
|
||||||
private isRecorderBusy: boolean;
|
private isRecorderBusy: boolean;
|
||||||
|
private recording!: Audio.Recording;
|
||||||
private recording: any;
|
|
||||||
|
|
||||||
private LastDuration: number;
|
private LastDuration: number;
|
||||||
|
|
||||||
constructor(props: IMessageBoxRecordAudioProps) {
|
constructor(props: IMessageBoxRecordAudioProps) {
|
||||||
|
@ -112,7 +107,7 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
onRecordingStatusUpdate = (status: any) => {
|
onRecordingStatusUpdate = (status: Audio.RecordingStatus) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isRecording: status.isRecording,
|
isRecording: status.isRecording,
|
||||||
recordingDurationMillis: status.durationMillis
|
recordingDurationMillis: status.durationMillis
|
||||||
|
@ -157,7 +152,7 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
||||||
await this.recording.stopAndUnloadAsync();
|
await this.recording.stopAndUnloadAsync();
|
||||||
|
|
||||||
const fileURI = this.recording.getURI();
|
const fileURI = this.recording.getURI();
|
||||||
const fileData = await getInfoAsync(fileURI);
|
const fileData = await getInfoAsync(fileURI as string);
|
||||||
const fileInfo = {
|
const fileInfo = {
|
||||||
name: `${Date.now()}.m4a`,
|
name: `${Date.now()}.m4a`,
|
||||||
mime: 'audio/aac',
|
mime: 'audio/aac',
|
||||||
|
@ -200,14 +195,10 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
||||||
}
|
}
|
||||||
if (!isRecording && !isRecorderActive) {
|
if (!isRecording && !isRecorderActive) {
|
||||||
return (
|
return (
|
||||||
<BorderlessButton
|
<BorderlessButton onPress={this.startRecordingAudio} style={styles.actionButton} testID='messagebox-send-audio'>
|
||||||
onPress={this.startRecordingAudio}
|
<View accessible accessibilityLabel={I18n.t('Send_audio_message')} accessibilityRole='button'>
|
||||||
style={styles.actionButton}
|
|
||||||
testID='messagebox-send-audio'
|
|
||||||
// @ts-ignore
|
|
||||||
accessibilityLabel={I18n.t('Send_audio_message')}
|
|
||||||
accessibilityTraits='button'>
|
|
||||||
<CustomIcon name='microphone' size={24} color={themes[theme].auxiliaryTintColor} />
|
<CustomIcon name='microphone' size={24} color={themes[theme].auxiliaryTintColor} />
|
||||||
|
</View>
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -216,23 +207,17 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
||||||
return (
|
return (
|
||||||
<View style={styles.recordingContent}>
|
<View style={styles.recordingContent}>
|
||||||
<View style={styles.textArea}>
|
<View style={styles.textArea}>
|
||||||
<BorderlessButton
|
<BorderlessButton onPress={this.cancelRecordingAudio} style={styles.actionButton}>
|
||||||
onPress={this.cancelRecordingAudio}
|
<View accessible accessibilityLabel={I18n.t('Cancel_recording')} accessibilityRole='button'>
|
||||||
// @ts-ignore
|
|
||||||
accessibilityLabel={I18n.t('Cancel_recording')}
|
|
||||||
accessibilityTraits='button'
|
|
||||||
style={styles.actionButton}>
|
|
||||||
<CustomIcon size={24} color={themes[theme].dangerColor} name='delete' />
|
<CustomIcon size={24} color={themes[theme].dangerColor} name='delete' />
|
||||||
|
</View>
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
<Text style={[styles.recordingDurationText, { color: themes[theme].titleText }]}>{this.GetLastDuration}</Text>
|
<Text style={[styles.recordingDurationText, { color: themes[theme].titleText }]}>{this.GetLastDuration}</Text>
|
||||||
</View>
|
</View>
|
||||||
<BorderlessButton
|
<BorderlessButton onPress={this.finishRecordingAudio} style={styles.actionButton}>
|
||||||
onPress={this.finishRecordingAudio}
|
<View accessible accessibilityLabel={I18n.t('Finish_recording')} accessibilityRole='button'>
|
||||||
// @ts-ignore
|
|
||||||
accessibilityLabel={I18n.t('Finish_recording')}
|
|
||||||
accessibilityTraits='button'
|
|
||||||
style={styles.actionButton}>
|
|
||||||
<CustomIcon size={24} color={themes[theme].tintColor} name='send-filled' />
|
<CustomIcon size={24} color={themes[theme].tintColor} name='send-filled' />
|
||||||
|
</View>
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -241,24 +226,18 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
||||||
return (
|
return (
|
||||||
<View style={styles.recordingContent}>
|
<View style={styles.recordingContent}>
|
||||||
<View style={styles.textArea}>
|
<View style={styles.textArea}>
|
||||||
<BorderlessButton
|
<BorderlessButton onPress={this.cancelRecordingAudio} style={styles.actionButton}>
|
||||||
onPress={this.cancelRecordingAudio}
|
<View accessible accessibilityLabel={I18n.t('Cancel_recording')} accessibilityRole='button'>
|
||||||
// @ts-ignore
|
|
||||||
accessibilityLabel={I18n.t('Cancel_recording')}
|
|
||||||
accessibilityTraits='button'
|
|
||||||
style={styles.actionButton}>
|
|
||||||
<CustomIcon size={24} color={themes[theme].dangerColor} name='delete' />
|
<CustomIcon size={24} color={themes[theme].dangerColor} name='delete' />
|
||||||
|
</View>
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
<Text style={[styles.recordingDurationText, { color: themes[theme].titleText }]}>{this.duration}</Text>
|
<Text style={[styles.recordingDurationText, { color: themes[theme].titleText }]}>{this.duration}</Text>
|
||||||
<CustomIcon size={24} color={themes[theme].dangerColor} name='record' />
|
<CustomIcon size={24} color={themes[theme].dangerColor} name='record' />
|
||||||
</View>
|
</View>
|
||||||
<BorderlessButton
|
<BorderlessButton onPress={this.finishRecordingAudio} style={styles.actionButton}>
|
||||||
onPress={this.finishRecordingAudio}
|
<View accessible accessibilityLabel={I18n.t('Finish_recording')} accessibilityRole='button'>
|
||||||
// @ts-ignore
|
|
||||||
accessibilityLabel={I18n.t('Finish_recording')}
|
|
||||||
accessibilityTraits='button'
|
|
||||||
style={styles.actionButton}>
|
|
||||||
<CustomIcon size={24} color={themes[theme].tintColor} name='send-filled' />
|
<CustomIcon size={24} color={themes[theme].tintColor} name='send-filled' />
|
||||||
|
</View>
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,8 +6,10 @@ import { connect } from 'react-redux';
|
||||||
import { MarkdownPreview } from '../markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { IMessage } from '../../definitions/IMessage';
|
import { IMessage } from '../../definitions/IMessage';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
import { IApplicationState } from '../../definitions';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -49,12 +51,13 @@ interface IMessageBoxReplyPreview {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
username: string;
|
username: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: Function;
|
||||||
theme: string;
|
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReplyPreview = React.memo(
|
const ReplyPreview = React.memo(
|
||||||
({ message, Message_TimeFormat, replying, close, theme, useRealName }: IMessageBoxReplyPreview) => {
|
({ message, Message_TimeFormat, replying, close, useRealName }: IMessageBoxReplyPreview) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
if (!replying) {
|
if (!replying) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -75,16 +78,14 @@ const ReplyPreview = React.memo(
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(prevProps: any, nextProps: any) =>
|
(prevProps: IMessageBoxReplyPreview, nextProps: IMessageBoxReplyPreview) =>
|
||||||
prevProps.replying === nextProps.replying &&
|
prevProps.replying === nextProps.replying && prevProps.message.id === nextProps.message.id
|
||||||
prevProps.theme === nextProps.theme &&
|
|
||||||
prevProps.message.id === nextProps.message.id
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
Message_TimeFormat: state.settings.Message_TimeFormat as string,
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
useRealName: state.settings.UI_Use_Real_Name
|
useRealName: state.settings.UI_Use_Real_Name as boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(ReplyPreview);
|
export default connect(mapStateToProps)(ReplyPreview);
|
||||||
|
|
|
@ -5,24 +5,20 @@ import { ActionsButton, SendButton } from './buttons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IMessageBoxRightButtons {
|
interface IMessageBoxRightButtons {
|
||||||
theme: string;
|
|
||||||
showSend: boolean;
|
showSend: boolean;
|
||||||
submit(): void;
|
submit(): void;
|
||||||
showMessageBoxActions(): void;
|
showMessageBoxActions(): void;
|
||||||
isActionsEnabled: boolean;
|
isActionsEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RightButtons = React.memo(
|
const RightButtons = React.memo(({ showSend, submit, showMessageBoxActions, isActionsEnabled }: IMessageBoxRightButtons) => {
|
||||||
({ theme, showSend, submit, showMessageBoxActions, isActionsEnabled }: IMessageBoxRightButtons) => {
|
|
||||||
if (showSend) {
|
if (showSend) {
|
||||||
return <SendButton onPress={submit} theme={theme} />;
|
return <SendButton onPress={submit} />;
|
||||||
}
|
}
|
||||||
if (isActionsEnabled) {
|
if (isActionsEnabled) {
|
||||||
return <ActionsButton onPress={showMessageBoxActions} theme={theme} />;
|
return <ActionsButton onPress={showMessageBoxActions} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <View style={styles.buttonsWhitespace} />;
|
return <View style={styles.buttonsWhitespace} />;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
export default RightButtons;
|
export default RightButtons;
|
||||||
|
|
|
@ -3,16 +3,15 @@ import React from 'react';
|
||||||
import { SendButton } from './buttons';
|
import { SendButton } from './buttons';
|
||||||
|
|
||||||
interface IMessageBoxRightButtons {
|
interface IMessageBoxRightButtons {
|
||||||
theme: string;
|
|
||||||
showSend: boolean;
|
showSend: boolean;
|
||||||
submit(): void;
|
submit(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RightButtons = React.memo(({ theme, showSend, submit }: IMessageBoxRightButtons) => {
|
const RightButtons = ({ showSend, submit }: IMessageBoxRightButtons) => {
|
||||||
if (showSend) {
|
if (showSend) {
|
||||||
return <SendButton theme={theme} onPress={submit} />;
|
return <SendButton onPress={submit} />;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
};
|
||||||
|
|
||||||
export default RightButtons;
|
export default RightButtons;
|
||||||
|
|
|
@ -3,12 +3,11 @@ import React from 'react';
|
||||||
import BaseButton from './BaseButton';
|
import BaseButton from './BaseButton';
|
||||||
|
|
||||||
interface IActionsButton {
|
interface IActionsButton {
|
||||||
theme: string;
|
|
||||||
onPress(): void;
|
onPress(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActionsButton = React.memo(({ theme, onPress }: IActionsButton) => (
|
const ActionsButton = ({ onPress }: IActionsButton) => (
|
||||||
<BaseButton onPress={onPress} testID='messagebox-actions' accessibilityLabel='Message_actions' icon='add' theme={theme} />
|
<BaseButton onPress={onPress} testID='messagebox-actions' accessibilityLabel='Message_actions' icon='add' />
|
||||||
));
|
);
|
||||||
|
|
||||||
export default ActionsButton;
|
export default ActionsButton;
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import React from 'react';
|
|
||||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||||
|
import React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../../constants/colors';
|
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import I18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
|
import { themes } from '../../../lib/constants';
|
||||||
|
|
||||||
interface IBaseButton {
|
interface IBaseButton {
|
||||||
theme: string;
|
|
||||||
onPress(): void;
|
onPress(): void;
|
||||||
testID: string;
|
testID: string;
|
||||||
accessibilityLabel: string;
|
accessibilityLabel: string;
|
||||||
|
@ -15,16 +16,18 @@ interface IBaseButton {
|
||||||
color: string;
|
color: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BaseButton = React.memo(({ onPress, testID, accessibilityLabel, icon, theme, color }: Partial<IBaseButton>) => (
|
const BaseButton = ({ accessibilityLabel, icon, color, ...props }: Partial<IBaseButton>) => {
|
||||||
<BorderlessButton
|
const { theme } = useTheme();
|
||||||
onPress={onPress}
|
return (
|
||||||
style={styles.actionButton}
|
<BorderlessButton {...props} style={styles.actionButton}>
|
||||||
testID={testID}
|
<View
|
||||||
// @ts-ignore
|
accessible
|
||||||
accessibilityLabel={I18n.t(accessibilityLabel)}
|
accessibilityLabel={accessibilityLabel ? i18n.t(accessibilityLabel) : accessibilityLabel}
|
||||||
accessibilityTraits='button'>
|
accessibilityRole='button'>
|
||||||
<CustomIcon name={icon} size={24} color={color ?? themes[theme!].auxiliaryTintColor} />
|
<CustomIcon name={icon} size={24} color={color || themes[theme].auxiliaryTintColor} />
|
||||||
|
</View>
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
));
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default BaseButton;
|
export default BaseButton;
|
||||||
|
|
|
@ -3,18 +3,11 @@ import React from 'react';
|
||||||
import BaseButton from './BaseButton';
|
import BaseButton from './BaseButton';
|
||||||
|
|
||||||
interface ICancelEditingButton {
|
interface ICancelEditingButton {
|
||||||
theme: string;
|
|
||||||
onPress(): void;
|
onPress(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CancelEditingButton = React.memo(({ theme, onPress }: ICancelEditingButton) => (
|
const CancelEditingButton = ({ onPress }: ICancelEditingButton) => (
|
||||||
<BaseButton
|
<BaseButton onPress={onPress} testID='messagebox-cancel-editing' accessibilityLabel='Cancel_editing' icon='close' />
|
||||||
onPress={onPress}
|
);
|
||||||
testID='messagebox-cancel-editing'
|
|
||||||
accessibilityLabel='Cancel_editing'
|
|
||||||
icon='close'
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
export default CancelEditingButton;
|
export default CancelEditingButton;
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { themes } from '../../../lib/constants';
|
||||||
|
import { useTheme } from '../../../theme';
|
||||||
import BaseButton from './BaseButton';
|
import BaseButton from './BaseButton';
|
||||||
import { themes } from '../../../constants/colors';
|
|
||||||
|
|
||||||
interface ISendButton {
|
interface ISendButton {
|
||||||
theme: string;
|
|
||||||
onPress(): void;
|
onPress(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SendButton = React.memo(({ theme, onPress }: ISendButton) => (
|
const SendButton = ({ onPress }: ISendButton) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
<BaseButton
|
<BaseButton
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
testID='messagebox-send-message'
|
testID='messagebox-send-message'
|
||||||
accessibilityLabel='Send_message'
|
accessibilityLabel='Send_message'
|
||||||
icon='send-filled'
|
icon='send-filled'
|
||||||
theme={theme}
|
|
||||||
color={themes[theme].tintColor}
|
color={themes[theme].tintColor}
|
||||||
/>
|
/>
|
||||||
));
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default SendButton;
|
export default SendButton;
|
||||||
|
|
|
@ -3,33 +3,18 @@ import React from 'react';
|
||||||
import BaseButton from './BaseButton';
|
import BaseButton from './BaseButton';
|
||||||
|
|
||||||
interface IToggleEmojiButton {
|
interface IToggleEmojiButton {
|
||||||
theme: string;
|
|
||||||
show: boolean;
|
show: boolean;
|
||||||
open(): void;
|
open(): void;
|
||||||
close(): void;
|
close(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToggleEmojiButton = React.memo(({ theme, show, open, close }: IToggleEmojiButton) => {
|
const ToggleEmojiButton = ({ show, open, close }: IToggleEmojiButton) => {
|
||||||
if (show) {
|
if (show) {
|
||||||
return (
|
return (
|
||||||
<BaseButton
|
<BaseButton onPress={close} testID='messagebox-close-emoji' accessibilityLabel='Close_emoji_selector' icon='keyboard' />
|
||||||
onPress={close}
|
|
||||||
testID='messagebox-close-emoji'
|
|
||||||
accessibilityLabel='Close_emoji_selector'
|
|
||||||
icon='keyboard'
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return <BaseButton onPress={open} testID='messagebox-open-emoji' accessibilityLabel='Open_emoji_selector' icon='emoji' />;
|
||||||
<BaseButton
|
};
|
||||||
onPress={open}
|
|
||||||
testID='messagebox-open-emoji'
|
|
||||||
accessibilityLabel='Open_emoji_selector'
|
|
||||||
icon='emoji'
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default ToggleEmojiButton;
|
export default ToggleEmojiButton;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||||
import { Alert, Keyboard, NativeModules, Text, View } from 'react-native';
|
import { Alert, Keyboard, NativeModules, Text, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
|
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
|
||||||
import ImagePicker, { Image, ImageOrVideo } from 'react-native-image-crop-picker';
|
import ImagePicker, { Image, ImageOrVideo, Options } from 'react-native-image-crop-picker';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import DocumentPicker from 'react-native-document-picker';
|
import DocumentPicker from 'react-native-document-picker';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
@ -20,7 +20,7 @@ import RecordAudio from './RecordAudio';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import ReplyPreview from './ReplyPreview';
|
import ReplyPreview from './ReplyPreview';
|
||||||
import debounce from '../../utils/debounce';
|
import debounce from '../../utils/debounce';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// eslint-disable-next-line import/extensions,import/no-unresolved
|
// eslint-disable-next-line import/extensions,import/no-unresolved
|
||||||
import LeftButtons from './LeftButtons';
|
import LeftButtons from './LeftButtons';
|
||||||
|
@ -44,13 +44,15 @@ import {
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import CommandsPreview from './CommandsPreview';
|
import CommandsPreview from './CommandsPreview';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import Navigation from '../../lib/Navigation';
|
import Navigation from '../../lib/navigation/appNavigation';
|
||||||
import { withActionSheet } from '../ActionSheet';
|
import { withActionSheet } from '../ActionSheet';
|
||||||
import { sanitizeLikeString } from '../../lib/database/utils';
|
import { sanitizeLikeString } from '../../lib/database/utils';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { IMessage } from '../../definitions/IMessage';
|
import { IMessage } from '../../definitions/IMessage';
|
||||||
import { forceJpgExtension } from './forceJpgExtension';
|
import { forceJpgExtension } from './forceJpgExtension';
|
||||||
import { IPreviewItem, IUser } from '../../definitions';
|
import { IBaseScreen, IPreviewItem, IUser, TSubscriptionModel, TThreadModel } from '../../definitions';
|
||||||
|
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||||
|
import { TSupportedThemes } from '../../theme';
|
||||||
|
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
require('./EmojiKeyboard');
|
require('./EmojiKeyboard');
|
||||||
|
@ -63,18 +65,18 @@ const imagePickerConfig = {
|
||||||
forceJpg: true
|
forceJpg: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const libraryPickerConfig = {
|
const libraryPickerConfig: Options = {
|
||||||
multiple: true,
|
multiple: true,
|
||||||
compressVideoPreset: 'Passthrough',
|
compressVideoPreset: 'Passthrough',
|
||||||
mediaType: 'any',
|
mediaType: 'any',
|
||||||
forceJpg: true
|
forceJpg: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const videoPickerConfig = {
|
const videoPickerConfig: Options = {
|
||||||
mediaType: 'video'
|
mediaType: 'video'
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IMessageBoxProps {
|
export interface IMessageBoxProps extends IBaseScreen<MasterDetailInsideStackParamList, any> {
|
||||||
rid: string;
|
rid: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
message: IMessage;
|
message: IMessage;
|
||||||
|
@ -94,10 +96,9 @@ export interface IMessageBoxProps {
|
||||||
editRequest: Function;
|
editRequest: Function;
|
||||||
onSubmit: Function;
|
onSubmit: Function;
|
||||||
typing: Function;
|
typing: Function;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
replyCancel(): void;
|
replyCancel(): void;
|
||||||
showSend: boolean;
|
showSend: boolean;
|
||||||
navigation: any;
|
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
showActionSheet: Function;
|
showActionSheet: Function;
|
||||||
|
@ -118,7 +119,7 @@ interface IMessageBoxState {
|
||||||
commandPreview: IPreviewItem[];
|
commandPreview: IPreviewItem[];
|
||||||
showCommandPreview: boolean;
|
showCommandPreview: boolean;
|
||||||
command: {
|
command: {
|
||||||
appId?: any;
|
appId?: string;
|
||||||
};
|
};
|
||||||
tshow: boolean;
|
tshow: boolean;
|
||||||
mentionLoading: boolean;
|
mentionLoading: boolean;
|
||||||
|
@ -132,17 +133,15 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
private focused: boolean;
|
private focused: boolean;
|
||||||
|
|
||||||
private options: any;
|
private imagePickerConfig: Options;
|
||||||
|
|
||||||
private imagePickerConfig: any;
|
private libraryPickerConfig: Options;
|
||||||
|
|
||||||
private libraryPickerConfig: any;
|
private videoPickerConfig: Options;
|
||||||
|
|
||||||
private videoPickerConfig: any;
|
private room!: TSubscriptionModel;
|
||||||
|
|
||||||
private room: any;
|
private thread!: TThreadModel;
|
||||||
|
|
||||||
private thread: any;
|
|
||||||
|
|
||||||
private unsubscribeFocus: any;
|
private unsubscribeFocus: any;
|
||||||
|
|
||||||
|
@ -681,7 +680,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Alert.alert(I18n.t('Error_uploading'), I18n.t(result.error));
|
Alert.alert(I18n.t('Error_uploading'), result.error && I18n.isTranslated(result.error) ? I18n.t(result.error) : result.error);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -713,7 +712,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
chooseFromLibrary = async () => {
|
chooseFromLibrary = async () => {
|
||||||
logEvent(events.ROOM_BOX_ACTION_LIBRARY);
|
logEvent(events.ROOM_BOX_ACTION_LIBRARY);
|
||||||
try {
|
try {
|
||||||
let attachments = (await ImagePicker.openPicker(this.libraryPickerConfig)) as ImageOrVideo[];
|
// The type can be video or photo, however the lib understands that it is just one of them.
|
||||||
|
let attachments = (await ImagePicker.openPicker(this.libraryPickerConfig)) as unknown as ImageOrVideo[];
|
||||||
attachments = attachments.map(att => forceJpgExtension(att));
|
attachments = attachments.map(att => forceJpgExtension(att));
|
||||||
this.openShareView(attachments);
|
this.openShareView(attachments);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -757,12 +757,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
openShareView = (attachments: any) => {
|
openShareView = (attachments: any) => {
|
||||||
const { message, replyCancel, replyWithMention } = this.props;
|
const { message, replyCancel, replyWithMention } = this.props;
|
||||||
// Start a thread with an attachment
|
// Start a thread with an attachment
|
||||||
let { thread } = this;
|
let value: TThreadModel | IMessage = this.thread;
|
||||||
if (replyWithMention) {
|
if (replyWithMention) {
|
||||||
thread = message;
|
value = message;
|
||||||
replyCancel();
|
replyCancel();
|
||||||
}
|
}
|
||||||
Navigation.navigate('ShareView', { room: this.room, thread, attachments });
|
Navigation.navigate('ShareView', { room: this.room, thread: value, attachments });
|
||||||
};
|
};
|
||||||
|
|
||||||
createDiscussion = () => {
|
createDiscussion = () => {
|
||||||
|
@ -1060,7 +1060,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
const commandsPreviewAndMentions = !recording ? (
|
const commandsPreviewAndMentions = !recording ? (
|
||||||
<>
|
<>
|
||||||
<CommandsPreview commandPreview={commandPreview} showCommandPreview={showCommandPreview} />
|
<CommandsPreview commandPreview={commandPreview} showCommandPreview={showCommandPreview} />
|
||||||
<Mentions mentions={mentions} trackingType={trackingType} theme={theme} loading={mentionLoading} />
|
<Mentions mentions={mentions} trackingType={trackingType} loading={mentionLoading} />
|
||||||
</>
|
</>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
@ -1071,7 +1071,6 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
username={user.username}
|
username={user.username}
|
||||||
replying={replying}
|
replying={replying}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||||
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
|
import { TSupportedThemes } from '../theme';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -24,7 +25,7 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IOrSeparator {
|
interface IOrSeparator {
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrSeparator = React.memo(({ theme }: IOrSeparator) => {
|
const OrSeparator = React.memo(({ theme }: IOrSeparator) => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { Text } from 'react-native';
|
import { Text } from 'react-native';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../lib/constants';
|
||||||
import Touch from '../../../utils/touch';
|
import Touch from '../../../utils/touch';
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { View } from 'react-native';
|
||||||
import range from 'lodash/range';
|
import range from 'lodash/range';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../lib/constants';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
const SIZE_EMPTY = 12;
|
const SIZE_EMPTY = 12;
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { View } from 'react-native';
|
||||||
import { Row } from 'react-native-easy-grid';
|
import { Row } from 'react-native-easy-grid';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../lib/constants';
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Grid } from 'react-native-easy-grid';
|
import { Grid } from 'react-native-easy-grid';
|
||||||
|
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../lib/constants';
|
||||||
import { resetAttempts } from '../../../utils/localAuthentication';
|
import { resetAttempts } from '../../../utils/localAuthentication';
|
||||||
import { TYPE } from '../constants';
|
import { TYPE } from '../constants';
|
||||||
import { getDiff, getLockedUntil } from '../utils';
|
import { getDiff, getLockedUntil } from '../utils';
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Text, View } from 'react-native';
|
||||||
import { Row } from 'react-native-easy-grid';
|
import { Row } from 'react-native-easy-grid';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../lib/constants';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
interface IPasscodeSubtitle {
|
interface IPasscodeSubtitle {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Text, View } from 'react-native';
|
||||||
import { Row } from 'react-native-easy-grid';
|
import { Row } from 'react-native-easy-grid';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../lib/constants';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
interface IPasscodeTitle {
|
interface IPasscodeTitle {
|
||||||
|
|
|
@ -9,8 +9,7 @@ import styles from './styles';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import Dots from './Dots';
|
import Dots from './Dots';
|
||||||
import { TYPE } from '../constants';
|
import { TYPE } from '../constants';
|
||||||
import { themes } from '../../../constants/colors';
|
import { PASSCODE_LENGTH, themes } from '../../../lib/constants';
|
||||||
import { PASSCODE_LENGTH } from '../../../constants/localAuthentication';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import LockIcon from './LockIcon';
|
import LockIcon from './LockIcon';
|
||||||
import Title from './Title';
|
import Title from './Title';
|
||||||
|
@ -20,7 +19,7 @@ interface IPasscodeBase {
|
||||||
type: string;
|
type: string;
|
||||||
previousPasscode?: string;
|
previousPasscode?: string;
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string | null;
|
||||||
showBiometry?: boolean;
|
showBiometry?: boolean;
|
||||||
onEndProcess: Function;
|
onEndProcess: Function;
|
||||||
onError?: Function;
|
onError?: Function;
|
||||||
|
|
|
@ -14,7 +14,7 @@ interface IPasscodeChoose {
|
||||||
const PasscodeChoose = ({ finishProcess, force = false }: IPasscodeChoose) => {
|
const PasscodeChoose = ({ finishProcess, force = false }: IPasscodeChoose) => {
|
||||||
const chooseRef = useRef<IBase>(null);
|
const chooseRef = useRef<IBase>(null);
|
||||||
const confirmRef = useRef<IBase>(null);
|
const confirmRef = useRef<IBase>(null);
|
||||||
const [subtitle, setSubtitle] = useState(null);
|
const [subtitle, setSubtitle] = useState<string | null>(null);
|
||||||
const [status, setStatus] = useState(TYPE.CHOOSE);
|
const [status, setStatus] = useState(TYPE.CHOOSE);
|
||||||
const [previousPasscode, setPreviouPasscode] = useState('');
|
const [previousPasscode, setPreviouPasscode] = useState('');
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ import { sha256 } from 'js-sha256';
|
||||||
import Base, { IBase } from './Base';
|
import Base, { IBase } from './Base';
|
||||||
import Locked from './Base/Locked';
|
import Locked from './Base/Locked';
|
||||||
import { TYPE } from './constants';
|
import { TYPE } from './constants';
|
||||||
import { ATTEMPTS_KEY, LOCKED_OUT_TIMER_KEY, MAX_ATTEMPTS, PASSCODE_KEY } from '../../constants/localAuthentication';
|
import { ATTEMPTS_KEY, LOCKED_OUT_TIMER_KEY, MAX_ATTEMPTS, PASSCODE_KEY } from '../../lib/constants';
|
||||||
import { biometryAuth, resetAttempts } from '../../utils/localAuthentication';
|
import { biometryAuth, resetAttempts } from '../../utils/localAuthentication';
|
||||||
import { getDiff, getLockedUntil } from './utils';
|
import { getDiff, getLockedUntil } from './utils';
|
||||||
import { useUserPreferences } from '../../lib/userPreferences';
|
import { useUserPreferences } from '../../lib/methods/userPreferences';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
|
||||||
interface IPasscodePasscodeEnter {
|
interface IPasscodePasscodeEnter {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import { LOCKED_OUT_TIMER_KEY, TIME_TO_LOCK } from '../../constants/localAuthentication';
|
import { LOCKED_OUT_TIMER_KEY, TIME_TO_LOCK } from '../../lib/constants';
|
||||||
|
|
||||||
export const getLockedUntil = async () => {
|
export const getLockedUntil = async () => {
|
||||||
const t = await AsyncStorage.getItem(LOCKED_OUT_TIMER_KEY);
|
const t = await AsyncStorage.getItem(LOCKED_OUT_TIMER_KEY);
|
||||||
|
|
|
@ -7,8 +7,8 @@ import Emoji from './message/Emoji';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { CustomIcon } from '../lib/Icons';
|
import { CustomIcon } from '../lib/Icons';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
import { withTheme } from '../theme';
|
import { TSupportedThemes, useTheme, withTheme } from '../theme';
|
||||||
import { TGetCustomEmoji } from '../definitions/IEmoji';
|
import { TGetCustomEmoji } from '../definitions/IEmoji';
|
||||||
import { TMessageModel, ILoggedUser } from '../definitions';
|
import { TMessageModel, ILoggedUser } from '../definitions';
|
||||||
import SafeAreaView from './SafeAreaView';
|
import SafeAreaView from './SafeAreaView';
|
||||||
|
@ -61,38 +61,37 @@ const styles = StyleSheet.create({
|
||||||
const standardEmojiStyle = { fontSize: 20 };
|
const standardEmojiStyle = { fontSize: 20 };
|
||||||
const customEmojiStyle = { width: 20, height: 20 };
|
const customEmojiStyle = { width: 20, height: 20 };
|
||||||
|
|
||||||
interface IItem {
|
interface ISharedFields {
|
||||||
|
user?: Pick<ILoggedUser, 'username'>;
|
||||||
|
baseUrl: string;
|
||||||
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IItem extends ISharedFields {
|
||||||
item: {
|
item: {
|
||||||
usernames: any;
|
usernames: string[];
|
||||||
emoji: string;
|
emoji: string;
|
||||||
};
|
};
|
||||||
user?: Pick<ILoggedUser, 'username'>;
|
|
||||||
baseUrl?: string;
|
|
||||||
getCustomEmoji?: TGetCustomEmoji;
|
|
||||||
theme?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IModalContent {
|
interface IModalContent extends ISharedFields {
|
||||||
message?: TMessageModel;
|
message?: TMessageModel;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
theme: string;
|
theme: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IReactionsModal {
|
interface IReactionsModal extends ISharedFields {
|
||||||
message?: any;
|
message?: TMessageModel;
|
||||||
user?: Pick<ILoggedUser, 'username'>;
|
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
onClose(): void;
|
onClose(): void;
|
||||||
baseUrl: string;
|
|
||||||
getCustomEmoji?: TGetCustomEmoji;
|
|
||||||
theme: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Item = React.memo(({ item, user, baseUrl, getCustomEmoji, theme }: IItem) => {
|
const Item = React.memo(({ item, user, baseUrl, getCustomEmoji }: IItem) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
const count = item.usernames.length;
|
const count = item.usernames.length;
|
||||||
let usernames = item.usernames
|
let usernames = item.usernames
|
||||||
.slice(0, 3)
|
.slice(0, 3)
|
||||||
.map((username: any) => (username === user?.username ? I18n.t('you') : username))
|
.map((username: string) => (username === user?.username ? I18n.t('you') : username))
|
||||||
.join(', ');
|
.join(', ');
|
||||||
if (count > 3) {
|
if (count > 3) {
|
||||||
usernames = `${usernames} ${I18n.t('and_more')} ${count - 3}`;
|
usernames = `${usernames} ${I18n.t('and_more')} ${count - 3}`;
|
||||||
|
@ -106,15 +105,15 @@ const Item = React.memo(({ item, user, baseUrl, getCustomEmoji, theme }: IItem)
|
||||||
content={item.emoji}
|
content={item.emoji}
|
||||||
standardEmojiStyle={standardEmojiStyle}
|
standardEmojiStyle={standardEmojiStyle}
|
||||||
customEmojiStyle={customEmojiStyle}
|
customEmojiStyle={customEmojiStyle}
|
||||||
baseUrl={baseUrl!}
|
baseUrl={baseUrl}
|
||||||
getCustomEmoji={getCustomEmoji!}
|
getCustomEmoji={getCustomEmoji}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.peopleItemContainer}>
|
<View style={styles.peopleItemContainer}>
|
||||||
<Text style={[styles.reactCount, { color: themes[theme!].buttonText }]}>
|
<Text style={[styles.reactCount, { color: themes[theme].buttonText }]}>
|
||||||
{count === 1 ? I18n.t('1_person_reacted') : I18n.t('N_people_reacted', { n: count })}
|
{count === 1 ? I18n.t('1_person_reacted') : I18n.t('N_people_reacted', { n: count })}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.peopleReacted, { color: themes[theme!].buttonText }]}>{usernames}</Text>
|
<Text style={[styles.peopleReacted, { color: themes[theme].buttonText }]}>{usernames}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -143,7 +142,9 @@ const ModalContent = React.memo(({ message, onClose, ...props }: IModalContent)
|
||||||
});
|
});
|
||||||
|
|
||||||
const ReactionsModal = React.memo(
|
const ReactionsModal = React.memo(
|
||||||
({ isVisible, onClose, theme, ...props }: IReactionsModal) => (
|
({ isVisible, onClose, ...props }: IReactionsModal) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isVisible={isVisible}
|
isVisible={isVisible}
|
||||||
onBackdropPress={onClose}
|
onBackdropPress={onClose}
|
||||||
|
@ -153,8 +154,9 @@ const ReactionsModal = React.memo(
|
||||||
swipeDirection={['up', 'left', 'right', 'down']}>
|
swipeDirection={['up', 'left', 'right', 'down']}>
|
||||||
<ModalContent onClose={onClose} theme={theme} {...props} />
|
<ModalContent onClose={onClose} theme={theme} {...props} />
|
||||||
</Modal>
|
</Modal>
|
||||||
),
|
);
|
||||||
(prevProps, nextProps) => prevProps.isVisible === nextProps.isVisible && prevProps.theme === nextProps.theme
|
},
|
||||||
|
(prevProps, nextProps) => prevProps.isVisible === nextProps.isVisible
|
||||||
);
|
);
|
||||||
|
|
||||||
ReactionsModal.displayName = 'ReactionsModal';
|
ReactionsModal.displayName = 'ReactionsModal';
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Dimensions, View } from 'react-native';
|
import { Dimensions, View } from 'react-native';
|
||||||
import { storiesOf } from '@storybook/react-native';
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
import Header from '../Header';
|
import Header from '../Header';
|
||||||
import { longText } from '../../../storybook/utils';
|
import { longText } from '../../../storybook/utils';
|
||||||
import { ThemeContext } from '../../theme';
|
import { ThemeContext } from '../../theme';
|
||||||
|
import { store } from '../../../storybook/stories';
|
||||||
import RoomHeaderComponent from './RoomHeader';
|
import RoomHeaderComponent from './RoomHeader';
|
||||||
|
|
||||||
const stories = storiesOf('RoomHeader', module);
|
const stories = storiesOf('RoomHeader', module).addDecorator(story => <Provider store={store}>{story()}</Provider>);
|
||||||
|
|
||||||
// TODO: refactor after react-navigation v6
|
// TODO: refactor after react-navigation v6
|
||||||
const HeaderExample = ({ title }) => (
|
const HeaderExample = ({ title }) => (
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { MarkdownPreview } from '../markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import RoomTypeIcon from '../RoomTypeIcon';
|
import RoomTypeIcon from '../RoomTypeIcon';
|
||||||
import { withTheme } from '../../theme';
|
import { TUserStatus, IOmnichannelSource } from '../../definitions';
|
||||||
import { TUserStatus } from '../../definitions';
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
const HIT_SLOP = {
|
const HIT_SLOP = {
|
||||||
top: 5,
|
top: 5,
|
||||||
|
@ -44,9 +44,8 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
type TRoomHeaderSubTitle = {
|
type TRoomHeaderSubTitle = {
|
||||||
usersTyping: [];
|
usersTyping: [];
|
||||||
theme: string;
|
|
||||||
subtitle: string;
|
subtitle: string;
|
||||||
renderFunc: any;
|
renderFunc?: () => React.ReactElement;
|
||||||
scale: number;
|
scale: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,7 +54,6 @@ type TRoomHeaderHeaderTitle = {
|
||||||
tmid: string;
|
tmid: string;
|
||||||
prid: string;
|
prid: string;
|
||||||
scale: number;
|
scale: number;
|
||||||
theme: string;
|
|
||||||
testID: string;
|
testID: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,15 +67,16 @@ interface IRoomHeader {
|
||||||
tmid: string;
|
tmid: string;
|
||||||
teamMain: boolean;
|
teamMain: boolean;
|
||||||
status: TUserStatus;
|
status: TUserStatus;
|
||||||
theme?: string;
|
|
||||||
usersTyping: [];
|
usersTyping: [];
|
||||||
isGroupChat: boolean;
|
isGroupChat: boolean;
|
||||||
parentTitle: string;
|
parentTitle: string;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
testID: string;
|
testID: string;
|
||||||
|
sourceType?: IOmnichannelSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, theme, scale }: TRoomHeaderSubTitle) => {
|
const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, scale }: TRoomHeaderSubTitle) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
const fontSize = getSubTitleSize(scale);
|
const fontSize = getSubTitleSize(scale);
|
||||||
// typing
|
// typing
|
||||||
if (usersTyping.length) {
|
if (usersTyping.length) {
|
||||||
|
@ -108,7 +107,8 @@ const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, theme, scale }
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const HeaderTitle = React.memo(({ title, tmid, prid, scale, theme, testID }: TRoomHeaderHeaderTitle) => {
|
const HeaderTitle = React.memo(({ title, tmid, prid, scale, testID }: TRoomHeaderHeaderTitle) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
const titleStyle = { fontSize: TITLE_SIZE * scale, color: themes[theme].headerTitleColor };
|
const titleStyle = { fontSize: TITLE_SIZE * scale, color: themes[theme].headerTitleColor };
|
||||||
if (!tmid && !prid) {
|
if (!tmid && !prid) {
|
||||||
return (
|
return (
|
||||||
|
@ -133,12 +133,13 @@ const Header = React.memo(
|
||||||
prid,
|
prid,
|
||||||
tmid,
|
tmid,
|
||||||
onPress,
|
onPress,
|
||||||
theme,
|
|
||||||
isGroupChat,
|
isGroupChat,
|
||||||
teamMain,
|
teamMain,
|
||||||
testID,
|
testID,
|
||||||
usersTyping = []
|
usersTyping = [],
|
||||||
|
sourceType
|
||||||
}: IRoomHeader) => {
|
}: IRoomHeader) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
const portrait = height > width;
|
const portrait = height > width;
|
||||||
let scale = 1;
|
let scale = 1;
|
||||||
|
|
||||||
|
@ -153,7 +154,7 @@ const Header = React.memo(
|
||||||
renderFunc = () => (
|
renderFunc = () => (
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
<RoomTypeIcon type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} teamMain={teamMain} />
|
<RoomTypeIcon type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} teamMain={teamMain} />
|
||||||
<Text style={[styles.subtitle, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}>
|
<Text style={[styles.subtitle, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
|
||||||
{parentTitle}
|
{parentTitle}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -168,25 +169,24 @@ const Header = React.memo(
|
||||||
accessibilityLabel={title}
|
accessibilityLabel={title}
|
||||||
onPress={handleOnPress}
|
onPress={handleOnPress}
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
// @ts-ignore
|
disabled={!!tmid}
|
||||||
disabled={tmid}
|
|
||||||
hitSlop={HIT_SLOP}>
|
hitSlop={HIT_SLOP}>
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
{tmid ? null : (
|
{tmid ? null : (
|
||||||
<RoomTypeIcon type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} teamMain={teamMain} />
|
<RoomTypeIcon
|
||||||
)}
|
type={prid ? 'discussion' : type}
|
||||||
<HeaderTitle title={title} tmid={tmid} prid={prid} scale={scale} theme={theme!} testID={testID} />
|
isGroupChat={isGroupChat}
|
||||||
</View>
|
status={status}
|
||||||
<SubTitle
|
teamMain={teamMain}
|
||||||
usersTyping={tmid ? [] : usersTyping}
|
sourceType={sourceType}
|
||||||
subtitle={subtitle}
|
|
||||||
theme={theme!}
|
|
||||||
renderFunc={renderFunc}
|
|
||||||
scale={scale}
|
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
<HeaderTitle title={title} tmid={tmid} prid={prid} scale={scale} testID={testID} />
|
||||||
|
</View>
|
||||||
|
<SubTitle usersTyping={tmid ? [] : usersTyping} subtitle={subtitle} renderFunc={renderFunc} scale={scale} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withTheme(Header);
|
export default Header;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,7 +2,7 @@ import { dequal } from 'dequal';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { IApplicationState, TUserStatus } from '../../definitions';
|
import { IApplicationState, TUserStatus, IOmnichannelSource } from '../../definitions';
|
||||||
import { withDimensions } from '../../dimensions';
|
import { withDimensions } from '../../dimensions';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import RoomHeader from './RoomHeader';
|
import RoomHeader from './RoomHeader';
|
||||||
|
@ -27,12 +27,26 @@ interface IRoomHeaderContainerProps {
|
||||||
parentTitle: string;
|
parentTitle: string;
|
||||||
isGroupChat: boolean;
|
isGroupChat: boolean;
|
||||||
testID: string;
|
testID: string;
|
||||||
|
sourceType?: IOmnichannelSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RoomHeaderContainer extends Component<IRoomHeaderContainerProps, any> {
|
class RoomHeaderContainer extends Component<IRoomHeaderContainerProps, any> {
|
||||||
shouldComponentUpdate(nextProps: IRoomHeaderContainerProps) {
|
shouldComponentUpdate(nextProps: IRoomHeaderContainerProps) {
|
||||||
const { type, title, subtitle, status, statusText, connecting, connected, onPress, usersTyping, width, height, teamMain } =
|
const {
|
||||||
this.props;
|
type,
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
status,
|
||||||
|
statusText,
|
||||||
|
connecting,
|
||||||
|
connected,
|
||||||
|
onPress,
|
||||||
|
usersTyping,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
teamMain,
|
||||||
|
sourceType
|
||||||
|
} = this.props;
|
||||||
if (nextProps.type !== type) {
|
if (nextProps.type !== type) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +77,9 @@ class RoomHeaderContainer extends Component<IRoomHeaderContainerProps, any> {
|
||||||
if (!dequal(nextProps.usersTyping, usersTyping)) {
|
if (!dequal(nextProps.usersTyping, usersTyping)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!dequal(nextProps.sourceType, sourceType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (nextProps.onPress !== onPress) {
|
if (nextProps.onPress !== onPress) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -90,7 +107,8 @@ class RoomHeaderContainer extends Component<IRoomHeaderContainerProps, any> {
|
||||||
height,
|
height,
|
||||||
parentTitle,
|
parentTitle,
|
||||||
isGroupChat,
|
isGroupChat,
|
||||||
testID
|
testID,
|
||||||
|
sourceType
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let subtitle;
|
let subtitle;
|
||||||
|
@ -118,6 +136,7 @@ class RoomHeaderContainer extends Component<IRoomHeaderContainerProps, any> {
|
||||||
isGroupChat={isGroupChat}
|
isGroupChat={isGroupChat}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
|
sourceType={sourceType}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,34 +4,15 @@ 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 { DisplayMode, themes } from '../../lib/constants';
|
||||||
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';
|
||||||
|
import { ILeftActionsProps, IRightActionsProps } from './interfaces';
|
||||||
interface ILeftActions {
|
|
||||||
theme: string;
|
|
||||||
transX: any;
|
|
||||||
isRead: boolean;
|
|
||||||
width: number;
|
|
||||||
onToggleReadPress(): void;
|
|
||||||
displayMode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRightActions {
|
|
||||||
theme: string;
|
|
||||||
transX: any;
|
|
||||||
favorite: boolean;
|
|
||||||
width: number;
|
|
||||||
toggleFav(): void;
|
|
||||||
onHidePress(): void;
|
|
||||||
displayMode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reverse = new Animated.Value(isRTL() ? -1 : 1);
|
const reverse = new Animated.Value(isRTL() ? -1 : 1);
|
||||||
const CONDENSED_ICON_SIZE = 24;
|
const CONDENSED_ICON_SIZE = 24;
|
||||||
const EXPANDED_ICON_SIZE = 28;
|
const EXPANDED_ICON_SIZE = 28;
|
||||||
|
|
||||||
export const LeftActions = React.memo(({ theme, transX, isRead, width, onToggleReadPress, displayMode }: ILeftActions) => {
|
export const LeftActions = React.memo(({ theme, transX, isRead, width, onToggleReadPress, displayMode }: ILeftActionsProps) => {
|
||||||
const translateX = Animated.multiply(
|
const translateX = Animated.multiply(
|
||||||
transX.interpolate({
|
transX.interpolate({
|
||||||
inputRange: [0, ACTION_WIDTH],
|
inputRange: [0, ACTION_WIDTH],
|
||||||
|
@ -44,7 +25,7 @@ export const LeftActions = React.memo(({ theme, transX, isRead, width, onToggleR
|
||||||
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
const viewHeight = isCondensed ? { height: ROW_HEIGHT_CONDENSED } : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.actionsContainer, styles.actionLeftContainer]} pointerEvents='box-none'>
|
<View style={[styles.actionsContainer, styles.actionsLeftContainer]} pointerEvents='box-none'>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={[
|
style={[
|
||||||
styles.actionLeftButtonContainer,
|
styles.actionLeftButtonContainer,
|
||||||
|
@ -71,7 +52,7 @@ export const LeftActions = React.memo(({ theme, transX, isRead, width, onToggleR
|
||||||
});
|
});
|
||||||
|
|
||||||
export const RightActions = React.memo(
|
export const RightActions = React.memo(
|
||||||
({ transX, favorite, width, toggleFav, onHidePress, theme, displayMode }: IRightActions) => {
|
({ transX, favorite, width, toggleFav, onHidePress, theme, displayMode }: IRightActionsProps) => {
|
||||||
const translateXFav = Animated.multiply(
|
const translateXFav = Animated.multiply(
|
||||||
transX.interpolate({
|
transX.interpolate({
|
||||||
inputRange: [-width / 2, -ACTION_WIDTH * 2, 0],
|
inputRange: [-width / 2, -ACTION_WIDTH * 2, 0],
|
|
@ -2,8 +2,8 @@ import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../Avatar';
|
||||||
import { DisplayMode } from '../../constants/constantDisplayMode';
|
import { DisplayMode } from '../../lib/constants';
|
||||||
import TypeIcon from './TypeIcon';
|
import TypeIcon from './TypeIcon';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ const IconOrAvatar = ({
|
||||||
teamMain,
|
teamMain,
|
||||||
showLastMessage,
|
showLastMessage,
|
||||||
theme,
|
theme,
|
||||||
displayMode
|
displayMode,
|
||||||
|
sourceType
|
||||||
}) => {
|
}) => {
|
||||||
if (showAvatar) {
|
if (showAvatar) {
|
||||||
return (
|
return (
|
||||||
|
@ -38,6 +39,7 @@ const IconOrAvatar = ({
|
||||||
teamMain={teamMain}
|
teamMain={teamMain}
|
||||||
size={24}
|
size={24}
|
||||||
style={{ marginRight: 12 }}
|
style={{ marginRight: 12 }}
|
||||||
|
sourceType={sourceType}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
|
@ -3,28 +3,11 @@ import { dequal } from 'dequal';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { MarkdownPreview } from '../../containers/markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import { themes } from '../../constants/colors';
|
import { E2E_MESSAGE_TYPE, E2E_STATUS, themes } from '../../lib/constants';
|
||||||
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
|
import { ILastMessageProps } from './interfaces';
|
||||||
|
|
||||||
interface ILastMessage {
|
const formatMsg = ({ lastMessage, type, showLastMessage, username, useRealName }: Partial<ILastMessageProps>) => {
|
||||||
theme: string;
|
|
||||||
lastMessage: {
|
|
||||||
u: any;
|
|
||||||
pinned: boolean;
|
|
||||||
t: string;
|
|
||||||
attachments: any;
|
|
||||||
msg: string;
|
|
||||||
e2e: string;
|
|
||||||
};
|
|
||||||
type: string;
|
|
||||||
showLastMessage: boolean;
|
|
||||||
username: string;
|
|
||||||
useRealName: boolean;
|
|
||||||
alert: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatMsg = ({ lastMessage, type, showLastMessage, username, useRealName }: Partial<ILastMessage>) => {
|
|
||||||
if (!showLastMessage) {
|
if (!showLastMessage) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -64,7 +47,7 @@ const formatMsg = ({ lastMessage, type, showLastMessage, username, useRealName }
|
||||||
const arePropsEqual = (oldProps: any, newProps: any) => dequal(oldProps, newProps);
|
const arePropsEqual = (oldProps: any, newProps: any) => dequal(oldProps, newProps);
|
||||||
|
|
||||||
const LastMessage = React.memo(
|
const LastMessage = React.memo(
|
||||||
({ lastMessage, type, showLastMessage, username, alert, useRealName, theme }: ILastMessage) => (
|
({ lastMessage, type, showLastMessage, username, alert, useRealName, theme }: ILastMessageProps) => (
|
||||||
<MarkdownPreview
|
<MarkdownPreview
|
||||||
msg={formatMsg({
|
msg={formatMsg({
|
||||||
lastMessage,
|
lastMessage,
|
|
@ -11,57 +11,8 @@ 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 { DisplayMode } from '../../constants/constantDisplayMode';
|
import { DisplayMode } from '../../lib/constants';
|
||||||
import { TUserStatus } from '../../definitions';
|
import { IRoomItemProps } from './interfaces';
|
||||||
|
|
||||||
interface IRoomItem {
|
|
||||||
rid: string;
|
|
||||||
type: string;
|
|
||||||
prid: string;
|
|
||||||
name: string;
|
|
||||||
avatar: string;
|
|
||||||
showLastMessage: boolean;
|
|
||||||
username: string;
|
|
||||||
avatarSize: number;
|
|
||||||
testID: string;
|
|
||||||
width: number;
|
|
||||||
status: TUserStatus;
|
|
||||||
useRealName: boolean;
|
|
||||||
theme: string;
|
|
||||||
isFocused: boolean;
|
|
||||||
isGroupChat: boolean;
|
|
||||||
isRead: boolean;
|
|
||||||
teamMain: boolean;
|
|
||||||
date: string;
|
|
||||||
accessibilityLabel: string;
|
|
||||||
lastMessage: {
|
|
||||||
u: any;
|
|
||||||
pinned: boolean;
|
|
||||||
t: string;
|
|
||||||
attachments: any;
|
|
||||||
msg: string;
|
|
||||||
e2e: string;
|
|
||||||
};
|
|
||||||
favorite: boolean;
|
|
||||||
alert: boolean;
|
|
||||||
hideUnreadStatus: boolean;
|
|
||||||
unread: number;
|
|
||||||
userMentions: number;
|
|
||||||
groupMentions: number;
|
|
||||||
tunread: [];
|
|
||||||
tunreadUser: [];
|
|
||||||
tunreadGroup: [];
|
|
||||||
swipeEnabled: boolean;
|
|
||||||
toggleFav(): void;
|
|
||||||
toggleRead(): void;
|
|
||||||
onPress(): void;
|
|
||||||
onLongPress(): void;
|
|
||||||
hideChannel(): void;
|
|
||||||
autoJoin: boolean;
|
|
||||||
size?: number;
|
|
||||||
showAvatar: boolean;
|
|
||||||
displayMode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RoomItem = ({
|
const RoomItem = ({
|
||||||
rid,
|
rid,
|
||||||
|
@ -70,7 +21,6 @@ const RoomItem = ({
|
||||||
name,
|
name,
|
||||||
avatar,
|
avatar,
|
||||||
width,
|
width,
|
||||||
avatarSize = 48,
|
|
||||||
username,
|
username,
|
||||||
showLastMessage,
|
showLastMessage,
|
||||||
status = 'offline',
|
status = 'offline',
|
||||||
|
@ -101,8 +51,9 @@ const RoomItem = ({
|
||||||
teamMain,
|
teamMain,
|
||||||
autoJoin,
|
autoJoin,
|
||||||
showAvatar,
|
showAvatar,
|
||||||
displayMode
|
displayMode,
|
||||||
}: IRoomItem) => (
|
sourceType
|
||||||
|
}: IRoomItemProps) => (
|
||||||
<Touchable
|
<Touchable
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
|
@ -122,7 +73,6 @@ const RoomItem = ({
|
||||||
<Wrapper
|
<Wrapper
|
||||||
accessibilityLabel={accessibilityLabel}
|
accessibilityLabel={accessibilityLabel}
|
||||||
avatar={avatar}
|
avatar={avatar}
|
||||||
avatarSize={avatarSize}
|
|
||||||
type={type}
|
type={type}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
rid={rid}
|
rid={rid}
|
||||||
|
@ -132,12 +82,20 @@ const RoomItem = ({
|
||||||
teamMain={teamMain}
|
teamMain={teamMain}
|
||||||
displayMode={displayMode}
|
displayMode={displayMode}
|
||||||
showAvatar={showAvatar}
|
showAvatar={showAvatar}
|
||||||
showLastMessage={showLastMessage}>
|
showLastMessage={showLastMessage}
|
||||||
|
sourceType={sourceType}>
|
||||||
{showLastMessage && displayMode === DisplayMode.Expanded ? (
|
{showLastMessage && displayMode === DisplayMode.Expanded ? (
|
||||||
<>
|
<>
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
{showAvatar ? (
|
{showAvatar ? (
|
||||||
<TypeIcon type={type} prid={prid} status={status} isGroupChat={isGroupChat} theme={theme} teamMain={teamMain} />
|
<TypeIcon
|
||||||
|
type={type}
|
||||||
|
prid={prid}
|
||||||
|
status={status}
|
||||||
|
isGroupChat={isGroupChat}
|
||||||
|
teamMain={teamMain}
|
||||||
|
sourceType={sourceType}
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<Title name={name} theme={theme} hideUnreadStatus={hideUnreadStatus} alert={alert} />
|
<Title name={name} theme={theme} hideUnreadStatus={hideUnreadStatus} alert={alert} />
|
||||||
{autoJoin ? <Tag testID='auto-join-tag' name={I18n.t('Auto-join')} /> : null}
|
{autoJoin ? <Tag testID='auto-join-tag' name={I18n.t('Auto-join')} /> : null}
|
||||||
|
@ -170,10 +128,10 @@ const RoomItem = ({
|
||||||
prid={prid}
|
prid={prid}
|
||||||
status={status}
|
status={status}
|
||||||
isGroupChat={isGroupChat}
|
isGroupChat={isGroupChat}
|
||||||
theme={theme}
|
|
||||||
teamMain={teamMain}
|
teamMain={teamMain}
|
||||||
size={22}
|
size={22}
|
||||||
style={{ marginRight: 8 }}
|
style={{ marginRight: 8 }}
|
||||||
|
sourceType={sourceType}
|
||||||
/>
|
/>
|
||||||
<Title name={name} theme={theme} hideUnreadStatus={hideUnreadStatus} alert={alert} />
|
<Title name={name} theme={theme} hideUnreadStatus={hideUnreadStatus} alert={alert} />
|
||||||
{autoJoin ? <Tag name={I18n.t('Auto-join')} /> : null}
|
{autoJoin ? <Tag name={I18n.t('Auto-join')} /> : null}
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ interface ITag {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Tag = React.memo(({ name, testID }: ITag) => {
|
const Tag = React.memo(({ name, testID }: ITag) => {
|
||||||
const { theme }: any = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.tagContainer, { backgroundColor: themes[theme].borderColor }]}>
|
<View style={[styles.tagContainer, { backgroundColor: themes[theme].borderColor }]}>
|
|
@ -2,16 +2,10 @@ import React from 'react';
|
||||||
import { Text } from 'react-native';
|
import { Text } from 'react-native';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
|
import { ITitleProps } from './interfaces';
|
||||||
|
|
||||||
interface ITitle {
|
const Title = React.memo(({ name, theme, hideUnreadStatus, alert }: ITitleProps) => (
|
||||||
name: string;
|
|
||||||
theme: string;
|
|
||||||
hideUnreadStatus: boolean;
|
|
||||||
alert: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Title = React.memo(({ name, theme, hideUnreadStatus, alert }: ITitle) => (
|
|
||||||
<Text
|
<Text
|
||||||
style={[styles.title, alert && !hideUnreadStatus && styles.alert, { color: themes[theme].titleText }]}
|
style={[styles.title, alert && !hideUnreadStatus && styles.alert, { color: themes[theme].titleText }]}
|
||||||
ellipsizeMode='tail'
|
ellipsizeMode='tail'
|
|
@ -1,45 +1,28 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Animated } from 'react-native';
|
import { Animated } from 'react-native';
|
||||||
import { LongPressGestureHandler, PanGestureHandler, State } from 'react-native-gesture-handler';
|
import {
|
||||||
|
GestureEvent,
|
||||||
|
HandlerStateChangeEventPayload,
|
||||||
|
LongPressGestureHandler,
|
||||||
|
PanGestureHandler,
|
||||||
|
PanGestureHandlerEventPayload,
|
||||||
|
State
|
||||||
|
} from 'react-native-gesture-handler';
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
import { ACTION_WIDTH, LONG_SWIPE, SMALL_SWIPE } from './styles';
|
import { ACTION_WIDTH, LONG_SWIPE, SMALL_SWIPE } from './styles';
|
||||||
import { isRTL } from '../../i18n';
|
import { isRTL } from '../../i18n';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { LeftActions, RightActions } from './Actions';
|
import { LeftActions, RightActions } from './Actions';
|
||||||
|
import { ITouchableProps } from './interfaces';
|
||||||
interface ITouchableProps {
|
|
||||||
children: JSX.Element;
|
|
||||||
type: string;
|
|
||||||
onPress(): void;
|
|
||||||
onLongPress(): void;
|
|
||||||
testID: string;
|
|
||||||
width: number;
|
|
||||||
favorite: boolean;
|
|
||||||
isRead: boolean;
|
|
||||||
rid: string;
|
|
||||||
toggleFav: Function;
|
|
||||||
toggleRead: Function;
|
|
||||||
hideChannel: Function;
|
|
||||||
theme: string;
|
|
||||||
isFocused: boolean;
|
|
||||||
swipeEnabled: boolean;
|
|
||||||
displayMode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Touchable extends React.Component<ITouchableProps, any> {
|
class Touchable extends React.Component<ITouchableProps, any> {
|
||||||
private dragX: Animated.Value;
|
private dragX: Animated.Value;
|
||||||
|
|
||||||
private rowOffSet: Animated.Value;
|
private rowOffSet: Animated.Value;
|
||||||
|
|
||||||
private reverse: Animated.Value;
|
private reverse: Animated.Value;
|
||||||
|
|
||||||
private transX: Animated.AnimatedAddition;
|
private transX: Animated.AnimatedAddition;
|
||||||
|
|
||||||
private transXReverse: Animated.AnimatedMultiplication;
|
private transXReverse: Animated.AnimatedMultiplication;
|
||||||
|
private _onGestureEvent: (event: GestureEvent<PanGestureHandlerEventPayload>) => void;
|
||||||
private _onGestureEvent: (...args: any[]) => void;
|
|
||||||
|
|
||||||
private _value: number;
|
private _value: number;
|
||||||
|
|
||||||
constructor(props: ITouchableProps) {
|
constructor(props: ITouchableProps) {
|
||||||
|
@ -56,19 +39,19 @@ class Touchable extends React.Component<ITouchableProps, any> {
|
||||||
this._value = 0;
|
this._value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onHandlerStateChange = ({ nativeEvent }: any) => {
|
_onHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload & PanGestureHandlerEventPayload }) => {
|
||||||
if (nativeEvent.oldState === State.ACTIVE) {
|
if (nativeEvent.oldState === State.ACTIVE) {
|
||||||
this._handleRelease(nativeEvent);
|
this._handleRelease(nativeEvent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onLongPressHandlerStateChange = ({ nativeEvent }: any) => {
|
onLongPressHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
|
||||||
if (nativeEvent.state === State.ACTIVE) {
|
if (nativeEvent.state === State.ACTIVE) {
|
||||||
this.onLongPress();
|
this.onLongPress();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleRelease = (nativeEvent: any) => {
|
_handleRelease = (nativeEvent: PanGestureHandlerEventPayload) => {
|
||||||
const { translationX } = nativeEvent;
|
const { translationX } = nativeEvent;
|
||||||
const { rowState } = this.state;
|
const { rowState } = this.state;
|
||||||
this._value += translationX;
|
this._value += translationX;
|
||||||
|
@ -154,7 +137,7 @@ class Touchable extends React.Component<ITouchableProps, any> {
|
||||||
this._animateRow(toValue);
|
this._animateRow(toValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
_animateRow = (toValue: any) => {
|
_animateRow = (toValue: number) => {
|
||||||
this.rowOffSet.setValue(this._value);
|
this.rowOffSet.setValue(this._value);
|
||||||
this._value = toValue;
|
this._value = toValue;
|
||||||
this.dragX.setValue(0);
|
this.dragX.setValue(0);
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import RoomTypeIcon from '../RoomTypeIcon';
|
||||||
|
import { ITypeIconProps } from './interfaces';
|
||||||
|
|
||||||
|
const TypeIcon = React.memo(({ type, prid, status, isGroupChat, teamMain, size, style }: ITypeIconProps) => (
|
||||||
|
<RoomTypeIcon
|
||||||
|
type={prid ? 'discussion' : type}
|
||||||
|
isGroupChat={isGroupChat}
|
||||||
|
status={status}
|
||||||
|
teamMain={teamMain}
|
||||||
|
size={size}
|
||||||
|
style={style}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
export default TypeIcon;
|
|
@ -2,17 +2,11 @@ import React from 'react';
|
||||||
import { Text } from 'react-native';
|
import { Text } from 'react-native';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../lib/constants';
|
||||||
import { capitalize } from '../../utils/room';
|
import { capitalize } from '../../utils/room';
|
||||||
|
import { IUpdatedAtProps } from './interfaces';
|
||||||
|
|
||||||
interface IUpdatedAt {
|
const UpdatedAt = React.memo(({ date, theme, hideUnreadStatus, alert }: IUpdatedAtProps) => {
|
||||||
date: string;
|
|
||||||
theme: string;
|
|
||||||
hideUnreadStatus: boolean;
|
|
||||||
alert: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UpdatedAt = React.memo(({ date, theme, hideUnreadStatus, alert }: IUpdatedAt) => {
|
|
||||||
if (!date) {
|
if (!date) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
|
@ -1,29 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { DisplayMode, themes } from '../../lib/constants';
|
||||||
import { DisplayMode } from '../../constants/constantDisplayMode';
|
|
||||||
import IconOrAvatar from './IconOrAvatar';
|
import IconOrAvatar from './IconOrAvatar';
|
||||||
|
import { IWrapperProps } from './interfaces';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IWrapper {
|
const Wrapper = ({ accessibilityLabel, theme, children, displayMode, ...props }: IWrapperProps): React.ReactElement => (
|
||||||
accessibilityLabel: string;
|
|
||||||
avatar: string;
|
|
||||||
avatarSize: number;
|
|
||||||
type: string;
|
|
||||||
theme: string;
|
|
||||||
rid: string;
|
|
||||||
children: JSX.Element;
|
|
||||||
displayMode: string;
|
|
||||||
prid: string;
|
|
||||||
showLastMessage: boolean;
|
|
||||||
status: string;
|
|
||||||
isGroupChat: boolean;
|
|
||||||
teamMain: boolean;
|
|
||||||
showAvatar: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Wrapper = ({ accessibilityLabel, theme, children, displayMode, ...props }: IWrapper) => (
|
|
||||||
<View
|
<View
|
||||||
style={[styles.container, displayMode === DisplayMode.Condensed && styles.containerCondensed]}
|
style={[styles.container, displayMode === DisplayMode.Condensed && styles.containerCondensed]}
|
||||||
accessibilityLabel={accessibilityLabel}>
|
accessibilityLabel={accessibilityLabel}>
|
|
@ -5,36 +5,10 @@ import I18n from '../../i18n';
|
||||||
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
|
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
|
||||||
import { formatDate } from '../../utils/room';
|
import { formatDate } from '../../utils/room';
|
||||||
import RoomItem from './RoomItem';
|
import RoomItem from './RoomItem';
|
||||||
import { TUserStatus } from '../../definitions';
|
import { ISubscription, TUserStatus } from '../../definitions';
|
||||||
|
import { IRoomItemContainerProps } from './interfaces';
|
||||||
|
|
||||||
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
|
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
|
||||||
interface IRoomItemContainerProps {
|
|
||||||
item: any;
|
|
||||||
showLastMessage: boolean;
|
|
||||||
id: string;
|
|
||||||
onPress: Function;
|
|
||||||
onLongPress: Function;
|
|
||||||
username: string;
|
|
||||||
avatarSize: number;
|
|
||||||
width: number;
|
|
||||||
status: TUserStatus;
|
|
||||||
toggleFav(): void;
|
|
||||||
toggleRead(): void;
|
|
||||||
hideChannel(): void;
|
|
||||||
useRealName: boolean;
|
|
||||||
getUserPresence: Function;
|
|
||||||
connected: boolean;
|
|
||||||
theme: string;
|
|
||||||
isFocused: boolean;
|
|
||||||
getRoomTitle: Function;
|
|
||||||
getRoomAvatar: Function;
|
|
||||||
getIsGroupChat: Function;
|
|
||||||
getIsRead: Function;
|
|
||||||
swipeEnabled: boolean;
|
|
||||||
autoJoin: boolean;
|
|
||||||
showAvatar: boolean;
|
|
||||||
displayMode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const attrs = [
|
const attrs = [
|
||||||
'width',
|
'width',
|
||||||
|
@ -50,12 +24,9 @@ const attrs = [
|
||||||
];
|
];
|
||||||
|
|
||||||
class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
||||||
private mounted: boolean;
|
private roomSubscription: ISubscription | undefined;
|
||||||
|
|
||||||
private roomSubscription: any;
|
|
||||||
|
|
||||||
static defaultProps: Partial<IRoomItemContainerProps> = {
|
static defaultProps: Partial<IRoomItemContainerProps> = {
|
||||||
avatarSize: 48,
|
|
||||||
status: 'offline',
|
status: 'offline',
|
||||||
getUserPresence: () => {},
|
getUserPresence: () => {},
|
||||||
getRoomTitle: () => 'title',
|
getRoomTitle: () => 'title',
|
||||||
|
@ -67,24 +38,22 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
||||||
|
|
||||||
constructor(props: IRoomItemContainerProps) {
|
constructor(props: IRoomItemContainerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.mounted = false;
|
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.mounted = true;
|
|
||||||
const { connected, getUserPresence, id } = this.props;
|
const { connected, getUserPresence, id } = this.props;
|
||||||
if (connected && this.isDirect) {
|
if (connected && this.isDirect) {
|
||||||
getUserPresence(id);
|
getUserPresence(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: any) {
|
shouldComponentUpdate(nextProps: IRoomItemContainerProps) {
|
||||||
const { props }: any = this;
|
const { props } = this;
|
||||||
return !attrs.every(key => props[key] === nextProps[key]);
|
return !attrs.every(key => props[key] === nextProps[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: any) {
|
componentDidUpdate(prevProps: IRoomItemContainerProps) {
|
||||||
const { connected, getUserPresence, id } = this.props;
|
const { connected, getUserPresence, id } = this.props;
|
||||||
if (prevProps.connected !== connected && connected && this.isDirect) {
|
if (prevProps.connected !== connected && connected && this.isDirect) {
|
||||||
getUserPresence(id);
|
getUserPresence(id);
|
||||||
|
@ -106,7 +75,7 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
||||||
const {
|
const {
|
||||||
item: { t },
|
item: { t },
|
||||||
id
|
id
|
||||||
}: any = this.props;
|
} = this.props;
|
||||||
return t === 'd' && id && !this.isGroupChat;
|
return t === 'd' && id && !this.isGroupChat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +113,6 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
||||||
hideChannel,
|
hideChannel,
|
||||||
theme,
|
theme,
|
||||||
isFocused,
|
isFocused,
|
||||||
avatarSize,
|
|
||||||
status,
|
status,
|
||||||
showLastMessage,
|
showLastMessage,
|
||||||
username,
|
username,
|
||||||
|
@ -177,7 +145,6 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// @ts-ignore
|
|
||||||
<RoomItem
|
<RoomItem
|
||||||
name={name}
|
name={name}
|
||||||
avatar={avatar}
|
avatar={avatar}
|
||||||
|
@ -197,7 +164,6 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
||||||
type={item.t}
|
type={item.t}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
isFocused={isFocused}
|
isFocused={isFocused}
|
||||||
size={avatarSize}
|
|
||||||
prid={item.prid}
|
prid={item.prid}
|
||||||
status={status}
|
status={status}
|
||||||
hideUnreadStatus={item.hideUnreadStatus}
|
hideUnreadStatus={item.hideUnreadStatus}
|
||||||
|
@ -217,6 +183,7 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
|
||||||
autoJoin={autoJoin}
|
autoJoin={autoJoin}
|
||||||
showAvatar={showAvatar}
|
showAvatar={showAvatar}
|
||||||
displayMode={displayMode}
|
displayMode={displayMode}
|
||||||
|
sourceType={item.source}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Animated } from 'react-native';
|
||||||
|
|
||||||
|
import { TSupportedThemes } from '../../theme';
|
||||||
|
import { TUserStatus, ILastMessage, SubscriptionType, IOmnichannelSource } from '../../definitions';
|
||||||
|
|
||||||
|
export interface ILeftActionsProps {
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
transX: Animated.AnimatedAddition | Animated.AnimatedMultiplication;
|
||||||
|
isRead: boolean;
|
||||||
|
width: number;
|
||||||
|
onToggleReadPress(): void;
|
||||||
|
displayMode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRightActionsProps {
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
transX: Animated.AnimatedAddition | Animated.AnimatedMultiplication;
|
||||||
|
favorite: boolean;
|
||||||
|
width: number;
|
||||||
|
toggleFav(): void;
|
||||||
|
onHidePress(): void;
|
||||||
|
displayMode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITitleProps {
|
||||||
|
name: string;
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
hideUnreadStatus: boolean;
|
||||||
|
alert: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUpdatedAtProps {
|
||||||
|
date: string;
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
hideUnreadStatus: boolean;
|
||||||
|
alert: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWrapperProps {
|
||||||
|
accessibilityLabel: string;
|
||||||
|
avatar: string;
|
||||||
|
type: string;
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
rid: string;
|
||||||
|
children: React.ReactElement;
|
||||||
|
displayMode: string;
|
||||||
|
prid: string;
|
||||||
|
showLastMessage: boolean;
|
||||||
|
status: string;
|
||||||
|
isGroupChat: boolean;
|
||||||
|
teamMain: boolean;
|
||||||
|
showAvatar: boolean;
|
||||||
|
sourceType: IOmnichannelSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITypeIconProps {
|
||||||
|
type: string;
|
||||||
|
status: TUserStatus;
|
||||||
|
prid: string;
|
||||||
|
isGroupChat: boolean;
|
||||||
|
teamMain: boolean;
|
||||||
|
theme?: TSupportedThemes;
|
||||||
|
size?: number;
|
||||||
|
style?: object;
|
||||||
|
sourceType: IOmnichannelSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRoomItemContainerProps {
|
||||||
|
[key: string]: string | boolean | Function | number;
|
||||||
|
item: any;
|
||||||
|
showLastMessage: boolean;
|
||||||
|
id: string;
|
||||||
|
onPress: (item: any) => void;
|
||||||
|
onLongPress: (item: any) => Promise<void>;
|
||||||
|
username: string;
|
||||||
|
width: number;
|
||||||
|
status: TUserStatus;
|
||||||
|
toggleFav(): void;
|
||||||
|
toggleRead(): void;
|
||||||
|
hideChannel(): void;
|
||||||
|
useRealName: boolean;
|
||||||
|
getUserPresence: (uid: string) => void;
|
||||||
|
connected: boolean;
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
isFocused: boolean;
|
||||||
|
getRoomTitle: (item: any) => string;
|
||||||
|
getRoomAvatar: (item: any) => string;
|
||||||
|
getIsGroupChat: (item: any) => boolean;
|
||||||
|
getIsRead: (item: any) => boolean;
|
||||||
|
swipeEnabled: boolean;
|
||||||
|
autoJoin: boolean;
|
||||||
|
showAvatar: boolean;
|
||||||
|
displayMode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRoomItemProps {
|
||||||
|
rid: string;
|
||||||
|
type: SubscriptionType;
|
||||||
|
prid: string;
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
showLastMessage: boolean;
|
||||||
|
username: string;
|
||||||
|
testID: string;
|
||||||
|
width: number;
|
||||||
|
status: TUserStatus;
|
||||||
|
useRealName: boolean;
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
isFocused: boolean;
|
||||||
|
isGroupChat: boolean;
|
||||||
|
isRead: boolean;
|
||||||
|
teamMain: boolean;
|
||||||
|
date: string;
|
||||||
|
accessibilityLabel: string;
|
||||||
|
lastMessage: ILastMessage;
|
||||||
|
favorite: boolean;
|
||||||
|
alert: boolean;
|
||||||
|
hideUnreadStatus: boolean;
|
||||||
|
unread: number;
|
||||||
|
userMentions: number;
|
||||||
|
groupMentions: number;
|
||||||
|
tunread: [];
|
||||||
|
tunreadUser: [];
|
||||||
|
tunreadGroup: [];
|
||||||
|
swipeEnabled: boolean;
|
||||||
|
toggleFav(): void;
|
||||||
|
toggleRead(): void;
|
||||||
|
onPress(): void;
|
||||||
|
onLongPress(): void;
|
||||||
|
hideChannel(): void;
|
||||||
|
autoJoin: boolean;
|
||||||
|
size?: number;
|
||||||
|
showAvatar: boolean;
|
||||||
|
displayMode: string;
|
||||||
|
sourceType: IOmnichannelSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ILastMessageProps {
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
lastMessage: ILastMessage;
|
||||||
|
type: SubscriptionType;
|
||||||
|
showLastMessage: boolean;
|
||||||
|
username: string;
|
||||||
|
useRealName: boolean;
|
||||||
|
alert: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITouchableProps {
|
||||||
|
children: JSX.Element;
|
||||||
|
type: string;
|
||||||
|
onPress(): void;
|
||||||
|
onLongPress(): void;
|
||||||
|
testID: string;
|
||||||
|
width: number;
|
||||||
|
favorite: boolean;
|
||||||
|
isRead: boolean;
|
||||||
|
rid: string;
|
||||||
|
toggleFav: Function;
|
||||||
|
toggleRead: Function;
|
||||||
|
hideChannel: Function;
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
isFocused: boolean;
|
||||||
|
swipeEnabled: boolean;
|
||||||
|
displayMode: string;
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ export const ACTION_WIDTH = 80;
|
||||||
export const SMALL_SWIPE = ACTION_WIDTH / 2;
|
export const SMALL_SWIPE = ACTION_WIDTH / 2;
|
||||||
export const LONG_SWIPE = ACTION_WIDTH * 3;
|
export const LONG_SWIPE = ACTION_WIDTH * 3;
|
||||||
|
|
||||||
export default StyleSheet.create<any>({
|
export default StyleSheet.create({
|
||||||
flex: {
|
flex: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
|
@ -0,0 +1,51 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleProp, ViewStyle } from 'react-native';
|
||||||
|
import { SvgUri } from 'react-native-svg';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { OmnichannelSourceType, IApplicationState, IOmnichannelSource } from '../../definitions';
|
||||||
|
import { STATUS_COLORS } from '../../lib/constants';
|
||||||
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
|
|
||||||
|
const iconMap = {
|
||||||
|
widget: 'livechat-monochromatic',
|
||||||
|
email: 'mail',
|
||||||
|
sms: 'sms',
|
||||||
|
app: 'omnichannel',
|
||||||
|
api: 'omnichannel',
|
||||||
|
other: 'omnichannel'
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IOmnichannelRoomIconProps {
|
||||||
|
size: number;
|
||||||
|
type: string;
|
||||||
|
style?: StyleProp<ViewStyle>;
|
||||||
|
status?: string;
|
||||||
|
sourceType?: IOmnichannelSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OmnichannelRoomIcon = ({ size, style, sourceType, status }: IOmnichannelRoomIconProps) => {
|
||||||
|
const baseUrl = useSelector((state: IApplicationState) => state.server?.server);
|
||||||
|
const connected = useSelector((state: IApplicationState) => state.meteor?.connected);
|
||||||
|
|
||||||
|
if (sourceType?.type === OmnichannelSourceType.APP && sourceType.id && sourceType.sidebarIcon && connected) {
|
||||||
|
return (
|
||||||
|
<SvgUri
|
||||||
|
height={size}
|
||||||
|
width={size}
|
||||||
|
color={STATUS_COLORS[status || 'offline']}
|
||||||
|
uri={`${baseUrl}/api/apps/public/${sourceType.id}/get-sidebar-icon?icon=${sourceType.sidebarIcon}`}
|
||||||
|
style={style}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CustomIcon
|
||||||
|
name={iconMap[sourceType?.type || 'other']}
|
||||||
|
size={size}
|
||||||
|
style={style}
|
||||||
|
color={STATUS_COLORS[status || 'offline']}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,11 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, ViewStyle } from 'react-native';
|
import { StyleSheet, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { CustomIcon } from '../lib/Icons';
|
import { OmnichannelRoomIcon } from './OmnichannelRoomIcon';
|
||||||
import { STATUS_COLORS, themes } from '../constants/colors';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import Status from './Status/Status';
|
import { STATUS_COLORS, themes } from '../../lib/constants';
|
||||||
import { withTheme } from '../theme';
|
import Status from '../Status/Status';
|
||||||
import { TUserStatus } from '../definitions';
|
import { useTheme } from '../../theme';
|
||||||
|
import { TUserStatus, IOmnichannelSource } from '../../definitions';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
icon: {
|
icon: {
|
||||||
|
@ -14,21 +15,23 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IRoomTypeIcon {
|
interface IRoomTypeIcon {
|
||||||
theme?: string;
|
|
||||||
type: string;
|
type: string;
|
||||||
isGroupChat?: boolean;
|
isGroupChat?: boolean;
|
||||||
teamMain?: boolean;
|
teamMain?: boolean;
|
||||||
status?: TUserStatus;
|
status?: TUserStatus;
|
||||||
size?: number;
|
size?: number;
|
||||||
style?: ViewStyle;
|
style?: ViewStyle;
|
||||||
|
sourceType?: IOmnichannelSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoomTypeIcon = React.memo(({ type, isGroupChat, status, style, theme, teamMain, size = 16 }: IRoomTypeIcon) => {
|
const RoomTypeIcon = React.memo(({ type, isGroupChat, status, style, teamMain, size = 16, sourceType }: IRoomTypeIcon) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const color = themes[theme!].titleText;
|
const color = themes[theme].titleText;
|
||||||
const iconStyle = [styles.icon, { color }, style];
|
const iconStyle = [styles.icon, { color }, style];
|
||||||
|
|
||||||
if (type === 'd' && !isGroupChat) {
|
if (type === 'd' && !isGroupChat) {
|
||||||
|
@ -38,6 +41,10 @@ const RoomTypeIcon = React.memo(({ type, isGroupChat, status, style, theme, team
|
||||||
return <Status style={[iconStyle, { color: STATUS_COLORS[status] }]} size={size} status={status} />;
|
return <Status style={[iconStyle, { color: STATUS_COLORS[status] }]} size={size} status={status} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'l') {
|
||||||
|
return <OmnichannelRoomIcon style={[styles.icon, style]} size={size} type={type} status={status} sourceType={sourceType} />;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: move this to a separate function
|
// TODO: move this to a separate function
|
||||||
let icon = 'channel-private';
|
let icon = 'channel-private';
|
||||||
if (teamMain) {
|
if (teamMain) {
|
||||||
|
@ -52,11 +59,9 @@ const RoomTypeIcon = React.memo(({ type, isGroupChat, status, style, theme, team
|
||||||
} else {
|
} else {
|
||||||
icon = 'mention';
|
icon = 'mention';
|
||||||
}
|
}
|
||||||
} else if (type === 'l') {
|
|
||||||
icon = 'omnichannel';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <CustomIcon name={icon} size={size} style={iconStyle} />;
|
return <CustomIcon name={icon} size={size} style={iconStyle} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withTheme(RoomTypeIcon);
|
export default RoomTypeIcon;
|
|
@ -1,9 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet, ViewProps } from 'react-native';
|
||||||
import { SafeAreaView as SafeAreaContext } from 'react-native-safe-area-context';
|
import { SafeAreaView as SafeAreaContext } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
import { withTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
view: {
|
view: {
|
||||||
|
@ -11,22 +11,24 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ISafeAreaView {
|
type SupportedChildren = React.ReactElement | React.ReactElement[] | null;
|
||||||
testID?: string;
|
type TSafeAreaViewChildren = SupportedChildren | SupportedChildren[];
|
||||||
theme?: string;
|
|
||||||
|
interface ISafeAreaView extends ViewProps {
|
||||||
vertical?: boolean;
|
vertical?: boolean;
|
||||||
style?: object;
|
children: TSafeAreaViewChildren;
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SafeAreaView = React.memo(({ style, children, testID, theme, vertical = true, ...props }: ISafeAreaView) => (
|
const SafeAreaView = React.memo(({ style, children, vertical = true, ...props }: ISafeAreaView) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
<SafeAreaContext
|
<SafeAreaContext
|
||||||
style={[styles.view, { backgroundColor: themes[theme!].auxiliaryBackground }, style]}
|
style={[styles.view, { backgroundColor: themes[theme].auxiliaryBackground }, style]}
|
||||||
edges={vertical ? ['right', 'left'] : undefined}
|
edges={vertical ? ['right', 'left'] : undefined}
|
||||||
testID={testID}
|
|
||||||
{...props}>
|
{...props}>
|
||||||
{children}
|
{children}
|
||||||
</SafeAreaContext>
|
</SafeAreaContext>
|
||||||
));
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default withTheme(SafeAreaView);
|
export default SafeAreaView;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { StyleSheet, Text, TextInput as RNTextInput, TextInputProps, View } from 'react-native';
|
import { StyleSheet, Text, TextInput as RNTextInput, TextInputProps, View } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { CustomIcon } from '../lib/Icons';
|
import { CustomIcon } from '../lib/Icons';
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from '../presentation/TextInput';
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { StyleSheet, View } from 'react-native';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../lib/constants';
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from '../presentation/TextInput';
|
||||||
import { isIOS, isTablet } from '../utils/deviceInfo';
|
import { isIOS, isTablet } from '../utils/deviceInfo';
|
||||||
import { useOrientation } from '../dimensions';
|
import { useOrientation } from '../dimensions';
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React from 'react';
|
||||||
|
// @ts-ignore // TODO: Remove on react-native update
|
||||||
|
import { Pressable, Text, View } from 'react-native';
|
||||||
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
|
|
||||||
|
import { IServerInfo } from '../../definitions';
|
||||||
|
import Check from '../Check';
|
||||||
|
import styles, { ROW_HEIGHT } from './styles';
|
||||||
|
import { themes } from '../../lib/constants';
|
||||||
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
|
export { ROW_HEIGHT };
|
||||||
|
|
||||||
|
interface IServerItem {
|
||||||
|
item: IServerInfo;
|
||||||
|
onPress(): void;
|
||||||
|
onLongPress?(): void;
|
||||||
|
hasCheck?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultLogo = require('../../static/images/logo.png');
|
||||||
|
|
||||||
|
const ServerItem = React.memo(({ item, onPress, onLongPress, hasCheck }: IServerItem) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
onPress={onPress}
|
||||||
|
onLongPress={() => onLongPress?.()}
|
||||||
|
testID={`rooms-list-header-server-${item.id}`}
|
||||||
|
android_ripple={{ color: themes[theme].bannerBackground }}
|
||||||
|
style={({ pressed }: { pressed: boolean }) => ({
|
||||||
|
backgroundColor: isIOS && pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor
|
||||||
|
})}>
|
||||||
|
<View style={styles.serverItemContainer}>
|
||||||
|
{item.iconURL ? (
|
||||||
|
<FastImage
|
||||||
|
source={{
|
||||||
|
uri: item.iconURL,
|
||||||
|
priority: FastImage.priority.high
|
||||||
|
}}
|
||||||
|
// @ts-ignore TODO: Remove when updating FastImage
|
||||||
|
defaultSource={defaultLogo}
|
||||||
|
style={styles.serverIcon}
|
||||||
|
onError={() => console.log('err_loading_server_icon')}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FastImage source={defaultLogo} style={styles.serverIcon} />
|
||||||
|
)}
|
||||||
|
<View style={styles.serverTextContainer}>
|
||||||
|
<Text numberOfLines={1} style={[styles.serverName, { color: themes[theme].titleText }]}>
|
||||||
|
{item.name || item.id}
|
||||||
|
</Text>
|
||||||
|
<Text numberOfLines={1} style={[styles.serverUrl, { color: themes[theme].auxiliaryText }]}>
|
||||||
|
{item.id}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{hasCheck ? <Check /> : null}
|
||||||
|
</View>
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ServerItem;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue