Compare commits
2 Commits
develop
...
chore/eval
Author | SHA1 | Date |
---|---|---|
GleidsonDaniel | adde82889a | |
GleidsonDaniel | cc52d50ea8 |
|
@ -244,6 +244,7 @@ module.exports = {
|
|||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {},
|
||||
node: {
|
||||
extensions: ['.js', '.ts', '.tsx']
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ const mentions = {
|
|||
mentionOtherColor: '#F3BE08'
|
||||
};
|
||||
|
||||
export const themes: any = {
|
||||
export const colors = {
|
||||
light: {
|
||||
backgroundColor: '#ffffff',
|
||||
focusedBackground: '#ffffff',
|
||||
|
@ -171,3 +171,5 @@ export const themes: any = {
|
|||
...mentions
|
||||
}
|
||||
};
|
||||
|
||||
export const themes: any = colors;
|
||||
|
|
|
@ -3,7 +3,6 @@ import React from 'react';
|
|||
import { CancelEditingButton, ToggleEmojiButton } from './buttons';
|
||||
|
||||
interface IMessageBoxLeftButtons {
|
||||
theme: string;
|
||||
showEmojiKeyboard: boolean;
|
||||
openEmoji(): void;
|
||||
closeEmoji(): void;
|
||||
|
@ -11,13 +10,11 @@ interface IMessageBoxLeftButtons {
|
|||
editCancel(): void;
|
||||
}
|
||||
|
||||
const LeftButtons = React.memo(
|
||||
({ theme, showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji }: IMessageBoxLeftButtons) => {
|
||||
if (editing) {
|
||||
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
||||
}
|
||||
return <ToggleEmojiButton show={showEmojiKeyboard} open={openEmoji} close={closeEmoji} theme={theme} />;
|
||||
const LeftButtons = ({ showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji }: IMessageBoxLeftButtons) => {
|
||||
if (editing) {
|
||||
return <CancelEditingButton onPress={editCancel} />;
|
||||
}
|
||||
);
|
||||
return <ToggleEmojiButton show={showEmojiKeyboard} open={openEmoji} close={closeEmoji} />;
|
||||
};
|
||||
|
||||
export default LeftButtons;
|
||||
|
|
|
@ -5,23 +5,20 @@ import { ActionsButton, CancelEditingButton } from './buttons';
|
|||
import styles from './styles';
|
||||
|
||||
interface IMessageBoxLeftButtons {
|
||||
theme: string;
|
||||
showMessageBoxActions(): void;
|
||||
editing: boolean;
|
||||
editCancel(): void;
|
||||
isActionsEnabled: boolean;
|
||||
}
|
||||
|
||||
const LeftButtons = React.memo(
|
||||
({ theme, showMessageBoxActions, editing, editCancel, isActionsEnabled }: IMessageBoxLeftButtons) => {
|
||||
if (editing) {
|
||||
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
||||
}
|
||||
if (isActionsEnabled) {
|
||||
return <ActionsButton onPress={showMessageBoxActions} theme={theme} />;
|
||||
}
|
||||
return <View style={styles.buttonsWhitespace} />;
|
||||
const LeftButtons = ({ showMessageBoxActions, editing, editCancel, isActionsEnabled }: IMessageBoxLeftButtons) => {
|
||||
if (editing) {
|
||||
return <CancelEditingButton onPress={editCancel} />;
|
||||
}
|
||||
);
|
||||
if (isActionsEnabled) {
|
||||
return <ActionsButton onPress={showMessageBoxActions} />;
|
||||
}
|
||||
return <View style={styles.buttonsWhitespace} />;
|
||||
};
|
||||
|
||||
export default LeftButtons;
|
||||
|
|
|
@ -12,17 +12,15 @@ interface IMessageBoxRightButtons {
|
|||
isActionsEnabled: boolean;
|
||||
}
|
||||
|
||||
const RightButtons = React.memo(
|
||||
({ theme, showSend, submit, showMessageBoxActions, isActionsEnabled }: IMessageBoxRightButtons) => {
|
||||
if (showSend) {
|
||||
return <SendButton onPress={submit} theme={theme} />;
|
||||
}
|
||||
if (isActionsEnabled) {
|
||||
return <ActionsButton onPress={showMessageBoxActions} theme={theme} />;
|
||||
}
|
||||
|
||||
return <View style={styles.buttonsWhitespace} />;
|
||||
const RightButtons = ({ showSend, submit, showMessageBoxActions, isActionsEnabled }: IMessageBoxRightButtons) => {
|
||||
if (showSend) {
|
||||
return <SendButton onPress={submit} />;
|
||||
}
|
||||
);
|
||||
if (isActionsEnabled) {
|
||||
return <ActionsButton onPress={showMessageBoxActions} />;
|
||||
}
|
||||
|
||||
return <View style={styles.buttonsWhitespace} />;
|
||||
};
|
||||
|
||||
export default RightButtons;
|
||||
|
|
|
@ -3,12 +3,11 @@ import React from 'react';
|
|||
import BaseButton from './BaseButton';
|
||||
|
||||
interface IActionsButton {
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
}
|
||||
|
||||
const ActionsButton = React.memo(({ theme, onPress }: IActionsButton) => (
|
||||
<BaseButton onPress={onPress} testID='messagebox-actions' accessibilityLabel='Message_actions' icon='add' theme={theme} />
|
||||
));
|
||||
const ActionsButton = ({ onPress }: IActionsButton) => (
|
||||
<BaseButton onPress={onPress} testID='messagebox-actions' accessibilityLabel='Message_actions' icon='add' />
|
||||
);
|
||||
|
||||
export default ActionsButton;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React from 'react';
|
||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||
import React from 'react';
|
||||
|
||||
import I18n from '@app/i18n';
|
||||
import { useColors } from '@app/lib/hooks/useColors';
|
||||
import { CustomIcon } from '@app/lib/Icons';
|
||||
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import styles from '../styles';
|
||||
import I18n from '../../../i18n';
|
||||
|
||||
interface IBaseButton {
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
testID: string;
|
||||
accessibilityLabel: string;
|
||||
|
@ -15,16 +15,18 @@ interface IBaseButton {
|
|||
color: string;
|
||||
}
|
||||
|
||||
const BaseButton = React.memo(({ onPress, testID, accessibilityLabel, icon, theme, color }: Partial<IBaseButton>) => (
|
||||
<BorderlessButton
|
||||
onPress={onPress}
|
||||
style={styles.actionButton}
|
||||
testID={testID}
|
||||
// @ts-ignore
|
||||
accessibilityLabel={I18n.t(accessibilityLabel)}
|
||||
accessibilityTraits='button'>
|
||||
<CustomIcon name={icon} size={24} color={color ?? themes[theme!].auxiliaryTintColor} />
|
||||
</BorderlessButton>
|
||||
));
|
||||
const BaseButton = ({ accessibilityLabel, icon, color, ...props }: Partial<IBaseButton>) => {
|
||||
const { colors } = useColors();
|
||||
return (
|
||||
<BorderlessButton
|
||||
{...props}
|
||||
style={styles.actionButton}
|
||||
// @ts-ignore
|
||||
accessibilityLabel={I18n.t(accessibilityLabel)}
|
||||
accessibilityTraits='button'>
|
||||
<CustomIcon name={icon} size={24} color={color || colors.auxiliaryTintColor} />
|
||||
</BorderlessButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaseButton;
|
||||
|
|
|
@ -3,18 +3,11 @@ import React from 'react';
|
|||
import BaseButton from './BaseButton';
|
||||
|
||||
interface ICancelEditingButton {
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
}
|
||||
|
||||
const CancelEditingButton = React.memo(({ theme, onPress }: ICancelEditingButton) => (
|
||||
<BaseButton
|
||||
onPress={onPress}
|
||||
testID='messagebox-cancel-editing'
|
||||
accessibilityLabel='Cancel_editing'
|
||||
icon='close'
|
||||
theme={theme}
|
||||
/>
|
||||
));
|
||||
const CancelEditingButton = ({ onPress }: ICancelEditingButton) => (
|
||||
<BaseButton onPress={onPress} testID='messagebox-cancel-editing' accessibilityLabel='Cancel_editing' icon='close' />
|
||||
);
|
||||
|
||||
export default CancelEditingButton;
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
import React from 'react';
|
||||
|
||||
import { useColors } from '@app/lib/hooks/useColors';
|
||||
|
||||
import BaseButton from './BaseButton';
|
||||
import { themes } from '../../../constants/colors';
|
||||
|
||||
interface ISendButton {
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
}
|
||||
|
||||
const SendButton = React.memo(({ theme, onPress }: ISendButton) => (
|
||||
<BaseButton
|
||||
onPress={onPress}
|
||||
testID='messagebox-send-message'
|
||||
accessibilityLabel='Send_message'
|
||||
icon='send-filled'
|
||||
theme={theme}
|
||||
color={themes[theme].tintColor}
|
||||
/>
|
||||
));
|
||||
const SendButton = ({ onPress }: ISendButton) => {
|
||||
const { colors } = useColors();
|
||||
return (
|
||||
<BaseButton
|
||||
onPress={onPress}
|
||||
testID='messagebox-send-message'
|
||||
accessibilityLabel='Send_message'
|
||||
icon='send-filled'
|
||||
color={colors.tintColor}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SendButton;
|
||||
|
|
|
@ -3,33 +3,18 @@ import React from 'react';
|
|||
import BaseButton from './BaseButton';
|
||||
|
||||
interface IToggleEmojiButton {
|
||||
theme: string;
|
||||
show: boolean;
|
||||
open(): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
const ToggleEmojiButton = React.memo(({ theme, show, open, close }: IToggleEmojiButton) => {
|
||||
const ToggleEmojiButton = ({ show, open, close }: IToggleEmojiButton) => {
|
||||
if (show) {
|
||||
return (
|
||||
<BaseButton
|
||||
onPress={close}
|
||||
testID='messagebox-close-emoji'
|
||||
accessibilityLabel='Close_emoji_selector'
|
||||
icon='keyboard'
|
||||
theme={theme}
|
||||
/>
|
||||
<BaseButton onPress={close} testID='messagebox-close-emoji' accessibilityLabel='Close_emoji_selector' icon='keyboard' />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<BaseButton
|
||||
onPress={open}
|
||||
testID='messagebox-open-emoji'
|
||||
accessibilityLabel='Open_emoji_selector'
|
||||
icon='emoji'
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return <BaseButton onPress={open} testID='messagebox-open-emoji' accessibilityLabel='Open_emoji_selector' icon='emoji' />;
|
||||
};
|
||||
|
||||
export default ToggleEmojiButton;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { useTheme } from '@app/theme';
|
||||
import { colors as themeColors } from '@app/constants/colors';
|
||||
|
||||
export type TColors = typeof themeColors;
|
||||
export const auxColors: TColors = themeColors;
|
||||
|
||||
export const useColors = () => {
|
||||
const { theme } = useTheme();
|
||||
const auxTheme = theme as keyof TColors;
|
||||
const colors = auxColors[auxTheme];
|
||||
return { colors };
|
||||
};
|
|
@ -3,9 +3,10 @@ import hoistNonReactStatics from 'hoist-non-react-statics';
|
|||
|
||||
import { TNavigationOptions } from './definitions/navigationTypes';
|
||||
import { IThemePreference } from './definitions/ITheme';
|
||||
import { TColors } from './lib/hooks/useColors';
|
||||
|
||||
interface IThemeContextProps {
|
||||
theme: string;
|
||||
theme: keyof TColors | string;
|
||||
themePreferences?: IThemePreference;
|
||||
setTheme?: (newTheme?: {}) => void;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
module.exports = {
|
||||
presets: ['module:metro-react-native-babel-preset'],
|
||||
plugins: [['@babel/plugin-proposal-decorators', { legacy: true }], 'react-native-reanimated/plugin'],
|
||||
plugins: [
|
||||
['@babel/plugin-proposal-decorators', { legacy: true }],
|
||||
'react-native-reanimated/plugin',
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
extensions: ['.ios.js', '.android.js', '.js', '.ts', '.tsx', '.json'],
|
||||
alias: {
|
||||
'@app': './app'
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
env: {
|
||||
production: {
|
||||
plugins: ['transform-remove-console']
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true
|
||||
"experimentalDecorators": true,
|
||||
"paths": {
|
||||
"@app/*": ["./app/*"]
|
||||
},
|
||||
"baseUrl": "./"
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
|
@ -164,11 +164,13 @@
|
|||
"@typescript-eslint/parser": "^4.28.5",
|
||||
"axios": "0.21.1",
|
||||
"babel-jest": "^27.0.6",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||
"codecov": "3.8.2",
|
||||
"detox": "18.17.0",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "2.22.0",
|
||||
"eslint-plugin-jest": "24.7.0",
|
||||
"eslint-plugin-jsx-a11y": "6.3.1",
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
|
||||
"baseUrl": "app" /* Base directory to resolve non-absolute module names. */,
|
||||
"baseUrl": "./" /* Base directory to resolve non-absolute module names. */,
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
|
@ -67,7 +67,10 @@
|
|||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true /* Skip type checking of declaration files. */,
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
|
||||
"paths": {
|
||||
"@app/*": ["app/*"]
|
||||
},
|
||||
},
|
||||
"exclude": ["node_modules", "e2e/docker", "__mocks__"]
|
||||
}
|
||||
|
|
59
yarn.lock
59
yarn.lock
|
@ -5744,6 +5744,17 @@ babel-plugin-minify-type-constructors@^0.4.3:
|
|||
dependencies:
|
||||
babel-helper-is-void-0 "^0.4.3"
|
||||
|
||||
babel-plugin-module-resolver@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz#22a4f32f7441727ec1fbf4967b863e1e3e9f33e2"
|
||||
integrity sha512-MlX10UDheRr3lb3P0WcaIdtCSRlxdQsB1sBqL7W0raF070bGl1HQQq5K3T2vf2XAYie+ww+5AKC/WrkjRO2knA==
|
||||
dependencies:
|
||||
find-babel-config "^1.2.0"
|
||||
glob "^7.1.6"
|
||||
pkg-up "^3.1.0"
|
||||
reselect "^4.0.0"
|
||||
resolve "^1.13.1"
|
||||
|
||||
babel-plugin-polyfill-corejs2@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327"
|
||||
|
@ -8187,6 +8198,17 @@ eslint-import-resolver-node@^0.3.6:
|
|||
debug "^3.2.7"
|
||||
resolve "^1.20.0"
|
||||
|
||||
eslint-import-resolver-typescript@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a"
|
||||
integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==
|
||||
dependencies:
|
||||
debug "^4.3.1"
|
||||
glob "^7.1.7"
|
||||
is-glob "^4.0.1"
|
||||
resolve "^1.20.0"
|
||||
tsconfig-paths "^3.9.0"
|
||||
|
||||
eslint-module-utils@^2.6.0:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233"
|
||||
|
@ -8969,6 +8991,14 @@ finalhandler@1.1.2, finalhandler@~1.1.2:
|
|||
statuses "~1.5.0"
|
||||
unpipe "~1.0.0"
|
||||
|
||||
find-babel-config@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2"
|
||||
integrity sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==
|
||||
dependencies:
|
||||
json5 "^0.5.1"
|
||||
path-exists "^3.0.0"
|
||||
|
||||
find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
|
||||
|
@ -9484,6 +9514,18 @@ glob@^6.0.1:
|
|||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.1.7:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
global-modules@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
|
||||
|
@ -11491,6 +11533,11 @@ json3@^3.3.2:
|
|||
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
|
||||
integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==
|
||||
|
||||
json5@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
|
||||
integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
|
||||
|
||||
json5@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
||||
|
@ -13631,6 +13678,13 @@ pkg-up@2.0.0, pkg-up@^2.0.0:
|
|||
dependencies:
|
||||
find-up "^2.1.0"
|
||||
|
||||
pkg-up@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
||||
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
please-upgrade-node@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
|
||||
|
@ -15172,6 +15226,11 @@ reselect@4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
|
||||
integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
|
||||
|
||||
reselect@^4.0.0:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6"
|
||||
integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==
|
||||
|
||||
resize-observer-polyfill@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
|
||||
|
|
Loading…
Reference in New Issue