Compare commits
71 Commits
develop
...
improvemen
Author | SHA1 | Date |
---|---|---|
AlexAlexandre | 7b9f00510d | |
AlexAlexandre | 3604e7d4b3 | |
AlexAlexandre | 69888c4944 | |
AlexAlexandre | 0738bfb620 | |
AlexAlexandre | 7cf55c1f3b | |
AlexAlexandre | 1737756b43 | |
AlexAlexandre | 43e238292e | |
AlexAlexandre | d63ee28275 | |
AlexAlexandre | 078c361efc | |
AlexAlexandre | 1596f1621f | |
AlexAlexandre | 49b9cf436f | |
AlexAlexandre | 50a99fb846 | |
Alex Junior | 20d7b6a051 | |
Alex Junior | 757347fa29 | |
Alex Junior | ca2e28f893 | |
Diego Mello | 3f75741dee | |
AlexAlexandre | 3e74f59ca3 | |
AlexAlexandre | 91e6566542 | |
AlexAlexandre | 9d312bd9a6 | |
AlexAlexandre | 601a45a194 | |
AlexAlexandre | a533e5c827 | |
AlexAlexandre | 72df0aef7e | |
AlexAlexandre | 3f5f309643 | |
AlexAlexandre | bacaf7fc33 | |
AlexAlexandre | c6e7826500 | |
AlexAlexandre | 107d4da851 | |
AlexAlexandre | 9525786c0a | |
AlexAlexandre | 07741f0b98 | |
AlexAlexandre | 9179e90920 | |
AlexAlexandre | 2c9f6f7f82 | |
AlexAlexandre | 11eaadde2d | |
AlexAlexandre | e38e5880a8 | |
AlexAlexandre | 9a1b705471 | |
AlexAlexandre | 369b3f2ef6 | |
AlexAlexandre | 60f659618d | |
AlexAlexandre | e8b58323c7 | |
AlexAlexandre | d5c2decfb9 | |
AlexAlexandre | 4f65c1b371 | |
AlexAlexandre | 9e0154c436 | |
Alex Junior | e9229e5097 | |
AlexAlexandre | 0182853fdb | |
AlexAlexandre | db11e664f6 | |
AlexAlexandre | 024c744615 | |
AlexAlexandre | 6db54e9920 | |
AlexAlexandre | 43c4234f73 | |
AlexAlexandre | d3986689e5 | |
AlexAlexandre | 2b533f86e3 | |
AlexAlexandre | f9b1e0eab4 | |
AlexAlexandre | a557d7f5d2 | |
AlexAlexandre | 1c7ae63ac1 | |
AlexAlexandre | e0930466b4 | |
AlexAlexandre | ee7ad68e92 | |
AlexAlexandre | 4d07101a56 | |
AlexAlexandre | b795b6fc5a | |
AlexAlexandre | 8ef11eab49 | |
AlexAlexandre | d060edb05f | |
AlexAlexandre | f5eee1cbaf | |
AlexAlexandre | 66a4627fec | |
AlexAlexandre | 19d8ab1a57 | |
AlexAlexandre | b0ddb87c88 | |
AlexAlexandre | a18b13c06b | |
AlexAlexandre | 0354b49b08 | |
AlexAlexandre | acdc2ce8f5 | |
AlexAlexandre | 802c7b2b28 | |
AlexAlexandre | 99b6c3d073 | |
AlexAlexandre | eadbb59e09 | |
AlexAlexandre | 584a16b20e | |
AlexAlexandre | b6577de7c2 | |
AlexAlexandre | fdcfd3e0ee | |
AlexAlexandre | 8a1dc781eb | |
AlexAlexandre | 7bfe1fcadb |
143
.eslintrc.js
143
.eslintrc.js
|
@ -2,12 +2,14 @@ module.exports = {
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": {
|
"import/resolver": {
|
||||||
"node": {
|
"node": {
|
||||||
"extensions": [".js", ".ios.js", ".android.js", ".native.js", ".tsx"]
|
"extensions": [".js", ".ios.js", ".android.js", ".native.js", ".ts", ".tsx"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parser": "@babel/eslint-parser",
|
"parser": "@babel/eslint-parser",
|
||||||
"extends": "airbnb",
|
"extends": [
|
||||||
|
"@rocket.chat/eslint-config",
|
||||||
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"ecmaVersion": 2017,
|
"ecmaVersion": 2017,
|
||||||
|
@ -33,23 +35,40 @@ module.exports = {
|
||||||
"mocha": true
|
"mocha": true
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"import/extensions": [
|
||||||
|
"error",
|
||||||
|
"ignorePackages",
|
||||||
|
{
|
||||||
|
"js": "warning",
|
||||||
|
"jsx": "warning",
|
||||||
|
"ts": "warning",
|
||||||
|
"tsx": "warning"
|
||||||
|
}
|
||||||
|
],
|
||||||
"react/jsx-filename-extension": [1, {
|
"react/jsx-filename-extension": [1, {
|
||||||
"extensions": [".js", ".jsx"]
|
"extensions": [".js", ".jsx", ".ts", ".tsx"]
|
||||||
}],
|
}],
|
||||||
"react/require-default-props": [0],
|
"react/require-default-props": [0],
|
||||||
"react/no-unused-prop-types": [2, {
|
"ordered-imports": [0],
|
||||||
"skipShapeProps": true
|
// "react/no-unused-prop-types": [2, {
|
||||||
}],
|
// "skipShapeProps": true
|
||||||
|
// }],
|
||||||
"react/no-did-mount-set-state": 0,
|
"react/no-did-mount-set-state": 0,
|
||||||
"react/no-multi-comp": [0],
|
"react/no-multi-comp": [0],
|
||||||
"react/jsx-indent": [2, "tab"],
|
"react/jsx-indent": [2, "tab"],
|
||||||
"react/jsx-indent-props": [2, "tab"],
|
"react/jsx-indent-props": [2, "tab"],
|
||||||
"react/forbid-prop-types": 0,
|
// "react/forbid-prop-types": 0,
|
||||||
"jsx-quotes": [2, "prefer-single"],
|
"jsx-quotes": [2, "prefer-single"],
|
||||||
"jsx-a11y/href-no-hash": 0,
|
"jsx-a11y/href-no-hash": 0,
|
||||||
"jsx-a11y/aria-role": 0,
|
"jsx-a11y/aria-role": 0,
|
||||||
"import/prefer-default-export": 0,
|
"import/prefer-default-export": 0,
|
||||||
"import/no-cycle": 0,
|
"import/no-cycle": 0,
|
||||||
|
"import/order":[
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"newlines-between": "ignore",
|
||||||
|
}
|
||||||
|
],
|
||||||
"camelcase": 0,
|
"camelcase": 0,
|
||||||
"no-underscore-dangle": 0,
|
"no-underscore-dangle": 0,
|
||||||
"no-return-assign": 0,
|
"no-return-assign": 0,
|
||||||
|
@ -91,10 +110,7 @@ module.exports = {
|
||||||
"no-undef": 2,
|
"no-undef": 2,
|
||||||
"no-unreachable": 2,
|
"no-unreachable": 2,
|
||||||
"no-unused-expressions": 0,
|
"no-unused-expressions": 0,
|
||||||
"no-unused-vars": [2, {
|
"no-unused-vars": "off",
|
||||||
"vars": "all",
|
|
||||||
"args": "after-used"
|
|
||||||
}],
|
|
||||||
"max-len": 0,
|
"max-len": 0,
|
||||||
"react/jsx-uses-vars": 2,
|
"react/jsx-uses-vars": 2,
|
||||||
"no-void": 2,
|
"no-void": 2,
|
||||||
|
@ -120,7 +136,7 @@ module.exports = {
|
||||||
"block-scoped-var": 2,
|
"block-scoped-var": 2,
|
||||||
"curly": [2, "all"],
|
"curly": [2, "all"],
|
||||||
"eqeqeq": [2, "allow-null"],
|
"eqeqeq": [2, "allow-null"],
|
||||||
"new-cap": [2],
|
"new-cap": "off",
|
||||||
"use-isnan": 2,
|
"use-isnan": 2,
|
||||||
"valid-typeof": 2,
|
"valid-typeof": 2,
|
||||||
"linebreak-style": 0,
|
"linebreak-style": 0,
|
||||||
|
@ -136,23 +152,14 @@ module.exports = {
|
||||||
"react/jsx-one-expression-per-line": 0,
|
"react/jsx-one-expression-per-line": 0,
|
||||||
"require-await": 2,
|
"require-await": 2,
|
||||||
"func-names": 0,
|
"func-names": 0,
|
||||||
"react/sort-comp": ["error", {
|
|
||||||
"order": [
|
|
||||||
"static-variables",
|
|
||||||
"static-methods",
|
|
||||||
"lifecycle",
|
|
||||||
"everything-else",
|
|
||||||
"render"
|
|
||||||
]
|
|
||||||
}],
|
|
||||||
"react/static-property-placement": [0],
|
"react/static-property-placement": [0],
|
||||||
"arrow-parens": ["error", "as-needed", { requireForBlockBody: true }],
|
"arrow-parens": ["error", "as-needed", { requireForBlockBody: true }],
|
||||||
"react/jsx-props-no-spreading": [1],
|
|
||||||
"react/jsx-curly-newline": [0],
|
"react/jsx-curly-newline": [0],
|
||||||
"react/state-in-constructor": [0],
|
"react/state-in-constructor": [0],
|
||||||
"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",
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"__DEV__": true
|
"__DEV__": true
|
||||||
|
@ -173,6 +180,96 @@ module.exports = {
|
||||||
'no-await-in-loop': 0,
|
'no-await-in-loop': 0,
|
||||||
'no-restricted-syntax': 0
|
'no-restricted-syntax': 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"@rocket.chat/eslint-config"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaVersion": 2018,
|
||||||
|
"warnOnUnsupportedTypeScriptVersion": false,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true,
|
||||||
|
"legacyDecorators": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react",
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": [0],
|
||||||
|
"@typescript-eslint/ban-types": [0],
|
||||||
|
"func-call-spacing": "off",
|
||||||
|
"jsx-quotes": [
|
||||||
|
"error",
|
||||||
|
"prefer-single"
|
||||||
|
],
|
||||||
|
"indent": "off",
|
||||||
|
"no-return-assign": 0,
|
||||||
|
"no-dupe-class-members": "off",
|
||||||
|
"no-extra-parens": "off",
|
||||||
|
"no-spaced-func": "off",
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"no-useless-constructor": "off",
|
||||||
|
"no-use-before-define": "off",
|
||||||
|
"react/jsx-uses-react": "error",
|
||||||
|
"react/jsx-uses-vars": "error",
|
||||||
|
"react/jsx-no-undef": "error",
|
||||||
|
"react/jsx-fragments": [
|
||||||
|
"error",
|
||||||
|
"syntax"
|
||||||
|
],
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"@typescript-eslint/indent": [
|
||||||
|
"error",
|
||||||
|
"tab",
|
||||||
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-extra-parens": [
|
||||||
|
"error",
|
||||||
|
"all",
|
||||||
|
{
|
||||||
|
"conditionalAssign": true,
|
||||||
|
"nestedBinaryExpressions": false,
|
||||||
|
"returnAssign": true,
|
||||||
|
"ignoreJSX": "all",
|
||||||
|
"enforceForArrowConditionals": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-dupe-class-members": "error",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": ["error", {
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"ignoreRestSiblings": true
|
||||||
|
}],
|
||||||
|
"new-cap": "off",
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"JSX": true,
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"node": {
|
||||||
|
"extensions": [
|
||||||
|
".js",
|
||||||
|
".ts",
|
||||||
|
".tsx"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
module.exports = {
|
||||||
|
bracketSpacing: false,
|
||||||
|
jsxBracketSameLine: true,
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: 'all',
|
||||||
|
printWidth: 130,
|
||||||
|
};
|
|
@ -4693,7 +4693,7 @@ exports[`Storyshots List pressable 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 92,
|
"height": 92,
|
||||||
},
|
},
|
||||||
|
@ -6211,7 +6211,7 @@ exports[`Storyshots List with bigger font 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 69,
|
"height": 69,
|
||||||
},
|
},
|
||||||
|
@ -6625,7 +6625,7 @@ exports[`Storyshots List with bigger font 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 69,
|
"height": 69,
|
||||||
},
|
},
|
||||||
|
@ -7080,7 +7080,7 @@ exports[`Storyshots List with black theme 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 92,
|
"height": 92,
|
||||||
},
|
},
|
||||||
|
@ -7494,7 +7494,7 @@ exports[`Storyshots List with black theme 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 92,
|
"height": 92,
|
||||||
},
|
},
|
||||||
|
@ -7972,7 +7972,7 @@ exports[`Storyshots List with custom colors 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 92,
|
"height": 92,
|
||||||
},
|
},
|
||||||
|
@ -8129,7 +8129,7 @@ exports[`Storyshots List with dark theme 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 92,
|
"height": 92,
|
||||||
},
|
},
|
||||||
|
@ -8543,7 +8543,7 @@ exports[`Storyshots List with dark theme 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 92,
|
"height": 92,
|
||||||
},
|
},
|
||||||
|
@ -10410,7 +10410,7 @@ exports[`Storyshots List with small font 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 36.800000000000004,
|
"height": 36.800000000000004,
|
||||||
},
|
},
|
||||||
|
@ -10824,7 +10824,7 @@ exports[`Storyshots List with small font 1`] = `
|
||||||
"justifyContent": "center",
|
"justifyContent": "center",
|
||||||
"paddingHorizontal": 12,
|
"paddingHorizontal": 12,
|
||||||
},
|
},
|
||||||
false,
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 36.800000000000004,
|
"height": 36.800000000000004,
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,7 +25,7 @@ import com.android.build.OutputFile
|
||||||
* bundleAssetName: "index.android.bundle",
|
* bundleAssetName: "index.android.bundle",
|
||||||
*
|
*
|
||||||
* // the entry file for bundle generation. If none specified and
|
* // the entry file for bundle generation. If none specified and
|
||||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
* // "index.android.js" exists, it will be used. Otherwise "index.tsx" is
|
||||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||||
* entryFile: "index.android.js",
|
* entryFile: "index.android.js",
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
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';
|
||||||
|
@ -7,15 +6,12 @@ import { connect } from 'react-redux';
|
||||||
import Navigation from './lib/Navigation';
|
import Navigation from './lib/Navigation';
|
||||||
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
||||||
import {
|
import {
|
||||||
ROOT_LOADING, ROOT_OUTSIDE, ROOT_NEW_SERVER, ROOT_INSIDE, ROOT_SET_USERNAME
|
ROOT_INSIDE, ROOT_LOADING, ROOT_NEW_SERVER, ROOT_OUTSIDE, ROOT_SET_USERNAME,
|
||||||
} from './actions/app';
|
} from './actions/app';
|
||||||
|
|
||||||
// Stacks
|
// Stacks
|
||||||
import AuthLoadingView from './views/AuthLoadingView';
|
import AuthLoadingView from './views/AuthLoadingView';
|
||||||
|
|
||||||
// SetUsername Stack
|
// SetUsername Stack
|
||||||
import SetUsernameView from './views/SetUsernameView';
|
import SetUsernameView from './views/SetUsernameView';
|
||||||
|
|
||||||
import OutsideStack from './stacks/OutsideStack';
|
import OutsideStack from './stacks/OutsideStack';
|
||||||
import InsideStack from './stacks/InsideStack';
|
import InsideStack from './stacks/InsideStack';
|
||||||
import MasterDetailStack from './stacks/MasterDetailStack';
|
import MasterDetailStack from './stacks/MasterDetailStack';
|
||||||
|
@ -35,7 +31,7 @@ const SetUsernameStack = () => (
|
||||||
|
|
||||||
// App
|
// App
|
||||||
const Stack = createStackNavigator();
|
const Stack = createStackNavigator();
|
||||||
const App = React.memo(({ root, isMasterDetail }) => {
|
const App = React.memo(({ root, isMasterDetail }: {root: string, isMasterDetail: boolean}) => {
|
||||||
if (!root) {
|
if (!root) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -100,15 +96,10 @@ const App = React.memo(({ root, isMasterDetail }) => {
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
root: state.app.root,
|
root: state.app.root,
|
||||||
isMasterDetail: state.app.isMasterDetail
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
});
|
});
|
||||||
|
|
||||||
App.propTypes = {
|
|
||||||
root: PropTypes.string,
|
|
||||||
isMasterDetail: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppContainer = connect(mapStateToProps)(App);
|
const AppContainer = connect(mapStateToProps)(App);
|
||||||
export default AppContainer;
|
export default AppContainer;
|
|
@ -4,7 +4,7 @@ const FAILURE = 'FAILURE';
|
||||||
const defaultTypes = [REQUEST, SUCCESS, FAILURE];
|
const defaultTypes = [REQUEST, SUCCESS, FAILURE];
|
||||||
function createRequestTypes(base, types = defaultTypes) {
|
function createRequestTypes(base, types = defaultTypes) {
|
||||||
const res = {};
|
const res = {};
|
||||||
types.forEach(type => (res[type] = `${ base }_${ type }`));
|
types.forEach(type => res[type] = `${ base }_${ type }`);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,15 +125,15 @@ const keyCommands = [
|
||||||
discoverabilityTitle: I18n.t('Add_server')
|
discoverabilityTitle: I18n.t('Add_server')
|
||||||
},
|
},
|
||||||
// Refers to select rooms on list
|
// Refers to select rooms on list
|
||||||
...([1, 2, 3, 4, 5, 6, 7, 8, 9].map(value => ({
|
...[1, 2, 3, 4, 5, 6, 7, 8, 9].map(value => ({
|
||||||
input: `${ value }`,
|
input: `${ value }`,
|
||||||
modifierFlags: constants.keyModifierCommand
|
modifierFlags: constants.keyModifierCommand
|
||||||
}))),
|
})),
|
||||||
// Refers to select servers on list
|
// Refers to select servers on list
|
||||||
...([1, 2, 3, 4, 5, 6, 7, 8, 9].map(value => ({
|
...[1, 2, 3, 4, 5, 6, 7, 8, 9].map(value => ({
|
||||||
input: `${ value }`,
|
input: `${ value }`,
|
||||||
modifierFlags: constants.keyModifierCommand | constants.keyModifierAlternate
|
modifierFlags: constants.keyModifierCommand | constants.keyModifierAlternate
|
||||||
})))
|
}))
|
||||||
];
|
];
|
||||||
|
|
||||||
export const setKeyCommands = () => KeyCommands.setKeyCommands(keyCommands);
|
export const setKeyCommands = () => KeyCommands.setKeyCommands(keyCommands);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
export const STATUS_COLORS = {
|
export const STATUS_COLORS: any = {
|
||||||
online: '#2de0a5',
|
online: '#2de0a5',
|
||||||
busy: '#f5455c',
|
busy: '#f5455c',
|
||||||
away: '#ffd21f',
|
away: '#ffd21f',
|
||||||
offline: '#cbced1',
|
offline: '#cbced1',
|
||||||
loading: '#9ea2a8'
|
loading: '#9ea2a8',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SWITCH_TRACK_COLOR = {
|
export const SWITCH_TRACK_COLOR = {
|
||||||
false: '#f5455c',
|
false: '#f5455c',
|
||||||
true: '#2de0a5'
|
true: '#2de0a5',
|
||||||
};
|
};
|
||||||
|
|
||||||
const mentions = {
|
const mentions = {
|
||||||
|
@ -16,10 +16,10 @@ const mentions = {
|
||||||
tunreadColor: '#1d74f5',
|
tunreadColor: '#1d74f5',
|
||||||
mentionMeColor: '#F5455C',
|
mentionMeColor: '#F5455C',
|
||||||
mentionGroupColor: '#F38C39',
|
mentionGroupColor: '#F38C39',
|
||||||
mentionOtherColor: '#F3BE08'
|
mentionOtherColor: '#F3BE08',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const themes = {
|
export const themes: any = {
|
||||||
light: {
|
light: {
|
||||||
backgroundColor: '#ffffff',
|
backgroundColor: '#ffffff',
|
||||||
focusedBackground: '#ffffff',
|
focusedBackground: '#ffffff',
|
||||||
|
@ -65,7 +65,7 @@ export const themes = {
|
||||||
previewBackground: '#1F2329',
|
previewBackground: '#1F2329',
|
||||||
previewTintColor: '#ffffff',
|
previewTintColor: '#ffffff',
|
||||||
backdropOpacity: 0.3,
|
backdropOpacity: 0.3,
|
||||||
...mentions
|
...mentions,
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
backgroundColor: '#030b1b',
|
backgroundColor: '#030b1b',
|
||||||
|
@ -112,7 +112,7 @@ export const themes = {
|
||||||
previewBackground: '#030b1b',
|
previewBackground: '#030b1b',
|
||||||
previewTintColor: '#ffffff',
|
previewTintColor: '#ffffff',
|
||||||
backdropOpacity: 0.9,
|
backdropOpacity: 0.9,
|
||||||
...mentions
|
...mentions,
|
||||||
},
|
},
|
||||||
black: {
|
black: {
|
||||||
backgroundColor: '#000000',
|
backgroundColor: '#000000',
|
||||||
|
@ -159,6 +159,6 @@ export const themes = {
|
||||||
previewBackground: '#000000',
|
previewBackground: '#000000',
|
||||||
previewTintColor: '#ffffff',
|
previewTintColor: '#ffffff',
|
||||||
backdropOpacity: 0.9,
|
backdropOpacity: 0.9,
|
||||||
...mentions
|
...mentions,
|
||||||
}
|
},
|
||||||
};
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
SENT: 0,
|
SENT: 0,
|
||||||
TEMP: 1,
|
TEMP: 1,
|
||||||
ERROR: 2
|
ERROR: 2,
|
||||||
};
|
};
|
|
@ -1,206 +1,206 @@
|
||||||
export default {
|
export default {
|
||||||
Accounts_AllowEmailChange: {
|
Accounts_AllowEmailChange: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_AllowPasswordChange: {
|
Accounts_AllowPasswordChange: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_AllowRealNameChange: {
|
Accounts_AllowRealNameChange: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_AllowUserAvatarChange: {
|
Accounts_AllowUserAvatarChange: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_AllowUserProfileChange: {
|
Accounts_AllowUserProfileChange: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_AllowUserStatusMessageChange: {
|
Accounts_AllowUserStatusMessageChange: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_AllowUsernameChange: {
|
Accounts_AllowUsernameChange: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_AvatarBlockUnauthenticatedAccess: {
|
Accounts_AvatarBlockUnauthenticatedAccess: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_CustomFields: {
|
Accounts_CustomFields: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Accounts_EmailOrUsernamePlaceholder: {
|
Accounts_EmailOrUsernamePlaceholder: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Accounts_EmailVerification: {
|
Accounts_EmailVerification: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_NamePlaceholder: {
|
Accounts_NamePlaceholder: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Accounts_PasswordPlaceholder: {
|
Accounts_PasswordPlaceholder: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Accounts_PasswordReset: {
|
Accounts_PasswordReset: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_RegistrationForm: {
|
Accounts_RegistrationForm: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Accounts_RegistrationForm_LinkReplacementText: {
|
Accounts_RegistrationForm_LinkReplacementText: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Accounts_ShowFormLogin: {
|
Accounts_ShowFormLogin: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_ManuallyApproveNewUsers: {
|
Accounts_ManuallyApproveNewUsers: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
API_Use_REST_For_DDP_Calls: {
|
API_Use_REST_For_DDP_Calls: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_iframe_enabled: {
|
Accounts_iframe_enabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_Iframe_api_url: {
|
Accounts_Iframe_api_url: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Accounts_Iframe_api_method: {
|
Accounts_Iframe_api_method: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
CROWD_Enable: {
|
CROWD_Enable: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
DirectMesssage_maxUsers: {
|
DirectMesssage_maxUsers: {
|
||||||
type: 'valueAsNumber'
|
type: 'valueAsNumber',
|
||||||
},
|
},
|
||||||
E2E_Enable: {
|
E2E_Enable: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_Directory_DefaultView: {
|
Accounts_Directory_DefaultView: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
FEDERATION_Enabled: {
|
FEDERATION_Enabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Hide_System_Messages: {
|
Hide_System_Messages: {
|
||||||
type: 'valueAsArray'
|
type: 'valueAsArray',
|
||||||
},
|
},
|
||||||
LDAP_Enable: {
|
LDAP_Enable: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Livechat_request_comment_when_closing_conversation: {
|
Livechat_request_comment_when_closing_conversation: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Jitsi_Enabled: {
|
Jitsi_Enabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Jitsi_SSL: {
|
Jitsi_SSL: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Jitsi_Domain: {
|
Jitsi_Domain: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Jitsi_Enabled_TokenAuth: {
|
Jitsi_Enabled_TokenAuth: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Jitsi_URL_Room_Hash: {
|
Jitsi_URL_Room_Hash: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Jitsi_URL_Room_Prefix: {
|
Jitsi_URL_Room_Prefix: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Message_AllowDeleting: {
|
Message_AllowDeleting: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Message_AllowDeleting_BlockDeleteInMinutes: {
|
Message_AllowDeleting_BlockDeleteInMinutes: {
|
||||||
type: 'valueAsNumber'
|
type: 'valueAsNumber',
|
||||||
},
|
},
|
||||||
Message_AllowEditing: {
|
Message_AllowEditing: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Message_AllowEditing_BlockEditInMinutes: {
|
Message_AllowEditing_BlockEditInMinutes: {
|
||||||
type: 'valueAsNumber'
|
type: 'valueAsNumber',
|
||||||
},
|
},
|
||||||
Message_AllowPinning: {
|
Message_AllowPinning: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Message_AllowStarring: {
|
Message_AllowStarring: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Message_AudioRecorderEnabled: {
|
Message_AudioRecorderEnabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Message_GroupingPeriod: {
|
Message_GroupingPeriod: {
|
||||||
type: 'valueAsNumber'
|
type: 'valueAsNumber',
|
||||||
},
|
},
|
||||||
Message_TimeFormat: {
|
Message_TimeFormat: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Message_TimeAndDateFormat: {
|
Message_TimeAndDateFormat: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Site_Name: {
|
Site_Name: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Site_Url: {
|
Site_Url: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Store_Last_Message: {
|
Store_Last_Message: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
uniqueID: {
|
uniqueID: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
UI_Allow_room_names_with_special_chars: {
|
UI_Allow_room_names_with_special_chars: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
UI_Use_Real_Name: {
|
UI_Use_Real_Name: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Assets_favicon_512: {
|
Assets_favicon_512: {
|
||||||
type: null
|
type: null,
|
||||||
},
|
},
|
||||||
Message_Read_Receipt_Enabled: {
|
Message_Read_Receipt_Enabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Message_Read_Receipt_Store_Users: {
|
Message_Read_Receipt_Store_Users: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Threads_enabled: {
|
Threads_enabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
FileUpload_MediaTypeWhiteList: {
|
FileUpload_MediaTypeWhiteList: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
FileUpload_MaxFileSize: {
|
FileUpload_MaxFileSize: {
|
||||||
type: 'valueAsNumber'
|
type: 'valueAsNumber',
|
||||||
},
|
},
|
||||||
API_Gitlab_URL: {
|
API_Gitlab_URL: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
AutoTranslate_Enabled: {
|
AutoTranslate_Enabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
CAS_enabled: {
|
CAS_enabled: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
CAS_login_url: {
|
CAS_login_url: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Force_Screen_Lock: {
|
Force_Screen_Lock: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Force_Screen_Lock_After: {
|
Force_Screen_Lock_After: {
|
||||||
type: 'valueAsNumber'
|
type: 'valueAsNumber',
|
||||||
},
|
},
|
||||||
Allow_Save_Media_to_Gallery: {
|
Allow_Save_Media_to_Gallery: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Accounts_AllowInvisibleStatusOption: {
|
Accounts_AllowInvisibleStatusOption: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString',
|
||||||
},
|
},
|
||||||
Jitsi_Enable_Teams: {
|
Jitsi_Enable_Teams: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean',
|
||||||
},
|
},
|
||||||
Jitsi_Enable_Channels: {
|
Jitsi_Enable_Channels: {
|
||||||
type: 'valuesAsBoolean'
|
type: 'valuesAsBoolean',
|
||||||
}
|
},
|
||||||
};
|
};
|
|
@ -1,23 +1,17 @@
|
||||||
import React, {
|
import React, {
|
||||||
|
forwardRef,
|
||||||
|
isValidElement,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
useEffect,
|
|
||||||
forwardRef,
|
|
||||||
useImperativeHandle,
|
|
||||||
useCallback,
|
|
||||||
isValidElement
|
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Keyboard, Text } from 'react-native';
|
import { Keyboard, Text } from 'react-native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { TapGestureHandler, State } from 'react-native-gesture-handler';
|
import { State, TapGestureHandler } from 'react-native-gesture-handler';
|
||||||
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
|
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
|
||||||
import Animated, {
|
import Animated, { Easing, Extrapolate, Value, interpolate } from 'react-native-reanimated';
|
||||||
Extrapolate,
|
|
||||||
interpolate,
|
|
||||||
Value,
|
|
||||||
Easing
|
|
||||||
} from 'react-native-reanimated';
|
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
import { useBackHandler } from '@react-native-community/hooks';
|
import { useBackHandler } from '@react-native-community/hooks';
|
||||||
|
|
||||||
|
@ -26,12 +20,19 @@ import { Handle } from './Handle';
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import styles, { ITEM_HEIGHT } from './styles';
|
import styles, { ITEM_HEIGHT } from './styles';
|
||||||
import { isTablet, isIOS } from '../../utils/deviceInfo';
|
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
||||||
import * as List from '../List';
|
import * as List from '../List';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { useOrientation, useDimensions } from '../../dimensions';
|
import { IDimensionsContextProps, useDimensions, useOrientation } from '../../dimensions';
|
||||||
|
|
||||||
const getItemLayout = (data, index) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
|
interface IActionSheetData {
|
||||||
|
options: any;
|
||||||
|
headerHeight?: number;
|
||||||
|
hasCancel?: boolean;
|
||||||
|
customHeader: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getItemLayout = (data: any, index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
|
||||||
|
|
||||||
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
||||||
const MAX_SNAP_HEIGHT = 16;
|
const MAX_SNAP_HEIGHT = 16;
|
||||||
|
@ -42,20 +43,20 @@ const ANIMATION_DURATION = 250;
|
||||||
const ANIMATION_CONFIG = {
|
const ANIMATION_CONFIG = {
|
||||||
duration: ANIMATION_DURATION,
|
duration: ANIMATION_DURATION,
|
||||||
// https://easings.net/#easeInOutCubic
|
// https://easings.net/#easeInOutCubic
|
||||||
easing: Easing.bezier(0.645, 0.045, 0.355, 1.0)
|
easing: Easing.bezier(0.645, 0.045, 0.355, 1.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
const ActionSheet = React.memo(forwardRef(({ children, theme }: {children: JSX.Element; theme: string}, ref) => {
|
||||||
const bottomSheetRef = useRef();
|
const bottomSheetRef: any = useRef();
|
||||||
const [data, setData] = useState({});
|
const [data, setData] = useState<IActionSheetData>({} as IActionSheetData);
|
||||||
const [isVisible, setVisible] = useState(false);
|
const [isVisible, setVisible] = useState(false);
|
||||||
const { height } = useDimensions();
|
const { height }: Partial<IDimensionsContextProps> = useDimensions();
|
||||||
const { isLandscape } = useOrientation();
|
const { isLandscape } = useOrientation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
const maxSnap = Math.max(
|
const maxSnap = Math.max(
|
||||||
(
|
|
||||||
height
|
height!
|
||||||
// Items height
|
// Items height
|
||||||
- (ITEM_HEIGHT * (data?.options?.length || 0))
|
- (ITEM_HEIGHT * (data?.options?.length || 0))
|
||||||
// Handle height
|
// Handle height
|
||||||
|
@ -66,8 +67,8 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
- insets.bottom
|
- insets.bottom
|
||||||
// Cancel button height
|
// Cancel button height
|
||||||
- (data?.hasCancel ? CANCEL_HEIGHT : 0)
|
- (data?.hasCancel ? CANCEL_HEIGHT : 0)
|
||||||
),
|
,
|
||||||
MAX_SNAP_HEIGHT
|
MAX_SNAP_HEIGHT,
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -77,7 +78,7 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
* we'll provide more one snap
|
* we'll provide more one snap
|
||||||
* that point 50% of the whole screen
|
* that point 50% of the whole screen
|
||||||
*/
|
*/
|
||||||
const snaps = (height - maxSnap > height * 0.6) && !isLandscape ? [maxSnap, height * 0.5, height] : [maxSnap, height];
|
const snaps: any = (height! - maxSnap > height! * 0.6) && !isLandscape ? [maxSnap, height! * 0.5, height] : [maxSnap, height];
|
||||||
const openedSnapIndex = snaps.length > 2 ? 1 : 0;
|
const openedSnapIndex = snaps.length > 2 ? 1 : 0;
|
||||||
const closedSnapIndex = snaps.length - 1;
|
const closedSnapIndex = snaps.length - 1;
|
||||||
|
|
||||||
|
@ -87,12 +88,12 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
bottomSheetRef.current?.snapTo(closedSnapIndex);
|
bottomSheetRef.current?.snapTo(closedSnapIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const show = (options) => {
|
const show = (options: any) => {
|
||||||
setData(options);
|
setData(options);
|
||||||
toggleVisible();
|
toggleVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBackdropPressed = ({ nativeEvent }) => {
|
const onBackdropPressed = ({ nativeEvent }: any) => {
|
||||||
if (nativeEvent.oldState === State.ACTIVE) {
|
if (nativeEvent.oldState === State.ACTIVE) {
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
@ -120,7 +121,7 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
showActionSheet: show,
|
showActionSheet: show,
|
||||||
hideActionSheet: hide
|
hideActionSheet: hide,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const renderHandle = useCallback(() => (
|
const renderHandle = useCallback(() => (
|
||||||
|
@ -128,7 +129,7 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
<Handle theme={theme} />
|
<Handle theme={theme} />
|
||||||
{isValidElement(data?.customHeader) ? data.customHeader : null}
|
{isValidElement(data?.customHeader) ? data.customHeader : null}
|
||||||
</>
|
</>
|
||||||
));
|
), [theme, data]);
|
||||||
|
|
||||||
const renderFooter = useCallback(() => (data?.hasCancel ? (
|
const renderFooter = useCallback(() => (data?.hasCancel ? (
|
||||||
<Button
|
<Button
|
||||||
|
@ -140,15 +141,15 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
{I18n.t('Cancel')}
|
{I18n.t('Cancel')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
) : null));
|
) : null), [theme, data, hide]);
|
||||||
|
|
||||||
const renderItem = useCallback(({ item }) => <Item item={item} hide={hide} theme={theme} />);
|
const renderItem = useCallback(({ item }) => <Item item={item} hide={hide} theme={theme} />, []);
|
||||||
|
|
||||||
const animatedPosition = React.useRef(new Value(0));
|
const animatedPosition = React.useRef(new Value(0));
|
||||||
const opacity = interpolate(animatedPosition.current, {
|
const opacity = interpolate(animatedPosition.current, {
|
||||||
inputRange: [0, 1],
|
inputRange: [0, 1],
|
||||||
outputRange: [0, themes[theme].backdropOpacity],
|
outputRange: [0, themes[theme].backdropOpacity],
|
||||||
extrapolate: Extrapolate.CLAMP
|
extrapolate: Extrapolate.CLAMP,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -163,8 +164,8 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
styles.backdrop,
|
styles.backdrop,
|
||||||
{
|
{
|
||||||
backgroundColor: themes[theme].backdropColor,
|
backgroundColor: themes[theme].backdropColor,
|
||||||
opacity
|
opacity,
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</TapGestureHandler>
|
</TapGestureHandler>
|
||||||
|
@ -175,18 +176,18 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
snapPoints={snaps}
|
snapPoints={snaps}
|
||||||
initialSnapIndex={closedSnapIndex}
|
initialSnapIndex={closedSnapIndex}
|
||||||
renderHandle={renderHandle}
|
renderHandle={renderHandle}
|
||||||
onSettle={index => (index === closedSnapIndex) && toggleVisible()}
|
onSettle={(index) => (index === closedSnapIndex) && toggleVisible()}
|
||||||
animatedPosition={animatedPosition.current}
|
animatedPosition={animatedPosition.current}
|
||||||
containerStyle={[
|
containerStyle={[
|
||||||
styles.container,
|
styles.container,
|
||||||
{ backgroundColor: themes[theme].focusedBackground },
|
{ backgroundColor: themes[theme].focusedBackground },
|
||||||
(isLandscape || isTablet) && styles.bottomSheet
|
(isLandscape || isTablet) && styles.bottomSheet,
|
||||||
]}
|
] as any}
|
||||||
animationConfig={ANIMATION_CONFIG}
|
animationConfig={ANIMATION_CONFIG}
|
||||||
// FlatList props
|
// FlatList props
|
||||||
data={data?.options}
|
data={data?.options}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={item => item.title}
|
keyExtractor={(item: any) => item.title}
|
||||||
style={{ backgroundColor: themes[theme].focusedBackground }}
|
style={{ backgroundColor: themes[theme].focusedBackground }}
|
||||||
contentContainerStyle={styles.content}
|
contentContainerStyle={styles.content}
|
||||||
ItemSeparatorComponent={List.Separator}
|
ItemSeparatorComponent={List.Separator}
|
||||||
|
@ -200,9 +201,5 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
ActionSheet.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ActionSheet;
|
export default ActionSheet;
|
|
@ -1,15 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
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 '../../constants/colors';
|
||||||
|
|
||||||
export const Handle = React.memo(({ theme }) => (
|
export const Handle = React.memo(({ theme }: {theme: string}) => (
|
||||||
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
|
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'>
|
||||||
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
|
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} />
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
Handle.propTypes = {
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
|
@ -1,13 +1,25 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import styles from './styles';
|
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
export const Item = React.memo(({ item, hide, theme }) => {
|
interface IActionSheetItem {
|
||||||
|
item: {
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
danger: boolean;
|
||||||
|
testID: string;
|
||||||
|
onPress(): void;
|
||||||
|
right: Function;
|
||||||
|
};
|
||||||
|
theme: string
|
||||||
|
hide(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Item = React.memo(({ item, hide, theme }: IActionSheetItem) => {
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
hide();
|
hide();
|
||||||
item?.onPress();
|
item?.onPress();
|
||||||
|
@ -37,15 +49,3 @@ export const Item = React.memo(({ item, hide, theme }) => {
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Item.propTypes = {
|
|
||||||
item: PropTypes.shape({
|
|
||||||
title: PropTypes.string,
|
|
||||||
icon: PropTypes.string,
|
|
||||||
danger: PropTypes.bool,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
right: PropTypes.func,
|
|
||||||
testID: PropTypes.string
|
|
||||||
}),
|
|
||||||
hide: PropTypes.func,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
|
@ -1,45 +0,0 @@
|
||||||
import React, { useRef, useContext, forwardRef } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import ActionSheet from './ActionSheet';
|
|
||||||
import { useTheme } from '../../theme';
|
|
||||||
|
|
||||||
const context = React.createContext({
|
|
||||||
showActionSheet: () => {},
|
|
||||||
hideActionSheet: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const useActionSheet = () => useContext(context);
|
|
||||||
|
|
||||||
const { Provider, Consumer } = context;
|
|
||||||
|
|
||||||
export const withActionSheet = Component => forwardRef((props, ref) => (
|
|
||||||
<Consumer>
|
|
||||||
{contexts => <Component {...props} {...contexts} ref={ref} />}
|
|
||||||
</Consumer>
|
|
||||||
));
|
|
||||||
|
|
||||||
export const ActionSheetProvider = React.memo(({ children }) => {
|
|
||||||
const ref = useRef();
|
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
const getContext = () => ({
|
|
||||||
showActionSheet: (options) => {
|
|
||||||
ref.current?.showActionSheet(options);
|
|
||||||
},
|
|
||||||
hideActionSheet: () => {
|
|
||||||
ref.current?.hideActionSheet();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Provider value={getContext()}>
|
|
||||||
<ActionSheet ref={ref} theme={theme}>
|
|
||||||
{children}
|
|
||||||
</ActionSheet>
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
ActionSheetProvider.propTypes = {
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react';
|
||||||
|
|
||||||
|
import ActionSheet from './ActionSheet';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
|
interface IActionSheetProvider {
|
||||||
|
Provider: any;
|
||||||
|
Consumer: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const context: IActionSheetProvider = React.createContext({
|
||||||
|
showActionSheet: () => {},
|
||||||
|
hideActionSheet: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useActionSheet = () => useContext(context);
|
||||||
|
|
||||||
|
const { Provider, Consumer } = context;
|
||||||
|
|
||||||
|
export const withActionSheet = (Component: React.FC) => forwardRef((props: any, ref: ForwardedRef<any>) => (
|
||||||
|
<Consumer>
|
||||||
|
{(contexts: any) => <Component {...props} {...contexts} ref={ref} />}
|
||||||
|
</Consumer>
|
||||||
|
));
|
||||||
|
|
||||||
|
export const ActionSheetProvider = React.memo(({ children }: {children: JSX.Element}) => {
|
||||||
|
const ref: ForwardedRef<any> = useRef();
|
||||||
|
const { theme }: any = useTheme();
|
||||||
|
|
||||||
|
const getContext = () => ({
|
||||||
|
showActionSheet: (options: any) => {
|
||||||
|
ref.current?.showActionSheet(options);
|
||||||
|
},
|
||||||
|
hideActionSheet: () => {
|
||||||
|
ref.current?.hideActionSheet();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Provider value={getContext()}>
|
||||||
|
<ActionSheet ref={ref} theme={theme}>
|
||||||
|
{children}
|
||||||
|
</ActionSheet>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
});
|
|
@ -8,46 +8,46 @@ export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
borderTopLeftRadius: 16,
|
borderTopLeftRadius: 16,
|
||||||
borderTopRightRadius: 16
|
borderTopRightRadius: 16,
|
||||||
},
|
},
|
||||||
item: {
|
item: {
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
height: ITEM_HEIGHT,
|
height: ITEM_HEIGHT,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexDirection: 'row'
|
flexDirection: 'row',
|
||||||
},
|
},
|
||||||
separator: {
|
separator: {
|
||||||
marginHorizontal: 16
|
marginHorizontal: 16,
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
paddingTop: 16
|
paddingTop: 16,
|
||||||
},
|
},
|
||||||
titleContainer: {
|
titleContainer: {
|
||||||
flex: 1
|
flex: 1,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
marginLeft: 16,
|
marginLeft: 16,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
handle: {
|
handle: {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingBottom: 8
|
paddingBottom: 8,
|
||||||
},
|
},
|
||||||
handleIndicator: {
|
handleIndicator: {
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 4,
|
height: 4,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
margin: 8
|
margin: 8,
|
||||||
},
|
},
|
||||||
backdrop: {
|
backdrop: {
|
||||||
...StyleSheet.absoluteFillObject
|
...StyleSheet.absoluteFillObject,
|
||||||
},
|
},
|
||||||
bottomSheet: {
|
bottomSheet: {
|
||||||
width: '50%',
|
width: '50%',
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
left: '25%'
|
left: '25%',
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
marginHorizontal: 16,
|
marginHorizontal: 16,
|
||||||
|
@ -55,14 +55,14 @@ export default StyleSheet.create({
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
height: ITEM_HEIGHT,
|
height: ITEM_HEIGHT,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
marginBottom: 12
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textMedium,
|
...sharedStyles.textMedium,
|
||||||
...sharedStyles.textAlignCenter
|
...sharedStyles.textAlignCenter,
|
||||||
},
|
},
|
||||||
rightContainer: {
|
rightContainer: {
|
||||||
paddingLeft: 12
|
paddingLeft: 12,
|
||||||
}
|
},
|
||||||
});
|
});
|
|
@ -1,12 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ActivityIndicator, StyleSheet } from 'react-native';
|
import { ActivityIndicator, ActivityIndicatorProps, StyleSheet } from 'react-native';
|
||||||
import { PropTypes } from 'prop-types';
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
|
|
||||||
|
type TTheme = 'light' | 'dark' | 'black' | string;
|
||||||
|
|
||||||
|
interface IActivityIndicator extends ActivityIndicatorProps{
|
||||||
|
theme?: TTheme;
|
||||||
|
absolute?: boolean;
|
||||||
|
props?: object;
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
indicator: {
|
indicator: {
|
||||||
padding: 16,
|
padding: 16,
|
||||||
flex: 1
|
flex: 1,
|
||||||
},
|
},
|
||||||
absolute: {
|
absolute: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -15,11 +23,11 @@ const styles = StyleSheet.create({
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const RCActivityIndicator = ({ theme, absolute, ...props }) => (
|
const RCActivityIndicator = ({ theme = 'light', absolute, ...props }: IActivityIndicator) => (
|
||||||
<ActivityIndicator
|
<ActivityIndicator
|
||||||
style={[styles.indicator, absolute && styles.absolute]}
|
style={[styles.indicator, absolute && styles.absolute]}
|
||||||
color={themes[theme].auxiliaryText}
|
color={themes[theme].auxiliaryText}
|
||||||
|
@ -27,14 +35,4 @@ const RCActivityIndicator = ({ theme, absolute, ...props }) => (
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
RCActivityIndicator.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
absolute: PropTypes.bool,
|
|
||||||
props: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
RCActivityIndicator.defaultProps = {
|
|
||||||
theme: 'light'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RCActivityIndicator;
|
export default RCActivityIndicator;
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, View, Text } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
@ -10,25 +9,21 @@ import I18n from '../i18n';
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end'
|
justifyContent: 'flex-end',
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular,
|
||||||
fontSize: 13
|
fontSize: 13,
|
||||||
},
|
},
|
||||||
bold: {
|
bold: {
|
||||||
...sharedStyles.textSemibold
|
...sharedStyles.textSemibold,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const AppVersion = React.memo(({ theme }) => (
|
const AppVersion = React.memo(({ theme }: {theme: string}) => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>{I18n.t('Version_no', { version: '' })}<Text style={styles.bold}>{getReadableVersion}</Text></Text>
|
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>{I18n.t('Version_no', { version: '' })}<Text style={styles.bold}>{getReadableVersion}</Text></Text>
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
AppVersion.propTypes = {
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AppVersion;
|
export default AppVersion;
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
@ -7,15 +6,12 @@ import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
||||||
|
|
||||||
import { avatarURL } from '../../utils/avatar';
|
import { avatarURL } from '../../utils/avatar';
|
||||||
import Emoji from '../markdown/Emoji';
|
import Emoji from '../markdown/Emoji';
|
||||||
|
import { IAvatar } from './interfaces';
|
||||||
|
|
||||||
const Avatar = React.memo(({
|
const Avatar = React.memo(({
|
||||||
text,
|
|
||||||
size,
|
|
||||||
server,
|
server,
|
||||||
borderRadius,
|
|
||||||
style,
|
style,
|
||||||
avatar,
|
avatar,
|
||||||
type,
|
|
||||||
children,
|
children,
|
||||||
user,
|
user,
|
||||||
onPress,
|
onPress,
|
||||||
|
@ -26,8 +22,12 @@ const Avatar = React.memo(({
|
||||||
isStatic,
|
isStatic,
|
||||||
rid,
|
rid,
|
||||||
blockUnauthenticatedAccess,
|
blockUnauthenticatedAccess,
|
||||||
serverVersion
|
serverVersion,
|
||||||
}) => {
|
text,
|
||||||
|
size = 25,
|
||||||
|
borderRadius = 4,
|
||||||
|
type = 'd',
|
||||||
|
}: Partial<IAvatar>) => {
|
||||||
if ((!text && !avatar && !emoji && !rid) || !server) {
|
if ((!text && !avatar && !emoji && !rid) || !server) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ const Avatar = React.memo(({
|
||||||
const avatarStyle = {
|
const avatarStyle = {
|
||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
borderRadius
|
borderRadius,
|
||||||
};
|
};
|
||||||
|
|
||||||
let image;
|
let image;
|
||||||
|
@ -63,7 +63,7 @@ const Avatar = React.memo(({
|
||||||
avatarETag,
|
avatarETag,
|
||||||
serverVersion,
|
serverVersion,
|
||||||
rid,
|
rid,
|
||||||
blockUnauthenticatedAccess
|
blockUnauthenticatedAccess,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ const Avatar = React.memo(({
|
||||||
source={{
|
source={{
|
||||||
uri,
|
uri,
|
||||||
headers: RocketChatSettings.customHeaders,
|
headers: RocketChatSettings.customHeaders,
|
||||||
priority: FastImage.priority.high
|
priority: FastImage.priority.high,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -96,35 +96,4 @@ const Avatar = React.memo(({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Avatar.propTypes = {
|
|
||||||
server: PropTypes.string,
|
|
||||||
style: PropTypes.any,
|
|
||||||
text: PropTypes.string,
|
|
||||||
avatar: PropTypes.string,
|
|
||||||
emoji: PropTypes.string,
|
|
||||||
size: PropTypes.number,
|
|
||||||
borderRadius: PropTypes.number,
|
|
||||||
type: PropTypes.string,
|
|
||||||
children: PropTypes.object,
|
|
||||||
user: PropTypes.shape({
|
|
||||||
id: PropTypes.string,
|
|
||||||
token: PropTypes.string
|
|
||||||
}),
|
|
||||||
theme: PropTypes.string,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
getCustomEmoji: PropTypes.func,
|
|
||||||
avatarETag: PropTypes.string,
|
|
||||||
isStatic: PropTypes.bool,
|
|
||||||
rid: PropTypes.string,
|
|
||||||
blockUnauthenticatedAccess: PropTypes.bool,
|
|
||||||
serverVersion: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
Avatar.defaultProps = {
|
|
||||||
text: '',
|
|
||||||
size: 25,
|
|
||||||
type: 'd',
|
|
||||||
borderRadius: 4
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Avatar;
|
export default Avatar;
|
|
@ -1,27 +1,23 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
|
import { IAvatar } from './interfaces';
|
||||||
|
|
||||||
class AvatarContainer extends React.Component {
|
class AvatarContainer extends React.Component<Partial<IAvatar>, any> {
|
||||||
static propTypes = {
|
private mounted: boolean;
|
||||||
rid: PropTypes.string,
|
|
||||||
text: PropTypes.string,
|
private subscription!: any;
|
||||||
type: PropTypes.string,
|
|
||||||
blockUnauthenticatedAccess: PropTypes.bool,
|
|
||||||
serverVersion: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
text: '',
|
text: '',
|
||||||
type: 'd'
|
type: 'd',
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: Partial<IAvatar>) {
|
||||||
super(props);
|
super(props);
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
this.state = { avatarETag: '' };
|
this.state = { avatarETag: '' };
|
||||||
|
@ -32,7 +28,7 @@ class AvatarContainer extends React.Component {
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps: any) {
|
||||||
const { text, type } = this.props;
|
const { text, type } = this.props;
|
||||||
if (prevProps.text !== text || prevProps.type !== type) {
|
if (prevProps.text !== text || prevProps.type !== type) {
|
||||||
this.init();
|
this.init();
|
||||||
|
@ -59,7 +55,7 @@ class AvatarContainer extends React.Component {
|
||||||
try {
|
try {
|
||||||
if (this.isDirect) {
|
if (this.isDirect) {
|
||||||
const { text } = this.props;
|
const { text } = this.props;
|
||||||
const [user] = await usersCollection.query(Q.where('username', text)).fetch();
|
const [user] = await usersCollection.query(Q.where('username', text!)).fetch();
|
||||||
record = user;
|
record = user;
|
||||||
} else {
|
} else {
|
||||||
const { rid } = this.props;
|
const { rid } = this.props;
|
||||||
|
@ -71,11 +67,12 @@ class AvatarContainer extends React.Component {
|
||||||
|
|
||||||
if (record) {
|
if (record) {
|
||||||
const observable = record.observe();
|
const observable = record.observe();
|
||||||
this.subscription = observable.subscribe((r) => {
|
this.subscription = observable.subscribe((r: any) => {
|
||||||
const { avatarETag } = r;
|
const { avatarETag } = r;
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ avatarETag });
|
this.setState({ avatarETag });
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
this.state.avatarETag = avatarETag;
|
this.state.avatarETag = avatarETag;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -95,13 +92,13 @@ class AvatarContainer extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
server: state.share.server.server || state.server.server,
|
server: state.share.server.server || state.server.server,
|
||||||
serverVersion: state.share.server.version || state.server.version,
|
serverVersion: state.share.server.version || state.server.version,
|
||||||
blockUnauthenticatedAccess:
|
blockUnauthenticatedAccess:
|
||||||
state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess
|
state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess
|
||||||
?? state.settings.Accounts_AvatarBlockUnauthenticatedAccess
|
?? state.settings.Accounts_AvatarBlockUnauthenticatedAccess
|
||||||
?? true
|
?? true,
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps)(AvatarContainer);
|
export default connect(mapStateToProps)(AvatarContainer);
|
|
@ -0,0 +1,23 @@
|
||||||
|
export interface IAvatar {
|
||||||
|
server: string;
|
||||||
|
style: any,
|
||||||
|
text: string;
|
||||||
|
avatar: string;
|
||||||
|
emoji: string;
|
||||||
|
size: number;
|
||||||
|
borderRadius: number;
|
||||||
|
type: string;
|
||||||
|
children: JSX.Element;
|
||||||
|
user: {
|
||||||
|
id: string;
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
theme: string;
|
||||||
|
onPress(): void;
|
||||||
|
getCustomEmoji(): any;
|
||||||
|
avatarETag: string;
|
||||||
|
isStatic: boolean;
|
||||||
|
rid: string;
|
||||||
|
blockUnauthenticatedAccess: boolean;
|
||||||
|
serverVersion: string;
|
||||||
|
}
|
|
@ -2,9 +2,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { storiesOf } from '@storybook/react-native';
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
|
||||||
import BackgroundContainer from '.';
|
|
||||||
import { ThemeContext } from '../../theme';
|
import { ThemeContext } from '../../theme';
|
||||||
import { longText } from '../../../storybook/utils';
|
import { longText } from '../../../storybook/utils';
|
||||||
|
import BackgroundContainer from '.';
|
||||||
|
|
||||||
const stories = storiesOf('BackgroundContainer', module);
|
const stories = storiesOf('BackgroundContainer', module);
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { ActivityIndicator, ImageBackground, StyleSheet, Text, View } from 'react-native';
|
||||||
ImageBackground, StyleSheet, Text, View, ActivityIndicator
|
|
||||||
} from 'react-native';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
|
||||||
|
interface IBackgroundContainer {
|
||||||
|
text: string;
|
||||||
|
theme: string;
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1
|
flex: 1,
|
||||||
},
|
},
|
||||||
image: {
|
image: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
position: 'absolute'
|
position: 'absolute',
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -25,21 +28,17 @@ const styles = StyleSheet.create({
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
paddingHorizontal: 24,
|
paddingHorizontal: 24,
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular,
|
||||||
...sharedStyles.textAlignCenter
|
...sharedStyles.textAlignCenter,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const BackgroundContainer = ({ theme, text, loading }) => (
|
const BackgroundContainer = ({ theme, text, loading }: IBackgroundContainer) => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ImageBackground source={{ uri: `message_empty_${ theme }` }} style={styles.image} />
|
<ImageBackground source={{ uri: `message_empty_${ theme }` }} style={styles.image} />
|
||||||
{text ? <Text style={[styles.text, { color: themes[theme].auxiliaryTintColor }]}>{text}</Text> : null}
|
{text ? <Text style={[styles.text, { color: themes[theme].auxiliaryTintColor }]}>{text}</Text> : null}
|
||||||
|
{/* @ts-ignore*/}
|
||||||
{loading ? <ActivityIndicator style={[styles.text, { color: themes[theme].auxiliaryTintColor }]} /> : null}
|
{loading ? <ActivityIndicator style={[styles.text, { color: themes[theme].auxiliaryTintColor }]} /> : null}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
BackgroundContainer.propTypes = {
|
|
||||||
text: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
loading: PropTypes.bool
|
|
||||||
};
|
|
||||||
export default withTheme(BackgroundContainer);
|
export default withTheme(BackgroundContainer);
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
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';
|
||||||
|
|
||||||
|
@ -7,44 +6,45 @@ import { themes } from '../../constants/colors';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import ActivityIndicator from '../ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
|
interface IButtonProps {
|
||||||
|
title: string;
|
||||||
|
type: string
|
||||||
|
onPress(): void;
|
||||||
|
disabled: boolean;
|
||||||
|
backgroundColor: string
|
||||||
|
loading: boolean;
|
||||||
|
theme: string
|
||||||
|
color: string
|
||||||
|
fontSize: any
|
||||||
|
style: any
|
||||||
|
testID: string;
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
height: 48,
|
height: 48,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
marginBottom: 12
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textMedium,
|
...sharedStyles.textMedium,
|
||||||
...sharedStyles.textAlignCenter
|
...sharedStyles.textAlignCenter,
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
opacity: 0.3
|
opacity: 0.3,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class Button extends React.PureComponent {
|
export default class Button extends React.PureComponent<Partial<IButtonProps>, any> {
|
||||||
static propTypes = {
|
|
||||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
|
||||||
type: PropTypes.string,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
backgroundColor: PropTypes.string,
|
|
||||||
loading: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
color: PropTypes.string,
|
|
||||||
fontSize: PropTypes.string,
|
|
||||||
style: PropTypes.any
|
|
||||||
}
|
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
title: 'Press me!',
|
title: 'Press me!',
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
onPress: () => alert('It works!'),
|
onPress: () => alert('It works!'),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
loading: false
|
loading: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -53,7 +53,7 @@ export default class Button extends React.PureComponent {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const isPrimary = type === 'primary';
|
const isPrimary = type === 'primary';
|
||||||
|
|
||||||
let textColor = isPrimary ? themes[theme].buttonText : themes[theme].bodyText;
|
let textColor = isPrimary ? themes[theme!].buttonText : themes[theme!].bodyText;
|
||||||
if (color) {
|
if (color) {
|
||||||
textColor = color;
|
textColor = color;
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,9 @@ export default class Button extends React.PureComponent {
|
||||||
styles.container,
|
styles.container,
|
||||||
backgroundColor
|
backgroundColor
|
||||||
? { backgroundColor }
|
? { backgroundColor }
|
||||||
: { backgroundColor: isPrimary ? themes[theme].actionTintColor : themes[theme].backgroundColor },
|
: { backgroundColor: isPrimary ? themes[theme!].actionTintColor : themes[theme!].backgroundColor },
|
||||||
disabled && styles.disabled,
|
disabled && styles.disabled,
|
||||||
style
|
style,
|
||||||
]}
|
]}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
|
@ -80,7 +80,7 @@ export default class Button extends React.PureComponent {
|
||||||
style={[
|
style={[
|
||||||
styles.text,
|
styles.text,
|
||||||
{ color: textColor },
|
{ color: textColor },
|
||||||
fontSize && { fontSize }
|
fontSize && { fontSize },
|
||||||
]}
|
]}
|
||||||
accessibilityLabel={title}
|
accessibilityLabel={title}
|
||||||
>
|
>
|
|
@ -1,23 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { StyleSheet } from 'react-native';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { CustomIcon } from '../lib/Icons';
|
|
||||||
import { themes } from '../constants/colors';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
icon: {
|
|
||||||
width: 22,
|
|
||||||
height: 22,
|
|
||||||
marginHorizontal: 15
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const Check = React.memo(({ theme, style }) => <CustomIcon style={[styles.icon, style]} color={themes[theme].tintColor} size={22} name='check' />);
|
|
||||||
|
|
||||||
Check.propTypes = {
|
|
||||||
style: PropTypes.object,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Check;
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { CustomIcon } from '../lib/Icons';
|
||||||
|
import { themes } from '../constants/colors';
|
||||||
|
|
||||||
|
interface ICheck {
|
||||||
|
style?: object;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
icon: {
|
||||||
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
marginHorizontal: 15,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Check = React.memo(({ theme, style }: ICheck) => <CustomIcon style={[styles.icon, style]} color={themes[theme].tintColor} size={22} name='check' />);
|
||||||
|
|
||||||
|
export default Check;
|
|
@ -1,13 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const CustomEmoji = React.memo(({ baseUrl, emoji, style }) => (
|
import { ICustomEmoji } from './interfaces';
|
||||||
|
|
||||||
|
const CustomEmoji = React.memo(({ baseUrl, emoji, style }: ICustomEmoji) => (
|
||||||
<FastImage
|
<FastImage
|
||||||
style={style}
|
style={style}
|
||||||
source={{
|
source={{
|
||||||
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,
|
||||||
}}
|
}}
|
||||||
resizeMode={FastImage.resizeMode.contain}
|
resizeMode={FastImage.resizeMode.contain}
|
||||||
/>
|
/>
|
||||||
|
@ -17,10 +18,4 @@ const CustomEmoji = React.memo(({ baseUrl, emoji, style }) => (
|
||||||
return prevEmoji === nextEmoji;
|
return prevEmoji === nextEmoji;
|
||||||
});
|
});
|
||||||
|
|
||||||
CustomEmoji.propTypes = {
|
|
||||||
baseUrl: PropTypes.string.isRequired,
|
|
||||||
emoji: PropTypes.object.isRequired,
|
|
||||||
style: PropTypes.any
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomEmoji;
|
export default CustomEmoji;
|
|
@ -1,15 +1,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { FlatList, Text, TouchableOpacity } from 'react-native';
|
||||||
import { Text, TouchableOpacity, FlatList } from 'react-native';
|
|
||||||
|
|
||||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import CustomEmoji from './CustomEmoji';
|
import CustomEmoji from './CustomEmoji';
|
||||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||||
|
import { IEmoji, IEmojiCategory } from './interfaces';
|
||||||
|
|
||||||
const EMOJI_SIZE = 50;
|
const EMOJI_SIZE = 50;
|
||||||
|
|
||||||
const renderEmoji = (emoji, size, baseUrl) => {
|
const renderEmoji = (emoji: IEmoji, size: number, baseUrl: string) => {
|
||||||
if (emoji && emoji.isCustom) {
|
if (emoji && emoji.isCustom) {
|
||||||
return <CustomEmoji style={[styles.customCategoryEmoji, { height: size - 16, width: size - 16 }]} emoji={emoji} baseUrl={baseUrl} />;
|
return <CustomEmoji style={[styles.customCategoryEmoji, { height: size - 16, width: size - 16 }]} emoji={emoji} baseUrl={baseUrl} />;
|
||||||
}
|
}
|
||||||
|
@ -20,25 +20,17 @@ const renderEmoji = (emoji, size, baseUrl) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
class EmojiCategory extends React.Component {
|
class EmojiCategory extends React.Component<Partial<IEmojiCategory>> {
|
||||||
static propTypes = {
|
renderItem(emoji: any) {
|
||||||
baseUrl: PropTypes.string.isRequired,
|
|
||||||
emojis: PropTypes.any,
|
|
||||||
onEmojiSelected: PropTypes.func,
|
|
||||||
emojisPerRow: PropTypes.number,
|
|
||||||
width: PropTypes.number
|
|
||||||
}
|
|
||||||
|
|
||||||
renderItem(emoji) {
|
|
||||||
const { baseUrl, onEmojiSelected } = this.props;
|
const { baseUrl, onEmojiSelected } = this.props;
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
key={emoji && emoji.isCustom ? emoji.content : emoji}
|
key={emoji && emoji.isCustom ? emoji.content : emoji}
|
||||||
onPress={() => onEmojiSelected(emoji)}
|
onPress={() => onEmojiSelected!(emoji)}
|
||||||
testID={`reaction-picker-${ emoji && emoji.isCustom ? emoji.content : emoji }`}
|
testID={`reaction-picker-${ emoji && emoji.isCustom ? emoji.content : emoji }`}
|
||||||
>
|
>
|
||||||
{renderEmoji(emoji, EMOJI_SIZE, baseUrl)}
|
{renderEmoji(emoji, EMOJI_SIZE, baseUrl!)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -54,11 +46,12 @@ class EmojiCategory extends React.Component {
|
||||||
const marginHorizontal = (width - (numColumns * EMOJI_SIZE)) / 2;
|
const marginHorizontal = (width - (numColumns * EMOJI_SIZE)) / 2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// @ts-ignore
|
||||||
<FlatList
|
<FlatList
|
||||||
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 }`}
|
||||||
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}
|
||||||
renderItem={({ item }) => this.renderItem(item)}
|
renderItem={({ item }) => this.renderItem(item)}
|
|
@ -1,19 +1,19 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { View, TouchableOpacity, Text } from 'react-native';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
|
||||||
export default class TabBar extends React.Component {
|
interface ITabBarProps {
|
||||||
static propTypes = {
|
goToPage: Function;
|
||||||
goToPage: PropTypes.func,
|
activeTab: number,
|
||||||
activeTab: PropTypes.number,
|
tabs: [],
|
||||||
tabs: PropTypes.array,
|
tabEmojiStyle: object,
|
||||||
tabEmojiStyle: PropTypes.object,
|
theme: string
|
||||||
theme: PropTypes.string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
export default class TabBar extends React.Component<Partial<ITabBarProps>> {
|
||||||
|
shouldComponentUpdate(nextProps: any) {
|
||||||
const { activeTab, theme } = this.props;
|
const { activeTab, theme } = this.props;
|
||||||
if (nextProps.activeTab !== activeTab) {
|
if (nextProps.activeTab !== activeTab) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -25,22 +25,20 @@ export default class TabBar extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { tabs, goToPage, tabEmojiStyle, activeTab, theme } = this.props;
|
||||||
tabs, goToPage, tabEmojiStyle, activeTab, theme
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.tabsContainer}>
|
<View style={styles.tabsContainer}>
|
||||||
{tabs.map((tab, i) => (
|
{tabs!.map((tab, i) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
key={tab}
|
key={tab}
|
||||||
onPress={() => goToPage(i)}
|
onPress={() => goToPage!(i)}
|
||||||
style={styles.tab}
|
style={styles.tab}
|
||||||
testID={`reaction-picker-${ tab }`}
|
testID={`reaction-picker-${ tab }`}
|
||||||
>
|
>
|
||||||
<Text style={[styles.tabEmoji, tabEmojiStyle]}>{tab}</Text>
|
<Text style={[styles.tabEmoji, tabEmojiStyle]}>{tab}</Text>
|
||||||
{activeTab === i ? <View style={[styles.activeTabLine, { backgroundColor: themes[theme].tintColor }]} /> : <View style={styles.tabLine} />}
|
{activeTab === i ? <View style={[styles.activeTabLine, { backgroundColor: themes[theme!].tintColor }]} /> : <View style={styles.tabLine} />}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
|
@ -2,43 +2,43 @@ const list = ['frequentlyUsed', 'custom', 'people', 'nature', 'food', 'activity'
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
tabLabel: '🕒',
|
tabLabel: '🕒',
|
||||||
category: list[0]
|
category: list[0],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '🚀',
|
tabLabel: '🚀',
|
||||||
category: list[1]
|
category: list[1],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '😃',
|
tabLabel: '😃',
|
||||||
category: list[2]
|
category: list[2],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '🐶',
|
tabLabel: '🐶',
|
||||||
category: list[3]
|
category: list[3],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '🍔',
|
tabLabel: '🍔',
|
||||||
category: list[4]
|
category: list[4],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '⚽',
|
tabLabel: '⚽',
|
||||||
category: list[5]
|
category: list[5],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '🚌',
|
tabLabel: '🚌',
|
||||||
category: list[6]
|
category: list[6],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '💡',
|
tabLabel: '💡',
|
||||||
category: list[7]
|
category: list[7],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '💛',
|
tabLabel: '💛',
|
||||||
category: list[8]
|
category: list[8],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tabLabel: '🏁',
|
tabLabel: '🏁',
|
||||||
category: list[9]
|
category: list[9],
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
export default { list, tabs };
|
export default { list, tabs };
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -18,35 +17,46 @@ import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
|
import { IEmoji } from './interfaces';
|
||||||
|
|
||||||
const scrollProps = {
|
const scrollProps = {
|
||||||
keyboardShouldPersistTaps: 'always',
|
keyboardShouldPersistTaps: 'always',
|
||||||
keyboardDismissMode: 'none'
|
keyboardDismissMode: 'none',
|
||||||
};
|
};
|
||||||
|
|
||||||
class EmojiPicker extends Component {
|
interface IEmojiPickerProps {
|
||||||
static propTypes = {
|
isMessageContainsOnlyEmoji: boolean;
|
||||||
baseUrl: PropTypes.string.isRequired,
|
getCustomEmoji?: Function;
|
||||||
customEmojis: PropTypes.object,
|
baseUrl: string;
|
||||||
onEmojiSelected: PropTypes.func,
|
customEmojis?: any;
|
||||||
tabEmojiStyle: PropTypes.object,
|
style: object;
|
||||||
theme: PropTypes.string
|
theme?: string;
|
||||||
};
|
onEmojiSelected?: Function;
|
||||||
|
tabEmojiStyle?: object;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
interface IEmojiPickerState {
|
||||||
|
frequentlyUsed: [];
|
||||||
|
customEmojis: any;
|
||||||
|
show: boolean;
|
||||||
|
width: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
|
||||||
|
constructor(props: IEmojiPickerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
const customEmojis = Object.keys(props.customEmojis)
|
const customEmojis = Object.keys(props.customEmojis)
|
||||||
.filter(item => item === props.customEmojis[item].name)
|
.filter((item) => item === props.customEmojis[item].name)
|
||||||
.map(item => ({
|
.map((item) => ({
|
||||||
content: props.customEmojis[item].name,
|
content: props.customEmojis[item].name,
|
||||||
extension: props.customEmojis[item].extension,
|
extension: props.customEmojis[item].extension,
|
||||||
isCustom: true
|
isCustom: true,
|
||||||
}));
|
}));
|
||||||
this.state = {
|
this.state = {
|
||||||
frequentlyUsed: [],
|
frequentlyUsed: [],
|
||||||
customEmojis,
|
customEmojis,
|
||||||
show: false,
|
show: false,
|
||||||
width: null
|
width: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +65,7 @@ class EmojiPicker extends Component {
|
||||||
this.setState({ show: true });
|
this.setState({ show: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps: any, nextState: any) {
|
||||||
const { frequentlyUsed, show, width } = this.state;
|
const { frequentlyUsed, show, width } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
|
@ -73,19 +83,19 @@ class EmojiPicker extends Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmojiSelected = (emoji) => {
|
onEmojiSelected = (emoji: IEmoji) => {
|
||||||
try {
|
try {
|
||||||
const { onEmojiSelected } = this.props;
|
const { onEmojiSelected } = this.props;
|
||||||
if (emoji.isCustom) {
|
if (emoji.isCustom) {
|
||||||
this._addFrequentlyUsed({
|
this._addFrequentlyUsed({
|
||||||
content: emoji.content, extension: emoji.extension, isCustom: true
|
content: emoji.content, extension: emoji.extension, isCustom: true,
|
||||||
});
|
});
|
||||||
onEmojiSelected(`:${ emoji.content }:`);
|
onEmojiSelected!(`:${ emoji.content }:`);
|
||||||
} else {
|
} else {
|
||||||
const content = emoji;
|
const content = emoji;
|
||||||
this._addFrequentlyUsed({ content, isCustom: false });
|
this._addFrequentlyUsed({ content, isCustom: false });
|
||||||
const shortname = `:${ emoji }:`;
|
const shortname = `:${ emoji }:`;
|
||||||
onEmojiSelected(shortnameToUnicode(shortname), shortname);
|
onEmojiSelected!(shortnameToUnicode(shortname), shortname);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -93,10 +103,10 @@ class EmojiPicker extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/sort-comp
|
// eslint-disable-next-line react/sort-comp
|
||||||
_addFrequentlyUsed = protectedFunction(async(emoji) => {
|
_addFrequentlyUsed = protectedFunction(async (emoji: IEmoji) => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||||
let freqEmojiRecord;
|
let freqEmojiRecord: any;
|
||||||
try {
|
try {
|
||||||
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -105,11 +115,11 @@ class EmojiPicker extends Component {
|
||||||
|
|
||||||
await db.action(async () => {
|
await db.action(async () => {
|
||||||
if (freqEmojiRecord) {
|
if (freqEmojiRecord) {
|
||||||
await freqEmojiRecord.update((f) => {
|
await freqEmojiRecord.update((f: any) => {
|
||||||
f.count += 1;
|
f.count += 1;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await freqEmojiCollection.create((f) => {
|
await freqEmojiCollection.create((f: any) => {
|
||||||
f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema);
|
f._raw = sanitizedRaw({ id: emoji.content }, freqEmojiCollection.schema);
|
||||||
Object.assign(f, emoji);
|
Object.assign(f, emoji);
|
||||||
f.count = 1;
|
f.count = 1;
|
||||||
|
@ -121,8 +131,8 @@ class EmojiPicker extends Component {
|
||||||
updateFrequentlyUsed = async () => {
|
updateFrequentlyUsed = async () => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
||||||
let frequentlyUsed = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
let frequentlyUsed: any = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
||||||
frequentlyUsed = frequentlyUsed.map((item) => {
|
frequentlyUsed = frequentlyUsed.map((item: IEmoji) => {
|
||||||
if (item.isCustom) {
|
if (item.isCustom) {
|
||||||
return { content: item.content, extension: item.extension, isCustom: item.isCustom };
|
return { content: item.content, extension: item.extension, isCustom: item.isCustom };
|
||||||
}
|
}
|
||||||
|
@ -131,9 +141,9 @@ class EmojiPicker extends Component {
|
||||||
this.setState({ frequentlyUsed });
|
this.setState({ frequentlyUsed });
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayout = ({ nativeEvent: { layout: { width } } }) => this.setState({ width });
|
onLayout = ({ nativeEvent: { layout: { width } } }: any) => this.setState({ width });
|
||||||
|
|
||||||
renderCategory(category, i, label) {
|
renderCategory(category: any, i: number, label: string) {
|
||||||
const { frequentlyUsed, customEmojis, width } = this.state;
|
const { frequentlyUsed, customEmojis, width } = this.state;
|
||||||
const { baseUrl } = this.props;
|
const { baseUrl } = this.props;
|
||||||
|
|
||||||
|
@ -148,9 +158,9 @@ class EmojiPicker extends Component {
|
||||||
return (
|
return (
|
||||||
<EmojiCategory
|
<EmojiCategory
|
||||||
emojis={emojis}
|
emojis={emojis}
|
||||||
onEmojiSelected={emoji => this.onEmojiSelected(emoji)}
|
onEmojiSelected={(emoji) => this.onEmojiSelected(emoji)}
|
||||||
style={styles.categoryContainer}
|
style={styles.categoryContainer}
|
||||||
width={width}
|
width={width!}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
tabLabel={label}
|
tabLabel={label}
|
||||||
/>
|
/>
|
||||||
|
@ -168,15 +178,15 @@ class EmojiPicker extends Component {
|
||||||
<View onLayout={this.onLayout} style={{ flex: 1 }}>
|
<View onLayout={this.onLayout} style={{ flex: 1 }}>
|
||||||
<ScrollableTabView
|
<ScrollableTabView
|
||||||
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} theme={theme} />}
|
renderTabBar={() => <TabBar tabEmojiStyle={tabEmojiStyle} theme={theme} />}
|
||||||
|
/* @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) ? null // when no frequentlyUsed don't show the tab
|
i === 0 && frequentlyUsed.length === 0 ? null // when no frequentlyUsed don't show the tab
|
||||||
: (
|
: this.renderCategory(tab.category, i, tab.tabLabel)
|
||||||
this.renderCategory(tab.category, i, tab.tabLabel)
|
))
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
</ScrollableTabView>
|
</ScrollableTabView>
|
||||||
</View>
|
</View>
|
||||||
|
@ -184,8 +194,8 @@ class EmojiPicker extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
customEmojis: state.customEmojis
|
customEmojis: state.customEmojis,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(EmojiPicker));
|
export default connect(mapStateToProps)(withTheme(EmojiPicker));
|
|
@ -0,0 +1,22 @@
|
||||||
|
export interface IEmoji {
|
||||||
|
content: any;
|
||||||
|
name: string;
|
||||||
|
extension: any;
|
||||||
|
isCustom: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICustomEmoji {
|
||||||
|
baseUrl: string,
|
||||||
|
emoji: IEmoji,
|
||||||
|
style: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEmojiCategory {
|
||||||
|
baseUrl: string;
|
||||||
|
emojis: IEmoji[];
|
||||||
|
onEmojiSelected: Function;
|
||||||
|
emojisPerRow: number;
|
||||||
|
width: number;
|
||||||
|
style: any;
|
||||||
|
tabLabel: string;
|
||||||
|
}
|
|
@ -4,29 +4,29 @@ import sharedStyles from '../../views/Styles';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1
|
flex: 1,
|
||||||
},
|
},
|
||||||
tabsContainer: {
|
tabsContainer: {
|
||||||
height: 45,
|
height: 45,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
paddingTop: 5
|
paddingTop: 5,
|
||||||
},
|
},
|
||||||
tab: {
|
tab: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingBottom: 10
|
paddingBottom: 10,
|
||||||
},
|
},
|
||||||
tabEmoji: {
|
tabEmoji: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: 'black'
|
color: 'black',
|
||||||
},
|
},
|
||||||
activeTabLine: {
|
activeTabLine: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
height: 2,
|
height: 2,
|
||||||
bottom: 0
|
bottom: 0,
|
||||||
},
|
},
|
||||||
tabLine: {
|
tabLine: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -34,25 +34,25 @@ export default StyleSheet.create({
|
||||||
right: 0,
|
right: 0,
|
||||||
height: 2,
|
height: 2,
|
||||||
backgroundColor: 'rgba(0,0,0,0.05)',
|
backgroundColor: 'rgba(0,0,0,0.05)',
|
||||||
bottom: 0
|
bottom: 0,
|
||||||
},
|
},
|
||||||
categoryContainer: {
|
categoryContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'flex-start'
|
alignItems: 'flex-start',
|
||||||
},
|
},
|
||||||
categoryInner: {
|
categoryInner: {
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
flex: 1
|
flex: 1,
|
||||||
},
|
},
|
||||||
categoryEmoji: {
|
categoryEmoji: {
|
||||||
...sharedStyles.textAlignCenter,
|
...sharedStyles.textAlignCenter,
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
color: '#ffffff'
|
color: '#ffffff',
|
||||||
},
|
},
|
||||||
customCategoryEmoji: {
|
customCategoryEmoji: {
|
||||||
margin: 8
|
margin: 8,
|
||||||
}
|
},
|
||||||
});
|
});
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
@ -11,27 +10,33 @@ import AppVersion from './AppVersion';
|
||||||
import { isTablet } from '../utils/deviceInfo';
|
import { isTablet } from '../utils/deviceInfo';
|
||||||
import SafeAreaView from './SafeAreaView';
|
import SafeAreaView from './SafeAreaView';
|
||||||
|
|
||||||
|
interface IFormContainer {
|
||||||
|
theme: string;
|
||||||
|
testID: string;
|
||||||
|
children: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
scrollView: {
|
scrollView: {
|
||||||
minHeight: '100%'
|
minHeight: '100%',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const FormContainerInner = ({ children }) => (
|
export const FormContainerInner = ({ children }: {children: JSX.Element}) => (
|
||||||
<View style={[sharedStyles.container, isTablet && sharedStyles.tabletScreenContent]}>
|
<View style={[sharedStyles.container, isTablet && sharedStyles.tabletScreenContent]}>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
const FormContainer = ({
|
const FormContainer = ({ children, theme, testID, ...props }: IFormContainer) => (
|
||||||
children, theme, testID, ...props
|
// @ts-ignore
|
||||||
}) => (
|
|
||||||
<KeyboardView
|
<KeyboardView
|
||||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||||
contentContainerStyle={sharedStyles.container}
|
contentContainerStyle={sharedStyles.container}
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
|
{/* @ts-ignore*/}
|
||||||
<ScrollView
|
<ScrollView
|
||||||
style={sharedStyles.container}
|
style={sharedStyles.container}
|
||||||
contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}
|
contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}
|
||||||
|
@ -46,14 +51,4 @@ const FormContainer = ({
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
);
|
);
|
||||||
|
|
||||||
FormContainer.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
testID: PropTypes.string,
|
|
||||||
children: PropTypes.element
|
|
||||||
};
|
|
||||||
|
|
||||||
FormContainerInner.propTypes = {
|
|
||||||
children: PropTypes.element
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FormContainer;
|
export default FormContainer;
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
import { themedHeader } from '../../utils/navigation';
|
||||||
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
||||||
|
@ -10,20 +10,27 @@ import { withTheme } from '../../theme';
|
||||||
// Get from https://github.com/react-navigation/react-navigation/blob/master/packages/stack/src/views/Header/HeaderSegment.tsx#L69
|
// Get from https://github.com/react-navigation/react-navigation/blob/master/packages/stack/src/views/Header/HeaderSegment.tsx#L69
|
||||||
export const headerHeight = isIOS ? 44 : 56;
|
export const headerHeight = isIOS ? 44 : 56;
|
||||||
|
|
||||||
export const getHeaderHeight = (isLandscape) => {
|
export const getHeaderHeight = (isLandscape: boolean) => {
|
||||||
if (isIOS) {
|
if (isIOS) {
|
||||||
if (isLandscape && !isTablet) {
|
if (isLandscape && !isTablet) {
|
||||||
return 32;
|
return 32;
|
||||||
} else {
|
|
||||||
return 44;
|
|
||||||
}
|
}
|
||||||
|
return 44;
|
||||||
}
|
}
|
||||||
return 56;
|
return 56;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getHeaderTitlePosition = ({ insets, numIconsRight }) => ({
|
interface IHeaderTitlePosition {
|
||||||
|
insets: {
|
||||||
|
left: number;
|
||||||
|
right: number;
|
||||||
|
};
|
||||||
|
numIconsRight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHeaderTitlePosition = ({ insets, numIconsRight }: IHeaderTitlePosition) => ({
|
||||||
left: insets.left + 60,
|
left: insets.left + 60,
|
||||||
right: insets.right + Math.max(45 * numIconsRight, 15)
|
right: insets.right + Math.max(45 * numIconsRight, 15),
|
||||||
});
|
});
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -31,13 +38,18 @@ const styles = StyleSheet.create({
|
||||||
height: headerHeight,
|
height: headerHeight,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
elevation: 4
|
elevation: 4,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const Header = ({
|
interface IHeader {
|
||||||
theme, headerLeft, headerTitle, headerRight
|
theme: string;
|
||||||
}) => (
|
headerLeft(): void;
|
||||||
|
headerTitle(): void;
|
||||||
|
headerRight(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header = ({ theme, headerLeft, headerTitle, headerRight }: IHeader) => (
|
||||||
<SafeAreaView style={{ backgroundColor: themes[theme].headerBackground }} edges={['top', 'left', 'right']}>
|
<SafeAreaView style={{ backgroundColor: themes[theme].headerBackground }} edges={['top', 'left', 'right']}>
|
||||||
<View style={[styles.container, { ...themedHeader(theme).headerStyle }]}>
|
<View style={[styles.container, { ...themedHeader(theme).headerStyle }]}>
|
||||||
{headerLeft ? headerLeft() : null}
|
{headerLeft ? headerLeft() : null}
|
||||||
|
@ -47,11 +59,4 @@ const Header = ({
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
|
|
||||||
Header.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
headerLeft: PropTypes.element,
|
|
||||||
headerTitle: PropTypes.element,
|
|
||||||
headerRight: PropTypes.element
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withTheme(Header);
|
export default withTheme(Header);
|
|
@ -1,27 +1,30 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { isIOS } from '../../utils/deviceInfo';
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import Container from './HeaderButtonContainer';
|
import Container from './HeaderButtonContainer';
|
||||||
import Item from './HeaderButtonItem';
|
import Item from './HeaderButtonItem';
|
||||||
|
|
||||||
|
interface IHeaderButtonCommon {
|
||||||
|
navigation: any;
|
||||||
|
onPress(): void;
|
||||||
|
testID: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Left
|
// Left
|
||||||
export const Drawer = React.memo(({ navigation, testID, ...props }) => (
|
export const Drawer = React.memo(({ navigation, testID, ...props }: Partial<IHeaderButtonCommon>) => (
|
||||||
<Container left>
|
<Container left>
|
||||||
<Item iconName='hamburguer' onPress={() => navigation.toggleDrawer()} testID={testID} {...props} />
|
<Item iconName='hamburguer' onPress={() => navigation.toggleDrawer()} testID={testID} {...props} />
|
||||||
</Container>
|
</Container>
|
||||||
));
|
));
|
||||||
|
|
||||||
export const CloseModal = React.memo(({
|
export const CloseModal = React.memo(({ navigation, testID, onPress = () => navigation.pop(), ...props }: IHeaderButtonCommon) => (
|
||||||
navigation, testID, onPress = () => navigation.pop(), ...props
|
|
||||||
}) => (
|
|
||||||
<Container left>
|
<Container left>
|
||||||
<Item iconName='close' onPress={onPress} testID={testID} {...props} />
|
<Item iconName='close' onPress={onPress} testID={testID} {...props} />
|
||||||
</Container>
|
</Container>
|
||||||
));
|
));
|
||||||
|
|
||||||
export const CancelModal = React.memo(({ onPress, testID }) => (
|
export const CancelModal = React.memo(({ onPress, testID }: Partial<IHeaderButtonCommon>) => (
|
||||||
<Container left>
|
<Container left>
|
||||||
{isIOS
|
{isIOS
|
||||||
? <Item title={I18n.t('Cancel')} onPress={onPress} testID={testID} />
|
? <Item title={I18n.t('Cancel')} onPress={onPress} testID={testID} />
|
||||||
|
@ -31,54 +34,24 @@ export const CancelModal = React.memo(({ onPress, testID }) => (
|
||||||
));
|
));
|
||||||
|
|
||||||
// Right
|
// Right
|
||||||
export const More = React.memo(({ onPress, testID }) => (
|
export const More = React.memo(({ onPress, testID }: Partial<IHeaderButtonCommon>) => (
|
||||||
<Container>
|
<Container>
|
||||||
<Item iconName='kebab' onPress={onPress} testID={testID} />
|
<Item iconName='kebab' onPress={onPress} testID={testID} />
|
||||||
</Container>
|
</Container>
|
||||||
));
|
));
|
||||||
|
|
||||||
export const Download = React.memo(({ onPress, testID, ...props }) => (
|
export const Download = React.memo(({ onPress, testID, ...props }: Partial<IHeaderButtonCommon>) => (
|
||||||
<Container>
|
<Container>
|
||||||
<Item iconName='download' onPress={onPress} testID={testID} {...props} />
|
<Item iconName='download' onPress={onPress} testID={testID} {...props} />
|
||||||
</Container>
|
</Container>
|
||||||
));
|
));
|
||||||
|
|
||||||
export const Preferences = React.memo(({ onPress, testID, ...props }) => (
|
export const Preferences = React.memo(({ onPress, testID, ...props }: Partial<IHeaderButtonCommon>) => (
|
||||||
<Container>
|
<Container>
|
||||||
<Item iconName='settings' onPress={onPress} testID={testID} {...props} />
|
<Item iconName='settings' onPress={onPress} testID={testID} {...props} />
|
||||||
</Container>
|
</Container>
|
||||||
));
|
));
|
||||||
|
|
||||||
export const Legal = React.memo(({ navigation, testID }) => (
|
export const Legal = React.memo(({ navigation, testID }: Partial<IHeaderButtonCommon>) => (
|
||||||
<More onPress={() => navigation.navigate('LegalView')} testID={testID} />
|
<More onPress={() => navigation.navigate('LegalView')} testID={testID} />
|
||||||
));
|
));
|
||||||
|
|
||||||
Drawer.propTypes = {
|
|
||||||
navigation: PropTypes.object.isRequired,
|
|
||||||
testID: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
CloseModal.propTypes = {
|
|
||||||
navigation: PropTypes.object.isRequired,
|
|
||||||
testID: PropTypes.string.isRequired,
|
|
||||||
onPress: PropTypes.func
|
|
||||||
};
|
|
||||||
CancelModal.propTypes = {
|
|
||||||
onPress: PropTypes.func.isRequired,
|
|
||||||
testID: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
More.propTypes = {
|
|
||||||
onPress: PropTypes.func.isRequired,
|
|
||||||
testID: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
Download.propTypes = {
|
|
||||||
onPress: PropTypes.func.isRequired,
|
|
||||||
testID: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
Preferences.propTypes = {
|
|
||||||
onPress: PropTypes.func.isRequired,
|
|
||||||
testID: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
Legal.propTypes = {
|
|
||||||
navigation: PropTypes.object.isRequired,
|
|
||||||
testID: PropTypes.string.isRequired
|
|
||||||
};
|
|
|
@ -1,36 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { View, StyleSheet } from 'react-native';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center'
|
|
||||||
},
|
|
||||||
left: {
|
|
||||||
marginLeft: 5
|
|
||||||
},
|
|
||||||
right: {
|
|
||||||
marginRight: 5
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const Container = ({ children, left }) => (
|
|
||||||
<View style={[styles.container, left ? styles.left : styles.right]}>
|
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
|
|
||||||
Container.propTypes = {
|
|
||||||
children: PropTypes.arrayOf(PropTypes.element),
|
|
||||||
left: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
Container.defaultProps = {
|
|
||||||
left: false
|
|
||||||
};
|
|
||||||
|
|
||||||
Container.displayName = 'HeaderButton.Container';
|
|
||||||
|
|
||||||
export default Container;
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
|
interface IHeaderButtonContainer {
|
||||||
|
children: JSX.Element;
|
||||||
|
left?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
left: {
|
||||||
|
marginLeft: 5,
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
marginRight: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Container = ({ children, left = false }: IHeaderButtonContainer) => (
|
||||||
|
<View style={[styles.container, left ? styles.left : styles.right]}>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
Container.displayName = 'HeaderButton.Container';
|
||||||
|
|
||||||
|
export default Container;
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, StyleSheet, Platform } from 'react-native';
|
import { Platform, StyleSheet, Text } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
|
@ -8,30 +7,37 @@ import { withTheme } from '../../theme';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
|
|
||||||
|
interface IHeaderButtonItem {
|
||||||
|
title: string;
|
||||||
|
iconName: string;
|
||||||
|
onPress(): void;
|
||||||
|
testID: string;
|
||||||
|
theme: string;
|
||||||
|
badge(): void;
|
||||||
|
}
|
||||||
|
|
||||||
export const BUTTON_HIT_SLOP = {
|
export const BUTTON_HIT_SLOP = {
|
||||||
top: 5, right: 5, bottom: 5, left: 5
|
top: 5, right: 5, bottom: 5, left: 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
marginHorizontal: 6
|
marginHorizontal: 6,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
...Platform.select({
|
...Platform.select({
|
||||||
android: {
|
android: {
|
||||||
fontSize: 14
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
fontSize: 17
|
fontSize: 17,
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const Item = ({
|
const Item = ({ title, iconName, onPress, testID, theme, badge }: IHeaderButtonItem) => (
|
||||||
title, iconName, onPress, testID, theme, badge
|
|
||||||
}) => (
|
|
||||||
<Touchable onPress={onPress} testID={testID} hitSlop={BUTTON_HIT_SLOP} style={styles.container}>
|
<Touchable onPress={onPress} testID={testID} hitSlop={BUTTON_HIT_SLOP} style={styles.container}>
|
||||||
<>
|
<>
|
||||||
{
|
{
|
||||||
|
@ -44,15 +50,6 @@ const Item = ({
|
||||||
</Touchable>
|
</Touchable>
|
||||||
);
|
);
|
||||||
|
|
||||||
Item.propTypes = {
|
|
||||||
onPress: PropTypes.func.isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
iconName: PropTypes.string,
|
|
||||||
testID: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
badge: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
Item.displayName = 'HeaderButton.Item';
|
Item.displayName = 'HeaderButton.Item';
|
||||||
|
|
||||||
export default withTheme(Item);
|
export default withTheme(Item);
|
|
@ -11,8 +11,8 @@ const styles = StyleSheet.create({
|
||||||
top: -3,
|
top: -3,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Badge = ({ ...props }) => (
|
export const Badge = ({ ...props }) => (
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, View, Text } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Notifier } from 'react-native-notifier';
|
import { Notifier } from 'react-native-notifier';
|
||||||
|
@ -16,10 +15,13 @@ import { goRoom } from '../../utils/goRoom';
|
||||||
import Navigation from '../../lib/Navigation';
|
import Navigation from '../../lib/Navigation';
|
||||||
import { useOrientation } from '../../dimensions';
|
import { useOrientation } from '../../dimensions';
|
||||||
|
|
||||||
|
interface INotifierComponent {
|
||||||
|
notification: object;
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const AVATAR_SIZE = 48;
|
const AVATAR_SIZE = 48;
|
||||||
const BUTTON_HIT_SLOP = {
|
const BUTTON_HIT_SLOP = { top: 12, right: 12, bottom: 12, left: 12 };
|
||||||
top: 12, right: 12, bottom: 12, left: 12
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -30,50 +32,50 @@ const styles = StyleSheet.create({
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
marginHorizontal: 10,
|
marginHorizontal: 10,
|
||||||
borderWidth: StyleSheet.hairlineWidth,
|
borderWidth: StyleSheet.hairlineWidth,
|
||||||
borderRadius: 4
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
inner: {
|
inner: {
|
||||||
flex: 1
|
flex: 1,
|
||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
marginRight: 10
|
marginRight: 10,
|
||||||
},
|
},
|
||||||
roomName: {
|
roomName: {
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
lineHeight: 20,
|
lineHeight: 20,
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium,
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
lineHeight: 17,
|
lineHeight: 17,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
close: {
|
close: {
|
||||||
marginLeft: 10
|
marginLeft: 10,
|
||||||
},
|
},
|
||||||
small: {
|
small: {
|
||||||
width: '50%',
|
width: '50%',
|
||||||
alignSelf: 'center'
|
alignSelf: 'center',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const hideNotification = () => Notifier.hideNotification();
|
const hideNotification = () => Notifier.hideNotification();
|
||||||
|
|
||||||
const NotifierComponent = React.memo(({ notification, isMasterDetail }) => {
|
const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifierComponent) => {
|
||||||
const { theme } = useTheme();
|
const { theme }: any = useTheme();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { isLandscape } = useOrientation();
|
const { isLandscape } = useOrientation();
|
||||||
|
|
||||||
const { text, payload } = notification;
|
const { text, payload }: any = notification;
|
||||||
const { type, rid } = payload;
|
const { type, rid } = payload;
|
||||||
const name = type === 'd' ? payload.sender.username : payload.name;
|
const name = type === 'd' ? payload.sender.username : payload.name;
|
||||||
// if sub is not on local database, title and avatar will be null, so we use payload from notification
|
// if sub is not on local database, title and avatar will be null, so we use payload from notification
|
||||||
const { title = name, avatar = name } = notification;
|
const { title = name, avatar = name }: any = notification;
|
||||||
|
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
const { prid, _id } = payload;
|
const { prid, _id } = payload;
|
||||||
|
@ -81,7 +83,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const item = {
|
const item = {
|
||||||
rid, name: title, t: type, prid
|
rid, name: title, t: type, prid,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
|
@ -100,8 +102,8 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }) => {
|
||||||
{
|
{
|
||||||
backgroundColor: themes[theme].focusedBackground,
|
backgroundColor: themes[theme].focusedBackground,
|
||||||
borderColor: themes[theme].separatorColor,
|
borderColor: themes[theme].separatorColor,
|
||||||
marginTop: insets.top
|
marginTop: insets.top,
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Touchable
|
<Touchable
|
||||||
|
@ -129,13 +131,8 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
NotifierComponent.propTypes = {
|
const mapStateToProps = (state: any) => ({
|
||||||
notification: PropTypes.object,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
isMasterDetail: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
isMasterDetail: state.app.isMasterDetail
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(NotifierComponent);
|
export default connect(mapStateToProps)(NotifierComponent);
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { memo, useEffect } from 'react';
|
import React, { memo, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Easing, Notifier, NotifierRoot } from 'react-native-notifier';
|
||||||
import { NotifierRoot, Notifier, Easing } from 'react-native-notifier';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
|
||||||
|
@ -11,8 +10,8 @@ import { getActiveRoute } from '../../utils/navigation';
|
||||||
|
|
||||||
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
|
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
|
||||||
|
|
||||||
const InAppNotification = memo(({ rooms, appState }) => {
|
const InAppNotification = memo(({ rooms, appState }: {rooms: any, appState: string}) => {
|
||||||
const show = (notification) => {
|
const show = (notification: any) => {
|
||||||
if (appState !== 'foreground') {
|
if (appState !== 'foreground') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -28,8 +27,8 @@ const InAppNotification = memo(({ rooms, appState }) => {
|
||||||
showEasing: Easing.inOut(Easing.quad),
|
showEasing: Easing.inOut(Easing.quad),
|
||||||
Component: NotifierComponent,
|
Component: NotifierComponent,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
notification
|
notification,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -44,14 +43,9 @@ const InAppNotification = memo(({ rooms, appState }) => {
|
||||||
return <NotifierRoot />;
|
return <NotifierRoot />;
|
||||||
}, (prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms));
|
}, (prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms));
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
rooms: state.room.rooms,
|
rooms: state.room.rooms,
|
||||||
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
|
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background',
|
||||||
});
|
});
|
||||||
|
|
||||||
InAppNotification.propTypes = {
|
|
||||||
rooms: PropTypes.array,
|
|
||||||
appState: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(InAppNotification);
|
export default connect(mapStateToProps)(InAppNotification);
|
|
@ -1,16 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, StyleSheet } from 'react-native';
|
import { ScrollView, StyleSheet } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
paddingVertical: 16
|
paddingVertical: 16,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListContainer = React.memo(({ children, ...props }) => (
|
interface IListContainer {
|
||||||
|
children: JSX.Element;
|
||||||
|
}
|
||||||
|
const ListContainer = React.memo(({ children, ...props }: IListContainer) => (
|
||||||
|
// @ts-ignore
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={styles.container}
|
contentContainerStyle={styles.container}
|
||||||
scrollIndicatorInsets={{ right: 1 }} // https://github.com/facebook/react-native/issues/26610#issuecomment-539843444
|
scrollIndicatorInsets={{ right: 1 }} // https://github.com/facebook/react-native/issues/26610#issuecomment-539843444
|
||||||
|
@ -21,10 +25,6 @@ const ListContainer = React.memo(({ children, ...props }) => (
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
));
|
));
|
||||||
|
|
||||||
ListContainer.propTypes = {
|
|
||||||
children: PropTypes.array.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
ListContainer.displayName = 'List.Container';
|
ListContainer.displayName = 'List.Container';
|
||||||
|
|
||||||
export default withTheme(ListContainer);
|
export default withTheme(ListContainer);
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, StyleSheet } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
@ -11,30 +10,26 @@ import { PADDING_HORIZONTAL } from './constants';
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
paddingBottom: 12,
|
paddingBottom: 12,
|
||||||
paddingHorizontal: PADDING_HORIZONTAL
|
paddingHorizontal: PADDING_HORIZONTAL,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListHeader = React.memo(({ title, theme, translateTitle }) => (
|
interface IListHeader {
|
||||||
|
title: string;
|
||||||
|
theme: string;
|
||||||
|
translateTitle: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListHeader = React.memo(({ title, theme, translateTitle = true }: IListHeader) => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={[styles.title, { color: themes[theme].infoText }]} numberOfLines={1}>{translateTitle ? I18n.t(title) : title}</Text>
|
<Text style={[styles.title, { color: themes[theme].infoText }]} numberOfLines={1}>{translateTitle ? I18n.t(title) : title}</Text>
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
ListHeader.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
translateTitle: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
ListHeader.defaultProps = {
|
|
||||||
translateTitle: true
|
|
||||||
};
|
|
||||||
|
|
||||||
ListHeader.displayName = 'List.Header';
|
ListHeader.displayName = 'List.Header';
|
||||||
|
|
||||||
export default withTheme(ListHeader);
|
export default withTheme(ListHeader);
|
|
@ -1,26 +1,27 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { ICON_SIZE } from './constants';
|
import { ICON_SIZE } from './constants';
|
||||||
|
|
||||||
|
interface IListIcon {
|
||||||
|
theme: string;
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
style: object;
|
||||||
|
testID: string;
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
icon: {
|
icon: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListIcon = React.memo(({
|
const ListIcon = React.memo(({ theme, name, color, style, testID }: IListIcon) => (
|
||||||
theme,
|
|
||||||
name,
|
|
||||||
color,
|
|
||||||
style,
|
|
||||||
testID
|
|
||||||
}) => (
|
|
||||||
<View style={[styles.icon, style]}>
|
<View style={[styles.icon, style]}>
|
||||||
<CustomIcon
|
<CustomIcon
|
||||||
name={name}
|
name={name}
|
||||||
|
@ -31,14 +32,6 @@ const ListIcon = React.memo(({
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
ListIcon.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
name: PropTypes.string,
|
|
||||||
color: PropTypes.string,
|
|
||||||
style: PropTypes.object,
|
|
||||||
testID: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
ListIcon.displayName = 'List.Icon';
|
ListIcon.displayName = 'List.Icon';
|
||||||
|
|
||||||
export default withTheme(ListIcon);
|
export default withTheme(ListIcon);
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, StyleSheet } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
@ -11,30 +10,26 @@ import I18n from '../../i18n';
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
paddingTop: 8,
|
paddingTop: 8,
|
||||||
paddingHorizontal: PADDING_HORIZONTAL
|
paddingHorizontal: PADDING_HORIZONTAL,
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListInfo = React.memo(({ info, translateInfo, theme }) => (
|
interface IListHeader {
|
||||||
|
info: string;
|
||||||
|
theme: string;
|
||||||
|
translateInfo: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListInfo = React.memo(({ info, theme, translateInfo = true }: IListHeader) => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={[styles.text, { color: themes[theme].infoText }]}>{translateInfo ? I18n.t(info) : info}</Text>
|
<Text style={[styles.text, { color: themes[theme].infoText }]}>{translateInfo ? I18n.t(info) : info}</Text>
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
ListInfo.propTypes = {
|
|
||||||
info: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
translateInfo: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
ListInfo.defaultProps = {
|
|
||||||
translateInfo: true
|
|
||||||
};
|
|
||||||
|
|
||||||
ListInfo.displayName = 'List.Info';
|
ListInfo.displayName = 'List.Info';
|
||||||
|
|
||||||
export default withTheme(ListInfo);
|
export default withTheme(ListInfo);
|
|
@ -1,8 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { I18nManager, StyleSheet, Text, View } from 'react-native';
|
||||||
View, Text, StyleSheet, I18nManager
|
|
||||||
} from 'react-native';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
@ -20,48 +17,64 @@ const styles = StyleSheet.create({
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingHorizontal: PADDING_HORIZONTAL
|
paddingHorizontal: PADDING_HORIZONTAL,
|
||||||
},
|
},
|
||||||
leftContainer: {
|
leftContainer: {
|
||||||
paddingRight: PADDING_HORIZONTAL
|
paddingRight: PADDING_HORIZONTAL,
|
||||||
},
|
},
|
||||||
rightContainer: {
|
rightContainer: {
|
||||||
paddingLeft: PADDING_HORIZONTAL
|
paddingLeft: PADDING_HORIZONTAL,
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
opacity: 0.3
|
opacity: 0.3,
|
||||||
},
|
},
|
||||||
textContainer: {
|
textContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center'
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
textAlertContainer: {
|
textAlertContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
alertIcon: {
|
alertIcon: {
|
||||||
paddingLeft: 4
|
paddingLeft: 4,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
flexShrink: 1,
|
flexShrink: 1,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
subtitle: {
|
subtitle: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
actionIndicator: {
|
actionIndicator: {
|
||||||
...I18nManager.isRTL
|
...I18nManager.isRTL
|
||||||
? { transform: [{ rotate: '180deg' }] }
|
? { transform: [{ rotate: '180deg' }] }
|
||||||
: {}
|
: {},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface IListItemContent {
|
||||||
|
title?: string;
|
||||||
|
subtitle?: string;
|
||||||
|
left?: Function;
|
||||||
|
right?: Function;
|
||||||
|
disabled?: boolean;
|
||||||
|
testID?: string;
|
||||||
|
theme: string;
|
||||||
|
color?: string;
|
||||||
|
translateTitle?: boolean;
|
||||||
|
translateSubtitle?: boolean;
|
||||||
|
showActionIndicator?: boolean;
|
||||||
|
fontScale?: number;
|
||||||
|
alert?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const Content = React.memo(({
|
const Content = React.memo(({
|
||||||
title, subtitle, disabled, testID, left, right, color, theme, translateTitle, translateSubtitle, showActionIndicator, fontScale, alert
|
title, subtitle, disabled, testID, left, right, color, theme, fontScale, alert, translateTitle = true, translateSubtitle = true, showActionIndicator = false,
|
||||||
}) => (
|
}: IListItemContent) => (
|
||||||
<View style={[styles.container, disabled && styles.disabled, { height: BASE_HEIGHT * fontScale }]} testID={testID}>
|
<View style={[styles.container, disabled && styles.disabled, { height: BASE_HEIGHT * fontScale! }]} testID={testID}>
|
||||||
{left
|
{left
|
||||||
? (
|
? (
|
||||||
<View style={styles.leftContainer}>
|
<View style={styles.leftContainer}>
|
||||||
|
@ -92,9 +105,16 @@ const Content = React.memo(({
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
const Button = React.memo(({
|
interface IListItemButton {
|
||||||
onPress, backgroundColor, underlayColor, ...props
|
title?: string;
|
||||||
}) => (
|
onPress: Function;
|
||||||
|
disabled?: boolean;
|
||||||
|
theme: string;
|
||||||
|
backgroundColor: string;
|
||||||
|
underlayColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = React.memo(({ onPress, backgroundColor, underlayColor, ...props }: IListItemButton) => (
|
||||||
<Touch
|
<Touch
|
||||||
onPress={() => onPress(props.title)}
|
onPress={() => onPress(props.title)}
|
||||||
style={{ backgroundColor: backgroundColor || themes[props.theme].backgroundColor }}
|
style={{ backgroundColor: backgroundColor || themes[props.theme].backgroundColor }}
|
||||||
|
@ -106,7 +126,13 @@ const Button = React.memo(({
|
||||||
</Touch>
|
</Touch>
|
||||||
));
|
));
|
||||||
|
|
||||||
const ListItem = React.memo(({ ...props }) => {
|
interface IListItem {
|
||||||
|
onPress: Function;
|
||||||
|
theme: string;
|
||||||
|
backgroundColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListItem = React.memo(({ ...props }: IListItem) => {
|
||||||
if (props.onPress) {
|
if (props.onPress) {
|
||||||
return <Button {...props} />;
|
return <Button {...props} />;
|
||||||
}
|
}
|
||||||
|
@ -117,47 +143,6 @@ const ListItem = React.memo(({ ...props }) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ListItem.propTypes = {
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
backgroundColor: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
ListItem.displayName = 'List.Item';
|
ListItem.displayName = 'List.Item';
|
||||||
|
|
||||||
Content.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
subtitle: PropTypes.string,
|
|
||||||
left: PropTypes.func,
|
|
||||||
right: PropTypes.func,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
testID: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
color: PropTypes.string,
|
|
||||||
translateTitle: PropTypes.bool,
|
|
||||||
translateSubtitle: PropTypes.bool,
|
|
||||||
showActionIndicator: PropTypes.bool,
|
|
||||||
fontScale: PropTypes.number,
|
|
||||||
alert: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
Content.defaultProps = {
|
|
||||||
translateTitle: true,
|
|
||||||
translateSubtitle: true,
|
|
||||||
showActionIndicator: false
|
|
||||||
};
|
|
||||||
|
|
||||||
Button.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
backgroundColor: PropTypes.string,
|
|
||||||
underlayColor: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
Button.defaultProps = {
|
|
||||||
disabled: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withTheme(withDimensions(ListItem));
|
export default withTheme(withDimensions(ListItem));
|
|
@ -1,28 +1,28 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { Header } from '.';
|
import { Header } from '.';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
marginVertical: 16
|
marginVertical: 16,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListSection = React.memo(({ children, title, translateTitle }) => (
|
interface IListSection {
|
||||||
|
children: JSX.Element;
|
||||||
|
title: string;
|
||||||
|
translateTitle: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListSection = React.memo(({ children, title, translateTitle }: IListSection) => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{title ? <Header {...{ title, translateTitle }} /> : null}
|
{title ? <Header {...{ title, translateTitle }} /> : null}
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
ListSection.propTypes = {
|
|
||||||
children: PropTypes.array.isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
translateTitle: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
ListSection.displayName = 'List.Section';
|
ListSection.displayName = 'List.Section';
|
||||||
|
|
||||||
export default withTheme(ListSection);
|
export default withTheme(ListSection);
|
|
@ -1,32 +1,30 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
separator: {
|
separator: {
|
||||||
height: StyleSheet.hairlineWidth
|
height: StyleSheet.hairlineWidth,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface IListSeparator {
|
||||||
|
style: object;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
const ListSeparator = React.memo(({ style, theme }) => (
|
const ListSeparator = React.memo(({ style, theme }: IListSeparator) => (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.separator,
|
styles.separator,
|
||||||
style,
|
style,
|
||||||
{ backgroundColor: themes[theme].separatorColor }
|
{ backgroundColor: themes[theme].separatorColor },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
ListSeparator.propTypes = {
|
|
||||||
style: PropTypes.object,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
ListSeparator.displayName = 'List.Separator';
|
ListSeparator.displayName = 'List.Separator';
|
||||||
|
|
||||||
export default withTheme(ListSeparator);
|
export default withTheme(ListSeparator);
|
|
@ -2,6 +2,6 @@ import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export const styles = StyleSheet.create({
|
export const styles = StyleSheet.create({
|
||||||
contentContainerStyleFlatList: {
|
contentContainerStyleFlatList: {
|
||||||
paddingVertical: 32
|
paddingVertical: 32,
|
||||||
}
|
},
|
||||||
});
|
});
|
|
@ -1,8 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Animated, Modal, StyleSheet, View } from 'react-native';
|
||||||
import {
|
|
||||||
StyleSheet, Modal, Animated, View
|
|
||||||
} from 'react-native';
|
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
|
|
||||||
|
@ -10,26 +8,30 @@ const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
image: {
|
image: {
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
resizeMode: 'contain'
|
resizeMode: 'contain',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class Loading extends React.PureComponent {
|
interface ILoadingProps {
|
||||||
static propTypes = {
|
visible: boolean;
|
||||||
visible: PropTypes.bool,
|
theme: string;
|
||||||
theme: PropTypes.string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Loading extends React.PureComponent<ILoadingProps, any> {
|
||||||
state = {
|
state = {
|
||||||
scale: new Animated.Value(1),
|
scale: new Animated.Value(1),
|
||||||
opacity: new Animated.Value(0)
|
opacity: new Animated.Value(0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private opacityAnimation: any;
|
||||||
|
|
||||||
|
private scaleAnimation: any;
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { opacity, scale } = this.state;
|
const { opacity, scale } = this.state;
|
||||||
const { visible } = this.props;
|
const { visible } = this.props;
|
||||||
|
@ -39,8 +41,8 @@ class Loading extends React.PureComponent {
|
||||||
{
|
{
|
||||||
toValue: 1,
|
toValue: 1,
|
||||||
duration: 200,
|
duration: 200,
|
||||||
useNativeDriver: true
|
useNativeDriver: true,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
this.scaleAnimation = Animated.loop(Animated.sequence([
|
this.scaleAnimation = Animated.loop(Animated.sequence([
|
||||||
Animated.timing(
|
Animated.timing(
|
||||||
|
@ -48,17 +50,17 @@ class Loading extends React.PureComponent {
|
||||||
{
|
{
|
||||||
toValue: 0,
|
toValue: 0,
|
||||||
duration: 1000,
|
duration: 1000,
|
||||||
useNativeDriver: true
|
useNativeDriver: true,
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
Animated.timing(
|
Animated.timing(
|
||||||
scale,
|
scale,
|
||||||
{
|
{
|
||||||
toValue: 1,
|
toValue: 1,
|
||||||
duration: 1000,
|
duration: 1000,
|
||||||
useNativeDriver: true
|
useNativeDriver: true,
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
@ -66,7 +68,7 @@ class Loading extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps: any) {
|
||||||
const { visible } = this.props;
|
const { visible } = this.props;
|
||||||
if (visible && visible !== prevProps.visible) {
|
if (visible && visible !== prevProps.visible) {
|
||||||
this.startAnimations();
|
this.startAnimations();
|
||||||
|
@ -97,13 +99,13 @@ class Loading extends React.PureComponent {
|
||||||
|
|
||||||
const scaleAnimation = scale.interpolate({
|
const scaleAnimation = scale.interpolate({
|
||||||
inputRange: [0, 0.5, 1],
|
inputRange: [0, 0.5, 1],
|
||||||
outputRange: [1, 1.1, 1]
|
outputRange: [1, 1.1, 1],
|
||||||
});
|
});
|
||||||
|
|
||||||
const opacityAnimation = opacity.interpolate({
|
const opacityAnimation = opacity.interpolate({
|
||||||
inputRange: [0, 1],
|
inputRange: [0, 1],
|
||||||
outputRange: [0, themes[theme].backdropOpacity],
|
outputRange: [0, themes[theme].backdropOpacity],
|
||||||
extrapolate: 'clamp'
|
extrapolate: 'clamp',
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -118,17 +120,18 @@ class Loading extends React.PureComponent {
|
||||||
>
|
>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={[{
|
style={[{
|
||||||
|
// @ts-ignore
|
||||||
...StyleSheet.absoluteFill,
|
...StyleSheet.absoluteFill,
|
||||||
backgroundColor: themes[theme].backdropColor,
|
backgroundColor: themes[theme].backdropColor,
|
||||||
opacity: opacityAnimation
|
opacity: opacityAnimation,
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
<Animated.Image
|
<Animated.Image
|
||||||
source={require('../static/images/logo.png')}
|
source={require('../static/images/logo.png')}
|
||||||
style={[styles.image, {
|
style={[styles.image, {
|
||||||
transform: [{
|
transform: [{
|
||||||
scale: scaleAnimation
|
scale: scaleAnimation,
|
||||||
}]
|
}],
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
@ -137,4 +140,4 @@ class Loading extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (withTheme(Loading));
|
export default withTheme(Loading);
|
|
@ -1,8 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { Animated, Easing, Linking, StyleSheet, Text, View } from 'react-native';
|
||||||
View, StyleSheet, Text, Animated, Easing, Linking
|
|
||||||
} from 'react-native';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
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';
|
||||||
|
@ -15,7 +12,7 @@ import OrSeparator from './OrSeparator';
|
||||||
import Touch from '../utils/touch';
|
import Touch from '../utils/touch';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import random from '../utils/random';
|
import random from '../utils/random';
|
||||||
import { logEvent, events } from '../utils/log';
|
import { events, logEvent } from '../utils/log';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import { CustomIcon } from '../lib/Icons';
|
import { CustomIcon } from '../lib/Icons';
|
||||||
|
|
||||||
|
@ -30,7 +27,7 @@ const LOGIN_STYPE_REDIRECT = 'redirect';
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
serviceButton: {
|
serviceButton: {
|
||||||
borderRadius: BORDER_RADIUS,
|
borderRadius: BORDER_RADIUS,
|
||||||
marginBottom: 10
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
serviceButtonContainer: {
|
serviceButtonContainer: {
|
||||||
borderRadius: BORDER_RADIUS,
|
borderRadius: BORDER_RADIUS,
|
||||||
|
@ -39,46 +36,70 @@ const styles = StyleSheet.create({
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingHorizontal: 15
|
paddingHorizontal: 15,
|
||||||
},
|
},
|
||||||
serviceIcon: {
|
serviceIcon: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 15,
|
left: 15,
|
||||||
top: 12,
|
top: 12,
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24
|
height: 24,
|
||||||
},
|
},
|
||||||
serviceText: {
|
serviceText: {
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular,
|
||||||
fontSize: 16
|
fontSize: 16,
|
||||||
},
|
},
|
||||||
serviceName: {
|
serviceName: {
|
||||||
...sharedStyles.textSemibold
|
...sharedStyles.textSemibold,
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
marginBottom: 0
|
marginBottom: 0,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class LoginServices extends React.PureComponent {
|
interface IOpenOAuth {
|
||||||
static propTypes = {
|
url?: string;
|
||||||
navigation: PropTypes.object,
|
ssoToken?: string;
|
||||||
server: PropTypes.string,
|
authType?: string;
|
||||||
services: PropTypes.object,
|
|
||||||
Gitlab_URL: PropTypes.string,
|
|
||||||
CAS_enabled: PropTypes.bool,
|
|
||||||
CAS_login_url: PropTypes.string,
|
|
||||||
separator: PropTypes.bool,
|
|
||||||
theme: PropTypes.string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IService {
|
||||||
|
name: string;
|
||||||
|
service: string;
|
||||||
|
authType: string;
|
||||||
|
buttonColor: string;
|
||||||
|
buttonLabelColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILoginServicesProps {
|
||||||
|
navigation: any;
|
||||||
|
server: string;
|
||||||
|
services: {
|
||||||
|
facebook: {clientId: string;};
|
||||||
|
github: {clientId: string;};
|
||||||
|
gitlab: {clientId: string;};
|
||||||
|
google: {clientId: string;};
|
||||||
|
linkedin: {clientId: string;};
|
||||||
|
'meteor-developer': {clientId: string;};
|
||||||
|
wordpress: {clientId: string; serverURL: string;};
|
||||||
|
};
|
||||||
|
Gitlab_URL: string;
|
||||||
|
CAS_enabled: boolean;
|
||||||
|
CAS_login_url: string;
|
||||||
|
separator: boolean;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginServices extends React.PureComponent<ILoginServicesProps, any> {
|
||||||
|
private _animation: any;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
separator: true
|
separator: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
servicesHeight: new Animated.Value(SERVICES_COLLAPSED_HEIGHT)
|
servicesHeight: new Animated.Value(SERVICES_COLLAPSED_HEIGHT),
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressFacebook = () => {
|
onPressFacebook = () => {
|
||||||
|
@ -173,11 +194,11 @@ class LoginServices extends React.PureComponent {
|
||||||
this.openOAuth({ url: `${ endpoint }${ params }` });
|
this.openOAuth({ url: `${ endpoint }${ params }` });
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressCustomOAuth = (loginService) => {
|
onPressCustomOAuth = (loginService: any) => {
|
||||||
logEvent(events.ENTER_WITH_CUSTOM_OAUTH);
|
logEvent(events.ENTER_WITH_CUSTOM_OAUTH);
|
||||||
const { server } = this.props;
|
const { server } = this.props;
|
||||||
const {
|
const {
|
||||||
serverURL, authorizePath, clientId, scope, service
|
serverURL, authorizePath, clientId, scope, service,
|
||||||
} = loginService;
|
} = loginService;
|
||||||
const redirectUri = `${ server }/_oauth/${ service }`;
|
const redirectUri = `${ server }/_oauth/${ service }`;
|
||||||
const state = this.getOAuthState();
|
const state = this.getOAuthState();
|
||||||
|
@ -188,7 +209,7 @@ class LoginServices extends React.PureComponent {
|
||||||
this.openOAuth({ url });
|
this.openOAuth({ url });
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressSaml = (loginService) => {
|
onPressSaml = (loginService: any) => {
|
||||||
logEvent(events.ENTER_WITH_SAML);
|
logEvent(events.ENTER_WITH_SAML);
|
||||||
const { server } = this.props;
|
const { server } = this.props;
|
||||||
const { clientConfig } = loginService;
|
const { clientConfig } = loginService;
|
||||||
|
@ -212,8 +233,8 @@ class LoginServices extends React.PureComponent {
|
||||||
const { fullName, email, identityToken } = await AppleAuthentication.signInAsync({
|
const { fullName, email, identityToken } = await AppleAuthentication.signInAsync({
|
||||||
requestedScopes: [
|
requestedScopes: [
|
||||||
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
|
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
|
||||||
AppleAuthentication.AppleAuthenticationScope.EMAIL
|
AppleAuthentication.AppleAuthenticationScope.EMAIL,
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
await RocketChat.loginOAuthOrSso({ fullName, email, identityToken });
|
await RocketChat.loginOAuthOrSso({ fullName, email, identityToken });
|
||||||
|
@ -224,30 +245,32 @@ class LoginServices extends React.PureComponent {
|
||||||
|
|
||||||
getOAuthState = (loginStyle = LOGIN_STYPE_POPUP) => {
|
getOAuthState = (loginStyle = LOGIN_STYPE_POPUP) => {
|
||||||
const credentialToken = random(43);
|
const credentialToken = random(43);
|
||||||
let obj = { loginStyle, credentialToken, isCordova: true };
|
let obj: any = { loginStyle, credentialToken, isCordova: true };
|
||||||
if (loginStyle === LOGIN_STYPE_REDIRECT) {
|
if (loginStyle === LOGIN_STYPE_REDIRECT) {
|
||||||
obj = {
|
obj = {
|
||||||
...obj,
|
...obj,
|
||||||
redirectUrl: 'rocketchat://auth'
|
redirectUrl: 'rocketchat://auth',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return Base64.encodeURI(JSON.stringify(obj));
|
return Base64.encodeURI(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
openOAuth = ({ url, ssoToken, authType = 'oauth' }) => {
|
openOAuth = ({ url, ssoToken, authType = 'oauth' }: IOpenOAuth) => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('AuthenticationWebView', { url, authType, ssoToken });
|
navigation.navigate('AuthenticationWebView', { url, authType, ssoToken });
|
||||||
}
|
}
|
||||||
|
|
||||||
transitionServicesTo = (height) => {
|
transitionServicesTo = (height: number) => {
|
||||||
const { servicesHeight } = this.state;
|
const { servicesHeight } = this.state;
|
||||||
if (this._animation) {
|
if (this._animation) {
|
||||||
this._animation.stop();
|
this._animation.stop();
|
||||||
}
|
}
|
||||||
|
// @ts-ignore
|
||||||
this._animation = Animated.timing(servicesHeight, {
|
this._animation = Animated.timing(servicesHeight, {
|
||||||
toValue: height,
|
toValue: height,
|
||||||
duration: 300,
|
duration: 300,
|
||||||
easing: Easing.easeOutCubic
|
// @ts-ignore
|
||||||
|
easing: Easing.easeOutCubic,
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,11 +283,11 @@ class LoginServices extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.transitionServicesTo(SERVICES_COLLAPSED_HEIGHT);
|
this.transitionServicesTo(SERVICES_COLLAPSED_HEIGHT);
|
||||||
}
|
}
|
||||||
this.setState(prevState => ({ collapsed: !prevState.collapsed }));
|
this.setState((prevState: any) => ({ collapsed: !prevState.collapsed }));
|
||||||
}
|
}
|
||||||
|
|
||||||
getSocialOauthProvider = (name) => {
|
getSocialOauthProvider = (name: string) => {
|
||||||
const oauthProviders = {
|
const oauthProviders: any = {
|
||||||
facebook: this.onPressFacebook,
|
facebook: this.onPressFacebook,
|
||||||
github: this.onPressGithub,
|
github: this.onPressGithub,
|
||||||
gitlab: this.onPressGitlab,
|
gitlab: this.onPressGitlab,
|
||||||
|
@ -272,7 +295,7 @@ class LoginServices extends React.PureComponent {
|
||||||
linkedin: this.onPressLinkedin,
|
linkedin: this.onPressLinkedin,
|
||||||
'meteor-developer': this.onPressMeteor,
|
'meteor-developer': this.onPressMeteor,
|
||||||
twitter: this.onPressTwitter,
|
twitter: this.onPressTwitter,
|
||||||
wordpress: this.onPressWordpress
|
wordpress: this.onPressWordpress,
|
||||||
};
|
};
|
||||||
return oauthProviders[name];
|
return oauthProviders[name];
|
||||||
}
|
}
|
||||||
|
@ -303,7 +326,7 @@ class LoginServices extends React.PureComponent {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem = (service) => {
|
renderItem = (service: IService) => {
|
||||||
const { CAS_enabled, theme } = this.props;
|
const { CAS_enabled, theme } = this.props;
|
||||||
let { name } = service;
|
let { name } = service;
|
||||||
name = name === 'meteor-developer' ? 'meteor' : name;
|
name = name === 'meteor-developer' ? 'meteor' : name;
|
||||||
|
@ -373,14 +396,14 @@ class LoginServices extends React.PureComponent {
|
||||||
const { length } = Object.values(services);
|
const { length } = Object.values(services);
|
||||||
const style = {
|
const style = {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
height: servicesHeight
|
height: servicesHeight,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (length > 3 && separator) {
|
if (length > 3 && separator) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Animated.View style={style}>
|
<Animated.View style={style}>
|
||||||
{Object.values(services).map(service => this.renderItem(service))}
|
{Object.values(services).map((service: any) => this.renderItem(service))}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
{this.renderServicesSeparator()}
|
{this.renderServicesSeparator()}
|
||||||
</>
|
</>
|
||||||
|
@ -388,19 +411,19 @@ class LoginServices extends React.PureComponent {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.values(services).map(service => this.renderItem(service))}
|
{Object.values(services).map((service: any) => this.renderItem(service))}
|
||||||
{this.renderServicesSeparator()}
|
{this.renderServicesSeparator()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Gitlab_URL: state.settings.API_Gitlab_URL,
|
Gitlab_URL: state.settings.API_Gitlab_URL,
|
||||||
CAS_enabled: state.settings.CAS_enabled,
|
CAS_enabled: state.settings.CAS_enabled,
|
||||||
CAS_login_url: state.settings.CAS_login_url,
|
CAS_login_url: state.settings.CAS_login_url,
|
||||||
services: state.login.services
|
services: state.login.services,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(LoginServices));
|
export default connect(mapStateToProps)(withTheme(LoginServices));
|
|
@ -1,8 +1,5 @@
|
||||||
import React, { useEffect, useState, useCallback } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { FlatList, StyleSheet, Text, View } from 'react-native';
|
||||||
import {
|
|
||||||
View, Text, FlatList, StyleSheet
|
|
||||||
} from 'react-native';
|
|
||||||
|
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
@ -13,6 +10,27 @@ 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 '../EmojiPicker/interfaces';
|
||||||
|
|
||||||
|
interface IHeader {
|
||||||
|
handleReaction: Function;
|
||||||
|
server: string;
|
||||||
|
message: object;
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface THeaderItem {
|
||||||
|
item: IEmoji;
|
||||||
|
onReaction: Function;
|
||||||
|
server: string;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface THeaderFooter {
|
||||||
|
onReaction: any;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const HEADER_HEIGHT = 36;
|
export const HEADER_HEIGHT = 36;
|
||||||
const ITEM_SIZE = 36;
|
const ITEM_SIZE = 36;
|
||||||
|
@ -22,7 +40,7 @@ const ITEM_MARGIN = 8;
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginHorizontal: CONTAINER_MARGIN
|
marginHorizontal: CONTAINER_MARGIN,
|
||||||
},
|
},
|
||||||
headerItem: {
|
headerItem: {
|
||||||
height: ITEM_SIZE,
|
height: ITEM_SIZE,
|
||||||
|
@ -30,26 +48,24 @@ const styles = StyleSheet.create({
|
||||||
borderRadius: ITEM_SIZE / 2,
|
borderRadius: ITEM_SIZE / 2,
|
||||||
marginHorizontal: ITEM_MARGIN,
|
marginHorizontal: ITEM_MARGIN,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
headerIcon: {
|
headerIcon: {
|
||||||
...sharedStyles.textAlignCenter,
|
...sharedStyles.textAlignCenter,
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: '#fff'
|
color: '#fff',
|
||||||
},
|
},
|
||||||
customEmoji: {
|
customEmoji: {
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 20
|
width: 20,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const keyExtractor = item => item?.id || item;
|
const keyExtractor = (item: any) => item?.id || item;
|
||||||
|
|
||||||
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(({
|
const HeaderItem = React.memo(({ item, onReaction, server, theme }: THeaderItem) => (
|
||||||
item, onReaction, server, theme
|
|
||||||
}) => (
|
|
||||||
<Button
|
<Button
|
||||||
testID={`message-actions-emoji-${ item.content || item }`}
|
testID={`message-actions-emoji-${ item.content || item }`}
|
||||||
onPress={() => onReaction({ emoji: `:${ item.content || item }:` })}
|
onPress={() => onReaction({ emoji: `:${ item.content || item }:` })}
|
||||||
|
@ -65,14 +81,8 @@ const HeaderItem = React.memo(({
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
));
|
));
|
||||||
HeaderItem.propTypes = {
|
|
||||||
item: PropTypes.string,
|
|
||||||
onReaction: PropTypes.func,
|
|
||||||
server: PropTypes.string,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
const HeaderFooter = React.memo(({ onReaction, theme }) => (
|
const HeaderFooter = React.memo(({ onReaction, theme }: THeaderFooter) => (
|
||||||
<Button
|
<Button
|
||||||
testID='add-reaction'
|
testID='add-reaction'
|
||||||
onPress={onReaction}
|
onPress={onReaction}
|
||||||
|
@ -82,16 +92,10 @@ const HeaderFooter = React.memo(({ onReaction, theme }) => (
|
||||||
<CustomIcon name='reaction-add' size={24} color={themes[theme].bodyText} />
|
<CustomIcon name='reaction-add' size={24} color={themes[theme].bodyText} />
|
||||||
</Button>
|
</Button>
|
||||||
));
|
));
|
||||||
HeaderFooter.propTypes = {
|
|
||||||
onReaction: PropTypes.func,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
const Header = React.memo(({
|
const Header = React.memo(({ handleReaction, server, message, isMasterDetail, theme }: IHeader) => {
|
||||||
handleReaction, server, message, isMasterDetail, theme
|
|
||||||
}) => {
|
|
||||||
const [items, setItems] = useState([]);
|
const [items, setItems] = useState([]);
|
||||||
const { width, height } = useDimensions();
|
const { width, height }: any = useDimensions();
|
||||||
|
|
||||||
const setEmojis = async () => {
|
const setEmojis = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -114,11 +118,11 @@ const Header = React.memo(({
|
||||||
setEmojis();
|
setEmojis();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onReaction = ({ emoji }) => handleReaction(emoji, message);
|
const onReaction = ({ emoji }: {emoji: IEmoji}) => handleReaction(emoji, message);
|
||||||
|
|
||||||
const renderItem = useCallback(({ item }) => <HeaderItem item={item} onReaction={onReaction} server={server} theme={theme} />);
|
const renderItem = useCallback(({ item }) => <HeaderItem item={item} onReaction={onReaction} server={server} theme={theme} />, []);
|
||||||
|
|
||||||
const renderFooter = useCallback(() => <HeaderFooter onReaction={onReaction} theme={theme} />);
|
const renderFooter = useCallback(() => <HeaderFooter onReaction={onReaction} theme={theme} />, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { backgroundColor: themes[theme].focusedBackground }]}>
|
<View style={[styles.container, { backgroundColor: themes[theme].focusedBackground }]}>
|
||||||
|
@ -135,11 +139,5 @@ const Header = React.memo(({
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Header.propTypes = {
|
|
||||||
handleReaction: PropTypes.func,
|
|
||||||
server: PropTypes.string,
|
|
||||||
message: PropTypes.object,
|
|
||||||
isMasterDetail: PropTypes.bool,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
export default withTheme(Header);
|
export default withTheme(Header);
|
|
@ -1,5 +1,4 @@
|
||||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Alert, Clipboard, Share } from 'react-native';
|
import { Alert, Clipboard, Share } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
@ -17,6 +16,37 @@ import { useActionSheet } from '../ActionSheet';
|
||||||
import Header, { HEADER_HEIGHT } from './Header';
|
import Header, { HEADER_HEIGHT } from './Header';
|
||||||
import events from '../../utils/log/events';
|
import events from '../../utils/log/events';
|
||||||
|
|
||||||
|
interface IMessageActions {
|
||||||
|
room: {
|
||||||
|
rid: string | number;
|
||||||
|
autoTranslateLanguage: any;
|
||||||
|
autoTranslate: any;
|
||||||
|
reactWhenReadOnly: any;
|
||||||
|
};
|
||||||
|
tmid: string;
|
||||||
|
user: {
|
||||||
|
id: string | number;
|
||||||
|
};
|
||||||
|
editInit: Function;
|
||||||
|
reactionInit: Function;
|
||||||
|
onReactionPress: Function;
|
||||||
|
replyInit: Function;
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
isReadOnly: boolean;
|
||||||
|
Message_AllowDeleting: boolean;
|
||||||
|
Message_AllowDeleting_BlockDeleteInMinutes: number;
|
||||||
|
Message_AllowEditing: boolean;
|
||||||
|
Message_AllowEditing_BlockEditInMinutes: number;
|
||||||
|
Message_AllowPinning: boolean;
|
||||||
|
Message_AllowStarring: boolean;
|
||||||
|
Message_Read_Receipt_Store_Users: boolean;
|
||||||
|
server: string;
|
||||||
|
editMessagePermission: [];
|
||||||
|
deleteMessagePermission: [];
|
||||||
|
forceDeleteMessagePermission: [];
|
||||||
|
pinMessagePermission: [];
|
||||||
|
}
|
||||||
|
|
||||||
const MessageActions = React.memo(forwardRef(({
|
const MessageActions = React.memo(forwardRef(({
|
||||||
room,
|
room,
|
||||||
tmid,
|
tmid,
|
||||||
|
@ -38,10 +68,10 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
editMessagePermission,
|
editMessagePermission,
|
||||||
deleteMessagePermission,
|
deleteMessagePermission,
|
||||||
forceDeleteMessagePermission,
|
forceDeleteMessagePermission,
|
||||||
pinMessagePermission
|
pinMessagePermission,
|
||||||
}, ref) => {
|
}: IMessageActions, ref): any => {
|
||||||
let permissions = {};
|
let permissions: any = {};
|
||||||
const { showActionSheet, hideActionSheet } = useActionSheet();
|
const { showActionSheet, hideActionSheet }: any = useActionSheet();
|
||||||
|
|
||||||
const getPermissions = async () => {
|
const getPermissions = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -51,16 +81,16 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
hasEditPermission: result[0],
|
hasEditPermission: result[0],
|
||||||
hasDeletePermission: result[1],
|
hasDeletePermission: result[1],
|
||||||
hasForceDeletePermission: result[2],
|
hasForceDeletePermission: result[2],
|
||||||
hasPinPermission: result[3]
|
hasPinPermission: result[3],
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isOwn = message => message.u && message.u._id === user.id;
|
const isOwn = (message: any) => message.u && message.u._id === user.id;
|
||||||
|
|
||||||
const allowEdit = (message) => {
|
const allowEdit = (message: any) => {
|
||||||
if (isReadOnly) {
|
if (isReadOnly) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +105,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
if (message.ts != null) {
|
if (message.ts != null) {
|
||||||
msgTs = moment(message.ts);
|
msgTs = moment(message.ts);
|
||||||
}
|
}
|
||||||
let currentTsDiff;
|
let currentTsDiff: any;
|
||||||
if (msgTs != null) {
|
if (msgTs != null) {
|
||||||
currentTsDiff = moment().diff(msgTs, 'minutes');
|
currentTsDiff = moment().diff(msgTs, 'minutes');
|
||||||
}
|
}
|
||||||
|
@ -84,7 +114,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const allowDelete = (message) => {
|
const allowDelete = (message: any) => {
|
||||||
if (isReadOnly) {
|
if (isReadOnly) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +136,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
if (message.ts != null) {
|
if (message.ts != null) {
|
||||||
msgTs = moment(message.ts);
|
msgTs = moment(message.ts);
|
||||||
}
|
}
|
||||||
let currentTsDiff;
|
let currentTsDiff: any;
|
||||||
if (msgTs != null) {
|
if (msgTs != null) {
|
||||||
currentTsDiff = moment().diff(msgTs, 'minutes');
|
currentTsDiff = moment().diff(msgTs, 'minutes');
|
||||||
}
|
}
|
||||||
|
@ -115,19 +145,19 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPermalink = message => RocketChat.getPermalinkMessage(message);
|
const getPermalink = (message: any) => RocketChat.getPermalinkMessage(message);
|
||||||
|
|
||||||
const handleReply = (message) => {
|
const handleReply = (message: any) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_REPLY);
|
logEvent(events.ROOM_MSG_ACTION_REPLY);
|
||||||
replyInit(message, true);
|
replyInit(message, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = (message) => {
|
const handleEdit = (message: any) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_EDIT);
|
logEvent(events.ROOM_MSG_ACTION_EDIT);
|
||||||
editInit(message);
|
editInit(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateDiscussion = (message) => {
|
const handleCreateDiscussion = (message: any) => {
|
||||||
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) {
|
||||||
|
@ -137,7 +167,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUnread = async(message) => {
|
const handleUnread = async (message: any) => {
|
||||||
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;
|
||||||
|
@ -149,7 +179,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
const subRecord = await subCollection.find(rid);
|
const subRecord = await subCollection.find(rid);
|
||||||
await db.action(async () => {
|
await db.action(async () => {
|
||||||
try {
|
try {
|
||||||
await subRecord.update(sub => sub.lastOpen = ts);
|
await subRecord.update((sub: any) => sub.lastOpen = ts);
|
||||||
} catch {
|
} catch {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -162,10 +192,10 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePermalink = async(message) => {
|
const handlePermalink = async (message: any) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_PERMALINK);
|
logEvent(events.ROOM_MSG_ACTION_PERMALINK);
|
||||||
try {
|
try {
|
||||||
const permalink = await getPermalink(message);
|
const permalink: any = 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 {
|
||||||
|
@ -173,28 +203,28 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCopy = async(message) => {
|
const handleCopy = async (message: any) => {
|
||||||
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) => {
|
const handleShare = async (message: any) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_SHARE);
|
logEvent(events.ROOM_MSG_ACTION_SHARE);
|
||||||
try {
|
try {
|
||||||
const permalink = await getPermalink(message);
|
const permalink: any = await getPermalink(message);
|
||||||
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) => {
|
const handleQuote = (message: any) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_QUOTE);
|
logEvent(events.ROOM_MSG_ACTION_QUOTE);
|
||||||
replyInit(message, false);
|
replyInit(message, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStar = async(message) => {
|
const handleStar = async (message: any) => {
|
||||||
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);
|
||||||
|
@ -205,7 +235,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePin = async(message) => {
|
const handlePin = async (message: any) => {
|
||||||
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);
|
||||||
|
@ -215,7 +245,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReaction = (shortname, message) => {
|
const handleReaction = (shortname: any, message: any) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_REACTION);
|
logEvent(events.ROOM_MSG_ACTION_REACTION);
|
||||||
if (shortname) {
|
if (shortname) {
|
||||||
onReactionPress(shortname, message.id);
|
onReactionPress(shortname, message.id);
|
||||||
|
@ -226,7 +256,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
hideActionSheet();
|
hideActionSheet();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReadReceipt = (message) => {
|
const handleReadReceipt = (message: any) => {
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
Navigation.navigate('ModalStackNavigator', { screen: 'ReadReceiptsView', params: { messageId: message.id } });
|
Navigation.navigate('ModalStackNavigator', { screen: 'ReadReceiptsView', params: { messageId: message.id } });
|
||||||
} else {
|
} else {
|
||||||
|
@ -234,11 +264,11 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggleTranslation = async(message) => {
|
const handleToggleTranslation = async (message: any) => {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
await db.action(async () => {
|
await db.action(async () => {
|
||||||
await message.update((m) => {
|
await message.update((m: any) => {
|
||||||
m.autoTranslate = !m.autoTranslate;
|
m.autoTranslate = !m.autoTranslate;
|
||||||
m._updatedAt = new Date();
|
m._updatedAt = new Date();
|
||||||
});
|
});
|
||||||
|
@ -249,7 +279,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
_id: message.id,
|
_id: message.id,
|
||||||
rid: message.subscription.id,
|
rid: message.subscription.id,
|
||||||
u: message.u,
|
u: message.u,
|
||||||
msg: message.msg
|
msg: message.msg,
|
||||||
};
|
};
|
||||||
await RocketChat.translateMessage(m, room.autoTranslateLanguage);
|
await RocketChat.translateMessage(m, room.autoTranslateLanguage);
|
||||||
}
|
}
|
||||||
|
@ -258,7 +288,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReport = async(message) => {
|
const handleReport = async (message: any) => {
|
||||||
logEvent(events.ROOM_MSG_ACTION_REPORT);
|
logEvent(events.ROOM_MSG_ACTION_REPORT);
|
||||||
try {
|
try {
|
||||||
await RocketChat.reportMessage(message.id);
|
await RocketChat.reportMessage(message.id);
|
||||||
|
@ -269,7 +299,9 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = (message) => {
|
const handleDelete = (message: any) => {
|
||||||
|
// TODO - migrate this function for ts when fix the lint erros
|
||||||
|
// @ts-ignore
|
||||||
showConfirmationAlert({
|
showConfirmationAlert({
|
||||||
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
|
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
|
||||||
confirmationText: I18n.t('Delete'),
|
confirmationText: I18n.t('Delete'),
|
||||||
|
@ -281,19 +313,19 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
logEvent(events.ROOM_MSG_ACTION_DELETE_F);
|
logEvent(events.ROOM_MSG_ACTION_DELETE_F);
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOptions = (message) => {
|
const getOptions = (message: any) => {
|
||||||
let options = [];
|
let options: any = [];
|
||||||
|
|
||||||
// Reply
|
// Reply
|
||||||
if (!isReadOnly) {
|
if (!isReadOnly) {
|
||||||
options = [{
|
options = [{
|
||||||
title: I18n.t('Reply_in_Thread'),
|
title: I18n.t('Reply_in_Thread'),
|
||||||
icon: 'threads',
|
icon: 'threads',
|
||||||
onPress: () => handleReply(message)
|
onPress: () => handleReply(message),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +334,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Quote'),
|
title: I18n.t('Quote'),
|
||||||
icon: 'quote',
|
icon: 'quote',
|
||||||
onPress: () => handleQuote(message)
|
onPress: () => handleQuote(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +343,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Edit'),
|
title: I18n.t('Edit'),
|
||||||
icon: 'edit',
|
icon: 'edit',
|
||||||
onPress: () => handleEdit(message)
|
onPress: () => handleEdit(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,14 +351,14 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Permalink'),
|
title: I18n.t('Permalink'),
|
||||||
icon: 'link',
|
icon: 'link',
|
||||||
onPress: () => handlePermalink(message)
|
onPress: () => handlePermalink(message),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create Discussion
|
// Create Discussion
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Start_a_Discussion'),
|
title: I18n.t('Start_a_Discussion'),
|
||||||
icon: 'discussions',
|
icon: 'discussions',
|
||||||
onPress: () => handleCreateDiscussion(message)
|
onPress: () => handleCreateDiscussion(message),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mark as unread
|
// Mark as unread
|
||||||
|
@ -334,7 +366,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Mark_unread'),
|
title: I18n.t('Mark_unread'),
|
||||||
icon: 'flag',
|
icon: 'flag',
|
||||||
onPress: () => handleUnread(message)
|
onPress: () => handleUnread(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,14 +374,14 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Copy'),
|
title: I18n.t('Copy'),
|
||||||
icon: 'copy',
|
icon: 'copy',
|
||||||
onPress: () => handleCopy(message)
|
onPress: () => handleCopy(message),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Share
|
// Share
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Share'),
|
title: I18n.t('Share'),
|
||||||
icon: 'share',
|
icon: 'share',
|
||||||
onPress: () => handleShare(message)
|
onPress: () => handleShare(message),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Star
|
// Star
|
||||||
|
@ -357,7 +389,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t(message.starred ? 'Unstar' : 'Star'),
|
title: I18n.t(message.starred ? 'Unstar' : 'Star'),
|
||||||
icon: message.starred ? 'star-filled' : 'star',
|
icon: message.starred ? 'star-filled' : 'star',
|
||||||
onPress: () => handleStar(message)
|
onPress: () => handleStar(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +398,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t(message.pinned ? 'Unpin' : 'Pin'),
|
title: I18n.t(message.pinned ? 'Unpin' : 'Pin'),
|
||||||
icon: 'pin',
|
icon: 'pin',
|
||||||
onPress: () => handlePin(message)
|
onPress: () => handlePin(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +407,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Read_Receipt'),
|
title: I18n.t('Read_Receipt'),
|
||||||
icon: 'info',
|
icon: 'info',
|
||||||
onPress: () => handleReadReceipt(message)
|
onPress: () => handleReadReceipt(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,7 +416,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t(message.autoTranslate ? 'View_Original' : 'Translate'),
|
title: I18n.t(message.autoTranslate ? 'View_Original' : 'Translate'),
|
||||||
icon: 'language',
|
icon: 'language',
|
||||||
onPress: () => handleToggleTranslation(message)
|
onPress: () => handleToggleTranslation(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +425,7 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
title: I18n.t('Report'),
|
title: I18n.t('Report'),
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
danger: true,
|
danger: true,
|
||||||
onPress: () => handleReport(message)
|
onPress: () => handleReport(message),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
|
@ -402,27 +434,27 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
title: I18n.t('Delete'),
|
title: I18n.t('Delete'),
|
||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
danger: true,
|
danger: true,
|
||||||
onPress: () => handleDelete(message)
|
onPress: () => handleDelete(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
|
|
||||||
const showMessageActions = async(message) => {
|
const showMessageActions = async (message: any) => {
|
||||||
logEvent(events.ROOM_SHOW_MSG_ACTIONS);
|
logEvent(events.ROOM_SHOW_MSG_ACTIONS);
|
||||||
await getPermissions();
|
await getPermissions();
|
||||||
showActionSheet({
|
showActionSheet({
|
||||||
options: getOptions(message),
|
options: getOptions(message),
|
||||||
headerHeight: HEADER_HEIGHT,
|
headerHeight: HEADER_HEIGHT,
|
||||||
customHeader: (!isReadOnly || room.reactWhenReadOnly ? (
|
customHeader: !isReadOnly || room.reactWhenReadOnly ? (
|
||||||
<Header
|
<Header
|
||||||
server={server}
|
server={server}
|
||||||
handleReaction={handleReaction}
|
handleReaction={handleReaction}
|
||||||
isMasterDetail={isMasterDetail}
|
isMasterDetail={isMasterDetail}
|
||||||
message={message}
|
message={message}
|
||||||
/>
|
/>
|
||||||
) : null)
|
) : null,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -430,30 +462,8 @@ const MessageActions = React.memo(forwardRef(({
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}));
|
}));
|
||||||
MessageActions.propTypes = {
|
|
||||||
room: PropTypes.object,
|
|
||||||
tmid: PropTypes.string,
|
|
||||||
user: PropTypes.object,
|
|
||||||
editInit: PropTypes.func,
|
|
||||||
reactionInit: PropTypes.func,
|
|
||||||
onReactionPress: PropTypes.func,
|
|
||||||
replyInit: PropTypes.func,
|
|
||||||
isReadOnly: PropTypes.bool,
|
|
||||||
Message_AllowDeleting: PropTypes.bool,
|
|
||||||
Message_AllowDeleting_BlockDeleteInMinutes: PropTypes.number,
|
|
||||||
Message_AllowEditing: PropTypes.bool,
|
|
||||||
Message_AllowEditing_BlockEditInMinutes: PropTypes.number,
|
|
||||||
Message_AllowPinning: PropTypes.bool,
|
|
||||||
Message_AllowStarring: PropTypes.bool,
|
|
||||||
Message_Read_Receipt_Store_Users: PropTypes.bool,
|
|
||||||
server: PropTypes.string,
|
|
||||||
editMessagePermission: PropTypes.array,
|
|
||||||
deleteMessagePermission: PropTypes.array,
|
|
||||||
forceDeleteMessagePermission: PropTypes.array,
|
|
||||||
pinMessagePermission: PropTypes.array
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Message_AllowDeleting: state.settings.Message_AllowDeleting,
|
Message_AllowDeleting: state.settings.Message_AllowDeleting,
|
||||||
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
|
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
|
||||||
|
@ -466,7 +476,7 @@ const mapStateToProps = state => ({
|
||||||
editMessagePermission: state.permissions['edit-message'],
|
editMessagePermission: state.permissions['edit-message'],
|
||||||
deleteMessagePermission: state.permissions['delete-message'],
|
deleteMessagePermission: state.permissions['delete-message'],
|
||||||
forceDeleteMessagePermission: state.permissions['force-delete-message'],
|
forceDeleteMessagePermission: state.permissions['force-delete-message'],
|
||||||
pinMessagePermission: state.permissions['pin-message']
|
pinMessagePermission: state.permissions['pin-message'],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions);
|
export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions);
|
|
@ -1,5 +1,4 @@
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { TouchableOpacity } from 'react-native';
|
import { TouchableOpacity } from 'react-native';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
|
|
||||||
|
@ -9,7 +8,16 @@ import { themes } from '../../../constants/colors';
|
||||||
import MessageboxContext from '../Context';
|
import MessageboxContext from '../Context';
|
||||||
import ActivityIndicator from '../../ActivityIndicator';
|
import ActivityIndicator from '../../ActivityIndicator';
|
||||||
|
|
||||||
const Item = ({ item, theme }) => {
|
interface IMessageBoxCommandsPreviewItem {
|
||||||
|
item: {
|
||||||
|
type: string;
|
||||||
|
id: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item = ({ item, theme }: 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);
|
||||||
|
@ -38,9 +46,4 @@ const Item = ({ item, theme }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Item.propTypes = {
|
|
||||||
item: PropTypes.object,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Item;
|
export default Item;
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FlatList } from 'react-native';
|
import { FlatList } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
|
||||||
import Item from './Item';
|
import Item from './Item';
|
||||||
|
@ -8,7 +7,13 @@ import styles from '../styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { withTheme } from '../../../theme';
|
import { withTheme } from '../../../theme';
|
||||||
|
|
||||||
const CommandsPreview = React.memo(({ theme, commandPreview, showCommandPreview }) => {
|
interface IMessageBoxCommandsPreview {
|
||||||
|
commandPreview: [];
|
||||||
|
showCommandPreview: boolean;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CommandsPreview = React.memo(({ theme, commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => {
|
||||||
if (!showCommandPreview) {
|
if (!showCommandPreview) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +23,7 @@ const CommandsPreview = React.memo(({ theme, commandPreview, showCommandPreview
|
||||||
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} theme={theme} />}
|
||||||
keyExtractor={item => item.id}
|
keyExtractor={(item: any) => item.id}
|
||||||
keyboardShouldPersistTaps='always'
|
keyboardShouldPersistTaps='always'
|
||||||
horizontal
|
horizontal
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
|
@ -37,10 +42,4 @@ const CommandsPreview = React.memo(({ theme, commandPreview, showCommandPreview
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
CommandsPreview.propTypes = {
|
|
||||||
commandPreview: PropTypes.array,
|
|
||||||
showCommandPreview: PropTypes.bool,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withTheme(CommandsPreview);
|
export default withTheme(CommandsPreview);
|
|
@ -1,4 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const MessageboxContext = React.createContext();
|
|
||||||
export default MessageboxContext;
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const MessageboxContext = React.createContext<any>();
|
||||||
|
export default MessageboxContext;
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
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 PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import store from '../../lib/createStore';
|
import store from '../../lib/createStore';
|
||||||
import EmojiPicker from '../EmojiPicker';
|
import EmojiPicker from '../EmojiPicker';
|
||||||
|
@ -9,18 +8,20 @@ import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
|
|
||||||
export default class EmojiKeyboard extends React.PureComponent {
|
interface IMessageBoxEmojiKeyboard {
|
||||||
static propTypes = {
|
theme: string
|
||||||
theme: PropTypes.string
|
}
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiKeyboard, any> {
|
||||||
|
private readonly baseUrl: any;
|
||||||
|
|
||||||
|
constructor(props: IMessageBoxEmojiKeyboard) {
|
||||||
super(props);
|
super(props);
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
this.baseUrl = state.share.server.server || state.server.server;
|
this.baseUrl = state.share.server.server || state.server.server;
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmojiSelected = (emoji) => {
|
onEmojiSelected = (emoji: any) => {
|
||||||
KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji });
|
KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { CancelEditingButton, ToggleEmojiButton } from './buttons';
|
import { CancelEditingButton, ToggleEmojiButton } from './buttons';
|
||||||
|
|
||||||
|
interface IMessageBoxLeftButtons {
|
||||||
|
theme: string;
|
||||||
|
showEmojiKeyboard: boolean;
|
||||||
|
openEmoji(): void;
|
||||||
|
closeEmoji(): void;
|
||||||
|
editing: boolean;
|
||||||
|
editCancel(): void;
|
||||||
|
}
|
||||||
|
|
||||||
const LeftButtons = React.memo(({
|
const LeftButtons = React.memo(({
|
||||||
theme, showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji
|
theme, showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji,
|
||||||
}) => {
|
}: IMessageBoxLeftButtons) => {
|
||||||
if (editing) {
|
if (editing) {
|
||||||
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
||||||
}
|
}
|
||||||
|
@ -19,13 +27,4 @@ const LeftButtons = React.memo(({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
LeftButtons.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
showEmojiKeyboard: PropTypes.bool,
|
|
||||||
openEmoji: PropTypes.func.isRequired,
|
|
||||||
closeEmoji: PropTypes.func.isRequired,
|
|
||||||
editing: PropTypes.bool,
|
|
||||||
editCancel: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LeftButtons;
|
export default LeftButtons;
|
|
@ -1,13 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import { CancelEditingButton, ActionsButton } from './buttons';
|
import { ActionsButton, CancelEditingButton } from './buttons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
interface IMessageBoxLeftButtons {
|
||||||
|
theme: string;
|
||||||
|
showMessageBoxActions(): void;
|
||||||
|
editing: boolean;
|
||||||
|
editCancel(): void;
|
||||||
|
isActionsEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const LeftButtons = React.memo(({
|
const LeftButtons = React.memo(({
|
||||||
theme, showMessageBoxActions, editing, editCancel, isActionsEnabled
|
theme, showMessageBoxActions, editing, editCancel, isActionsEnabled,
|
||||||
}) => {
|
}: IMessageBoxLeftButtons) => {
|
||||||
if (editing) {
|
if (editing) {
|
||||||
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
||||||
}
|
}
|
||||||
|
@ -17,12 +24,4 @@ const LeftButtons = React.memo(({
|
||||||
return <View style={styles.buttonsWhitespace} />;
|
return <View style={styles.buttonsWhitespace} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
LeftButtons.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
showMessageBoxActions: PropTypes.func.isRequired,
|
|
||||||
editing: PropTypes.bool,
|
|
||||||
editCancel: PropTypes.func.isRequired,
|
|
||||||
isActionsEnabled: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LeftButtons;
|
export default LeftButtons;
|
|
@ -1,19 +1,26 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { TouchableOpacity, Text } from 'react-native';
|
import { Text, TouchableOpacity } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
|
|
||||||
const FixedMentionItem = ({ item, onPress, theme }) => (
|
interface IMessageBoxFixedMentionItem {
|
||||||
|
item: {
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
onPress: Function;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FixedMentionItem = ({ item, onPress, theme }: IMessageBoxFixedMentionItem) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.mentionItem,
|
styles.mentionItem,
|
||||||
{
|
{
|
||||||
backgroundColor: themes[theme].auxiliaryBackground,
|
backgroundColor: themes[theme].auxiliaryBackground,
|
||||||
borderTopColor: themes[theme].separatorColor
|
borderTopColor: themes[theme].separatorColor,
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
onPress={() => onPress(item)}
|
onPress={() => onPress(item)}
|
||||||
>
|
>
|
||||||
|
@ -24,10 +31,4 @@ const FixedMentionItem = ({ item, onPress, theme }) => (
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
||||||
FixedMentionItem.propTypes = {
|
|
||||||
item: PropTypes.object,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FixedMentionItem;
|
export default FixedMentionItem;
|
|
@ -6,8 +6,13 @@ import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import MessageboxContext from '../Context';
|
import MessageboxContext from '../Context';
|
||||||
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||||
|
import { IEmoji } from '../../EmojiPicker/interfaces';
|
||||||
|
|
||||||
const MentionEmoji = ({ item }) => {
|
interface IMessageBoxMentionEmoji {
|
||||||
|
item: IEmoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MentionEmoji = ({ item }: IMessageBoxMentionEmoji) => {
|
||||||
const context = useContext(MessageboxContext);
|
const context = useContext(MessageboxContext);
|
||||||
const { baseUrl } = context;
|
const { baseUrl } = context;
|
||||||
|
|
||||||
|
@ -28,7 +33,7 @@ const MentionEmoji = ({ item }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
MentionEmoji.propTypes = {
|
MentionEmoji.propTypes = {
|
||||||
item: PropTypes.object
|
item: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MentionEmoji;
|
export default MentionEmoji;
|
|
@ -1,25 +1,32 @@
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { TouchableOpacity, Text } from 'react-native';
|
import { Text, TouchableOpacity } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import Avatar from '../../Avatar';
|
import Avatar from '../../Avatar';
|
||||||
import MessageboxContext from '../Context';
|
import MessageboxContext from '../Context';
|
||||||
import FixedMentionItem from './FixedMentionItem';
|
import FixedMentionItem from './FixedMentionItem';
|
||||||
import MentionEmoji from './MentionEmoji';
|
import MentionEmoji from './MentionEmoji';
|
||||||
import {
|
import { MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_EMOJIS } from '../constants';
|
||||||
MENTIONS_TRACKING_TYPE_EMOJIS,
|
|
||||||
MENTIONS_TRACKING_TYPE_COMMANDS
|
|
||||||
} from '../constants';
|
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
|
import { IEmoji } from '../../EmojiPicker/interfaces';
|
||||||
|
|
||||||
const MentionItem = ({
|
interface IMessageBoxMentionItem {
|
||||||
item, trackingType, theme
|
item: {
|
||||||
}) => {
|
name: string;
|
||||||
|
command: string;
|
||||||
|
username: string;
|
||||||
|
t: string;
|
||||||
|
id: string;
|
||||||
|
} & IEmoji;
|
||||||
|
trackingType: string;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
|
||||||
const context = useContext(MessageboxContext);
|
const context = useContext(MessageboxContext);
|
||||||
const { onPressMention } = context;
|
const { onPressMention } = context;
|
||||||
|
|
||||||
const defineTestID = (type) => {
|
const defineTestID = (type: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MENTIONS_TRACKING_TYPE_EMOJIS:
|
case MENTIONS_TRACKING_TYPE_EMOJIS:
|
||||||
return `mention-item-${ item.name || item }`;
|
return `mention-item-${ item.name || item }`;
|
||||||
|
@ -72,8 +79,8 @@ const MentionItem = ({
|
||||||
styles.mentionItem,
|
styles.mentionItem,
|
||||||
{
|
{
|
||||||
backgroundColor: themes[theme].auxiliaryBackground,
|
backgroundColor: themes[theme].auxiliaryBackground,
|
||||||
borderTopColor: themes[theme].separatorColor
|
borderTopColor: themes[theme].separatorColor,
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
onPress={() => onPressMention(item)}
|
onPress={() => onPressMention(item)}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
|
@ -83,10 +90,4 @@ const MentionItem = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
MentionItem.propTypes = {
|
|
||||||
item: PropTypes.object,
|
|
||||||
trackingType: PropTypes.string,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MentionItem;
|
export default MentionItem;
|
|
@ -1,13 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FlatList, View } from 'react-native';
|
import { FlatList, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import MentionItem from './MentionItem';
|
import MentionItem from './MentionItem';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
|
|
||||||
const Mentions = React.memo(({ mentions, trackingType, theme }) => {
|
interface IMessageBoxMentions {
|
||||||
|
mentions: [];
|
||||||
|
trackingType: string;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Mentions = React.memo(({ mentions, trackingType, theme }: IMessageBoxMentions) => {
|
||||||
if (!trackingType) {
|
if (!trackingType) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +23,7 @@ const Mentions = React.memo(({ mentions, trackingType, theme }) => {
|
||||||
data={mentions}
|
data={mentions}
|
||||||
extraData={mentions}
|
extraData={mentions}
|
||||||
renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} theme={theme} />}
|
renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} theme={theme} />}
|
||||||
keyExtractor={item => item.rid || item.name || item.command || item}
|
keyExtractor={(item: any) => item.rid || item.name || item.command || item}
|
||||||
keyboardShouldPersistTaps='always'
|
keyboardShouldPersistTaps='always'
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
@ -36,10 +41,4 @@ const Mentions = React.memo(({ mentions, trackingType, theme }) => {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
Mentions.propTypes = {
|
|
||||||
mentions: PropTypes.array,
|
|
||||||
trackingType: PropTypes.string,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Mentions;
|
export default Mentions;
|
|
@ -1,16 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Text, View } from 'react-native';
|
||||||
import { View, Text } from 'react-native';
|
|
||||||
import { Audio } from 'expo-av';
|
import { Audio } from 'expo-av';
|
||||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||||
import { getInfoAsync } from 'expo-file-system';
|
import { getInfoAsync } from 'expo-file-system';
|
||||||
import { deactivateKeepAwake, activateKeepAwake } from 'expo-keep-awake';
|
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 '../../constants/colors';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { logEvent, events } from '../../utils/log';
|
import { events, logEvent } from '../../utils/log';
|
||||||
|
|
||||||
|
interface IMessageBoxRecordAudioProps {
|
||||||
|
theme: string;
|
||||||
|
recordingCallback: Function;
|
||||||
|
onFinish: Function;
|
||||||
|
}
|
||||||
|
|
||||||
const RECORDING_EXTENSION = '.aac';
|
const RECORDING_EXTENSION = '.aac';
|
||||||
const RECORDING_SETTINGS = {
|
const RECORDING_SETTINGS = {
|
||||||
|
@ -20,7 +25,7 @@ const RECORDING_SETTINGS = {
|
||||||
audioEncoder: Audio.RECORDING_OPTION_ANDROID_AUDIO_ENCODER_AAC,
|
audioEncoder: Audio.RECORDING_OPTION_ANDROID_AUDIO_ENCODER_AAC,
|
||||||
sampleRate: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.android.sampleRate,
|
sampleRate: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.android.sampleRate,
|
||||||
numberOfChannels: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.android.numberOfChannels,
|
numberOfChannels: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.android.numberOfChannels,
|
||||||
bitRate: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.android.bitRate
|
bitRate: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.android.bitRate,
|
||||||
},
|
},
|
||||||
ios: {
|
ios: {
|
||||||
extension: RECORDING_EXTENSION,
|
extension: RECORDING_EXTENSION,
|
||||||
|
@ -28,8 +33,8 @@ const RECORDING_SETTINGS = {
|
||||||
sampleRate: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.ios.sampleRate,
|
sampleRate: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.ios.sampleRate,
|
||||||
numberOfChannels: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.ios.numberOfChannels,
|
numberOfChannels: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.ios.numberOfChannels,
|
||||||
bitRate: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.ios.bitRate,
|
bitRate: Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY.ios.bitRate,
|
||||||
outputFormat: Audio.RECORDING_OPTION_IOS_OUTPUT_FORMAT_MPEG4AAC
|
outputFormat: Audio.RECORDING_OPTION_IOS_OUTPUT_FORMAT_MPEG4AAC,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
const RECORDING_MODE = {
|
const RECORDING_MODE = {
|
||||||
allowsRecordingIOS: true,
|
allowsRecordingIOS: true,
|
||||||
|
@ -38,30 +43,28 @@ const RECORDING_MODE = {
|
||||||
shouldDuckAndroid: true,
|
shouldDuckAndroid: true,
|
||||||
playThroughEarpieceAndroid: false,
|
playThroughEarpieceAndroid: false,
|
||||||
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
|
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
|
||||||
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX
|
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatTime = function(seconds) {
|
const formatTime = function(seconds: any) {
|
||||||
let minutes = Math.floor(seconds / 60);
|
let minutes: any = Math.floor(seconds / 60);
|
||||||
seconds %= 60;
|
seconds %= 60;
|
||||||
if (minutes < 10) { minutes = `0${ minutes }`; }
|
if (minutes < 10) { minutes = `0${ minutes }`; }
|
||||||
if (seconds < 10) { seconds = `0${ seconds }`; }
|
if (seconds < 10) { seconds = `0${ seconds }`; }
|
||||||
return `${ minutes }:${ seconds }`;
|
return `${ minutes }:${ seconds }`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class RecordAudio extends React.PureComponent {
|
export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAudioProps, any> {
|
||||||
static propTypes = {
|
private isRecorderBusy: boolean;
|
||||||
theme: PropTypes.string,
|
|
||||||
recordingCallback: PropTypes.func,
|
|
||||||
onFinish: PropTypes.func
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
private recording: any;
|
||||||
|
|
||||||
|
constructor(props: IMessageBoxRecordAudioProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.isRecorderBusy = false;
|
this.isRecorderBusy = false;
|
||||||
this.state = {
|
this.state = {
|
||||||
isRecording: false,
|
isRecording: false,
|
||||||
recordingDurationMillis: 0
|
recordingDurationMillis: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,10 +99,10 @@ export default class RecordAudio extends React.PureComponent {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onRecordingStatusUpdate = (status) => {
|
onRecordingStatusUpdate = (status: any) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isRecording: status.isRecording,
|
isRecording: status.isRecording,
|
||||||
recordingDurationMillis: status.durationMillis
|
recordingDurationMillis: status.durationMillis,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +148,7 @@ export default class RecordAudio extends React.PureComponent {
|
||||||
type: 'audio/aac',
|
type: 'audio/aac',
|
||||||
store: 'Uploads',
|
store: 'Uploads',
|
||||||
path: fileURI,
|
path: fileURI,
|
||||||
size: fileData.size
|
size: fileData.size,
|
||||||
};
|
};
|
||||||
|
|
||||||
onFinish(fileInfo);
|
onFinish(fileInfo);
|
||||||
|
@ -183,6 +186,7 @@ export default class RecordAudio extends React.PureComponent {
|
||||||
onPress={this.startRecordingAudio}
|
onPress={this.startRecordingAudio}
|
||||||
style={styles.actionButton}
|
style={styles.actionButton}
|
||||||
testID='messagebox-send-audio'
|
testID='messagebox-send-audio'
|
||||||
|
// @ts-ignore
|
||||||
accessibilityLabel={I18n.t('Send_audio_message')}
|
accessibilityLabel={I18n.t('Send_audio_message')}
|
||||||
accessibilityTraits='button'
|
accessibilityTraits='button'
|
||||||
>
|
>
|
||||||
|
@ -196,6 +200,7 @@ export default class RecordAudio extends React.PureComponent {
|
||||||
<View style={styles.textArea}>
|
<View style={styles.textArea}>
|
||||||
<BorderlessButton
|
<BorderlessButton
|
||||||
onPress={this.cancelRecordingAudio}
|
onPress={this.cancelRecordingAudio}
|
||||||
|
// @ts-ignore
|
||||||
accessibilityLabel={I18n.t('Cancel_recording')}
|
accessibilityLabel={I18n.t('Cancel_recording')}
|
||||||
accessibilityTraits='button'
|
accessibilityTraits='button'
|
||||||
style={styles.actionButton}
|
style={styles.actionButton}
|
||||||
|
@ -214,6 +219,7 @@ export default class RecordAudio extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
<BorderlessButton
|
<BorderlessButton
|
||||||
onPress={this.finishRecordingAudio}
|
onPress={this.finishRecordingAudio}
|
||||||
|
// @ts-ignore
|
||||||
accessibilityLabel={I18n.t('Finish_recording')}
|
accessibilityLabel={I18n.t('Finish_recording')}
|
||||||
accessibilityTraits='button'
|
accessibilityTraits='button'
|
||||||
style={styles.actionButton}
|
style={styles.actionButton}
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, StyleSheet } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
@ -12,38 +11,54 @@ import { themes } from '../../constants/colors';
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
paddingTop: 10
|
paddingTop: 10,
|
||||||
},
|
},
|
||||||
messageContainer: {
|
messageContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
marginHorizontal: 10,
|
marginHorizontal: 10,
|
||||||
paddingHorizontal: 15,
|
paddingHorizontal: 15,
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
borderRadius: 4
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium,
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
lineHeight: 16,
|
lineHeight: 16,
|
||||||
marginLeft: 6,
|
marginLeft: 6,
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular,
|
||||||
fontWeight: '300'
|
fontWeight: '300',
|
||||||
},
|
},
|
||||||
close: {
|
close: {
|
||||||
marginRight: 10
|
marginRight: 10,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface IMessageBoxReplyPreview {
|
||||||
|
replying: boolean;
|
||||||
|
message: {
|
||||||
|
ts: Date;
|
||||||
|
msg: string;
|
||||||
|
u: any;
|
||||||
|
};
|
||||||
|
Message_TimeFormat: string;
|
||||||
|
close(): void;
|
||||||
|
baseUrl: string;
|
||||||
|
username: string;
|
||||||
|
getCustomEmoji: Function;
|
||||||
|
theme: string;
|
||||||
|
useRealName: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const ReplyPreview = React.memo(({
|
const ReplyPreview = React.memo(({
|
||||||
message, Message_TimeFormat, baseUrl, username, replying, getCustomEmoji, close, theme, useRealName
|
message, Message_TimeFormat, baseUrl, username, replying, getCustomEmoji, close, theme, useRealName,
|
||||||
}) => {
|
}: IMessageBoxReplyPreview) => {
|
||||||
if (!replying) {
|
if (!replying) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -53,7 +68,7 @@ const ReplyPreview = React.memo(({
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.container,
|
styles.container,
|
||||||
{ backgroundColor: themes[theme].messageboxBackground }
|
{ backgroundColor: themes[theme].messageboxBackground },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<View style={[styles.messageContainer, { backgroundColor: themes[theme].chatComponentBackground }]}>
|
<View style={[styles.messageContainer, { backgroundColor: themes[theme].chatComponentBackground }]}>
|
||||||
|
@ -61,6 +76,7 @@ const ReplyPreview = React.memo(({
|
||||||
<Text style={[styles.username, { color: themes[theme].tintColor }]}>{useRealName ? message.u?.name : message.u?.username}</Text>
|
<Text style={[styles.username, { color: themes[theme].tintColor }]}>{useRealName ? message.u?.name : message.u?.username}</Text>
|
||||||
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
{/* @ts-ignore*/}
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={message.msg}
|
msg={message.msg}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
|
@ -74,24 +90,12 @@ const ReplyPreview = React.memo(({
|
||||||
<CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
|
<CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}, (prevProps, nextProps) => prevProps.replying === nextProps.replying && prevProps.theme === nextProps.theme && prevProps.message.id === nextProps.message.id);
|
}, (prevProps: any, nextProps: any) => prevProps.replying === nextProps.replying && prevProps.theme === nextProps.theme && prevProps.message.id === nextProps.message.id);
|
||||||
|
|
||||||
ReplyPreview.propTypes = {
|
const mapStateToProps = (state: any) => ({
|
||||||
replying: PropTypes.bool,
|
|
||||||
message: PropTypes.object.isRequired,
|
|
||||||
Message_TimeFormat: PropTypes.string.isRequired,
|
|
||||||
close: PropTypes.func.isRequired,
|
|
||||||
baseUrl: PropTypes.string.isRequired,
|
|
||||||
username: PropTypes.string.isRequired,
|
|
||||||
getCustomEmoji: PropTypes.func,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
useRealName: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
useRealName: state.settings.UI_Use_Real_Name
|
useRealName: state.settings.UI_Use_Real_Name,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(ReplyPreview);
|
export default connect(mapStateToProps)(ReplyPreview);
|
|
@ -1,13 +1,20 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import { SendButton, ActionsButton } from './buttons';
|
import { ActionsButton, SendButton } from './buttons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
interface IMessageBoxRightButtons {
|
||||||
|
theme: string;
|
||||||
|
showSend: boolean;
|
||||||
|
submit(): void;
|
||||||
|
showMessageBoxActions(): void;
|
||||||
|
isActionsEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const RightButtons = React.memo(({
|
const RightButtons = React.memo(({
|
||||||
theme, showSend, submit, showMessageBoxActions, isActionsEnabled
|
theme, showSend, submit, showMessageBoxActions, isActionsEnabled,
|
||||||
}) => {
|
}: IMessageBoxRightButtons) => {
|
||||||
if (showSend) {
|
if (showSend) {
|
||||||
return <SendButton onPress={submit} theme={theme} />;
|
return <SendButton onPress={submit} theme={theme} />;
|
||||||
}
|
}
|
||||||
|
@ -18,12 +25,4 @@ const RightButtons = React.memo(({
|
||||||
return <View style={styles.buttonsWhitespace} />;
|
return <View style={styles.buttonsWhitespace} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
RightButtons.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
showSend: PropTypes.bool,
|
|
||||||
submit: PropTypes.func.isRequired,
|
|
||||||
showMessageBoxActions: PropTypes.func.isRequired,
|
|
||||||
isActionsEnabled: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RightButtons;
|
export default RightButtons;
|
|
@ -1,19 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { SendButton } from './buttons';
|
|
||||||
|
|
||||||
const RightButtons = React.memo(({ theme, showSend, submit }) => {
|
|
||||||
if (showSend) {
|
|
||||||
return <SendButton theme={theme} onPress={submit} />;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
RightButtons.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
showSend: PropTypes.bool,
|
|
||||||
submit: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RightButtons;
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { SendButton } from './buttons';
|
||||||
|
|
||||||
|
interface IMessageBoxRightButtons {
|
||||||
|
theme: string;
|
||||||
|
showSend: boolean;
|
||||||
|
submit(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RightButtons = React.memo(({ theme, showSend, submit }: IMessageBoxRightButtons) => {
|
||||||
|
if (showSend) {
|
||||||
|
return <SendButton theme={theme} onPress={submit} />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default RightButtons;
|
|
@ -1,9 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import BaseButton from './BaseButton';
|
import BaseButton from './BaseButton';
|
||||||
|
|
||||||
const ActionsButton = React.memo(({ theme, onPress }) => (
|
interface IActionsButton {
|
||||||
|
theme: string;
|
||||||
|
onPress(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionsButton = React.memo(({ theme, onPress }: IActionsButton) => (
|
||||||
<BaseButton
|
<BaseButton
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
testID='messagebox-actions'
|
testID='messagebox-actions'
|
||||||
|
@ -13,9 +17,4 @@ const ActionsButton = React.memo(({ theme, onPress }) => (
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
ActionsButton.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
onPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ActionsButton;
|
export default ActionsButton;
|
|
@ -1,33 +1,31 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
|
|
||||||
const BaseButton = React.memo(({
|
interface IBaseButton {
|
||||||
onPress, testID, accessibilityLabel, icon, theme, color
|
theme: string;
|
||||||
}) => (
|
onPress(): void;
|
||||||
|
testID: string;
|
||||||
|
accessibilityLabel: string;
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BaseButton = React.memo(({ onPress, testID, accessibilityLabel, icon, theme, color }: Partial<IBaseButton>) => (
|
||||||
<BorderlessButton
|
<BorderlessButton
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={styles.actionButton}
|
style={styles.actionButton}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
|
// @ts-ignore
|
||||||
accessibilityLabel={I18n.t(accessibilityLabel)}
|
accessibilityLabel={I18n.t(accessibilityLabel)}
|
||||||
accessibilityTraits='button'
|
accessibilityTraits='button'
|
||||||
>
|
>
|
||||||
<CustomIcon name={icon} size={24} color={color ?? themes[theme].auxiliaryTintColor} />
|
<CustomIcon name={icon} size={24} color={color ?? themes[theme!].auxiliaryTintColor} />
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
));
|
));
|
||||||
|
|
||||||
BaseButton.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
onPress: PropTypes.func.isRequired,
|
|
||||||
testID: PropTypes.string.isRequired,
|
|
||||||
accessibilityLabel: PropTypes.string.isRequired,
|
|
||||||
icon: PropTypes.string.isRequired,
|
|
||||||
color: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BaseButton;
|
export default BaseButton;
|
|
@ -1,9 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import BaseButton from './BaseButton';
|
import BaseButton from './BaseButton';
|
||||||
|
|
||||||
const CancelEditingButton = React.memo(({ theme, onPress }) => (
|
interface ICancelEditingButton {
|
||||||
|
theme: string;
|
||||||
|
onPress():void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CancelEditingButton = React.memo(({ theme, onPress }: ICancelEditingButton) => (
|
||||||
<BaseButton
|
<BaseButton
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
testID='messagebox-cancel-editing'
|
testID='messagebox-cancel-editing'
|
||||||
|
@ -13,9 +17,4 @@ const CancelEditingButton = React.memo(({ theme, onPress }) => (
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
CancelEditingButton.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
onPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CancelEditingButton;
|
export default CancelEditingButton;
|
|
@ -1,10 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import BaseButton from './BaseButton';
|
import BaseButton from './BaseButton';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
|
|
||||||
const SendButton = React.memo(({ theme, onPress }) => (
|
interface ISendButton {
|
||||||
|
theme: string;
|
||||||
|
onPress(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SendButton = React.memo(({ theme, onPress }: ISendButton) => (
|
||||||
<BaseButton
|
<BaseButton
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
testID='messagebox-send-message'
|
testID='messagebox-send-message'
|
||||||
|
@ -15,9 +19,4 @@ const SendButton = React.memo(({ theme, onPress }) => (
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
SendButton.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
onPress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SendButton;
|
export default SendButton;
|
|
@ -1,11 +1,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import BaseButton from './BaseButton';
|
import BaseButton from './BaseButton';
|
||||||
|
|
||||||
const ToggleEmojiButton = React.memo(({
|
interface IToggleEmojiButton {
|
||||||
theme, show, open, close
|
theme: string;
|
||||||
}) => {
|
show: boolean;
|
||||||
|
open(): void;
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToggleEmojiButton = React.memo(({ theme, show, open, close }: IToggleEmojiButton) => {
|
||||||
if (show) {
|
if (show) {
|
||||||
return (
|
return (
|
||||||
<BaseButton
|
<BaseButton
|
||||||
|
@ -28,11 +32,4 @@ const ToggleEmojiButton = React.memo(({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ToggleEmojiButton.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
show: PropTypes.bool,
|
|
||||||
open: PropTypes.func.isRequired,
|
|
||||||
close: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ToggleEmojiButton;
|
export default ToggleEmojiButton;
|
|
@ -7,5 +7,5 @@ export {
|
||||||
CancelEditingButton,
|
CancelEditingButton,
|
||||||
ToggleEmojiButton,
|
ToggleEmojiButton,
|
||||||
SendButton,
|
SendButton,
|
||||||
ActionsButton
|
ActionsButton,
|
||||||
};
|
};
|
|
@ -1,8 +1,5 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { Alert, Keyboard, NativeModules, Text, View } from 'react-native';
|
||||||
import {
|
|
||||||
View, Alert, Keyboard, NativeModules, Text
|
|
||||||
} 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 from 'react-native-image-crop-picker';
|
import ImagePicker from 'react-native-image-crop-picker';
|
||||||
|
@ -18,31 +15,35 @@ import RocketChat from '../../lib/rocketchat';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import { emojis } from '../../emojis';
|
import { emojis } from '../../emojis';
|
||||||
import log, { logEvent, events } from '../../utils/log';
|
import log, { events, logEvent } from '../../utils/log';
|
||||||
import RecordAudio from './RecordAudio';
|
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 '../../constants/colors';
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line import/extensions,import/no-unresolved
|
||||||
import LeftButtons from './LeftButtons';
|
import LeftButtons from './LeftButtons';
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line import/extensions,import/no-unresolved
|
||||||
import RightButtons from './RightButtons';
|
import RightButtons from './RightButtons';
|
||||||
import { isAndroid, isTablet } from '../../utils/deviceInfo';
|
import { isAndroid, isTablet } from '../../utils/deviceInfo';
|
||||||
import { canUploadFile } from '../../utils/media';
|
import { canUploadFile } from '../../utils/media';
|
||||||
import EventEmiter from '../../utils/events';
|
import EventEmiter from '../../utils/events';
|
||||||
import {
|
import {
|
||||||
KEY_COMMAND,
|
KEY_COMMAND,
|
||||||
handleCommandTyping,
|
handleCommandShowUpload,
|
||||||
handleCommandSubmit,
|
handleCommandSubmit,
|
||||||
handleCommandShowUpload
|
handleCommandTyping,
|
||||||
} from '../../commands';
|
} from '../../commands';
|
||||||
import Mentions from './Mentions';
|
import Mentions from './Mentions';
|
||||||
import MessageboxContext from './Context';
|
import MessageboxContext from './Context';
|
||||||
import {
|
import {
|
||||||
MENTIONS_TRACKING_TYPE_EMOJIS,
|
|
||||||
MENTIONS_TRACKING_TYPE_COMMANDS,
|
|
||||||
MENTIONS_COUNT_TO_DISPLAY,
|
MENTIONS_COUNT_TO_DISPLAY,
|
||||||
|
MENTIONS_TRACKING_TYPE_COMMANDS,
|
||||||
|
MENTIONS_TRACKING_TYPE_EMOJIS,
|
||||||
|
MENTIONS_TRACKING_TYPE_ROOMS,
|
||||||
MENTIONS_TRACKING_TYPE_USERS,
|
MENTIONS_TRACKING_TYPE_USERS,
|
||||||
MENTIONS_TRACKING_TYPE_ROOMS
|
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import CommandsPreview from './CommandsPreview';
|
import CommandsPreview from './CommandsPreview';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
@ -59,67 +60,116 @@ const imagePickerConfig = {
|
||||||
cropping: true,
|
cropping: true,
|
||||||
compressImageQuality: 0.8,
|
compressImageQuality: 0.8,
|
||||||
avoidEmptySpaceAroundImage: false,
|
avoidEmptySpaceAroundImage: false,
|
||||||
freeStyleCropEnabled: true
|
freeStyleCropEnabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const libraryPickerConfig = {
|
const libraryPickerConfig = {
|
||||||
multiple: true,
|
multiple: true,
|
||||||
compressVideoPreset: 'Passthrough',
|
compressVideoPreset: 'Passthrough',
|
||||||
mediaType: 'any'
|
mediaType: 'any',
|
||||||
};
|
};
|
||||||
|
|
||||||
const videoPickerConfig = {
|
const videoPickerConfig = {
|
||||||
mediaType: 'video'
|
mediaType: 'video',
|
||||||
};
|
};
|
||||||
|
|
||||||
class MessageBox extends Component {
|
interface IMessageBoxProps {
|
||||||
static propTypes = {
|
rid: string;
|
||||||
rid: PropTypes.string.isRequired,
|
baseUrl: string;
|
||||||
baseUrl: PropTypes.string.isRequired,
|
message: {
|
||||||
message: PropTypes.object,
|
u: {
|
||||||
replying: PropTypes.bool,
|
username: string;
|
||||||
editing: PropTypes.bool,
|
};
|
||||||
threadsEnabled: PropTypes.bool,
|
id: any;
|
||||||
isFocused: PropTypes.func,
|
};
|
||||||
user: PropTypes.shape({
|
replying: boolean;
|
||||||
id: PropTypes.string,
|
editing: boolean;
|
||||||
username: PropTypes.string,
|
threadsEnabled: boolean;
|
||||||
token: PropTypes.string
|
isFocused(): boolean;
|
||||||
}),
|
user: {
|
||||||
roomType: PropTypes.string,
|
id: string;
|
||||||
tmid: PropTypes.string,
|
username: string;
|
||||||
replyWithMention: PropTypes.bool,
|
token: string;
|
||||||
FileUpload_MediaTypeWhiteList: PropTypes.string,
|
};
|
||||||
FileUpload_MaxFileSize: PropTypes.number,
|
roomType: string;
|
||||||
Message_AudioRecorderEnabled: PropTypes.bool,
|
tmid: string;
|
||||||
getCustomEmoji: PropTypes.func,
|
replyWithMention: boolean;
|
||||||
editCancel: PropTypes.func.isRequired,
|
FileUpload_MediaTypeWhiteList: string;
|
||||||
editRequest: PropTypes.func.isRequired,
|
FileUpload_MaxFileSize: number;
|
||||||
onSubmit: PropTypes.func.isRequired,
|
Message_AudioRecorderEnabled: boolean;
|
||||||
typing: PropTypes.func,
|
getCustomEmoji: Function;
|
||||||
theme: PropTypes.string,
|
editCancel: Function;
|
||||||
replyCancel: PropTypes.func,
|
editRequest: Function;
|
||||||
showSend: PropTypes.bool,
|
onSubmit: Function;
|
||||||
navigation: PropTypes.object,
|
typing: Function;
|
||||||
children: PropTypes.node,
|
theme: string;
|
||||||
isMasterDetail: PropTypes.bool,
|
replyCancel(): void;
|
||||||
showActionSheet: PropTypes.func,
|
showSend: boolean;
|
||||||
iOSScrollBehavior: PropTypes.number,
|
navigation: any;
|
||||||
sharing: PropTypes.bool,
|
children: JSX.Element;
|
||||||
isActionsEnabled: PropTypes.bool
|
isMasterDetail: boolean;
|
||||||
|
showActionSheet: Function;
|
||||||
|
iOSScrollBehavior: number;
|
||||||
|
sharing: boolean;
|
||||||
|
isActionsEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IMessageBoxState {
|
||||||
|
mentions: any[];
|
||||||
|
showEmojiKeyboard: boolean;
|
||||||
|
showSend: any;
|
||||||
|
recording: boolean;
|
||||||
|
trackingType: string;
|
||||||
|
commandPreview: [];
|
||||||
|
showCommandPreview: boolean;
|
||||||
|
command: {
|
||||||
|
appId?: any
|
||||||
|
};
|
||||||
|
tshow: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
private text: string;
|
||||||
|
|
||||||
|
private selection: { start: number; end: number };
|
||||||
|
|
||||||
|
private focused: boolean;
|
||||||
|
|
||||||
|
private options: any;
|
||||||
|
|
||||||
|
private imagePickerConfig: any;
|
||||||
|
|
||||||
|
private libraryPickerConfig: any;
|
||||||
|
|
||||||
|
private videoPickerConfig: any;
|
||||||
|
|
||||||
|
private room: any;
|
||||||
|
|
||||||
|
private thread: any;
|
||||||
|
|
||||||
|
private unsubscribeFocus: any;
|
||||||
|
|
||||||
|
private trackingTimeout: any;
|
||||||
|
|
||||||
|
private tracking: any;
|
||||||
|
|
||||||
|
private unsubscribeBlur: any;
|
||||||
|
|
||||||
|
private component: any;
|
||||||
|
|
||||||
|
private typingTimeout: any;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
message: {
|
message: {
|
||||||
id: ''
|
id: '',
|
||||||
},
|
},
|
||||||
sharing: false,
|
sharing: false,
|
||||||
iOSScrollBehavior: NativeModules.KeyboardTrackingViewTempManager?.KeyboardTrackingScrollBehaviorFixedOffset,
|
iOSScrollBehavior: NativeModules.KeyboardTrackingViewTempManager?.KeyboardTrackingScrollBehaviorFixedOffset,
|
||||||
isActionsEnabled: true,
|
isActionsEnabled: true,
|
||||||
getCustomEmoji: () => {}
|
getCustomEmoji: () => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: IMessageBoxProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
mentions: [],
|
mentions: [],
|
||||||
|
@ -130,7 +180,7 @@ class MessageBox extends Component {
|
||||||
commandPreview: [],
|
commandPreview: [],
|
||||||
showCommandPreview: false,
|
showCommandPreview: false,
|
||||||
command: {},
|
command: {},
|
||||||
tshow: false
|
tshow: false,
|
||||||
};
|
};
|
||||||
this.text = '';
|
this.text = '';
|
||||||
this.selection = { start: 0, end: 0 };
|
this.selection = { start: 0, end: 0 };
|
||||||
|
@ -141,54 +191,55 @@ class MessageBox extends Component {
|
||||||
{
|
{
|
||||||
title: I18n.t('Take_a_photo'),
|
title: I18n.t('Take_a_photo'),
|
||||||
icon: 'camera-photo',
|
icon: 'camera-photo',
|
||||||
onPress: this.takePhoto
|
onPress: this.takePhoto,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: I18n.t('Take_a_video'),
|
title: I18n.t('Take_a_video'),
|
||||||
icon: 'camera',
|
icon: 'camera',
|
||||||
onPress: this.takeVideo
|
onPress: this.takeVideo,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: I18n.t('Choose_from_library'),
|
title: I18n.t('Choose_from_library'),
|
||||||
icon: 'image',
|
icon: 'image',
|
||||||
onPress: this.chooseFromLibrary
|
onPress: this.chooseFromLibrary,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: I18n.t('Choose_file'),
|
title: I18n.t('Choose_file'),
|
||||||
icon: 'attach',
|
icon: 'attach',
|
||||||
onPress: this.chooseFile
|
onPress: this.chooseFile,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: I18n.t('Create_Discussion'),
|
title: I18n.t('Create_Discussion'),
|
||||||
icon: 'discussions',
|
icon: 'discussions',
|
||||||
onPress: this.createDiscussion
|
onPress: this.createDiscussion,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const libPickerLabels = {
|
const libPickerLabels = {
|
||||||
cropperChooseText: I18n.t('Choose'),
|
cropperChooseText: I18n.t('Choose'),
|
||||||
cropperCancelText: I18n.t('Cancel'),
|
cropperCancelText: I18n.t('Cancel'),
|
||||||
loadingLabelText: I18n.t('Processing')
|
loadingLabelText: I18n.t('Processing'),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.imagePickerConfig = {
|
this.imagePickerConfig = {
|
||||||
...imagePickerConfig,
|
...imagePickerConfig,
|
||||||
...libPickerLabels
|
...libPickerLabels,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.libraryPickerConfig = {
|
this.libraryPickerConfig = {
|
||||||
...libraryPickerConfig,
|
...libraryPickerConfig,
|
||||||
...libPickerLabels
|
...libPickerLabels,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.videoPickerConfig = {
|
this.videoPickerConfig = {
|
||||||
...videoPickerConfig,
|
...videoPickerConfig,
|
||||||
...libPickerLabels
|
...libPickerLabels,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const {
|
const { rid, tmid, navigation, sharing } = this.props;
|
||||||
rid, tmid, navigation, sharing
|
|
||||||
} = this.props;
|
|
||||||
let msg;
|
let msg;
|
||||||
try {
|
try {
|
||||||
const threadsCollection = db.get('threads');
|
const threadsCollection = db.get('threads');
|
||||||
|
@ -238,10 +289,8 @@ class MessageBox extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps: any) {
|
||||||
const {
|
const { isFocused, editing, replying, sharing } = this.props;
|
||||||
isFocused, editing, replying, sharing
|
|
||||||
} = this.props;
|
|
||||||
if (!isFocused?.()) {
|
if (!isFocused?.()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -266,14 +315,15 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps: any, nextState: any) {
|
||||||
const {
|
const {
|
||||||
showEmojiKeyboard, showSend, recording, mentions, commandPreview, tshow
|
showEmojiKeyboard, showSend, recording, mentions, commandPreview, tshow,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
roomType, replying, editing, isFocused, message, theme
|
roomType, replying, editing, isFocused, message, theme,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -341,19 +391,19 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeText = (text) => {
|
onChangeText: any = (text: string): void => {
|
||||||
const isTextEmpty = text.length === 0;
|
const isTextEmpty = text.length === 0;
|
||||||
this.setShowSend(!isTextEmpty);
|
this.setShowSend(!isTextEmpty);
|
||||||
this.debouncedOnChangeText(text);
|
this.debouncedOnChangeText(text);
|
||||||
this.setInput(text);
|
this.setInput(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectionChange = (e) => {
|
onSelectionChange = (e: any) => {
|
||||||
this.selection = e.nativeEvent.selection;
|
this.selection = e.nativeEvent.selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/sort-comp
|
// eslint-disable-next-line react/sort-comp
|
||||||
debouncedOnChangeText = debounce(async(text) => {
|
debouncedOnChangeText = debounce(async (text: any) => {
|
||||||
const { sharing } = this.props;
|
const { sharing } = this.props;
|
||||||
const isTextEmpty = text.length === 0;
|
const isTextEmpty = text.length === 0;
|
||||||
if (isTextEmpty) {
|
if (isTextEmpty) {
|
||||||
|
@ -389,22 +439,21 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.identifyMentionKeyword(command, MENTIONS_TRACKING_TYPE_COMMANDS);
|
return this.identifyMentionKeyword(command, MENTIONS_TRACKING_TYPE_COMMANDS);
|
||||||
} else if (channelMention) {
|
} if (channelMention) {
|
||||||
return this.identifyMentionKeyword(result, MENTIONS_TRACKING_TYPE_ROOMS);
|
return this.identifyMentionKeyword(result, MENTIONS_TRACKING_TYPE_ROOMS);
|
||||||
} else if (userMention) {
|
} if (userMention) {
|
||||||
return this.identifyMentionKeyword(result, MENTIONS_TRACKING_TYPE_USERS);
|
return this.identifyMentionKeyword(result, MENTIONS_TRACKING_TYPE_USERS);
|
||||||
} else if (emojiMention) {
|
} if (emojiMention) {
|
||||||
return this.identifyMentionKeyword(result, MENTIONS_TRACKING_TYPE_EMOJIS);
|
return this.identifyMentionKeyword(result, MENTIONS_TRACKING_TYPE_EMOJIS);
|
||||||
} else {
|
|
||||||
return this.stopTrackingMention();
|
|
||||||
}
|
}
|
||||||
|
return this.stopTrackingMention();
|
||||||
}, 100)
|
}, 100)
|
||||||
|
|
||||||
onKeyboardResigned = () => {
|
onKeyboardResigned = () => {
|
||||||
this.closeEmoji();
|
this.closeEmoji();
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressMention = (item) => {
|
onPressMention = (item: any) => {
|
||||||
if (!this.component) {
|
if (!this.component) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -416,21 +465,23 @@ class MessageBox extends Component {
|
||||||
const result = msg.substr(0, cursor).replace(regexp, '');
|
const result = msg.substr(0, cursor).replace(regexp, '');
|
||||||
const mentionName = trackingType === MENTIONS_TRACKING_TYPE_EMOJIS
|
const mentionName = trackingType === MENTIONS_TRACKING_TYPE_EMOJIS
|
||||||
? `${ item.name || item }:`
|
? `${ item.name || item }:`
|
||||||
: (item.username || item.name || item.command);
|
: item.username || item.name || item.command;
|
||||||
const text = `${ result }${ mentionName } ${ msg.slice(cursor) }`;
|
const text = `${ result }${ mentionName } ${ msg.slice(cursor) }`;
|
||||||
|
|
||||||
if ((trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) && item.providesPreview) {
|
if ((trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) && item.providesPreview) {
|
||||||
this.setState({ showCommandPreview: true });
|
this.setState({ showCommandPreview: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const newCursor = cursor + mentionName.length;
|
const newCursor = cursor + mentionName.length;
|
||||||
this.setInput(text, { start: newCursor, end: newCursor });
|
this.setInput(text, { start: newCursor, end: newCursor });
|
||||||
this.focus();
|
this.focus();
|
||||||
requestAnimationFrame(() => this.stopTrackingMention());
|
requestAnimationFrame(() => this.stopTrackingMention());
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressCommandPreview = (item) => {
|
onPressCommandPreview = (item: any) => {
|
||||||
const { command } = this.state;
|
const { command } = this.state;
|
||||||
const {
|
const {
|
||||||
rid, tmid, message: { id: messageTmid }, replyCancel
|
rid, tmid, message: { id: messageTmid }, replyCancel,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { text } = this;
|
const { text } = this;
|
||||||
const name = text.substr(0, text.indexOf(' ')).slice(1);
|
const name = text.substr(0, text.indexOf(' ')).slice(1);
|
||||||
|
@ -449,7 +500,7 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmojiSelected = (keyboardId, params) => {
|
onEmojiSelected = (keyboardId: any, params: any) => {
|
||||||
const { text } = this;
|
const { text } = this;
|
||||||
const { emoji } = params;
|
const { emoji } = params;
|
||||||
let newText = '';
|
let newText = '';
|
||||||
|
@ -463,7 +514,7 @@ class MessageBox extends Component {
|
||||||
this.setShowSend(true);
|
this.setShowSend(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPermalink = async(message) => {
|
getPermalink = async (message: any) => {
|
||||||
try {
|
try {
|
||||||
return await RocketChat.getPermalinkMessage(message);
|
return await RocketChat.getPermalinkMessage(message);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -471,8 +522,8 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getFixedMentions = (keyword) => {
|
getFixedMentions = (keyword: any) => {
|
||||||
let result = [];
|
let result: any = [];
|
||||||
if ('all'.indexOf(keyword) !== -1) {
|
if ('all'.indexOf(keyword) !== -1) {
|
||||||
result = [{ rid: -1, username: 'all' }];
|
result = [{ rid: -1, username: 'all' }];
|
||||||
}
|
}
|
||||||
|
@ -482,7 +533,7 @@ class MessageBox extends Component {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUsers = debounce(async(keyword) => {
|
getUsers = debounce(async (keyword: any) => {
|
||||||
let res = await RocketChat.search({ text: keyword, filterRooms: false, filterUsers: true });
|
let res = await RocketChat.search({ text: keyword, filterRooms: false, filterUsers: true });
|
||||||
res = [...this.getFixedMentions(keyword), ...res];
|
res = [...this.getFixedMentions(keyword), ...res];
|
||||||
this.setState({ mentions: res });
|
this.setState({ mentions: res });
|
||||||
|
@ -493,7 +544,7 @@ class MessageBox extends Component {
|
||||||
this.setState({ mentions: res });
|
this.setState({ mentions: res });
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
getEmojis = debounce(async(keyword) => {
|
getEmojis = debounce(async (keyword: any) => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const customEmojisCollection = db.get('custom_emojis');
|
const customEmojisCollection = db.get('custom_emojis');
|
||||||
const likeString = sanitizeLikeString(keyword);
|
const likeString = sanitizeLikeString(keyword);
|
||||||
|
@ -503,17 +554,17 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
let customEmojis = await customEmojisCollection.query(...whereClause).fetch();
|
let customEmojis = await customEmojisCollection.query(...whereClause).fetch();
|
||||||
customEmojis = customEmojis.slice(0, MENTIONS_COUNT_TO_DISPLAY);
|
customEmojis = customEmojis.slice(0, MENTIONS_COUNT_TO_DISPLAY);
|
||||||
const filteredEmojis = emojis.filter(emoji => emoji.indexOf(keyword) !== -1).slice(0, MENTIONS_COUNT_TO_DISPLAY);
|
const filteredEmojis = emojis.filter((emoji) => emoji.indexOf(keyword) !== -1).slice(0, MENTIONS_COUNT_TO_DISPLAY);
|
||||||
const mergedEmojis = [...customEmojis, ...filteredEmojis].slice(0, MENTIONS_COUNT_TO_DISPLAY);
|
const mergedEmojis = [...customEmojis, ...filteredEmojis].slice(0, MENTIONS_COUNT_TO_DISPLAY);
|
||||||
this.setState({ mentions: mergedEmojis || [] });
|
this.setState({ mentions: mergedEmojis || [] });
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
getSlashCommands = debounce(async(keyword) => {
|
getSlashCommands = debounce(async (keyword: any) => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const commandsCollection = db.get('slash_commands');
|
const commandsCollection = db.get('slash_commands');
|
||||||
const likeString = sanitizeLikeString(keyword);
|
const likeString = sanitizeLikeString(keyword);
|
||||||
const commands = await commandsCollection.query(
|
const commands = await commandsCollection.query(
|
||||||
Q.where('id', Q.like(`${ likeString }%`))
|
Q.where('id', Q.like(`${ likeString }%`)),
|
||||||
).fetch();
|
).fetch();
|
||||||
this.setState({ mentions: commands || [] });
|
this.setState({ mentions: commands || [] });
|
||||||
}, 300)
|
}, 300)
|
||||||
|
@ -524,7 +575,7 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTyping = (isTyping) => {
|
handleTyping = (isTyping: boolean) => {
|
||||||
const { typing, rid, sharing } = this.props;
|
const { typing, rid, sharing } = this.props;
|
||||||
if (sharing) {
|
if (sharing) {
|
||||||
return;
|
return;
|
||||||
|
@ -548,7 +599,7 @@ class MessageBox extends Component {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCommandPreview = async(command, name, params) => {
|
setCommandPreview = async (command: any, name: string, params: any) => {
|
||||||
const { rid } = this.props;
|
const { rid } = this.props;
|
||||||
try {
|
try {
|
||||||
const { success, preview } = await RocketChat.getCommandPreview(name, rid, params);
|
const { success, preview } = await RocketChat.getCommandPreview(name, rid, params);
|
||||||
|
@ -561,7 +612,7 @@ class MessageBox extends Component {
|
||||||
this.setState({ commandPreview: [], showCommandPreview: true, command: {} });
|
this.setState({ commandPreview: [], showCommandPreview: true, command: {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
setInput = (text, selection) => {
|
setInput = (text: any, selection?: any) => {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
if (selection) {
|
if (selection) {
|
||||||
return this.component.setTextAndSelection(text, selection);
|
return this.component.setTextAndSelection(text, selection);
|
||||||
|
@ -569,7 +620,7 @@ class MessageBox extends Component {
|
||||||
this.component.setNativeProps({ text });
|
this.component.setNativeProps({ text });
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowSend = (showSend) => {
|
setShowSend = (showSend: any) => {
|
||||||
const { showSend: prevShowSend } = this.state;
|
const { showSend: prevShowSend } = this.state;
|
||||||
const { showSend: propShowSend } = this.props;
|
const { showSend: propShowSend } = this.props;
|
||||||
if (prevShowSend !== showSend && !propShowSend) {
|
if (prevShowSend !== showSend && !propShowSend) {
|
||||||
|
@ -583,7 +634,7 @@ class MessageBox extends Component {
|
||||||
this.setState({ tshow: false });
|
this.setState({ tshow: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
canUploadFile = (file) => {
|
canUploadFile = (file: any) => {
|
||||||
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
|
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
|
||||||
const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize);
|
const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
@ -631,13 +682,13 @@ class MessageBox extends Component {
|
||||||
logEvent(events.ROOM_BOX_ACTION_FILE);
|
logEvent(events.ROOM_BOX_ACTION_FILE);
|
||||||
try {
|
try {
|
||||||
const res = await DocumentPicker.pick({
|
const res = await DocumentPicker.pick({
|
||||||
type: [DocumentPicker.types.allFiles]
|
type: [DocumentPicker.types.allFiles],
|
||||||
});
|
});
|
||||||
const file = {
|
const file = {
|
||||||
filename: res.name,
|
filename: res.name,
|
||||||
size: res.size,
|
size: res.size,
|
||||||
mime: res.type,
|
mime: res.type,
|
||||||
path: res.uri
|
path: res.uri,
|
||||||
};
|
};
|
||||||
if (this.canUploadFile(file)) {
|
if (this.canUploadFile(file)) {
|
||||||
this.openShareView([file]);
|
this.openShareView([file]);
|
||||||
|
@ -650,7 +701,7 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openShareView = (attachments) => {
|
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 { thread } = this;
|
||||||
|
@ -689,13 +740,13 @@ class MessageBox extends Component {
|
||||||
this.setState({ showEmojiKeyboard: true });
|
this.setState({ showEmojiKeyboard: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
recordingCallback = (recording) => {
|
recordingCallback = (recording: any) => {
|
||||||
this.setState({ recording });
|
this.setState({ recording });
|
||||||
}
|
}
|
||||||
|
|
||||||
finishAudioMessage = async(fileInfo) => {
|
finishAudioMessage = async (fileInfo: any) => {
|
||||||
const {
|
const {
|
||||||
rid, tmid, baseUrl: server, user
|
rid, tmid, baseUrl: server, user,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (fileInfo) {
|
if (fileInfo) {
|
||||||
|
@ -716,7 +767,7 @@ class MessageBox extends Component {
|
||||||
submit = async () => {
|
submit = async () => {
|
||||||
const { tshow } = this.state;
|
const { tshow } = this.state;
|
||||||
const {
|
const {
|
||||||
onSubmit, rid: roomId, tmid, showSend, sharing
|
onSubmit, rid: roomId, tmid, showSend, sharing,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const message = this.text;
|
const message = this.text;
|
||||||
|
|
||||||
|
@ -736,7 +787,7 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
editing, replying, message: { id: messageTmid }, replyCancel
|
editing, replying, message: { id: messageTmid }, replyCancel,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// Slash command
|
// Slash command
|
||||||
|
@ -746,7 +797,7 @@ class MessageBox extends Component {
|
||||||
const command = message.replace(/ .*/, '').slice(1);
|
const command = message.replace(/ .*/, '').slice(1);
|
||||||
const likeString = sanitizeLikeString(command);
|
const likeString = sanitizeLikeString(command);
|
||||||
const slashCommand = await commandsCollection.query(
|
const slashCommand = await commandsCollection.query(
|
||||||
Q.where('id', Q.like(`${ likeString }%`))
|
Q.where('id', Q.like(`${ likeString }%`)),
|
||||||
).fetch();
|
).fetch();
|
||||||
if (slashCommand.length > 0) {
|
if (slashCommand.length > 0) {
|
||||||
logEvent(events.COMMAND_RUN);
|
logEvent(events.COMMAND_RUN);
|
||||||
|
@ -767,13 +818,14 @@ class MessageBox extends Component {
|
||||||
// Edit
|
// Edit
|
||||||
if (editing) {
|
if (editing) {
|
||||||
const { message: editingMessage, editRequest } = this.props;
|
const { message: editingMessage, editRequest } = this.props;
|
||||||
|
// @ts-ignore
|
||||||
const { id, subscription: { id: rid } } = editingMessage;
|
const { id, subscription: { id: rid } } = editingMessage;
|
||||||
editRequest({ id, msg: message, rid });
|
editRequest({ id, msg: message, rid });
|
||||||
|
|
||||||
// Reply
|
// Reply
|
||||||
} else if (replying) {
|
} else if (replying) {
|
||||||
const {
|
const {
|
||||||
message: replyingMessage, threadsEnabled, replyWithMention
|
message: replyingMessage, threadsEnabled, replyWithMention,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// Thread
|
// Thread
|
||||||
|
@ -798,11 +850,12 @@ class MessageBox extends Component {
|
||||||
|
|
||||||
// Normal message
|
// Normal message
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
onSubmit(message, undefined, tshow);
|
onSubmit(message, undefined, tshow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMentions = (keyword, type) => {
|
updateMentions = (keyword: any, type: string) => {
|
||||||
if (type === MENTIONS_TRACKING_TYPE_USERS) {
|
if (type === MENTIONS_TRACKING_TYPE_USERS) {
|
||||||
this.getUsers(keyword);
|
this.getUsers(keyword);
|
||||||
} else if (type === MENTIONS_TRACKING_TYPE_EMOJIS) {
|
} else if (type === MENTIONS_TRACKING_TYPE_EMOJIS) {
|
||||||
|
@ -814,10 +867,10 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
identifyMentionKeyword = (keyword, type) => {
|
identifyMentionKeyword = (keyword: any, type: string) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showEmojiKeyboard: false,
|
showEmojiKeyboard: false,
|
||||||
trackingType: type
|
trackingType: type,
|
||||||
});
|
});
|
||||||
this.updateMentions(keyword, type);
|
this.updateMentions(keyword, type);
|
||||||
}
|
}
|
||||||
|
@ -831,11 +884,11 @@ class MessageBox extends Component {
|
||||||
mentions: [],
|
mentions: [],
|
||||||
trackingType: '',
|
trackingType: '',
|
||||||
commandPreview: [],
|
commandPreview: [],
|
||||||
showCommandPreview: false
|
showCommandPreview: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommands = ({ event }) => {
|
handleCommands = ({ event }: {event: any}) => {
|
||||||
if (handleCommandTyping(event)) {
|
if (handleCommandTyping(event)) {
|
||||||
if (this.focused) {
|
if (this.focused) {
|
||||||
Keyboard.dismiss();
|
Keyboard.dismiss();
|
||||||
|
@ -873,16 +926,16 @@ class MessageBox extends Component {
|
||||||
|
|
||||||
renderContent = () => {
|
renderContent = () => {
|
||||||
const {
|
const {
|
||||||
recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview
|
recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const {
|
const {
|
||||||
editing, message, replying, replyCancel, user, getCustomEmoji, theme, Message_AudioRecorderEnabled, children, isActionsEnabled, tmid
|
editing, message, replying, replyCancel, user, getCustomEmoji, theme, Message_AudioRecorderEnabled, children, isActionsEnabled, tmid,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const isAndroidTablet = isTablet && isAndroid ? {
|
const isAndroidTablet = isTablet && isAndroid ? {
|
||||||
multiline: false,
|
multiline: false,
|
||||||
onSubmitEditing: this.submit,
|
onSubmitEditing: this.submit,
|
||||||
returnKeyType: 'send'
|
returnKeyType: 'send',
|
||||||
} : {};
|
} : {};
|
||||||
|
|
||||||
const recordAudio = showSend || !Message_AudioRecorderEnabled ? null : (
|
const recordAudio = showSend || !Message_AudioRecorderEnabled ? null : (
|
||||||
|
@ -896,12 +949,14 @@ class MessageBox extends Component {
|
||||||
const commandsPreviewAndMentions = !recording ? (
|
const commandsPreviewAndMentions = !recording ? (
|
||||||
<>
|
<>
|
||||||
<CommandsPreview commandPreview={commandPreview} showCommandPreview={showCommandPreview} />
|
<CommandsPreview commandPreview={commandPreview} showCommandPreview={showCommandPreview} />
|
||||||
|
{/* @ts-ignore*/}
|
||||||
<Mentions mentions={mentions} trackingType={trackingType} theme={theme} />
|
<Mentions mentions={mentions} trackingType={trackingType} theme={theme} />
|
||||||
</>
|
</>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const replyPreview = !recording ? (
|
const replyPreview = !recording ? (
|
||||||
<ReplyPreview
|
<ReplyPreview
|
||||||
|
// @ts-ignore
|
||||||
message={message}
|
message={message}
|
||||||
close={replyCancel}
|
close={replyCancel}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
|
@ -924,9 +979,11 @@ class MessageBox extends Component {
|
||||||
isActionsEnabled={isActionsEnabled}
|
isActionsEnabled={isActionsEnabled}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
ref={component => this.component = component}
|
ref={(component) => this.component = component}
|
||||||
|
// @ts-ignore
|
||||||
style={[styles.textBoxInput, { color: themes[theme].bodyText }]}
|
style={[styles.textBoxInput, { color: themes[theme].bodyText }]}
|
||||||
returnKeyType='default'
|
returnKeyType='default'
|
||||||
|
// @ts-ignore
|
||||||
keyboardType='twitter'
|
keyboardType='twitter'
|
||||||
blurOnSubmit={false}
|
blurOnSubmit={false}
|
||||||
placeholder={I18n.t('New_Message')}
|
placeholder={I18n.t('New_Message')}
|
||||||
|
@ -959,7 +1016,7 @@ class MessageBox extends Component {
|
||||||
style={[
|
style={[
|
||||||
styles.textArea,
|
styles.textArea,
|
||||||
{ backgroundColor: themes[theme].messageboxBackground },
|
{ backgroundColor: themes[theme].messageboxBackground },
|
||||||
!recording && editing && { backgroundColor: themes[theme].chatComponentBackground }
|
!recording && editing && { backgroundColor: themes[theme].chatComponentBackground },
|
||||||
]}
|
]}
|
||||||
testID='messagebox'
|
testID='messagebox'
|
||||||
>
|
>
|
||||||
|
@ -977,7 +1034,7 @@ class MessageBox extends Component {
|
||||||
console.count(`${ this.constructor.name }.render calls`);
|
console.count(`${ this.constructor.name }.render calls`);
|
||||||
const { showEmojiKeyboard } = this.state;
|
const { showEmojiKeyboard } = this.state;
|
||||||
const {
|
const {
|
||||||
user, baseUrl, theme, iOSScrollBehavior
|
user, baseUrl, theme, iOSScrollBehavior,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<MessageboxContext.Provider
|
<MessageboxContext.Provider
|
||||||
|
@ -985,18 +1042,17 @@ class MessageBox extends Component {
|
||||||
user,
|
user,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onPressMention: this.onPressMention,
|
onPressMention: this.onPressMention,
|
||||||
onPressCommandPreview: this.onPressCommandPreview
|
onPressCommandPreview: this.onPressCommandPreview,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<KeyboardAccessoryView
|
<KeyboardAccessoryView
|
||||||
ref={ref => this.tracking = ref}
|
ref={(ref: any) => this.tracking = ref}
|
||||||
renderContent={this.renderContent}
|
renderContent={this.renderContent}
|
||||||
kbInputRef={this.component}
|
kbInputRef={this.component}
|
||||||
kbComponent={showEmojiKeyboard ? 'EmojiKeyboard' : null}
|
kbComponent={showEmojiKeyboard ? 'EmojiKeyboard' : null}
|
||||||
onKeyboardResigned={this.onKeyboardResigned}
|
onKeyboardResigned={this.onKeyboardResigned}
|
||||||
onItemSelected={this.onEmojiSelected}
|
onItemSelected={this.onEmojiSelected}
|
||||||
trackInteractive
|
trackInteractive
|
||||||
// revealKeyboardInteractive
|
|
||||||
requiresSameParentToManageScrollView
|
requiresSameParentToManageScrollView
|
||||||
addBottomView
|
addBottomView
|
||||||
bottomViewColor={themes[theme].messageboxBackground}
|
bottomViewColor={themes[theme].messageboxBackground}
|
||||||
|
@ -1007,18 +1063,18 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
threadsEnabled: state.settings.Threads_enabled,
|
threadsEnabled: state.settings.Threads_enabled,
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
|
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
|
||||||
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize,
|
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize,
|
||||||
Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled
|
Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled,
|
||||||
});
|
|
||||||
|
|
||||||
const dispatchToProps = ({
|
|
||||||
typing: (rid, status) => userTypingAction(rid, status)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dispatchToProps = {
|
||||||
|
typing: (rid: any, status: any) => userTypingAction(rid, status),
|
||||||
|
};
|
||||||
|
// @ts-ignore
|
||||||
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox));
|
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox));
|
|
@ -9,12 +9,12 @@ const SCROLLVIEW_MENTION_HEIGHT = 4 * MENTION_HEIGHT;
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
composer: {
|
composer: {
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
borderTopWidth: 1
|
borderTopWidth: 1,
|
||||||
},
|
},
|
||||||
textArea: {
|
textArea: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexGrow: 0
|
flexGrow: 0,
|
||||||
},
|
},
|
||||||
textBoxInput: {
|
textBoxInput: {
|
||||||
textAlignVertical: 'center',
|
textAlignVertical: 'center',
|
||||||
|
@ -28,48 +28,48 @@ export default StyleSheet.create({
|
||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
letterSpacing: 0,
|
letterSpacing: 0,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
actionButton: {
|
actionButton: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 48
|
height: 48,
|
||||||
},
|
},
|
||||||
mentionList: {
|
mentionList: {
|
||||||
maxHeight: MENTION_HEIGHT * 4
|
maxHeight: MENTION_HEIGHT * 4,
|
||||||
},
|
},
|
||||||
mentionItem: {
|
mentionItem: {
|
||||||
height: MENTION_HEIGHT,
|
height: MENTION_HEIGHT,
|
||||||
borderTopWidth: StyleSheet.hairlineWidth,
|
borderTopWidth: StyleSheet.hairlineWidth,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingHorizontal: 5
|
paddingHorizontal: 5,
|
||||||
},
|
},
|
||||||
mentionItemCustomEmoji: {
|
mentionItemCustomEmoji: {
|
||||||
margin: 8,
|
margin: 8,
|
||||||
width: 30,
|
width: 30,
|
||||||
height: 30
|
height: 30,
|
||||||
},
|
},
|
||||||
mentionItemEmoji: {
|
mentionItemEmoji: {
|
||||||
width: 46,
|
width: 46,
|
||||||
height: 36,
|
height: 36,
|
||||||
fontSize: isIOS ? 30 : 25,
|
fontSize: isIOS ? 30 : 25,
|
||||||
...sharedStyles.textAlignCenter
|
...sharedStyles.textAlignCenter,
|
||||||
},
|
},
|
||||||
fixedMentionAvatar: {
|
fixedMentionAvatar: {
|
||||||
width: 46,
|
width: 46,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
...sharedStyles.textBold,
|
...sharedStyles.textBold,
|
||||||
...sharedStyles.textAlignCenter
|
...sharedStyles.textAlignCenter,
|
||||||
},
|
},
|
||||||
mentionText: {
|
mentionText: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
emojiKeyboardContainer: {
|
emojiKeyboardContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
borderTopWidth: StyleSheet.hairlineWidth
|
borderTopWidth: StyleSheet.hairlineWidth,
|
||||||
},
|
},
|
||||||
slash: {
|
slash: {
|
||||||
height: 30,
|
height: 30,
|
||||||
|
@ -77,48 +77,48 @@ export default StyleSheet.create({
|
||||||
padding: 5,
|
padding: 5,
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
marginHorizontal: 10,
|
marginHorizontal: 10,
|
||||||
borderRadius: 2
|
borderRadius: 2,
|
||||||
},
|
},
|
||||||
commandPreviewImage: {
|
commandPreviewImage: {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
margin: 3,
|
margin: 3,
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 80,
|
height: 80,
|
||||||
borderRadius: 4
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
commandPreview: {
|
commandPreview: {
|
||||||
height: 100,
|
height: 100,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
avatar: {
|
avatar: {
|
||||||
margin: 8
|
margin: 8,
|
||||||
},
|
},
|
||||||
scrollViewMention: {
|
scrollViewMention: {
|
||||||
maxHeight: SCROLLVIEW_MENTION_HEIGHT
|
maxHeight: SCROLLVIEW_MENTION_HEIGHT,
|
||||||
},
|
},
|
||||||
recordingContent: {
|
recordingContent: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
recordingCancelText: {
|
recordingCancelText: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
buttonsWhitespace: {
|
buttonsWhitespace: {
|
||||||
width: 15
|
width: 15,
|
||||||
},
|
},
|
||||||
sendToChannelButton: {
|
sendToChannelButton: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingVertical: 8,
|
paddingVertical: 8,
|
||||||
paddingHorizontal: 18
|
paddingHorizontal: 18,
|
||||||
},
|
},
|
||||||
sendToChannelText: {
|
sendToChannelText: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
marginLeft: 4,
|
marginLeft: 4,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
}
|
},
|
||||||
});
|
});
|
|
@ -1,5 +1,4 @@
|
||||||
import { useImperativeHandle, forwardRef } from 'react';
|
import { forwardRef, useImperativeHandle } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
|
@ -8,17 +7,17 @@ import { useActionSheet } from './ActionSheet';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
|
|
||||||
const MessageErrorActions = forwardRef(({ tmid }, ref) => {
|
const MessageErrorActions = forwardRef(({ tmid }: any, ref): any => {
|
||||||
const { showActionSheet } = useActionSheet();
|
const { showActionSheet }: any = useActionSheet();
|
||||||
|
|
||||||
const handleResend = protectedFunction(async(message) => {
|
const handleResend = protectedFunction(async (message: any) => {
|
||||||
await RocketChat.resendMessage(message, tmid);
|
await RocketChat.resendMessage(message, tmid);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleDelete = async(message) => {
|
const handleDelete = async (message: any) => {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const deleteBatch = [];
|
const deleteBatch: any = [];
|
||||||
const msgCollection = db.get('messages');
|
const msgCollection = db.get('messages');
|
||||||
const threadCollection = db.get('threads');
|
const threadCollection = db.get('threads');
|
||||||
|
|
||||||
|
@ -39,10 +38,10 @@ const MessageErrorActions = forwardRef(({ tmid }, ref) => {
|
||||||
const msg = await msgCollection.find(tmid);
|
const msg = await msgCollection.find(tmid);
|
||||||
if (msg.tcount <= 1) {
|
if (msg.tcount <= 1) {
|
||||||
deleteBatch.push(
|
deleteBatch.push(
|
||||||
msg.prepareUpdate((m) => {
|
msg.prepareUpdate((m: any) => {
|
||||||
m.tcount = null;
|
m.tcount = null;
|
||||||
m.tlm = null;
|
m.tlm = null;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -54,9 +53,9 @@ const MessageErrorActions = forwardRef(({ tmid }, ref) => {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
deleteBatch.push(
|
deleteBatch.push(
|
||||||
msg.prepareUpdate((m) => {
|
msg.prepareUpdate((m: any) => {
|
||||||
m.tcount -= 1;
|
m.tcount -= 1;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -71,33 +70,30 @@ const MessageErrorActions = forwardRef(({ tmid }, ref) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showMessageErrorActions = (message) => {
|
const showMessageErrorActions = (message: any) => {
|
||||||
showActionSheet({
|
showActionSheet({
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
title: I18n.t('Resend'),
|
title: I18n.t('Resend'),
|
||||||
icon: 'send',
|
icon: 'send',
|
||||||
onPress: () => handleResend(message)
|
onPress: () => handleResend(message),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: I18n.t('Delete'),
|
title: I18n.t('Delete'),
|
||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
danger: true,
|
danger: true,
|
||||||
onPress: () => handleDelete(message)
|
onPress: () => handleDelete(message),
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
hasCancel: true
|
hasCancel: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
showMessageErrorActions
|
showMessageErrorActions,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
MessageErrorActions.propTypes = {
|
|
||||||
tmid: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MessageErrorActions;
|
export default MessageErrorActions;
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, StyleSheet, Text } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
@ -10,34 +9,34 @@ const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginVertical: 24
|
marginVertical: 24,
|
||||||
},
|
},
|
||||||
line: {
|
line: {
|
||||||
height: 1,
|
height: 1,
|
||||||
flex: 1
|
flex: 1,
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
marginLeft: 14,
|
marginLeft: 14,
|
||||||
marginRight: 14,
|
marginRight: 14,
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const OrSeparator = React.memo(({ theme }) => {
|
interface IOrSeparator {
|
||||||
|
theme: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const OrSeparator = React.memo(({ theme }: IOrSeparator) => {
|
||||||
const line = { backgroundColor: themes[theme].borderColor };
|
const line = { backgroundColor: themes[theme].borderColor };
|
||||||
const text = { color: themes[theme].auxiliaryText };
|
const text = { color: themes[theme].auxiliaryText };
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<View style={[styles.line, line]} />
|
<View style={[styles.line, line]} />
|
||||||
<Text style={[styles.text, styles.marginRight, styles.marginLeft, text]}>{I18n.t('OR')}</Text>
|
<Text style={[styles.text, text]}>{I18n.t('OR')}</Text>
|
||||||
<View style={[styles.line, line]} />
|
<View style={[styles.line, line]} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
OrSeparator.propTypes = {
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrSeparator;
|
export default OrSeparator;
|
|
@ -1,47 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { Text } from 'react-native';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import styles from './styles';
|
|
||||||
import { themes } from '../../../constants/colors';
|
|
||||||
import Touch from '../../../utils/touch';
|
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
|
||||||
|
|
||||||
const Button = React.memo(({
|
|
||||||
text, disabled, theme, onPress, icon
|
|
||||||
}) => {
|
|
||||||
const press = () => onPress && onPress(text);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Touch
|
|
||||||
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
|
|
||||||
underlayColor={themes[theme].passcodeButtonActive}
|
|
||||||
rippleColor={themes[theme].passcodeButtonActive}
|
|
||||||
enabled={!disabled}
|
|
||||||
theme={theme}
|
|
||||||
onPress={press}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
icon
|
|
||||||
? (
|
|
||||||
<CustomIcon name={icon} size={36} color={themes[theme].passcodePrimary} />
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<Text style={[styles.buttonText, { color: themes[theme].passcodePrimary }]}>
|
|
||||||
{text}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Touch>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
Button.propTypes = {
|
|
||||||
text: PropTypes.string,
|
|
||||||
icon: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
onPress: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Button;
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
|
||||||
|
import styles from './styles';
|
||||||
|
import { themes } from '../../../constants/colors';
|
||||||
|
import Touch from '../../../utils/touch';
|
||||||
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
|
|
||||||
|
interface IPasscodeButton {
|
||||||
|
text: string;
|
||||||
|
icon: string;
|
||||||
|
theme: string;
|
||||||
|
disabled: boolean;
|
||||||
|
onPress: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = React.memo(({ text, disabled, theme, onPress, icon }: Partial<IPasscodeButton>) => {
|
||||||
|
const press = () => onPress && onPress(text!);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Touch
|
||||||
|
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
|
||||||
|
underlayColor={themes[theme!].passcodeButtonActive}
|
||||||
|
rippleColor={themes[theme!].passcodeButtonActive}
|
||||||
|
enabled={!disabled}
|
||||||
|
theme={theme}
|
||||||
|
onPress={press}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
icon
|
||||||
|
? (
|
||||||
|
<CustomIcon name={icon} size={36} color={themes[theme!].passcodePrimary} />
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<Text style={[styles.buttonText, { color: themes[theme!].passcodePrimary }]}>
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Touch>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Button;
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import range from 'lodash/range';
|
import range from 'lodash/range';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
|
@ -9,10 +8,16 @@ import { themes } from '../../../constants/colors';
|
||||||
const SIZE_EMPTY = 12;
|
const SIZE_EMPTY = 12;
|
||||||
const SIZE_FULL = 16;
|
const SIZE_FULL = 16;
|
||||||
|
|
||||||
const Dots = React.memo(({ passcode, theme, length }) => (
|
interface IPasscodeDots {
|
||||||
|
passcode: string;
|
||||||
|
theme: string;
|
||||||
|
length: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Dots = React.memo(({ passcode, theme, length }: IPasscodeDots) => (
|
||||||
<View style={styles.dotsContainer}>
|
<View style={styles.dotsContainer}>
|
||||||
{range(length).map((val) => {
|
{range(length).map((val) => {
|
||||||
const lengthSup = (passcode.length >= val + 1);
|
const lengthSup = passcode.length >= val + 1;
|
||||||
const height = lengthSup ? SIZE_FULL : SIZE_EMPTY;
|
const height = lengthSup ? SIZE_FULL : SIZE_EMPTY;
|
||||||
const width = lengthSup ? SIZE_FULL : SIZE_EMPTY;
|
const width = lengthSup ? SIZE_FULL : SIZE_EMPTY;
|
||||||
let backgroundColor = '';
|
let backgroundColor = '';
|
||||||
|
@ -33,7 +38,7 @@ const Dots = React.memo(({ passcode, theme, length }) => (
|
||||||
borderRadius,
|
borderRadius,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
marginRight,
|
marginRight,
|
||||||
marginLeft
|
marginLeft,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
@ -42,10 +47,4 @@ const Dots = React.memo(({ passcode, theme, length }) => (
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
Dots.propTypes = {
|
|
||||||
passcode: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
length: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Dots;
|
export default Dots;
|
|
@ -1,13 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { Row } from 'react-native-easy-grid';
|
import { Row } from 'react-native-easy-grid';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
|
|
||||||
const LockIcon = React.memo(({ theme }) => (
|
const LockIcon = React.memo(({ theme }: {theme: string}) => (
|
||||||
<Row style={styles.row}>
|
<Row style={styles.row}>
|
||||||
<View style={styles.iconView}>
|
<View style={styles.iconView}>
|
||||||
<CustomIcon name='auth' size={40} color={themes[theme].passcodeLockIcon} />
|
<CustomIcon name='auth' size={40} color={themes[theme].passcodeLockIcon} />
|
||||||
|
@ -15,8 +14,4 @@ const LockIcon = React.memo(({ theme }) => (
|
||||||
</Row>
|
</Row>
|
||||||
));
|
));
|
||||||
|
|
||||||
LockIcon.propTypes = {
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LockIcon;
|
export default LockIcon;
|
|
@ -1,18 +1,28 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Grid } from 'react-native-easy-grid';
|
import { Grid } from 'react-native-easy-grid';
|
||||||
|
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { resetAttempts } from '../../../utils/localAuthentication';
|
import { resetAttempts } from '../../../utils/localAuthentication';
|
||||||
import { TYPE } from '../constants';
|
import { TYPE } from '../constants';
|
||||||
import { getLockedUntil, getDiff } from '../utils';
|
import { getDiff, getLockedUntil } from '../utils';
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import Title from './Title';
|
import Title from './Title';
|
||||||
import Subtitle from './Subtitle';
|
import Subtitle from './Subtitle';
|
||||||
import LockIcon from './LockIcon';
|
import LockIcon from './LockIcon';
|
||||||
|
|
||||||
const Timer = React.memo(({ time, theme, setStatus }) => {
|
interface IPasscodeTimer {
|
||||||
|
time: string;
|
||||||
|
theme: string;
|
||||||
|
setStatus: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPasscodeLocked {
|
||||||
|
theme: string;
|
||||||
|
setStatus: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Timer = React.memo(({ time, theme, setStatus }: IPasscodeTimer) => {
|
||||||
const calcTimeLeft = () => {
|
const calcTimeLeft = () => {
|
||||||
const diff = getDiff(time);
|
const diff = getDiff(time);
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
|
@ -20,7 +30,7 @@ const Timer = React.memo(({ time, theme, setStatus }) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [timeLeft, setTimeLeft] = useState(calcTimeLeft());
|
const [timeLeft, setTimeLeft] = useState<any>(calcTimeLeft());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -39,8 +49,8 @@ const Timer = React.memo(({ time, theme, setStatus }) => {
|
||||||
return <Subtitle text={I18n.t('Passcode_app_locked_subtitle', { timeLeft })} theme={theme} />;
|
return <Subtitle text={I18n.t('Passcode_app_locked_subtitle', { timeLeft })} theme={theme} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
const Locked = React.memo(({ theme, setStatus }) => {
|
const Locked = React.memo(({ theme, setStatus }: IPasscodeLocked) => {
|
||||||
const [lockedUntil, setLockedUntil] = useState(null);
|
const [lockedUntil, setLockedUntil] = useState<any>(null);
|
||||||
|
|
||||||
const readItemFromStorage = async () => {
|
const readItemFromStorage = async () => {
|
||||||
const l = await getLockedUntil();
|
const l = await getLockedUntil();
|
||||||
|
@ -52,6 +62,8 @@ const Locked = React.memo(({ theme, setStatus }) => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// TODO - verify if this 'r' it's correct
|
||||||
|
// @ts-ignore
|
||||||
<Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]} r>
|
<Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]} r>
|
||||||
<LockIcon theme={theme} />
|
<LockIcon theme={theme} />
|
||||||
<Title text={I18n.t('Passcode_app_locked_title')} theme={theme} />
|
<Title text={I18n.t('Passcode_app_locked_title')} theme={theme} />
|
||||||
|
@ -60,15 +72,4 @@ const Locked = React.memo(({ theme, setStatus }) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Locked.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
setStatus: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
Timer.propTypes = {
|
|
||||||
time: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
setStatus: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Locked;
|
export default Locked;
|
|
@ -1,12 +1,16 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
import { Row } from 'react-native-easy-grid';
|
import { Row } from 'react-native-easy-grid';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
|
|
||||||
const Subtitle = React.memo(({ text, theme }) => (
|
interface IPasscodeSubtitle {
|
||||||
|
text: string;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Subtitle = React.memo(({ text, theme }: IPasscodeSubtitle) => (
|
||||||
<Row style={styles.row}>
|
<Row style={styles.row}>
|
||||||
<View style={styles.subtitleView}>
|
<View style={styles.subtitleView}>
|
||||||
<Text style={[styles.textSubtitle, { color: themes[theme].passcodeSecondary }]}>{text}</Text>
|
<Text style={[styles.textSubtitle, { color: themes[theme].passcodeSecondary }]}>{text}</Text>
|
||||||
|
@ -14,9 +18,4 @@ const Subtitle = React.memo(({ text, theme }) => (
|
||||||
</Row>
|
</Row>
|
||||||
));
|
));
|
||||||
|
|
||||||
Subtitle.propTypes = {
|
|
||||||
text: PropTypes.string,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Subtitle;
|
export default Subtitle;
|
|
@ -1,12 +1,16 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
import { Row } from 'react-native-easy-grid';
|
import { Row } from 'react-native-easy-grid';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
|
|
||||||
const Title = React.memo(({ text, theme }) => (
|
interface IPasscodeTitle {
|
||||||
|
text: string;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Title = React.memo(({ text, theme }: IPasscodeTitle) => (
|
||||||
<Row style={styles.row}>
|
<Row style={styles.row}>
|
||||||
<View style={styles.titleView}>
|
<View style={styles.titleView}>
|
||||||
<Text style={[styles.textTitle, { color: themes[theme].passcodePrimary }]}>{text}</Text>
|
<Text style={[styles.textTitle, { color: themes[theme].passcodePrimary }]}>{text}</Text>
|
||||||
|
@ -14,9 +18,4 @@ const Title = React.memo(({ text, theme }) => (
|
||||||
</Row>
|
</Row>
|
||||||
));
|
));
|
||||||
|
|
||||||
Title.propTypes = {
|
|
||||||
text: PropTypes.string,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Title;
|
export default Title;
|
|
@ -1,9 +1,6 @@
|
||||||
import React, {
|
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
|
||||||
useState, forwardRef, useImperativeHandle, useRef
|
import { Col, Grid, Row } from 'react-native-easy-grid';
|
||||||
} from 'react';
|
|
||||||
import { Col, Row, Grid } from 'react-native-easy-grid';
|
|
||||||
import range from 'lodash/range';
|
import range from 'lodash/range';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
|
|
||||||
|
@ -17,11 +14,23 @@ import LockIcon from './LockIcon';
|
||||||
import Title from './Title';
|
import Title from './Title';
|
||||||
import Subtitle from './Subtitle';
|
import Subtitle from './Subtitle';
|
||||||
|
|
||||||
|
interface IPasscodeBase {
|
||||||
|
theme: string;
|
||||||
|
type: string;
|
||||||
|
previousPasscode: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
showBiometry: string;
|
||||||
|
onEndProcess: Function;
|
||||||
|
onError: Function;
|
||||||
|
onBiometryPress(): void;
|
||||||
|
}
|
||||||
|
|
||||||
const Base = forwardRef(({
|
const Base = forwardRef(({
|
||||||
theme, type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress
|
theme, type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress,
|
||||||
}, ref) => {
|
}: IPasscodeBase, ref) => {
|
||||||
const rootRef = useRef();
|
const rootRef = useRef<any>();
|
||||||
const dotsRef = useRef();
|
const dotsRef = useRef<any>();
|
||||||
const [passcode, setPasscode] = useState('');
|
const [passcode, setPasscode] = useState('');
|
||||||
|
|
||||||
const clearPasscode = () => setPasscode('');
|
const clearPasscode = () => setPasscode('');
|
||||||
|
@ -32,11 +41,11 @@ const Base = forwardRef(({
|
||||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
||||||
};
|
};
|
||||||
|
|
||||||
const animate = (animation, duration = 500) => {
|
const animate = (animation: string, duration = 500) => {
|
||||||
rootRef?.current?.[animation](duration);
|
rootRef?.current?.[animation](duration);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPressNumber = text => setPasscode((p) => {
|
const onPressNumber = (text: string) => setPasscode((p) => {
|
||||||
const currentPasscode = p + text;
|
const currentPasscode = p + text;
|
||||||
if (currentPasscode?.length === PASSCODE_LENGTH) {
|
if (currentPasscode?.length === PASSCODE_LENGTH) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -69,7 +78,7 @@ const Base = forwardRef(({
|
||||||
});
|
});
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
wrongPasscode, animate, clearPasscode
|
wrongPasscode, animate, clearPasscode,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -77,28 +86,28 @@ const Base = forwardRef(({
|
||||||
<Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}>
|
<Grid style={[styles.grid, { backgroundColor: themes[theme].passcodeBackground }]}>
|
||||||
<LockIcon theme={theme} />
|
<LockIcon theme={theme} />
|
||||||
<Title text={title} theme={theme} />
|
<Title text={title} theme={theme} />
|
||||||
<Subtitle text={subtitle} theme={theme} />
|
<Subtitle text={subtitle!} theme={theme} />
|
||||||
<Row style={styles.row}>
|
<Row style={styles.row}>
|
||||||
<Animatable.View ref={dotsRef}>
|
<Animatable.View ref={dotsRef}>
|
||||||
<Dots passcode={passcode} theme={theme} length={PASSCODE_LENGTH} />
|
<Dots passcode={passcode} theme={theme} length={PASSCODE_LENGTH} />
|
||||||
</Animatable.View>
|
</Animatable.View>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={[styles.row, styles.buttonRow]}>
|
<Row style={[styles.row, styles.buttonRow]}>
|
||||||
{range(1, 4).map(i => (
|
{range(1, 4).map((i: any) => (
|
||||||
<Col key={i} style={styles.colButton}>
|
<Col key={i} style={styles.colButton}>
|
||||||
<Button text={i} theme={theme} onPress={onPressNumber} />
|
<Button text={i} theme={theme} onPress={onPressNumber} />
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={[styles.row, styles.buttonRow]}>
|
<Row style={[styles.row, styles.buttonRow]}>
|
||||||
{range(4, 7).map(i => (
|
{range(4, 7).map((i: any) => (
|
||||||
<Col key={i} style={styles.colButton}>
|
<Col key={i} style={styles.colButton}>
|
||||||
<Button text={i} theme={theme} onPress={onPressNumber} />
|
<Button text={i} theme={theme} onPress={onPressNumber} />
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={[styles.row, styles.buttonRow]}>
|
<Row style={[styles.row, styles.buttonRow]}>
|
||||||
{range(7, 10).map(i => (
|
{range(7, 10).map((i: any) => (
|
||||||
<Col key={i} style={styles.colButton}>
|
<Col key={i} style={styles.colButton}>
|
||||||
<Button text={i} theme={theme} onPress={onPressNumber} />
|
<Button text={i} theme={theme} onPress={onPressNumber} />
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -124,16 +133,4 @@ const Base = forwardRef(({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Base.propTypes = {
|
|
||||||
theme: PropTypes.string,
|
|
||||||
type: PropTypes.string,
|
|
||||||
previousPasscode: PropTypes.string,
|
|
||||||
title: PropTypes.string,
|
|
||||||
subtitle: PropTypes.string,
|
|
||||||
showBiometry: PropTypes.string,
|
|
||||||
onEndProcess: PropTypes.func,
|
|
||||||
onError: PropTypes.func,
|
|
||||||
onBiometryPress: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Base;
|
export default Base;
|
|
@ -4,22 +4,22 @@ import sharedStyles from '../../../views/Styles';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1
|
flex: 1,
|
||||||
},
|
},
|
||||||
titleView: {
|
titleView: {
|
||||||
justifyContent: 'center'
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
subtitleView: {
|
subtitleView: {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
height: 32
|
height: 32,
|
||||||
},
|
},
|
||||||
row: {
|
row: {
|
||||||
flex: 0,
|
flex: 0,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
buttonRow: {
|
buttonRow: {
|
||||||
height: 102
|
height: 102,
|
||||||
},
|
},
|
||||||
colButton: {
|
colButton: {
|
||||||
flex: 0,
|
flex: 0,
|
||||||
|
@ -27,44 +27,44 @@ export default StyleSheet.create({
|
||||||
marginRight: 12,
|
marginRight: 12,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
width: 78,
|
width: 78,
|
||||||
height: 78
|
height: 78,
|
||||||
},
|
},
|
||||||
buttonText: {
|
buttonText: {
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
buttonView: {
|
buttonView: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
width: 78,
|
width: 78,
|
||||||
height: 78,
|
height: 78,
|
||||||
borderRadius: 4
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
textTitle: {
|
textTitle: {
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
},
|
},
|
||||||
textSubtitle: {
|
textSubtitle: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium,
|
||||||
},
|
},
|
||||||
dotsContainer: {
|
dotsContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginTop: 24,
|
marginTop: 24,
|
||||||
marginBottom: 40
|
marginBottom: 40,
|
||||||
},
|
},
|
||||||
dotsView: {
|
dotsView: {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: 16
|
height: 16,
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexDirection: 'column'
|
flexDirection: 'column',
|
||||||
},
|
},
|
||||||
iconView: {
|
iconView: {
|
||||||
marginVertical: 16
|
marginVertical: 16,
|
||||||
}
|
},
|
||||||
});
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue