Merge branch 'develop' into expo-image-picker
This commit is contained in:
commit
cc2b78b4fd
33
.eslintrc.js
33
.eslintrc.js
|
@ -156,22 +156,6 @@ module.exports = {
|
|||
__DEV__: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['e2e/**'],
|
||||
globals: {
|
||||
by: true,
|
||||
detox: true,
|
||||
device: true,
|
||||
element: true,
|
||||
expect: true,
|
||||
waitFor: true
|
||||
},
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
'no-await-in-loop': 0,
|
||||
'no-restricted-syntax': 0
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
extends: [
|
||||
|
@ -253,6 +237,23 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['e2e/**'],
|
||||
globals: {
|
||||
by: true,
|
||||
detox: true,
|
||||
device: true,
|
||||
element: true,
|
||||
waitFor: true
|
||||
},
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
'no-await-in-loop': 0,
|
||||
'no-restricted-syntax': 0,
|
||||
// TODO: remove this rule when update Detox to 20 and test if the namespace Detox is available
|
||||
'no-undef': 1
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
@ -66,5 +66,6 @@ artifacts
|
|||
e2e/docker/rc_test_env/docker-compose.yml
|
||||
e2e/docker/data/db
|
||||
e2e/e2e_account.js
|
||||
e2e/e2e_account.ts
|
||||
|
||||
*.p8
|
|
@ -22,6 +22,7 @@ const getStories = () => {
|
|||
require("../app/containers/Avatar/Avatar.stories.tsx"),
|
||||
require("../app/containers/BackgroundContainer/index.stories.tsx"),
|
||||
require("../app/containers/Button/Button.stories.tsx"),
|
||||
require("../app/containers/Chip/Chip.stories.tsx"),
|
||||
require("../app/containers/HeaderButton/HeaderButtons.stories.tsx"),
|
||||
require("../app/containers/List/List.stories.tsx"),
|
||||
require("../app/containers/LoginServices/LoginServices.stories.tsx"),
|
||||
|
@ -38,6 +39,7 @@ const getStories = () => {
|
|||
require("../app/containers/UIKit/UiKitModal.stories.tsx"),
|
||||
require("../app/containers/UnreadBadge/UnreadBadge.stories.tsx"),
|
||||
require("../app/views/CannedResponsesListView/CannedResponseItem.stories.tsx"),
|
||||
require("../app/views/CreateChannelView/RoomSettings/SwitchItem.stories.tsx"),
|
||||
require("../app/views/DiscussionsView/Item.stories.tsx"),
|
||||
require("../app/views/RoomView/LoadMore/LoadMore.stories.tsx"),
|
||||
require("../app/views/ThreadMessagesView/Item.stories.tsx"),
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
exports[`Storyshots BackgroundContainer Basic 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]}]}"`;
|
||||
|
||||
exports[`Storyshots BackgroundContainer Black Theme Loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_black\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},\\"color\\":\\"#f9f9f9\\"},\\"children\\":null}]}"`;
|
||||
exports[`Storyshots BackgroundContainer Black Theme Loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_black\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},\\"color\\":\\"#f9f9f9\\"},\\"children\\":null}]}"`;
|
||||
|
||||
exports[`Storyshots BackgroundContainer Black Theme Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_black\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#f9f9f9\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||
exports[`Storyshots BackgroundContainer Black Theme Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_black\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#f9f9f9\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||
|
||||
exports[`Storyshots BackgroundContainer Dark Theme Loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_dark\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},\\"color\\":\\"#f9f9f9\\"},\\"children\\":null}]}"`;
|
||||
exports[`Storyshots BackgroundContainer Dark Theme Loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_dark\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},\\"color\\":\\"#f9f9f9\\"},\\"children\\":null}]}"`;
|
||||
|
||||
exports[`Storyshots BackgroundContainer Dark Theme Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_dark\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#f9f9f9\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||
exports[`Storyshots BackgroundContainer Dark Theme Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_dark\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#f9f9f9\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||
|
||||
exports[`Storyshots BackgroundContainer Loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},\\"color\\":\\"#6C727A\\"},\\"children\\":null}]}"`;
|
||||
exports[`Storyshots BackgroundContainer Loading 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},\\"color\\":\\"#6C727A\\"},\\"children\\":null}]}"`;
|
||||
|
||||
exports[`Storyshots BackgroundContainer Long Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||
exports[`Storyshots BackgroundContainer Long Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries\\"]}]}"`;
|
||||
|
||||
exports[`Storyshots BackgroundContainer Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"Text here\\"]}]}"`;
|
||||
exports[`Storyshots BackgroundContainer Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessibilityIgnoresInvertColors\\":true,\\"style\\":{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\",\\"position\\":\\"absolute\\"}},\\"children\\":[{\\"type\\":\\"Image\\",\\"props\\":{\\"source\\":{\\"uri\\":\\"message_empty_light\\"},\\"style\\":[{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},{\\"width\\":\\"100%\\",\\"height\\":\\"100%\\"},null]},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"position\\":\\"absolute\\",\\"top\\":60,\\"left\\":0,\\"right\\":0,\\"fontSize\\":16,\\"paddingHorizontal\\":24,\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"Text here\\"]}]}"`;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots Button Custom Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"purple\\",\\"padding\\":10,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"yellow\\",\\"fontSize\\":18},[{\\"textAlign\\":\\"left\\"}]],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||
exports[`Storyshots Button Custom Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"purple\\",\\"padding\\":10,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"yellow\\",\\"fontSize\\":18},[{\\"textAlign\\":\\"left\\"}]],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||
|
||||
exports[`Storyshots Button Disabled Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":true},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":0.3}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#ffffff\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||
exports[`Storyshots Button Disabled Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":true},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":0.3}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#ffffff\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||
|
||||
exports[`Storyshots Button Disabled Loading Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":true},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":0.3}},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#ffffff\\"},\\"children\\":null}]}"`;
|
||||
|
||||
exports[`Storyshots Button Loading Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":true},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#ffffff\\"},\\"children\\":null}]}"`;
|
||||
|
||||
exports[`Storyshots Button Primary Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#ffffff\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||
exports[`Storyshots Button Primary Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#ffffff\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||
|
||||
exports[`Storyshots Button Secondary Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#ffffff\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||
exports[`Storyshots Button Secondary Button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#ffffff\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots Chip Chip Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"alignItems\\":\\"flex-start\\",\\"padding\\":16}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"style\\":[{\\"paddingHorizontal\\":8,\\"marginRight\\":8,\\"borderRadius\\":2,\\"justifyContent\\":\\"center\\",\\"maxWidth\\":192},{\\"backgroundColor\\":\\"#efeff4\\"},null],\\"collapsable\\":false},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginRight\\":8,\\"maxWidth\\":120}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"Rocket.Cat\\"]}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":16,\\"color\\":\\"#6C727A\\"},null,{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]}]}"`;
|
||||
|
||||
exports[`Storyshots Chip Chip With Short Text 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"alignItems\\":\\"flex-start\\",\\"padding\\":16}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"style\\":[{\\"paddingHorizontal\\":8,\\"marginRight\\":8,\\"borderRadius\\":2,\\"justifyContent\\":\\"center\\",\\"maxWidth\\":192},{\\"backgroundColor\\":\\"#efeff4\\"},null],\\"collapsable\\":false},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginRight\\":8,\\"maxWidth\\":120}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"Short\\"]}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":16,\\"color\\":\\"#6C727A\\"},null,{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]}]}"`;
|
||||
|
||||
exports[`Storyshots Chip Chip Without Avatar 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"alignItems\\":\\"flex-start\\",\\"padding\\":16}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"style\\":[{\\"paddingHorizontal\\":8,\\"marginRight\\":8,\\"borderRadius\\":2,\\"justifyContent\\":\\"center\\",\\"maxWidth\\":192},{\\"backgroundColor\\":\\"#efeff4\\"},null],\\"collapsable\\":false},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginRight\\":8,\\"maxWidth\\":120}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"Without Avatar\\"]}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":16,\\"color\\":\\"#6C727A\\"},null,{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]}]}"`;
|
||||
|
||||
exports[`Storyshots Chip Chip Without Avatar And Icon 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"alignItems\\":\\"flex-start\\",\\"padding\\":16}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":true},\\"focusable\\":true,\\"style\\":[{\\"paddingHorizontal\\":8,\\"marginRight\\":8,\\"borderRadius\\":2,\\"justifyContent\\":\\"center\\",\\"maxWidth\\":192},{\\"backgroundColor\\":\\"#efeff4\\"},null],\\"collapsable\\":false},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginRight\\":8,\\"maxWidth\\":120}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"Without Avatar and Icon\\"]}]}]}]}]}"`;
|
||||
|
||||
exports[`Storyshots Chip Chip Without Icon 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"alignItems\\":\\"flex-start\\",\\"padding\\":16}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":true},\\"focusable\\":true,\\"style\\":[{\\"paddingHorizontal\\":8,\\"marginRight\\":8,\\"borderRadius\\":2,\\"justifyContent\\":\\"center\\",\\"maxWidth\\":192},{\\"backgroundColor\\":\\"#efeff4\\"},null],\\"collapsable\\":false},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginRight\\":8,\\"maxWidth\\":120}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"Without Icon\\"]}]}]}]}]}"`;
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots Login Services Separators 1`] = `"[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"More options\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":0,\\"backgroundColor\\":\\"#ffffff\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#1d74f5\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"More options\\"},\\"children\\":[\\"More options\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":24}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"height\\":1,\\"flex\\":1},{\\"backgroundColor\\":\\"#e1e5e8\\"}]},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":14,\\"marginLeft\\":14,\\"marginRight\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"OR\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"height\\":1,\\"flex\\":1},{\\"backgroundColor\\":\\"#e1e5e8\\"}]},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Less options\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":0,\\"backgroundColor\\":\\"#ffffff\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#1d74f5\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Less options\\"},\\"children\\":[\\"Less options\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":24}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"height\\":1,\\"flex\\":1},{\\"backgroundColor\\":\\"#e1e5e8\\"}]},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":14,\\"marginLeft\\":14,\\"marginRight\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"OR\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"height\\":1,\\"flex\\":1},{\\"backgroundColor\\":\\"#e1e5e8\\"}]},\\"children\\":null}]}]"`;
|
||||
exports[`Storyshots Login Services Separators 1`] = `"[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"More options\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":0,\\"backgroundColor\\":\\"#ffffff\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#1d74f5\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"More options\\"},\\"children\\":[\\"More options\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":24}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"height\\":1,\\"flex\\":1},{\\"backgroundColor\\":\\"#e1e5e8\\"}]},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":14,\\"marginLeft\\":14,\\"marginRight\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"OR\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"height\\":1,\\"flex\\":1},{\\"backgroundColor\\":\\"#e1e5e8\\"}]},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Less options\\",\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":0,\\"backgroundColor\\":\\"#ffffff\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#1d74f5\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Less options\\"},\\"children\\":[\\"Less options\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":24}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"height\\":1,\\"flex\\":1},{\\"backgroundColor\\":\\"#e1e5e8\\"}]},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":14,\\"marginLeft\\":14,\\"marginRight\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"OR\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"height\\":1,\\"flex\\":1},{\\"backgroundColor\\":\\"#e1e5e8\\"}]},\\"children\\":null}]}]"`;
|
||||
|
||||
exports[`Storyshots Login Services Service List 1`] = `"[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"borderRadius\\":2,\\"width\\":\\"100%\\",\\"height\\":48,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"paddingHorizontal\\":15}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#0d0e12\\"},{\\"position\\":\\"absolute\\",\\"left\\":15,\\"top\\":12,\\"width\\":24,\\"height\\":24},{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":16},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Continue with\\",\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"600\\"}},\\"children\\":[\\"github\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"borderRadius\\":2,\\"width\\":\\"100%\\",\\"height\\":48,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"paddingHorizontal\\":15}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#0d0e12\\"},{\\"position\\":\\"absolute\\",\\"left\\":15,\\"top\\":12,\\"width\\":24,\\"height\\":24},{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":16},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Continue with\\",\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"600\\"}},\\"children\\":[\\"gitlab\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"borderRadius\\":2,\\"width\\":\\"100%\\",\\"height\\":48,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"paddingHorizontal\\":15}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#0d0e12\\"},{\\"position\\":\\"absolute\\",\\"left\\":15,\\"top\\":12,\\"width\\":24,\\"height\\":24},{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":16},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Continue with\\",\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"600\\"}},\\"children\\":[\\"google\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"borderRadius\\":2,\\"width\\":\\"100%\\",\\"height\\":48,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"paddingHorizontal\\":15}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#0d0e12\\"},{\\"position\\":\\"absolute\\",\\"left\\":15,\\"top\\":12,\\"width\\":24,\\"height\\":24},{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":16},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Continue with\\",\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"600\\"}},\\"children\\":[\\"apple\\"]}]}]}]"`;
|
||||
exports[`Storyshots Login Services Service List 1`] = `"[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"borderRadius\\":2,\\"width\\":\\"100%\\",\\"height\\":48,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"paddingHorizontal\\":15}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#0d0e12\\"},{\\"position\\":\\"absolute\\",\\"left\\":15,\\"top\\":12,\\"width\\":24,\\"height\\":24},{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":16},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Continue with\\",\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"}},\\"children\\":[\\"github\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"borderRadius\\":2,\\"width\\":\\"100%\\",\\"height\\":48,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"paddingHorizontal\\":15}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#0d0e12\\"},{\\"position\\":\\"absolute\\",\\"left\\":15,\\"top\\":12,\\"width\\":24,\\"height\\":24},{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":16},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Continue with\\",\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"}},\\"children\\":[\\"gitlab\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"borderRadius\\":2,\\"width\\":\\"100%\\",\\"height\\":48,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"paddingHorizontal\\":15}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#0d0e12\\"},{\\"position\\":\\"absolute\\",\\"left\\":15,\\"top\\":12,\\"width\\":24,\\"height\\":24},{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":16},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Continue with\\",\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"}},\\"children\\":[\\"google\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"borderRadius\\":2,\\"width\\":\\"100%\\",\\"height\\":48,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"paddingHorizontal\\":15}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#0d0e12\\"},{\\"position\\":\\"absolute\\",\\"left\\":15,\\"top\\":12,\\"width\\":24,\\"height\\":24},{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":16},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Continue with\\",\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"}},\\"children\\":[\\"apple\\"]}]}]}]"`;
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,3 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots SearchBox Basic 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"testID\\":\\"searchbox\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10},{\\"margin\\":16,\\"marginBottom\\":16}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"relative\\"}},\\"children\\":[{\\"type\\":\\"TextInput\\",\\"props\\":{\\"style\\":[{\\"color\\":\\"#0d0e12\\"},[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"height\\":48,\\"fontSize\\":16,\\"padding\\":14,\\"borderWidth\\":0.5,\\"borderRadius\\":2},null,{\\"paddingRight\\":45},{\\"backgroundColor\\":\\"#ffffff\\",\\"borderColor\\":\\"#cbcbcc\\",\\"color\\":\\"#0d0e12\\"},null,null],{\\"textAlign\\":\\"auto\\"}],\\"placeholderTextColor\\":\\"#9ca2a8\\",\\"keyboardAppearance\\":\\"light\\",\\"autoCorrect\\":false,\\"autoCapitalize\\":\\"none\\",\\"underlineColorAndroid\\":\\"transparent\\",\\"accessibilityLabel\\":\\"Search\\",\\"placeholder\\":\\"Search\\",\\"value\\":\\"\\",\\"blurOnSubmit\\":true,\\"returnKeyType\\":\\"search\\"},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#2f343d\\"},[{\\"position\\":\\"absolute\\",\\"top\\":14},{\\"right\\":15}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]}]}"`;
|
||||
exports[`Storyshots SearchBox Basic 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"testID\\":\\"searchbox\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10},{\\"margin\\":16,\\"marginBottom\\":16}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"relative\\"}},\\"children\\":[{\\"type\\":\\"TextInput\\",\\"props\\":{\\"style\\":[{\\"color\\":\\"#0d0e12\\"},[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"height\\":48,\\"fontSize\\":16,\\"padding\\":14,\\"borderWidth\\":0.5,\\"borderRadius\\":2},null,{\\"paddingRight\\":45},{\\"backgroundColor\\":\\"#ffffff\\",\\"borderColor\\":\\"#cbcbcc\\",\\"color\\":\\"#0d0e12\\"},null,null],{\\"textAlign\\":\\"auto\\"}],\\"placeholderTextColor\\":\\"#9ca2a8\\",\\"keyboardAppearance\\":\\"light\\",\\"autoCorrect\\":false,\\"autoCapitalize\\":\\"none\\",\\"underlineColorAndroid\\":\\"transparent\\",\\"accessibilityLabel\\":\\"Search\\",\\"placeholder\\":\\"Search\\",\\"value\\":\\"\\",\\"blurOnSubmit\\":true,\\"returnKeyType\\":\\"search\\"},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#2f343d\\"},[{\\"position\\":\\"absolute\\",\\"top\\":14},{\\"right\\":15}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]}]}"`;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,3 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots TextInput Short And Long 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"paddingHorizontal\\":14}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10,\\"fontSize\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"},null]},\\"children\\":[\\"Short Text\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"relative\\"}},\\"children\\":[{\\"type\\":\\"TextInput\\",\\"props\\":{\\"style\\":[{\\"color\\":\\"#0d0e12\\"},[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"height\\":48,\\"fontSize\\":16,\\"padding\\":14,\\"borderWidth\\":0.5,\\"borderRadius\\":2},null,null,{\\"backgroundColor\\":\\"#ffffff\\",\\"borderColor\\":\\"#cbcbcc\\",\\"color\\":\\"#0d0e12\\"},null,null],{\\"textAlign\\":\\"auto\\"}],\\"placeholderTextColor\\":\\"#9ca2a8\\",\\"keyboardAppearance\\":\\"light\\",\\"autoCorrect\\":false,\\"autoCapitalize\\":\\"none\\",\\"underlineColorAndroid\\":\\"transparent\\",\\"accessibilityLabel\\":\\"placeholder\\",\\"placeholder\\":\\"placeholder\\",\\"value\\":\\"Rocket.Chat\\"},\\"children\\":null}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10,\\"fontSize\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"},null]},\\"children\\":[\\"Long Text\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"relative\\"}},\\"children\\":[{\\"type\\":\\"TextInput\\",\\"props\\":{\\"style\\":[{\\"color\\":\\"#0d0e12\\"},[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\",\\"height\\":48,\\"fontSize\\":16,\\"padding\\":14,\\"borderWidth\\":0.5,\\"borderRadius\\":2},null,null,{\\"backgroundColor\\":\\"#ffffff\\",\\"borderColor\\":\\"#cbcbcc\\",\\"color\\":\\"#0d0e12\\"},null,null],{\\"textAlign\\":\\"auto\\"}],\\"placeholderTextColor\\":\\"#9ca2a8\\",\\"keyboardAppearance\\":\\"light\\",\\"autoCorrect\\":false,\\"autoCapitalize\\":\\"none\\",\\"underlineColorAndroid\\":\\"transparent\\",\\"accessibilityLabel\\":\\"placeholder\\",\\"placeholder\\":\\"placeholder\\",\\"value\\":\\"https://open.rocket.chat/images/logo/android-chrome-512x512.png\\"},\\"children\\":null}]}]}]}"`;
|
||||
exports[`Storyshots TextInput Short And Long 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"paddingHorizontal\\":14}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10,\\"fontSize\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"},null]},\\"children\\":[\\"Short Text\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"relative\\"}},\\"children\\":[{\\"type\\":\\"TextInput\\",\\"props\\":{\\"style\\":[{\\"color\\":\\"#0d0e12\\"},[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"height\\":48,\\"fontSize\\":16,\\"padding\\":14,\\"borderWidth\\":0.5,\\"borderRadius\\":2},null,null,{\\"backgroundColor\\":\\"#ffffff\\",\\"borderColor\\":\\"#cbcbcc\\",\\"color\\":\\"#0d0e12\\"},null,null],{\\"textAlign\\":\\"auto\\"}],\\"placeholderTextColor\\":\\"#9ca2a8\\",\\"keyboardAppearance\\":\\"light\\",\\"autoCorrect\\":false,\\"autoCapitalize\\":\\"none\\",\\"underlineColorAndroid\\":\\"transparent\\",\\"accessibilityLabel\\":\\"placeholder\\",\\"placeholder\\":\\"placeholder\\",\\"value\\":\\"Rocket.Chat\\"},\\"children\\":null}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"marginBottom\\":10,\\"fontSize\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"},null]},\\"children\\":[\\"Long Text\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"relative\\"}},\\"children\\":[{\\"type\\":\\"TextInput\\",\\"props\\":{\\"style\\":[{\\"color\\":\\"#0d0e12\\"},[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"height\\":48,\\"fontSize\\":16,\\"padding\\":14,\\"borderWidth\\":0.5,\\"borderRadius\\":2},null,null,{\\"backgroundColor\\":\\"#ffffff\\",\\"borderColor\\":\\"#cbcbcc\\",\\"color\\":\\"#0d0e12\\"},null,null],{\\"textAlign\\":\\"auto\\"}],\\"placeholderTextColor\\":\\"#9ca2a8\\",\\"keyboardAppearance\\":\\"light\\",\\"autoCorrect\\":false,\\"autoCapitalize\\":\\"none\\",\\"underlineColorAndroid\\":\\"transparent\\",\\"accessibilityLabel\\":\\"placeholder\\",\\"placeholder\\":\\"placeholder\\",\\"value\\":\\"https://open.rocket.chat/images/logo/android-chrome-512x512.png\\"},\\"children\\":null}]}]}]}"`;
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,3 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots CollapsibleQuote Item 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"padding\\":10}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"collapsibleQuoteTouchable-Engineering (9 today)\\",\\"hitSlop\\":{\\"top\\":4,\\"right\\":4,\\"bottom\\":4,\\"left\\":4},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginTop\\":6,\\"borderWidth\\":1,\\"borderRadius\\":4,\\"minHeight\\":40,\\"backgroundColor\\":\\"#f3f4f5\\",\\"borderLeftColor\\":\\"#CBCED1\\",\\"borderTopColor\\":\\"#e1e5e8\\",\\"borderRightColor\\":\\"#e1e5e8\\",\\"borderBottomColor\\":\\"#e1e5e8\\",\\"borderLeftWidth\\":2,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"borderRadius\\":4,\\"padding\\":8}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"Engineering (9 today)\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"width\\":20,\\"height\\":20,\\"right\\":8,\\"top\\":8,\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":22,\\"color\\":\\"#6C727A\\"},null,{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]}]}]}"`;
|
||||
exports[`Storyshots CollapsibleQuote Item 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"padding\\":10}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"collapsibleQuoteTouchable-Engineering (9 today)\\",\\"hitSlop\\":{\\"top\\":4,\\"right\\":4,\\"bottom\\":4,\\"left\\":4},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginTop\\":6,\\"borderWidth\\":1,\\"borderRadius\\":4,\\"minHeight\\":40,\\"backgroundColor\\":\\"#f3f4f5\\",\\"borderLeftColor\\":\\"#CBCED1\\",\\"borderTopColor\\":\\"#e1e5e8\\",\\"borderRightColor\\":\\"#e1e5e8\\",\\"borderBottomColor\\":\\"#e1e5e8\\",\\"borderLeftWidth\\":2,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"borderRadius\\":4,\\"padding\\":8}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"Engineering (9 today)\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"width\\":20,\\"height\\":20,\\"right\\":8,\\"top\\":8,\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":22,\\"color\\":\\"#6C727A\\"},null,{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]}]}]}"`;
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,3 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots SwitchItem Switch 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"alignItems\\":\\"flex-start\\",\\"padding\\":16}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"minHeight\\":54,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"space-between\\",\\"flexDirection\\":\\"row\\",\\"maxHeight\\":80,\\"marginBottom\\":12},{\\"backgroundColor\\":\\"#ffffff\\"}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"marginRight\\":8}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}]},\\"children\\":[\\"Welcome to Rocket.Chat\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"testID\\":\\"create-channel-switch-id-hint\\",\\"style\\":[{\\"fontSize\\":14,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"Only authorized users can write new messages\\"]}]},{\\"type\\":\\"RCTSwitch\\",\\"props\\":{\\"testID\\":\\"create-channel-switch-id\\",\\"disabled\\":false,\\"onTintColor\\":\\"#2de0a5\\",\\"style\\":{\\"height\\":31,\\"width\\":51},\\"tintColor\\":\\"#f5455c\\",\\"value\\":false,\\"accessibilityRole\\":\\"switch\\"},\\"children\\":null}]}]}"`;
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -147,7 +147,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode VERSIONCODE as Integer
|
||||
versionName "4.30.0"
|
||||
versionName "4.31.0"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
if (!isFoss) {
|
||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -13,8 +13,7 @@ buildscript {
|
|||
buildToolsVersion = "31.0.0"
|
||||
minSdkVersion = 23
|
||||
compileSdkVersion = 31
|
||||
// TODO: Fix "android:exported" issue and target 31 again
|
||||
targetSdkVersion = 30
|
||||
targetSdkVersion = 31
|
||||
if (System.properties['os.arch'] == "aarch64") {
|
||||
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
||||
ndkVersion = "24.0.8215888"
|
||||
|
@ -27,8 +26,8 @@ buildscript {
|
|||
kotlinVersion = '1.6.10'
|
||||
supportLibVersion = "28.0.0"
|
||||
libre_build = !(isPlay.toBoolean())
|
||||
jitsi_url = isPlay ? "https://github.com/RocketChat/jitsi-maven-repository/raw/master/releases" : "https://github.com/RocketChat/jitsi-maven-repository/raw/libre/releases"
|
||||
jitsi_version = isPlay ? "3.6.0" : "3.6.0-libre"
|
||||
jitsi_url = "https://github.com/RocketChat/jitsi-maven-repository/raw/master/releases"
|
||||
jitsi_version = "3.7.0"
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
|
|
@ -13,6 +13,7 @@ interface ILoginRequest extends Action {
|
|||
credentials: any;
|
||||
logoutOnError?: boolean;
|
||||
isFromWebView?: boolean;
|
||||
registerCustomFields?: any;
|
||||
}
|
||||
|
||||
interface ILoginSuccess extends Action {
|
||||
|
@ -56,13 +57,15 @@ export type TActionsLogin = ILoginRequest &
|
|||
export function loginRequest(
|
||||
credentials: Partial<ICredentials>,
|
||||
logoutOnError?: boolean,
|
||||
isFromWebView?: boolean
|
||||
isFromWebView?: boolean,
|
||||
registerCustomFields?: any
|
||||
): ILoginRequest {
|
||||
return {
|
||||
type: types.LOGIN.REQUEST,
|
||||
credentials,
|
||||
logoutOnError,
|
||||
isFromWebView
|
||||
isFromWebView,
|
||||
registerCustomFields
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
import Chip, { IChip } from './index';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'flex-start',
|
||||
padding: 16
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
title: 'Chip'
|
||||
};
|
||||
|
||||
const ChipWrapped = ({ avatar, text, onPress, testID, style }: IChip) => (
|
||||
<View style={styles.container}>
|
||||
<Chip avatar={avatar} text={text} onPress={onPress} testID={testID} style={style} />
|
||||
</View>
|
||||
);
|
||||
|
||||
export const ChipText = () => <ChipWrapped avatar='rocket.cat' text={'Rocket.Cat'} onPress={() => {}} />;
|
||||
|
||||
export const ChipWithShortText = () => <ChipWrapped avatar='rocket.cat' text={'Short'} onPress={() => {}} />;
|
||||
|
||||
export const ChipWithoutAvatar = () => <ChipWrapped text={'Without Avatar'} onPress={() => {}} />;
|
||||
|
||||
export const ChipWithoutIcon = () => <ChipWrapped avatar='rocket.cat' text='Without Icon' />;
|
||||
|
||||
export const ChipWithoutAvatarAndIcon = () => <ChipWrapped text='Without Avatar and Icon' />;
|
|
@ -0,0 +1,58 @@
|
|||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react-native';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import Chip, { IChip } from '.';
|
||||
import { ISelectedUser } from '../../reducers/selectedUsers';
|
||||
import { mockedStore as store } from '../../reducers/mockedStore';
|
||||
|
||||
const onPressMock = jest.fn((item: any) => item);
|
||||
|
||||
const testChip = {
|
||||
testID: 'test-chip-id',
|
||||
item: { fname: 'rocket.chat', name: 'rocket.chat' } as ISelectedUser,
|
||||
onPress: onPressMock
|
||||
};
|
||||
|
||||
const Render = ({ testID, text, avatar, onPress }: IChip) => (
|
||||
<Provider store={store}>
|
||||
<Chip testID={testID} text={text} onPress={onPress} avatar={avatar} />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
describe('Chips', () => {
|
||||
it('should render the Chip component', () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
text={testChip.item.fname}
|
||||
avatar={testChip.item.name}
|
||||
testID={testChip.testID}
|
||||
onPress={() => testChip.onPress(testChip.item)}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(findByTestId(testChip.testID)).toBeTruthy();
|
||||
});
|
||||
it("should not call onPress if it's not passed", async () => {
|
||||
const { findByTestId } = render(<Render text={testChip.item.fname} avatar={testChip.item.name} testID={testChip.testID} />);
|
||||
|
||||
const component = await findByTestId(testChip.testID);
|
||||
fireEvent.press(component);
|
||||
expect(onPressMock).not.toHaveBeenCalled();
|
||||
});
|
||||
it('should tap Chip and return item', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
text={testChip.item.fname}
|
||||
avatar={testChip.item.name}
|
||||
testID={testChip.testID}
|
||||
onPress={() => testChip.onPress(testChip.item)}
|
||||
/>
|
||||
);
|
||||
|
||||
const component = await findByTestId(testChip.testID);
|
||||
fireEvent.press(component);
|
||||
expect(onPressMock).toHaveBeenCalled();
|
||||
expect(onPressMock).toHaveReturnedWith(testChip.item);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import { Pressable, StyleSheet, View, Text, StyleProp, ViewStyle } from 'react-native';
|
||||
|
||||
import { useTheme } from '../../theme';
|
||||
import { CustomIcon } from '../CustomIcon';
|
||||
import sharedStyles from '../../views/Styles';
|
||||
import Avatar from '../Avatar';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
pressable: {
|
||||
paddingHorizontal: 8,
|
||||
marginRight: 8,
|
||||
borderRadius: 2,
|
||||
justifyContent: 'center',
|
||||
maxWidth: 192
|
||||
},
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
avatar: {
|
||||
marginRight: 8,
|
||||
marginVertical: 8
|
||||
},
|
||||
textContainer: {
|
||||
marginRight: 8,
|
||||
maxWidth: 120
|
||||
},
|
||||
name: {
|
||||
fontSize: 16,
|
||||
...sharedStyles.textMedium
|
||||
}
|
||||
});
|
||||
|
||||
export interface IChip {
|
||||
avatar?: string;
|
||||
text: string;
|
||||
onPress?: Function;
|
||||
testID?: string;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
const Chip = ({ avatar, text, onPress, testID, style }: IChip) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
testID={testID}
|
||||
style={({ pressed }) => [
|
||||
styles.pressable,
|
||||
{
|
||||
backgroundColor: pressed ? colors.bannerBackground : colors.auxiliaryBackground
|
||||
},
|
||||
style
|
||||
]}
|
||||
disabled={!onPress}
|
||||
onPress={() => onPress?.()}
|
||||
android_ripple={{
|
||||
color: colors.bannerBackground
|
||||
}}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
{avatar ? <Avatar text={avatar} size={28} style={styles.avatar} /> : null}
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={[styles.name, { color: colors.bodyText }]} numberOfLines={1}>
|
||||
{text}
|
||||
</Text>
|
||||
</View>
|
||||
{onPress ? <CustomIcon name='close' size={16} color={colors.auxiliaryTintColor} /> : null}
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
export default Chip;
|
|
@ -330,7 +330,7 @@ const MessageActions = React.memo(
|
|||
let options: TActionSheetOptionsItem[] = [];
|
||||
|
||||
// Reply
|
||||
if (!isReadOnly) {
|
||||
if (!isReadOnly && !tmid) {
|
||||
options = [
|
||||
{
|
||||
title: I18n.t('Reply_in_Thread'),
|
||||
|
|
|
@ -53,7 +53,7 @@ import {
|
|||
} from '../../definitions';
|
||||
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||
import { getPermalinkMessage, search, sendFileMessage } from '../../lib/methods';
|
||||
import { hasPermission, debounce, isAndroid, isIOS, isTablet } from '../../lib/methods/helpers';
|
||||
import { hasPermission, debounce, isAndroid, isIOS, isTablet, compareServerVersion } from '../../lib/methods/helpers';
|
||||
import { Services } from '../../lib/services';
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { ChatsStackParamList } from '../../stacks/types';
|
||||
|
@ -92,8 +92,8 @@ export interface IMessageBoxProps extends IBaseScreen<ChatsStackParamList & Mast
|
|||
isActionsEnabled: boolean;
|
||||
usedCannedResponse: string;
|
||||
uploadFilePermission: string[];
|
||||
serverVersion: string;
|
||||
goToCannedResponses: () => void | null;
|
||||
serverVersion: string;
|
||||
}
|
||||
|
||||
interface IMessageBoxState {
|
||||
|
@ -156,7 +156,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
commandPreview: [],
|
||||
showCommandPreview: false,
|
||||
command: {},
|
||||
tshow: false,
|
||||
tshow: this.sendThreadToChannel,
|
||||
mentionLoading: false,
|
||||
permissionToUpload: true
|
||||
};
|
||||
|
@ -166,6 +166,23 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
|
||||
}
|
||||
|
||||
get sendThreadToChannel() {
|
||||
const { user, serverVersion, tmid } = this.props;
|
||||
if (tmid && compareServerVersion(serverVersion, 'lowerThan', '5.0.0')) {
|
||||
return false;
|
||||
}
|
||||
if (tmid && user.alsoSendThreadToChannel === 'default') {
|
||||
return false;
|
||||
}
|
||||
if (user.alsoSendThreadToChannel === 'always') {
|
||||
return true;
|
||||
}
|
||||
if (user.alsoSendThreadToChannel === 'never') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const db = database.active;
|
||||
const { rid, tmid, navigation, sharing, usedCannedResponse, isMasterDetail } = this.props;
|
||||
|
@ -336,7 +353,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps: IMessageBoxProps) {
|
||||
const { uploadFilePermission, goToCannedResponses } = this.props;
|
||||
const { uploadFilePermission, goToCannedResponses, replyWithMention, threadsEnabled } = this.props;
|
||||
if (prevProps.replyWithMention !== replyWithMention) {
|
||||
if (threadsEnabled && replyWithMention) {
|
||||
this.setState({ tshow: this.sendThreadToChannel });
|
||||
}
|
||||
}
|
||||
if (!dequal(prevProps.uploadFilePermission, uploadFilePermission) || prevProps.goToCannedResponses !== goToCannedResponses) {
|
||||
this.setOptions();
|
||||
}
|
||||
|
@ -642,9 +664,13 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
};
|
||||
|
||||
clearInput = () => {
|
||||
const { tshow } = this.state;
|
||||
const { user, serverVersion } = this.props;
|
||||
this.setInput('');
|
||||
this.setShowSend(false);
|
||||
this.setState({ tshow: false });
|
||||
if (compareServerVersion(serverVersion, 'lowerThan', '5.0.0') || (tshow && user.alsoSendThreadToChannel === 'default')) {
|
||||
this.setState({ tshow: false });
|
||||
}
|
||||
};
|
||||
|
||||
canUploadFile = (file: any) => {
|
||||
|
@ -928,7 +954,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
// Normal message
|
||||
} else {
|
||||
// @ts-ignore
|
||||
onSubmit(message, undefined, tshow);
|
||||
onSubmit(message, undefined, tmid ? tshow : false);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -998,7 +1024,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
onPress={this.onPressSendToChannel}
|
||||
testID='messagebox-send-to-channel'
|
||||
>
|
||||
<CustomIcon name={tshow ? 'checkbox-checked' : 'checkbox-unchecked'} size={24} color={themes[theme].auxiliaryText} />
|
||||
<CustomIcon
|
||||
testID={tshow ? 'send-to-channel-checked' : 'send-to-channel-unchecked'}
|
||||
name={tshow ? 'checkbox-checked' : 'checkbox-unchecked'}
|
||||
size={24}
|
||||
color={themes[theme].auxiliaryText}
|
||||
/>
|
||||
<Text style={[styles.sendToChannelText, { color: themes[theme].auxiliaryText }]}>
|
||||
{I18n.t('Messagebox_Send_to_channel')}
|
||||
</Text>
|
||||
|
@ -1167,7 +1198,8 @@ const mapStateToProps = (state: IApplicationState) => ({
|
|||
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
|
||||
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize,
|
||||
Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled,
|
||||
uploadFilePermission: state.permissions['mobile-upload-file']
|
||||
uploadFilePermission: state.permissions['mobile-upload-file'],
|
||||
serverVersion: state.server.version
|
||||
});
|
||||
|
||||
const dispatchToProps = {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import React from 'react';
|
||||
import { RadioButton as RadioButtonUiLib } from 'react-native-ui-lib';
|
||||
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
export const RadioButton = ({ check, testID, size }: { check: boolean; testID?: string; size?: number }): React.ReactElement => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<RadioButtonUiLib
|
||||
testID={testID}
|
||||
selected={check}
|
||||
size={size || 20}
|
||||
color={check ? colors.tintActive : colors.auxiliaryTintColor}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import { Control, Controller } from 'react-hook-form';
|
||||
|
||||
import { FormTextInput, IRCTextInputProps } from './FormTextInput';
|
||||
|
||||
interface IControlledFormTextInputProps extends IRCTextInputProps {
|
||||
control: Control<any>;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const ControlledFormTextInput = ({ control, name, ...props }: IControlledFormTextInputProps) => (
|
||||
<Controller
|
||||
control={control}
|
||||
name={name}
|
||||
render={({ field: { onChange, value } }) => <FormTextInput onChangeText={onChange} value={value} {...props} />}
|
||||
/>
|
||||
);
|
|
@ -1,2 +1,3 @@
|
|||
export * from './TextInput';
|
||||
export * from './FormTextInput';
|
||||
export * from './ControlledFormTextInput';
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { Text, View } from 'react-native';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
|
||||
import Check from '../../Check';
|
||||
import * as List from '../../List';
|
||||
import { textParser } from '../utils';
|
||||
import styles from './styles';
|
||||
import { IItemData } from '.';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { CustomIcon } from '../../CustomIcon';
|
||||
|
||||
interface IItem {
|
||||
item: IItemData;
|
||||
|
@ -30,12 +30,18 @@ const Item = ({ item, selected, onSelect }: IItem) => {
|
|||
const itemName = item.value?.name || item.text.text.toLowerCase();
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<Touchable testID={`multi-select-item-${itemName}`} key={itemName} onPress={() => onSelect(item)} style={[styles.item]}>
|
||||
<>
|
||||
{item.imageUrl ? <FastImage style={styles.itemImage} source={{ uri: item.imageUrl }} /> : null}
|
||||
<Text style={{ color: colors.titleText }}>{textParser([item.text])}</Text>
|
||||
{selected ? <Check /> : null}
|
||||
</>
|
||||
<Touchable testID={`multi-select-item-${itemName}`} key={itemName} onPress={() => onSelect(item)}>
|
||||
<View style={styles.item}>
|
||||
<View style={styles.flexZ}>
|
||||
{item.imageUrl ? <FastImage style={styles.itemImage} source={{ uri: item.imageUrl }} /> : null}
|
||||
</View>
|
||||
<View style={styles.flex}>
|
||||
<Text numberOfLines={1} style={{ color: colors.titleText }}>
|
||||
{textParser([item.text])}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.flexZ}>{selected ? <CustomIcon color={colors.tintColor} size={22} name='check' /> : null}</View>
|
||||
</View>
|
||||
</Touchable>
|
||||
);
|
||||
};
|
||||
|
@ -43,8 +49,8 @@ const Item = ({ item, selected, onSelect }: IItem) => {
|
|||
const Items = ({ items, selected, onSelect }: IItems) => (
|
||||
<FlatList
|
||||
data={items}
|
||||
style={[styles.items]}
|
||||
contentContainerStyle={[styles.itemContent]}
|
||||
style={styles.items}
|
||||
contentContainerStyle={styles.itemContent}
|
||||
keyboardShouldPersistTaps='always'
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
keyExtractor={keyExtractor}
|
||||
|
|
|
@ -65,19 +65,21 @@ export const MultiSelectContent = React.memo(
|
|||
);
|
||||
|
||||
return (
|
||||
<View style={[styles.actionSheetContainer]}>
|
||||
<FormTextInput
|
||||
testID='multi-select-search'
|
||||
onChangeText={handleSearch}
|
||||
placeholder={I18n.t('Search')}
|
||||
inputStyle={{ backgroundColor: colors.focusedBackground }}
|
||||
bottomSheet={isIOS}
|
||||
onSubmitEditing={() => {
|
||||
setTimeout(() => {
|
||||
hideActionSheet();
|
||||
}, 150);
|
||||
}}
|
||||
/>
|
||||
<View style={styles.actionSheetContainer}>
|
||||
<View style={styles.inputStyle}>
|
||||
<FormTextInput
|
||||
testID='multi-select-search'
|
||||
onChangeText={handleSearch}
|
||||
placeholder={I18n.t('Search')}
|
||||
inputStyle={{ backgroundColor: colors.focusedBackground }}
|
||||
bottomSheet={isIOS}
|
||||
onSubmitEditing={() => {
|
||||
setTimeout(() => {
|
||||
hideActionSheet();
|
||||
}, 150);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<Items items={items || []} selected={selected} onSelect={onSelect} />
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -9,7 +9,6 @@ export default StyleSheet.create({
|
|||
justifyContent: 'flex-end'
|
||||
},
|
||||
actionSheetContainer: {
|
||||
padding: 16,
|
||||
flex: 1
|
||||
},
|
||||
content: {
|
||||
|
@ -28,9 +27,9 @@ export default StyleSheet.create({
|
|||
},
|
||||
item: {
|
||||
height: 48,
|
||||
maxWidth: '85%',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row'
|
||||
flexDirection: 'row',
|
||||
flex: 1
|
||||
},
|
||||
input: {
|
||||
minHeight: 48,
|
||||
|
@ -46,8 +45,12 @@ export default StyleSheet.create({
|
|||
right: 16
|
||||
},
|
||||
itemContent: {
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 36
|
||||
},
|
||||
inputStyle: {
|
||||
paddingHorizontal: 16
|
||||
},
|
||||
items: {
|
||||
height: 226
|
||||
},
|
||||
|
@ -83,5 +86,11 @@ export default StyleSheet.create({
|
|||
borderRadius: 2,
|
||||
width: 24,
|
||||
height: 24
|
||||
},
|
||||
flex: {
|
||||
flex: 1
|
||||
},
|
||||
flexZ: {
|
||||
flex: 0
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,9 +4,8 @@ import { Pressable, StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-n
|
|||
import Avatar from './Avatar';
|
||||
import { CustomIcon, TIconsName } from './CustomIcon';
|
||||
import sharedStyles from '../views/Styles';
|
||||
import { themes } from '../lib/constants';
|
||||
import { isIOS } from '../lib/methods/helpers';
|
||||
import { TSupportedThemes } from '../theme';
|
||||
import { useTheme } from '../theme';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
|
@ -26,13 +25,9 @@ const styles = StyleSheet.create({
|
|||
marginRight: 15
|
||||
},
|
||||
name: {
|
||||
fontSize: 17,
|
||||
fontSize: 16,
|
||||
...sharedStyles.textMedium
|
||||
},
|
||||
username: {
|
||||
fontSize: 14,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
icon: {
|
||||
marginHorizontal: 15,
|
||||
alignSelf: 'center'
|
||||
|
@ -47,34 +42,35 @@ interface IUserItem {
|
|||
onLongPress?: () => void;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
icon?: TIconsName | null;
|
||||
theme: TSupportedThemes;
|
||||
iconColor?: string;
|
||||
}
|
||||
|
||||
const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon, theme }: IUserItem) => (
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
testID={testID}
|
||||
android_ripple={{
|
||||
color: themes[theme].bannerBackground
|
||||
}}
|
||||
style={({ pressed }: any) => ({
|
||||
backgroundColor: isIOS && pressed ? themes[theme].bannerBackground : 'transparent'
|
||||
})}
|
||||
>
|
||||
<View style={[styles.container, styles.button, style]}>
|
||||
<Avatar text={username} size={30} style={styles.avatar} />
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>
|
||||
{name}
|
||||
</Text>
|
||||
<Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
|
||||
@{username}
|
||||
</Text>
|
||||
const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon, iconColor }: IUserItem) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
testID={testID}
|
||||
android_ripple={{
|
||||
color: colors.bannerBackground
|
||||
}}
|
||||
style={({ pressed }: any) => ({
|
||||
backgroundColor: isIOS && pressed ? colors.bannerBackground : 'transparent'
|
||||
})}
|
||||
>
|
||||
<View style={[styles.container, styles.button, style]}>
|
||||
<Avatar text={username} size={30} style={styles.avatar} />
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={[styles.name, { color: colors.bodyText }]} numberOfLines={1}>
|
||||
{name}
|
||||
</Text>
|
||||
</View>
|
||||
{icon ? <CustomIcon name={icon} size={22} color={iconColor || colors.actionTintColor} style={styles.icon} /> : null}
|
||||
</View>
|
||||
{icon ? <CustomIcon name={icon} size={22} color={themes[theme].actionTintColor} style={styles.icon} /> : null}
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserItem;
|
||||
|
|
|
@ -27,7 +27,7 @@ const styles = StyleSheet.create({
|
|||
flexShrink: 1,
|
||||
fontSize: 16,
|
||||
lineHeight: 22,
|
||||
...sharedStyles.textMedium
|
||||
...sharedStyles.textSemibold
|
||||
},
|
||||
usernameInfoMessage: {
|
||||
fontSize: 16,
|
||||
|
|
|
@ -115,12 +115,14 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
|
|||
}
|
||||
}
|
||||
|
||||
closeEmojiAndAction = () => {
|
||||
onPressAction = () => {
|
||||
const { closeEmojiAndAction } = this.props;
|
||||
|
||||
if (closeEmojiAndAction) {
|
||||
closeEmojiAndAction(this.onPress);
|
||||
return closeEmojiAndAction(this.onPress);
|
||||
}
|
||||
|
||||
return this.onPress();
|
||||
};
|
||||
|
||||
onPress = debounce(
|
||||
|
@ -382,7 +384,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
|
|||
value={{
|
||||
user,
|
||||
baseUrl,
|
||||
onPress: this.closeEmojiAndAction,
|
||||
onPress: this.onPressAction,
|
||||
onLongPress: this.onLongPress,
|
||||
reactionInit: this.reactionInit,
|
||||
onErrorPress: this.onErrorPress,
|
||||
|
|
|
@ -118,7 +118,6 @@ export default StyleSheet.create({
|
|||
...sharedStyles.textRegular
|
||||
},
|
||||
textInfo: {
|
||||
fontStyle: 'italic',
|
||||
fontSize: 16,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface ILoggedUser {
|
|||
showMessageInMainThread?: boolean;
|
||||
isFromWebView?: boolean;
|
||||
enableMessageParserEarlyAdoption: boolean;
|
||||
alsoSendThreadToChannel: 'default' | 'always' | 'never';
|
||||
}
|
||||
|
||||
export interface ILoggedUserResultFromServer
|
||||
|
|
|
@ -117,8 +117,7 @@
|
|||
"Black": "أسود",
|
||||
"Block_user": "حظر المستخدم",
|
||||
"Browser": "المتصفح",
|
||||
"Broadcast_channel_Description": "يمكن فقط للمستخدمين المصرح لهم كتابة رسائل جديدة، ولكن سيتمكن المستخدمون الآخرون من الرد",
|
||||
"Broadcast_Channel": "قناة البث",
|
||||
"Broadcast_hint": "يمكن فقط للمستخدمين المصرح لهم كتابة رسائل جديدة، ولكن سيتمكن المستخدمون الآخرون من الرد",
|
||||
"Busy": "مشغول",
|
||||
"By_proceeding_you_are_agreeing": "من خلال المتابعة، أنت توافق على",
|
||||
"Cancel_editing": "إلغاء التعديل",
|
||||
|
@ -389,7 +388,6 @@
|
|||
"Preferences": "التفضيلات",
|
||||
"Preferences_saved": "تم حفظ التفضيلات",
|
||||
"Privacy_Policy": "سياسة الخصوصية",
|
||||
"Private_Channel": "قناة خاصة",
|
||||
"Private": "خاص",
|
||||
"Processing": "جار معالجة...",
|
||||
"Profile_saved_successfully": "تم حفظ الملف الشخصي بنجاح!",
|
||||
|
@ -403,7 +401,6 @@
|
|||
"Reactions": "التفاعلات",
|
||||
"Read_External_Permission_Message": "يحتاج Rocket.chat للوصول إلى الصور والملفات الموجودة على الجهاز",
|
||||
"Read_External_Permission": "صلاحية قراءة الوسائط",
|
||||
"Read_Only_Channel": "قناة للقراءة فقط",
|
||||
"Read_Only": "قراءة فقط",
|
||||
"Read_Receipt": "قراءة المستلم",
|
||||
"Receive_Group_Mentions": "تلقي إشارات المجموعة",
|
||||
|
|
|
@ -119,8 +119,7 @@
|
|||
"Black": "Schwarz",
|
||||
"Block_user": "Benutzer blockieren",
|
||||
"Browser": "Browser",
|
||||
"Broadcast_channel_Description": "Nur autorisierte Benutzer können neue Nachrichten schreiben, die anderen Benutzer können jedoch antworten",
|
||||
"Broadcast_Channel": "Broadcast-Kanal",
|
||||
"Broadcast_hint": "Nur autorisierte Benutzer können neue Nachrichten schreiben, die anderen Benutzer können jedoch antworten",
|
||||
"Busy": "Beschäftigt",
|
||||
"By_proceeding_you_are_agreeing": "Indem Sie fortfahren, akzeptieren Sie unsere",
|
||||
"Cancel_editing": "Bearbeitung abbrechen",
|
||||
|
@ -394,7 +393,6 @@
|
|||
"Preferences": "Einstellungen",
|
||||
"Preferences_saved": "Einstellungen gespeichert!",
|
||||
"Privacy_Policy": " Datenschutzbestimmungen",
|
||||
"Private_Channel": "Privater Kanal",
|
||||
"Private": "Privat",
|
||||
"Processing": "Bearbeite …",
|
||||
"Profile_saved_successfully": "Profil erfolgreich gespeichert!",
|
||||
|
@ -409,7 +407,6 @@
|
|||
"Reactions": "Reaktionen",
|
||||
"Read_External_Permission_Message": "Rocket.Chat benötigt Zugriff auf Ihre Fotos, Medien und Dateien auf Ihrem Gerät",
|
||||
"Read_External_Permission": "Lese-Zugriff auf Medien",
|
||||
"Read_Only_Channel": "Nur-Lese-Kanal",
|
||||
"Read_Only": "Schreibgeschützt",
|
||||
"Read_Receipt": "Lesebestätigung",
|
||||
"Receive_Group_Mentions": "Gruppen-Benachrichtigungen erhalten",
|
||||
|
@ -705,9 +702,6 @@
|
|||
"Team_not_found": "Team nicht gefunden",
|
||||
"Create_Team": "Team erstellen",
|
||||
"Team_Name": "Team-Name",
|
||||
"Private_Team": "Privates Team",
|
||||
"Read_Only_Team": "Nur-Lesen-Team",
|
||||
"Broadcast_Team": "Broadcast-Team",
|
||||
"creating_team": "Team erstellen",
|
||||
"team-name-already-exists": "Ein Team mit diesem Namen existiert bereits",
|
||||
"Add_Channel_to_Team": "Kanal zum Team hinzufügen",
|
||||
|
|
|
@ -126,8 +126,6 @@
|
|||
"Black": "Black",
|
||||
"Block_user": "Block user",
|
||||
"Browser": "Browser",
|
||||
"Broadcast_channel_Description": "Only authorized users can write new messages, but the other users will be able to reply",
|
||||
"Broadcast_Channel": "Broadcast Channel",
|
||||
"Busy": "Busy",
|
||||
"By_proceeding_you_are_agreeing": "By proceeding you are agreeing to our",
|
||||
"Cancel_editing": "Cancel editing",
|
||||
|
@ -356,6 +354,7 @@
|
|||
"No_mentioned_messages": "No mentioned messages",
|
||||
"No_pinned_messages": "No pinned messages",
|
||||
"No_results_found": "No results found",
|
||||
"No_members_found": "No members found",
|
||||
"No_starred_messages": "No starred messages",
|
||||
"No_thread_messages": "No thread messages",
|
||||
"No_label_provided": "No {{label}} provided.",
|
||||
|
@ -407,7 +406,6 @@
|
|||
"Preferences": "Preferences",
|
||||
"Preferences_saved": "Preferences saved!",
|
||||
"Privacy_Policy": " Privacy Policy",
|
||||
"Private_Channel": "Private Channel",
|
||||
"Private": "Private",
|
||||
"Processing": "Processing...",
|
||||
"Profile_saved_successfully": "Profile saved successfully!",
|
||||
|
@ -422,7 +420,6 @@
|
|||
"Reactions": "Reactions",
|
||||
"Read_External_Permission_Message": "Rocket.Chat needs to access photos, media, and files on your device",
|
||||
"Read_External_Permission": "Read Media Permission",
|
||||
"Read_Only_Channel": "Read Only Channel",
|
||||
"Read_Only": "Read Only",
|
||||
"Read_Receipt": "Read Receipt",
|
||||
"Receive_Group_Mentions": "Receive Group Mentions",
|
||||
|
@ -725,9 +722,6 @@
|
|||
"Team_not_found": "Team not found",
|
||||
"Create_Team": "Create Team",
|
||||
"Team_Name": "Team Name",
|
||||
"Private_Team": "Private Team",
|
||||
"Read_Only_Team": "Read Only Team",
|
||||
"Broadcast_Team": "Broadcast Team",
|
||||
"creating_team": "creating team",
|
||||
"team-name-already-exists": "A team with that name already exists",
|
||||
"Add_Channel_to_Team": "Add Channel to Team",
|
||||
|
@ -842,5 +836,26 @@
|
|||
"error-init-video-conf": "Error starting video call",
|
||||
"totp-invalid": "Code or password invalid",
|
||||
"Close_Chat": "Close Chat",
|
||||
"Select_tags": "Select tags"
|
||||
"Select_tags": "Select tags",
|
||||
"Skip": "Skip",
|
||||
"N_Selected_members": "{{n}} selected",
|
||||
"Broadcast": "Broadcast",
|
||||
"Broadcast_hint": "Only authorized users can write new messages, but the other users will be able to reply",
|
||||
"Team_hint_private": "Only invited people can join",
|
||||
"Team_hint_public": "When disabled, anyone can join the team",
|
||||
"Team_hint_not_read_only": "All users in this team can write messages",
|
||||
"Team_hint_encrypted": "End to end encrypted team. Search will not work with encrypted Teams and notifications may not show the messages content.",
|
||||
"Team_hint_encrypted_not_available": "Only available for private team",
|
||||
"Channel_hint_private":"Only invited users can access this Channel",
|
||||
"Channel_hint_public":"Everyone can access this channel",
|
||||
"Channel_hint_encrypted": "End to end encrypted channel. Search will not work with encrypted channels and notifications may not show the messages content.",
|
||||
"Channel_hint_not_read_only": "All users in the channel can write new messages",
|
||||
"Channel_hint_encrypted_not_available": "Not available for Public Channels",
|
||||
"Read_only_hint":"Only authorized users can write new messages",
|
||||
"Discussion": "Discussion",
|
||||
"Channel": "Channel",
|
||||
"Team": "Team",
|
||||
"Select_Members": "Select Members",
|
||||
"Also_send_thread_message_to_channel_behavior": "Also send thread message to channel",
|
||||
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Allow users to select the Also send to channel behavior"
|
||||
}
|
|
@ -108,8 +108,7 @@
|
|||
"Back": "Volver",
|
||||
"Black": "Negro",
|
||||
"Block_user": "Bloquear usuario",
|
||||
"Broadcast_channel_Description": "Sólo los usuarios autorizados pueden escribir nuevos mensajes, el resto podrán responder sobre los mismos.",
|
||||
"Broadcast_Channel": "Canal de Transmisión",
|
||||
"Broadcast_hint": "Sólo los usuarios autorizados pueden escribir nuevos mensajes, el resto podrán responder sobre los mismos.",
|
||||
"Busy": "Ocupado",
|
||||
"By_proceeding_you_are_agreeing": "Al proceder estarás de acuerdo",
|
||||
"Cancel_editing": "Cancelar edición",
|
||||
|
@ -273,7 +272,6 @@
|
|||
"Preferences": "Preferencias",
|
||||
"Preferences_saved": "¡Preferencias guardadas!",
|
||||
"Privacy_Policy": "Política de privacidad",
|
||||
"Private_Channel": "Canal privado",
|
||||
"Private": "Privado",
|
||||
"Processing": "Procesando...",
|
||||
"Profile_saved_successfully": "¡Perfil guardado correctamente!",
|
||||
|
@ -286,7 +284,6 @@
|
|||
"Reactions_are_disabled": "Las reacciones están desactivadas",
|
||||
"Reactions_are_enabled": "Las reacciones están activadas",
|
||||
"Reactions": "Reacciones",
|
||||
"Read_Only_Channel": "Canal de sólo lectura",
|
||||
"Read_Only": "Sólo lectura ",
|
||||
"Read_Receipt": "Comprobante de lectura",
|
||||
"Receive_Group_Mentions": "Recibir menciones de grupo",
|
||||
|
|
|
@ -119,8 +119,7 @@
|
|||
"Black": "Noir",
|
||||
"Block_user": "Bloquer l'utilisateur",
|
||||
"Browser": "Navigateur",
|
||||
"Broadcast_channel_Description": "Seuls les utilisateurs autorisés peuvent écrire de nouveaux messages, mais les autres utilisateurs pourront répondre.",
|
||||
"Broadcast_Channel": "Canal de diffusion",
|
||||
"Broadcast_hint": "Seuls les utilisateurs autorisés peuvent écrire de nouveaux messages, mais les autres utilisateurs pourront répondre.",
|
||||
"Busy": "Occupé",
|
||||
"By_proceeding_you_are_agreeing": "En poursuivant, vous acceptez nos",
|
||||
"Cancel_editing": "Annuler la modification",
|
||||
|
@ -398,7 +397,6 @@
|
|||
"Preferences": "Préférences",
|
||||
"Preferences_saved": "Préférences sauvegardées !",
|
||||
"Privacy_Policy": " Politique de confidentialité",
|
||||
"Private_Channel": "Canal privé",
|
||||
"Private": "Privé",
|
||||
"Processing": "Traitement...",
|
||||
"Profile_saved_successfully": "Profil enregistré avec succès !",
|
||||
|
@ -413,7 +411,6 @@
|
|||
"Reactions": "Réactions",
|
||||
"Read_External_Permission_Message": "Rocket.Chat doit accéder aux photos, aux médias et aux fichiers sur votre appareil",
|
||||
"Read_External_Permission": "Permission de lecture des fichiers",
|
||||
"Read_Only_Channel": "Canal en lecture seule",
|
||||
"Read_Only": "Lecture seule",
|
||||
"Read_Receipt": "Accusé de réception",
|
||||
"Receive_Group_Mentions": "Recevoir des mentions de groupe",
|
||||
|
@ -715,9 +712,6 @@
|
|||
"Team_not_found": "Equipe non trouvée",
|
||||
"Create_Team": "Créer une équipe",
|
||||
"Team_Name": "Nom de l'équipe",
|
||||
"Private_Team": "Equipe privée",
|
||||
"Read_Only_Team": "Equipe en lecture seule",
|
||||
"Broadcast_Team": "Equipe de diffusion",
|
||||
"creating_team": "création de l'équipe",
|
||||
"team-name-already-exists": "Une équipe portant ce nom existe déjà",
|
||||
"Add_Channel_to_Team": "Ajouter un canal à l'équipe",
|
||||
|
|
|
@ -115,8 +115,7 @@
|
|||
"Black": "Nero",
|
||||
"Block_user": "Blocca utente",
|
||||
"Browser": "Browser",
|
||||
"Broadcast_channel_Description": "Solo gli utenti autorizzati possono scrivere messaggi, ma gli altri utenti saranno in grado di rispondere",
|
||||
"Broadcast_Channel": "Canale broadcast",
|
||||
"Broadcast_hint": "Solo gli utenti autorizzati possono scrivere messaggi, ma gli altri utenti saranno in grado di rispondere",
|
||||
"Busy": "Occupato",
|
||||
"By_proceeding_you_are_agreeing": "Procedendo accetti i nostri",
|
||||
"Cancel_editing": "Annulla modifica",
|
||||
|
@ -383,7 +382,6 @@
|
|||
"Preferences": "Impostazioni",
|
||||
"Preferences_saved": "Impostazioni salvate!",
|
||||
"Privacy_Policy": " Privacy Policy",
|
||||
"Private_Channel": "Canale privato",
|
||||
"Private": "Privato",
|
||||
"Processing": "Elaborazione...",
|
||||
"Profile_saved_successfully": "Profilo salvato correttamente!",
|
||||
|
@ -398,7 +396,6 @@
|
|||
"Reactions": "Reazioni",
|
||||
"Read_External_Permission_Message": "Rocket.Chat deve accedere alle foto, media, e documenti sul tuo dispositivo",
|
||||
"Read_External_Permission": "Permesso di Lettura della Memoria",
|
||||
"Read_Only_Channel": "Canale in sola lettura",
|
||||
"Read_Only": "Sola lettura",
|
||||
"Read_Receipt": "Conferma di lettura",
|
||||
"Receive_Group_Mentions": "Ricevi menzioni di gruppo",
|
||||
|
|
|
@ -119,8 +119,7 @@
|
|||
"Black": "ブラック",
|
||||
"Block_user": "ブロックしたユーザー",
|
||||
"Browser": "ブラウザ",
|
||||
"Broadcast_channel_Description": "許可されたユーザーのみが新しいメッセージを書き込めます。他のユーザーは返信することができます",
|
||||
"Broadcast_Channel": "配信チャンネル",
|
||||
"Broadcast_hint": "許可されたユーザーのみが新しいメッセージを書き込めます。他のユーザーは返信することができます",
|
||||
"Busy": "取り込み中",
|
||||
"By_proceeding_you_are_agreeing": "続行することにより、私達を承認します",
|
||||
"Cancel_editing": "編集をキャンセル",
|
||||
|
@ -371,7 +370,6 @@
|
|||
"Preferences": "設定",
|
||||
"Preferences_saved": "設定が保存されました。",
|
||||
"Privacy_Policy": " プライバシーポリシー",
|
||||
"Private_Channel": "プライベートチャンネル",
|
||||
"Private": "プライベート",
|
||||
"Processing": "処理中...",
|
||||
"Profile_saved_successfully": "プロフィールが保存されました!",
|
||||
|
@ -384,7 +382,6 @@
|
|||
"Reactions_are_disabled": "リアクションは無効化されています",
|
||||
"Reactions_are_enabled": "リアクションは有効化されています",
|
||||
"Reactions": "リアクション",
|
||||
"Read_Only_Channel": "読み取り専用チャンネル",
|
||||
"Read_Only": "読み取り専用",
|
||||
"Read_Receipt": "レシートを見る",
|
||||
"Receive_Group_Mentions": "グループの通知を受け取る",
|
||||
|
|
|
@ -119,8 +119,7 @@
|
|||
"Black": "Zwart",
|
||||
"Block_user": "Blokkeer gebruiker",
|
||||
"Browser": "Browser",
|
||||
"Broadcast_channel_Description": "Alleen geautoriseerde gebruikers kunnen nieuwe berichten schrijven, maar de andere gebruikers zullen kunnen antwoorden",
|
||||
"Broadcast_Channel": "Uitzendkanaal",
|
||||
"Broadcast_hint": "Alleen geautoriseerde gebruikers kunnen nieuwe berichten schrijven, maar de andere gebruikers zullen kunnen antwoorden",
|
||||
"Busy": "Bezig",
|
||||
"By_proceeding_you_are_agreeing": "Door verder te gaan ga je akkoord met onze",
|
||||
"Cancel_editing": "Bewerken annuleren",
|
||||
|
@ -398,7 +397,6 @@
|
|||
"Preferences": "Voorkeuren",
|
||||
"Preferences_saved": "Voorkeuren opgeslagen!",
|
||||
"Privacy_Policy": " Privacybeleid",
|
||||
"Private_Channel": "Privékanaal",
|
||||
"Private": "Privé",
|
||||
"Processing": "Verwerking...",
|
||||
"Profile_saved_successfully": "Profiel succesvol opgeslagen!",
|
||||
|
@ -413,7 +411,6 @@
|
|||
"Reactions": "Reacties",
|
||||
"Read_External_Permission_Message": "Rocket.Chat heeft toegang nodig tot foto's, media en bestanden op je apparaat",
|
||||
"Read_External_Permission": "Lees toestemming voor media",
|
||||
"Read_Only_Channel": "Alleen-lezen kanaal",
|
||||
"Read_Only": "Alleen lezen",
|
||||
"Read_Receipt": "Leesbevestiging",
|
||||
"Receive_Group_Mentions": "Groepsvermeldingen ontvangen",
|
||||
|
@ -715,9 +712,6 @@
|
|||
"Team_not_found": "Team niet gevonden",
|
||||
"Create_Team": "Team aanmaken",
|
||||
"Team_Name": "Teamnaam",
|
||||
"Private_Team": "Privé team",
|
||||
"Read_Only_Team": "Alleen-lezen team",
|
||||
"Broadcast_Team": "Broadcast team",
|
||||
"creating_team": "team maken",
|
||||
"team-name-already-exists": "Er bestaat al een team met die naam",
|
||||
"Add_Channel_to_Team": "Kanaal toevoegen aan team",
|
||||
|
|
|
@ -121,8 +121,6 @@
|
|||
"Black": "Preto",
|
||||
"Block_user": "Bloquear usuário",
|
||||
"Browser": "Navegador",
|
||||
"Broadcast_channel_Description": "Somente usuários autorizados podem escrever novas mensagens, mas os outros usuários poderão responder",
|
||||
"Broadcast_Channel": "Canal de Transmissão",
|
||||
"Busy": "Ocupado",
|
||||
"By_proceeding_you_are_agreeing": "Ao prosseguir você está aceitando",
|
||||
"Cancel_editing": "Cancelar edição",
|
||||
|
@ -334,6 +332,7 @@
|
|||
"No_mentioned_messages": "Não há menções",
|
||||
"No_pinned_messages": "Não há mensagens fixadas",
|
||||
"No_results_found": "Nenhum resultado encontrado",
|
||||
"No_members_found": "Nenhum usuário encontrado",
|
||||
"No_starred_messages": "Não há mensagens favoritas",
|
||||
"No_thread_messages": "Não há tópicos",
|
||||
"No_label_provided": "Sem {{label}}.",
|
||||
|
@ -383,7 +382,6 @@
|
|||
"Preferences": "Preferências",
|
||||
"Preferences_saved": "Preferências salvas!",
|
||||
"Privacy_Policy": " Política de Privacidade",
|
||||
"Private_Channel": "Canal Privado",
|
||||
"Private": "Privado",
|
||||
"Processing": "Processando...",
|
||||
"Profile_saved_successfully": "Perfil salvo com sucesso!",
|
||||
|
@ -398,7 +396,6 @@
|
|||
"Reactions": "Reações",
|
||||
"Read_External_Permission_Message": "Rocket.Chat precisa acessar fotos, mídia e arquivos no seu dispositivo",
|
||||
"Read_External_Permission": "Permissão de acesso à arquivos",
|
||||
"Read_Only_Channel": "Canal Somente Leitura",
|
||||
"Read_Only": "Somente Leitura",
|
||||
"Read_Receipt": "Lida por",
|
||||
"Receive_Group_Mentions": "Receber menções de grupo",
|
||||
|
@ -682,7 +679,6 @@
|
|||
"Teams": "Times",
|
||||
"No_team_channels_found": "Nenhum canal encontrado",
|
||||
"Team_not_found": "Time não encontrado",
|
||||
"Private_Team": "Equipe Privada",
|
||||
"Add_Channel_to_Team": "Adicionar Canal ao Time",
|
||||
"Left_The_Team_Successfully": "Saiu do time com sucesso",
|
||||
"Create_New": "Criar",
|
||||
|
@ -795,5 +791,26 @@
|
|||
"Show_badge_for_mentions_Info": "Mostrar contador somente para menções diretas",
|
||||
"totp-invalid": "Código ou senha inválida",
|
||||
"Close_Chat": "Fechar Conversa",
|
||||
"Select_tags": "Selecionar tag(s)"
|
||||
"Select_tags": "Selecionar tag(s)",
|
||||
"Skip": "Pular",
|
||||
"N_Selected_members": "{{n}} selecionados",
|
||||
"Broadcast": "Transmissão",
|
||||
"Broadcast_hint": "Somente usuários autorizados podem escrever novas mensagens, mas os outros usuários poderão responder",
|
||||
"Team_hint_private": "Apenas pessoas convidadas podem entrar",
|
||||
"Team_hint_public": "Quando desativado, qualquer um pode entrar na equipe",
|
||||
"Team_hint_not_read_only": "Todos os usuários nesta equipe podem escrever mensagens",
|
||||
"Team_hint_encrypted": "Equipe criptografada de ponta a ponta. A pesquisa não funcionará com equipes criptografadas e as notificações poderão não exibir o conteúdo das mensagens.",
|
||||
"Team_hint_encrypted_not_available": "Disponível apenas para equipes privadas",
|
||||
"Channel_hint_private":"Apenas usuários convidados podem acessar este canal",
|
||||
"Channel_hint_public":"Todos podem acessar este canal",
|
||||
"Channel_hint_encrypted": "Canal criptografado de ponta a ponta. A pesquisa não funcionará com canais criptografados e as notificações podem não mostrar o conteúdo das mensagens.",
|
||||
"Channel_hint_not_read_only": "Todos usuários no canal podem enviar mensagens novas",
|
||||
"Channel_hint_encrypted_not_available": "Indisponível para canais públicos",
|
||||
"Read_only_hint":"Somente usuários autorizados podem escrever novas mensagens",
|
||||
"Discussion": "Discussão",
|
||||
"Channel": "Canal",
|
||||
"Team": "Time",
|
||||
"Select_Members": "Selecionar Membros",
|
||||
"Also_send_thread_message_to_channel_behavior": "Também enviar mensagem do tópico para o canal",
|
||||
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Permitir que os usuários selecionem o comportamento Também enviar para o canal"
|
||||
}
|
|
@ -118,8 +118,7 @@
|
|||
"Black": "Preto",
|
||||
"Block_user": "Bloquear utilizador",
|
||||
"Browser": "Navegador",
|
||||
"Broadcast_channel_Description": "Apenas utilizadores autorizados podem escrever novas mensagens, mas os outros utilizadores poderão responder",
|
||||
"Broadcast_Channel": "Canal de Transmissão",
|
||||
"Broadcast_hint": "Apenas utilizadores autorizados podem escrever novas mensagens, mas os outros utilizadores poderão responder",
|
||||
"Busy": "Ocupado",
|
||||
"By_proceeding_you_are_agreeing": "Ao prosseguir você concorda com o(s) nosso(s)",
|
||||
"Cancel_editing": "Cancelar edição",
|
||||
|
@ -390,7 +389,6 @@
|
|||
"Preferences": "Preferências",
|
||||
"Preferences_saved": "Preferências guardadas!",
|
||||
"Privacy_Policy": " Política de Privacidade",
|
||||
"Private_Channel": "Canal Privado",
|
||||
"Private": "Privado",
|
||||
"Processing": "A processar...",
|
||||
"Profile_saved_successfully": "Perfil actualizado com sucesso!",
|
||||
|
@ -405,7 +403,6 @@
|
|||
"Reactions": "Reacções",
|
||||
"Read_External_Permission_Message": "Rocket.Chat precisa acessar fotos, média e arquivos em seu dispositivo",
|
||||
"Read_External_Permission": "Permissão de leitura da média",
|
||||
"Read_Only_Channel": "Canal só de leitura",
|
||||
"Read_Only": "Só de Leitura",
|
||||
"Read_Receipt": "Recibos de leitura",
|
||||
"Register": "Registar",
|
||||
|
|
|
@ -119,8 +119,7 @@
|
|||
"Black": "Черный",
|
||||
"Block_user": "Блокировать пользователя",
|
||||
"Browser": "Браузер",
|
||||
"Broadcast_channel_Description": "Только авторизованные пользователи могут писать новые сообщения, но другие пользователи смогут ответить",
|
||||
"Broadcast_Channel": "Широковещательный канал",
|
||||
"Broadcast_hint": "Только авторизованные пользователи могут писать новые сообщения, но другие пользователи смогут ответить",
|
||||
"Busy": "Занят",
|
||||
"By_proceeding_you_are_agreeing": "Продолжая, вы соглашаетесь с нашими",
|
||||
"Cancel_editing": "Отменить правку",
|
||||
|
@ -394,7 +393,6 @@
|
|||
"Preferences": "Настройки",
|
||||
"Preferences_saved": "Настройки сохранены!",
|
||||
"Privacy_Policy": " Политика конфиденциальности",
|
||||
"Private_Channel": "Приватный канал",
|
||||
"Private": "Приватный",
|
||||
"Processing": "Обработка...",
|
||||
"Profile_saved_successfully": "Профиль успешно сохранен!",
|
||||
|
@ -409,7 +407,6 @@
|
|||
"Reactions": "Реакции",
|
||||
"Read_External_Permission_Message": "Rocket.Chat необходим доступ к фотографиям, медиа и другим файлам на вашем устройстве",
|
||||
"Read_External_Permission": "Разрешение на Чтение Медиа",
|
||||
"Read_Only_Channel": "Канал только для чтения",
|
||||
"Read_Only": "Только для чтения",
|
||||
"Read_Receipt": "Уведомление о прочтении",
|
||||
"Receive_Group_Mentions": "Получать групповые уведомления",
|
||||
|
@ -706,9 +703,6 @@
|
|||
"Team_not_found": "Команда не найдена",
|
||||
"Create_Team": "Создать Команду",
|
||||
"Team_Name": "Имя Команды",
|
||||
"Private_Team": "Приватная Команда",
|
||||
"Read_Only_Team": "Команда только для чтения",
|
||||
"Broadcast_Team": "Широковещательная Команда",
|
||||
"creating_team": "создание Команды",
|
||||
"team-name-already-exists": "Команда с таким названием уже существует",
|
||||
"Add_Channel_to_Team": "Добавить канал в Команду",
|
||||
|
|
|
@ -115,8 +115,7 @@
|
|||
"Black": "Koyu",
|
||||
"Block_user": "Kullanıcıyı engelle",
|
||||
"Browser": "Tarayıcı",
|
||||
"Broadcast_channel_Description": "Yalnızca yetkili kullanıcılar yeni ileti yazabilir, ancak diğer kullanıcılar yanıt verebilir",
|
||||
"Broadcast_Channel": "Kanala Yayınla",
|
||||
"Broadcast_hint": "Yalnızca yetkili kullanıcılar yeni ileti yazabilir, ancak diğer kullanıcılar yanıt verebilir",
|
||||
"Busy": "Meşgul",
|
||||
"By_proceeding_you_are_agreeing": "Devam ederek kabul ediyorsunuz: ",
|
||||
"Cancel_editing": "Düzenlemeyi iptal et",
|
||||
|
@ -384,7 +383,6 @@
|
|||
"Preferences": "Tercihler",
|
||||
"Preferences_saved": "Tercihler kaydedildi!",
|
||||
"Privacy_Policy": " Privacy Policy",
|
||||
"Private_Channel": "Özel Kanal",
|
||||
"Private": "Özel",
|
||||
"Processing": "İşleniyor...",
|
||||
"Profile_saved_successfully": "Profil başarıyla kaydedildi!",
|
||||
|
@ -399,7 +397,6 @@
|
|||
"Reactions": "Tepkiler",
|
||||
"Read_External_Permission_Message": "Rocket.Chat'in cihazınızdaki fotoğraflara, medyaya ve dosyalara erişmesi gerekiyor",
|
||||
"Read_External_Permission": "Medya Okuma İzni ",
|
||||
"Read_Only_Channel": "Yazma Kısıtlı Kanal",
|
||||
"Read_Only": "Yazma Kısıtlı",
|
||||
"Read_Receipt": "Okundu Bilgisi",
|
||||
"Receive_Group_Mentions": "Grup Bahsetmelerini Al",
|
||||
|
|
|
@ -115,8 +115,7 @@
|
|||
"Black": "黑色",
|
||||
"Block_user": "屏蔽此用户",
|
||||
"Browser": "浏览器",
|
||||
"Broadcast_channel_Description": "只有经过授权的用户才能写新信息,但其他用户可以回复",
|
||||
"Broadcast_Channel": "广播频道",
|
||||
"Broadcast_hint": "只有经过授权的用户才能写新信息,但其他用户可以回复",
|
||||
"Busy": "忙碌",
|
||||
"By_proceeding_you_are_agreeing": "继续操作,请同意我们的",
|
||||
"Cancel_editing": "取消编辑",
|
||||
|
@ -381,7 +380,6 @@
|
|||
"Preferences": "偏好设置",
|
||||
"Preferences_saved": "偏好已保存!",
|
||||
"Privacy_Policy": "隐私政策",
|
||||
"Private_Channel": "私人频道",
|
||||
"Private": "私有的",
|
||||
"Processing": "处理中",
|
||||
"Profile_saved_successfully": "个人资料保存成功!",
|
||||
|
@ -396,7 +394,6 @@
|
|||
"Reactions": "表情貼",
|
||||
"Read_External_Permission_Message": "Rocket.Chat 需要存取您装置上的相片、多媒体及文件",
|
||||
"Read_External_Permission": "读取媒体权限",
|
||||
"Read_Only_Channel": "只读频道",
|
||||
"Read_Only": "只读",
|
||||
"Read_Receipt": "查看已读人员",
|
||||
"Receive_Group_Mentions": "接收群组提及",
|
||||
|
|
|
@ -116,8 +116,7 @@
|
|||
"Black": "黑色",
|
||||
"Block_user": "封鎖此用戶",
|
||||
"Browser": "瀏覽器",
|
||||
"Broadcast_channel_Description": "只有經過授權的使用者才能發送新訊息,但其他使用者可以回覆",
|
||||
"Broadcast_Channel": "廣播頻道",
|
||||
"Broadcast_hint": "只有經過授權的使用者才能發送新訊息,但其他使用者可以回覆",
|
||||
"Busy": "忙碌",
|
||||
"By_proceeding_you_are_agreeing": "若要繼續操作,請同意我們的",
|
||||
"Cancel_editing": "取消編輯",
|
||||
|
@ -383,7 +382,6 @@
|
|||
"Preferences": "偏好設定",
|
||||
"Preferences_saved": "偏好設定已被儲存!",
|
||||
"Privacy_Policy": "隱私政策",
|
||||
"Private_Channel": "私人頻道",
|
||||
"Private": "私有的",
|
||||
"Processing": "處理中",
|
||||
"Profile_saved_successfully": "個人資料儲存成功!",
|
||||
|
@ -398,7 +396,6 @@
|
|||
"Reactions": "表情貼",
|
||||
"Read_External_Permission_Message": "Rocket.Chat 需要存取您裝置上的相片、多媒體及檔案",
|
||||
"Read_External_Permission": "讀取媒體權限",
|
||||
"Read_Only_Channel": "唯讀頻道",
|
||||
"Read_Only": "唯讀",
|
||||
"Read_Receipt": "查看已讀人員",
|
||||
"Receive_Group_Mentions": "接收群組提及",
|
||||
|
|
|
@ -85,7 +85,7 @@ export function hasRole(role): boolean {
|
|||
return userRoles.indexOf(role) > -1;
|
||||
}
|
||||
|
||||
export async function hasPermission(permissions, rid?: any): boolean[] {
|
||||
export async function hasPermission(permissions, rid?: any): Promise<boolean[]> {
|
||||
let roomRoles = [];
|
||||
if (rid) {
|
||||
const db = database.active;
|
||||
|
|
|
@ -272,6 +272,10 @@ export default {
|
|||
RA_MOVE_TO_TEAM_F: 'ra_move_to_team_f',
|
||||
RA_SEARCH_TEAM: 'ra_search_team',
|
||||
|
||||
// ROOM MEMBERS ACTIONS VIEW
|
||||
RM_GO_SELECTEDUSERS: 'rm_go_selected_users',
|
||||
RM_GO_INVITEUSERS: 'rm_go_invite_users',
|
||||
|
||||
// ROOM INFO VIEW
|
||||
RI_GO_RI_EDIT: 'ri_go_ri_edit',
|
||||
RI_GO_LIVECHAT_EDIT: 'ri_go_livechat_edit',
|
||||
|
|
|
@ -4,6 +4,7 @@ import { DarkTheme, DefaultTheme } from '@react-navigation/native';
|
|||
import { themes } from '../../../constants';
|
||||
import { TSupportedThemes } from '../../../../theme';
|
||||
import { isIOS } from '../deviceInfo';
|
||||
import sharedStyles from '../../../../views/Styles';
|
||||
|
||||
export * from './animations';
|
||||
|
||||
|
@ -36,7 +37,7 @@ export const themedHeader = (theme: TSupportedThemes) => ({
|
|||
backgroundColor: themes[theme].headerBackground
|
||||
},
|
||||
headerTintColor: themes[theme].headerTintColor,
|
||||
headerTitleStyle: { color: themes[theme].headerTitleColor }
|
||||
headerTitleStyle: { ...sharedStyles.textSemibold, color: themes[theme].headerTitleColor, fontSize: 18 }
|
||||
});
|
||||
|
||||
export const navigationTheme = (theme: TSupportedThemes) => {
|
||||
|
|
|
@ -295,6 +295,9 @@ export default function subscribeRooms() {
|
|||
if ((['settings.preferences.showMessageInMainThread'] as any) in diff) {
|
||||
store.dispatch(setUser({ showMessageInMainThread: diff['settings.preferences.showMessageInMainThread'] }));
|
||||
}
|
||||
if ((['settings.preferences.alsoSendThreadToChannel'] as any) in diff) {
|
||||
store.dispatch(setUser({ alsoSendThreadToChannel: diff['settings.preferences.alsoSendThreadToChannel'] }));
|
||||
}
|
||||
}
|
||||
if (/subscriptions/.test(ev)) {
|
||||
if (type === 'removed') {
|
||||
|
|
|
@ -17,10 +17,15 @@ function replace(name: string, params: any) {
|
|||
navigationRef.current?.dispatch(StackActions.replace(name, params));
|
||||
}
|
||||
|
||||
function popToTop() {
|
||||
navigationRef.current?.dispatch(StackActions.popToTop());
|
||||
}
|
||||
|
||||
export default {
|
||||
navigationRef,
|
||||
routeNameRef,
|
||||
navigate,
|
||||
back,
|
||||
replace
|
||||
replace,
|
||||
popToTop
|
||||
};
|
||||
|
|
|
@ -268,8 +268,10 @@ async function login(credentials: ICredentials, isFromWebView = false): Promise<
|
|||
const result = sdk.current.currentLogin?.result;
|
||||
|
||||
let enableMessageParserEarlyAdoption = true;
|
||||
let showMessageInMainThread = false;
|
||||
if (compareServerVersion(serverVersion, 'lowerThan', '5.0.0')) {
|
||||
enableMessageParserEarlyAdoption = result.me.settings?.preferences?.enableMessageParserEarlyAdoption ?? true;
|
||||
showMessageInMainThread = result.me.settings?.preferences?.showMessageInMainThread ?? true;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
|
@ -287,8 +289,9 @@ async function login(credentials: ICredentials, isFromWebView = false): Promise<
|
|||
roles: result.me.roles,
|
||||
avatarETag: result.me.avatarETag,
|
||||
isFromWebView,
|
||||
showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true,
|
||||
enableMessageParserEarlyAdoption
|
||||
showMessageInMainThread,
|
||||
enableMessageParserEarlyAdoption,
|
||||
alsoSendThreadToChannel: result.me.settings?.preferences?.alsoSendThreadToChannel
|
||||
};
|
||||
return user;
|
||||
}
|
||||
|
|
|
@ -688,7 +688,7 @@ export const runSlashCommand = (command: string, roomId: string, params: string,
|
|||
roomId,
|
||||
params,
|
||||
triggerId,
|
||||
tmid
|
||||
...(tmid && { tmid })
|
||||
});
|
||||
|
||||
export const getCommandPreview = (command: string, roomId: string, params: string) =>
|
||||
|
|
|
@ -42,7 +42,12 @@ const loginWithPasswordCall = args => Services.loginWithPassword(args);
|
|||
const loginCall = (credentials, isFromWebView) => Services.login(credentials, isFromWebView);
|
||||
const logoutCall = args => logout(args);
|
||||
|
||||
const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false, isFromWebView = false }) {
|
||||
const handleLoginRequest = function* handleLoginRequest({
|
||||
credentials,
|
||||
logoutOnError = false,
|
||||
isFromWebView = false,
|
||||
registerCustomFields
|
||||
}) {
|
||||
logEvent(events.LOGIN_DEFAULT_LOGIN);
|
||||
try {
|
||||
let result;
|
||||
|
@ -78,6 +83,10 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE
|
|||
}
|
||||
});
|
||||
yield put(loginSuccess(result));
|
||||
if (registerCustomFields) {
|
||||
const updatedUser = yield call(Services.saveUserProfile, {}, { ...registerCustomFields });
|
||||
yield put(setUser({ ...result, ...updatedUser.user }));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e?.data?.message && /you've been logged out by the server/i.test(e.data.message)) {
|
||||
|
|
|
@ -95,7 +95,7 @@ const ChatsStackNavigator = () => {
|
|||
<ChatsStack.Screen name='SelectListView' component={SelectListView} options={SelectListView.navigationOptions} />
|
||||
<ChatsStack.Screen name='RoomInfoView' component={RoomInfoView} options={RoomInfoView.navigationOptions} />
|
||||
<ChatsStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />
|
||||
<ChatsStack.Screen name='RoomMembersView' component={RoomMembersView} options={RoomMembersView.navigationOptions} />
|
||||
<ChatsStack.Screen name='RoomMembersView' component={RoomMembersView} />
|
||||
<ChatsStack.Screen name='DiscussionsView' component={DiscussionsView} />
|
||||
<ChatsStack.Screen
|
||||
name='SearchMessagesView'
|
||||
|
@ -123,7 +123,7 @@ const ChatsStackNavigator = () => {
|
|||
options={ThreadMessagesView.navigationOptions}
|
||||
/>
|
||||
<ChatsStack.Screen name='TeamChannelsView' component={TeamChannelsView} />
|
||||
<ChatsStack.Screen name='CreateChannelView' component={CreateChannelView} options={CreateChannelView.navigationOptions} />
|
||||
<ChatsStack.Screen name='CreateChannelView' component={CreateChannelView} />
|
||||
<ChatsStack.Screen name='AddChannelTeamView' component={AddChannelTeamView} />
|
||||
<ChatsStack.Screen
|
||||
name='AddExistingChannelView'
|
||||
|
@ -252,13 +252,9 @@ const NewMessageStackNavigator = () => {
|
|||
<NewMessageStack.Navigator
|
||||
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}
|
||||
>
|
||||
<NewMessageStack.Screen name='NewMessageView' component={NewMessageView} options={NewMessageView.navigationOptions} />
|
||||
<NewMessageStack.Screen name='NewMessageView' component={NewMessageView} />
|
||||
<NewMessageStack.Screen name='SelectedUsersViewCreateChannel' component={SelectedUsersView} />
|
||||
<NewMessageStack.Screen
|
||||
name='CreateChannelView'
|
||||
component={CreateChannelView}
|
||||
options={CreateChannelView.navigationOptions}
|
||||
/>
|
||||
<NewMessageStack.Screen name='CreateChannelView' component={CreateChannelView} />
|
||||
<NewMessageStack.Screen name='CreateDiscussionView' component={CreateDiscussionView} />
|
||||
</NewMessageStack.Navigator>
|
||||
);
|
||||
|
|
|
@ -127,7 +127,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
|
|||
<ModalStack.Screen name='RoomInfoView' component={RoomInfoView} options={RoomInfoView.navigationOptions} />
|
||||
<ModalStack.Screen name='SelectListView' component={SelectListView} />
|
||||
<ModalStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />
|
||||
<ModalStack.Screen name='RoomMembersView' component={RoomMembersView} options={RoomMembersView.navigationOptions} />
|
||||
<ModalStack.Screen name='RoomMembersView' component={RoomMembersView} />
|
||||
<ModalStack.Screen
|
||||
name='SearchMessagesView'
|
||||
component={SearchMessagesView}
|
||||
|
@ -187,9 +187,9 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
|
|||
<ModalStack.Screen name='ProfileView' component={ProfileView} />
|
||||
<ModalStack.Screen name='DisplayPrefsView' component={DisplayPrefsView} />
|
||||
<ModalStack.Screen name='AdminPanelView' component={AdminPanelView} />
|
||||
<ModalStack.Screen name='NewMessageView' component={NewMessageView} options={NewMessageView.navigationOptions} />
|
||||
<ModalStack.Screen name='NewMessageView' component={NewMessageView} />
|
||||
<ModalStack.Screen name='SelectedUsersViewCreateChannel' component={SelectedUsersView} />
|
||||
<ModalStack.Screen name='CreateChannelView' component={CreateChannelView} options={CreateChannelView.navigationOptions} />
|
||||
<ModalStack.Screen name='CreateChannelView' component={CreateChannelView} />
|
||||
<ModalStack.Screen name='CreateDiscussionView' component={CreateDiscussionView} />
|
||||
<ModalStack.Screen name='E2ESaveYourPasswordView' component={E2ESaveYourPasswordView} />
|
||||
<ModalStack.Screen name='E2EHowItWorksView' component={E2EHowItWorksView} />
|
||||
|
|
|
@ -64,6 +64,7 @@ export type ModalStackParamList = {
|
|||
RoomMembersView: {
|
||||
rid: string;
|
||||
room: TSubscriptionModel;
|
||||
joined?: boolean;
|
||||
};
|
||||
DiscussionsView: {
|
||||
rid: string;
|
||||
|
|
|
@ -25,7 +25,7 @@ const _OutsideStack = () => {
|
|||
<Outside.Screen name='NewServerView' component={NewServerView} options={NewServerView.navigationOptions} />
|
||||
<Outside.Screen name='WorkspaceView' component={WorkspaceView} options={WorkspaceView.navigationOptions} />
|
||||
<Outside.Screen name='LoginView' component={LoginView} options={LoginView.navigationOptions} />
|
||||
<Outside.Screen name='ForgotPasswordView' component={ForgotPasswordView} options={ForgotPasswordView.navigationOptions} />
|
||||
<Outside.Screen name='ForgotPasswordView' component={ForgotPasswordView} />
|
||||
<Outside.Screen name='SendEmailConfirmationView' component={SendEmailConfirmationView} />
|
||||
<Outside.Screen name='RegisterView' component={RegisterView} options={RegisterView.navigationOptions} />
|
||||
<Outside.Screen name='LegalView' component={LegalView} />
|
||||
|
|
|
@ -75,6 +75,7 @@ export type ChatsStackParamList = {
|
|||
RoomMembersView: {
|
||||
rid: string;
|
||||
room: ISubscription;
|
||||
joined?: boolean;
|
||||
};
|
||||
DiscussionsView: {
|
||||
rid: string;
|
||||
|
|
|
@ -1,432 +0,0 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { FlatList, ScrollView, StyleSheet, Switch, Text, View, SwitchProps } from 'react-native';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import * as List from '../containers/List';
|
||||
import { TextInput } from '../containers/TextInput';
|
||||
import { sendLoadingEvent } from '../containers/Loading';
|
||||
import { createChannelRequest } from '../actions/createChannel';
|
||||
import { removeUser } from '../actions/selectedUsers';
|
||||
import KeyboardView from '../containers/KeyboardView';
|
||||
import scrollPersistTaps from '../lib/methods/helpers/scrollPersistTaps';
|
||||
import I18n from '../i18n';
|
||||
import UserItem from '../containers/UserItem';
|
||||
import * as HeaderButton from '../containers/HeaderButton';
|
||||
import StatusBar from '../containers/StatusBar';
|
||||
import { SWITCH_TRACK_COLOR, themes } from '../lib/constants';
|
||||
import { withTheme } from '../theme';
|
||||
import { Review } from '../lib/methods/helpers/review';
|
||||
import { getUserSelector } from '../selectors/login';
|
||||
import { events, logEvent } from '../lib/methods/helpers/log';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import sharedStyles from './Styles';
|
||||
import { ChatsStackParamList } from '../stacks/types';
|
||||
import { IApplicationState, IBaseScreen, IUser } from '../definitions';
|
||||
import { hasPermission } from '../lib/methods/helpers';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1
|
||||
},
|
||||
list: {
|
||||
width: '100%'
|
||||
},
|
||||
input: {
|
||||
height: 54,
|
||||
paddingHorizontal: 18,
|
||||
fontSize: 17,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
switchContainer: {
|
||||
height: 54,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 18
|
||||
},
|
||||
label: {
|
||||
fontSize: 17,
|
||||
...sharedStyles.textMedium
|
||||
},
|
||||
invitedHeader: {
|
||||
marginTop: 18,
|
||||
marginHorizontal: 15,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
},
|
||||
invitedTitle: {
|
||||
fontSize: 18,
|
||||
...sharedStyles.textSemibold,
|
||||
lineHeight: 41
|
||||
},
|
||||
invitedCount: {
|
||||
fontSize: 14,
|
||||
...sharedStyles.textRegular
|
||||
}
|
||||
});
|
||||
|
||||
interface IOtherUser {
|
||||
_id: string;
|
||||
name: string;
|
||||
fname: string;
|
||||
}
|
||||
|
||||
interface ICreateChannelViewState {
|
||||
channelName: string;
|
||||
type: boolean;
|
||||
readOnly: boolean;
|
||||
encrypted: boolean;
|
||||
broadcast: boolean;
|
||||
isTeam: boolean;
|
||||
permissions: boolean[];
|
||||
}
|
||||
|
||||
interface ICreateChannelViewProps extends IBaseScreen<ChatsStackParamList, 'CreateChannelView'> {
|
||||
baseUrl: string;
|
||||
error: object;
|
||||
failure: boolean;
|
||||
isFetching: boolean;
|
||||
encryptionEnabled: boolean;
|
||||
users: IOtherUser[];
|
||||
user: IUser;
|
||||
teamId: string;
|
||||
createPublicChannelPermission: string[] | undefined;
|
||||
createPrivateChannelPermission: string[] | undefined;
|
||||
}
|
||||
|
||||
interface ISwitch extends SwitchProps {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreateChannelViewState> {
|
||||
private teamId?: string;
|
||||
|
||||
constructor(props: ICreateChannelViewProps) {
|
||||
super(props);
|
||||
const { route } = this.props;
|
||||
const isTeam = route?.params?.isTeam || false;
|
||||
this.teamId = route?.params?.teamId;
|
||||
this.state = {
|
||||
channelName: '',
|
||||
type: true,
|
||||
readOnly: false,
|
||||
encrypted: false,
|
||||
broadcast: false,
|
||||
isTeam,
|
||||
permissions: []
|
||||
};
|
||||
this.setHeader();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.handleHasPermission();
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: ICreateChannelViewProps, nextState: ICreateChannelViewState) {
|
||||
const { channelName, type, readOnly, broadcast, encrypted, permissions } = this.state;
|
||||
const { users, isFetching, encryptionEnabled, theme, createPublicChannelPermission, createPrivateChannelPermission } =
|
||||
this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.channelName !== channelName) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.type !== type) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.readOnly !== readOnly) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.encrypted !== encrypted) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.broadcast !== broadcast) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.permissions !== permissions) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.isFetching !== isFetching) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.encryptionEnabled !== encryptionEnabled) {
|
||||
return true;
|
||||
}
|
||||
if (!dequal(nextProps.createPublicChannelPermission, createPublicChannelPermission)) {
|
||||
return true;
|
||||
}
|
||||
if (!dequal(nextProps.createPrivateChannelPermission, createPrivateChannelPermission)) {
|
||||
return true;
|
||||
}
|
||||
if (!dequal(nextProps.users, users)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ICreateChannelViewProps) {
|
||||
const { createPublicChannelPermission, createPrivateChannelPermission, isFetching } = this.props;
|
||||
if (
|
||||
!dequal(createPublicChannelPermission, prevProps.createPublicChannelPermission) ||
|
||||
!dequal(createPrivateChannelPermission, prevProps.createPrivateChannelPermission)
|
||||
) {
|
||||
this.handleHasPermission();
|
||||
}
|
||||
if (isFetching !== prevProps.isFetching) {
|
||||
sendLoadingEvent({ visible: isFetching });
|
||||
}
|
||||
}
|
||||
|
||||
setHeader = () => {
|
||||
const { navigation } = this.props;
|
||||
const { isTeam } = this.state;
|
||||
|
||||
navigation.setOptions({
|
||||
title: isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel')
|
||||
});
|
||||
};
|
||||
|
||||
toggleRightButton = (channelName: string) => {
|
||||
const { navigation } = this.props;
|
||||
navigation.setOptions({
|
||||
headerRight: () =>
|
||||
channelName.trim().length > 0 && (
|
||||
<HeaderButton.Container>
|
||||
<HeaderButton.Item title={I18n.t('Create')} onPress={this.submit} testID='create-channel-submit' />
|
||||
</HeaderButton.Container>
|
||||
)
|
||||
});
|
||||
};
|
||||
|
||||
onChangeText = (channelName: string) => {
|
||||
this.toggleRightButton(channelName);
|
||||
this.setState({ channelName });
|
||||
};
|
||||
|
||||
submit = () => {
|
||||
const { channelName, type, readOnly, broadcast, encrypted, isTeam } = this.state;
|
||||
const { users: usersProps, isFetching, dispatch } = this.props;
|
||||
|
||||
if (!channelName.trim() || isFetching) {
|
||||
return;
|
||||
}
|
||||
|
||||
// transform users object into array of usernames
|
||||
const users = usersProps.map(user => user.name);
|
||||
|
||||
// create channel or team
|
||||
const data = {
|
||||
name: channelName,
|
||||
users,
|
||||
type,
|
||||
readOnly,
|
||||
broadcast,
|
||||
encrypted,
|
||||
isTeam,
|
||||
teamId: this.teamId
|
||||
};
|
||||
dispatch(createChannelRequest(data));
|
||||
Review.pushPositiveEvent();
|
||||
};
|
||||
|
||||
removeUser = (user: IOtherUser) => {
|
||||
logEvent(events.CR_REMOVE_USER);
|
||||
const { dispatch } = this.props;
|
||||
dispatch(removeUser(user));
|
||||
};
|
||||
|
||||
renderSwitch = ({ id, value, label, onValueChange, disabled = false }: ISwitch) => {
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<View style={[styles.switchContainer, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||
<Text style={[styles.label, { color: themes[theme].titleText }]}>{I18n.t(label)}</Text>
|
||||
<Switch
|
||||
value={value}
|
||||
onValueChange={onValueChange}
|
||||
testID={`create-channel-${id}`}
|
||||
trackColor={SWITCH_TRACK_COLOR}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
handleHasPermission = async () => {
|
||||
const { createPublicChannelPermission, createPrivateChannelPermission } = this.props;
|
||||
const permissions = [createPublicChannelPermission, createPrivateChannelPermission];
|
||||
const permissionsToCreate = await hasPermission(permissions);
|
||||
this.setState({ permissions: permissionsToCreate });
|
||||
};
|
||||
|
||||
renderType() {
|
||||
const { type, isTeam, permissions } = this.state;
|
||||
const isDisabled = permissions.filter(r => r === true).length <= 1;
|
||||
|
||||
return this.renderSwitch({
|
||||
id: 'type',
|
||||
value: permissions[1] ? type : false,
|
||||
disabled: isDisabled,
|
||||
label: isTeam ? 'Private_Team' : 'Private_Channel',
|
||||
onValueChange: (value: boolean) => {
|
||||
logEvent(events.CR_TOGGLE_TYPE);
|
||||
// If we set the channel as public, encrypted status should be false
|
||||
this.setState(({ encrypted }) => ({ type: value, encrypted: value && encrypted }));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderReadOnly() {
|
||||
const { readOnly, broadcast, isTeam } = this.state;
|
||||
|
||||
return this.renderSwitch({
|
||||
id: 'readonly',
|
||||
value: readOnly,
|
||||
label: isTeam ? 'Read_Only_Team' : 'Read_Only_Channel',
|
||||
onValueChange: value => {
|
||||
logEvent(events.CR_TOGGLE_READ_ONLY);
|
||||
this.setState({ readOnly: value });
|
||||
},
|
||||
disabled: broadcast
|
||||
});
|
||||
}
|
||||
|
||||
renderEncrypted() {
|
||||
const { type, encrypted } = this.state;
|
||||
const { encryptionEnabled } = this.props;
|
||||
|
||||
if (!encryptionEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.renderSwitch({
|
||||
id: 'encrypted',
|
||||
value: encrypted,
|
||||
label: 'Encrypted',
|
||||
onValueChange: value => {
|
||||
logEvent(events.CR_TOGGLE_ENCRYPTED);
|
||||
this.setState({ encrypted: value });
|
||||
},
|
||||
disabled: !type
|
||||
});
|
||||
}
|
||||
|
||||
renderBroadcast() {
|
||||
const { broadcast, readOnly, isTeam } = this.state;
|
||||
|
||||
return this.renderSwitch({
|
||||
id: 'broadcast',
|
||||
value: broadcast,
|
||||
label: isTeam ? 'Broadcast_Team' : 'Broadcast_Channel',
|
||||
onValueChange: value => {
|
||||
logEvent(events.CR_TOGGLE_BROADCAST);
|
||||
this.setState({
|
||||
broadcast: value,
|
||||
readOnly: value ? true : readOnly
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderItem = ({ item }: { item: IOtherUser }) => {
|
||||
const { theme } = this.props;
|
||||
|
||||
return (
|
||||
<UserItem
|
||||
name={item.fname}
|
||||
username={item.name}
|
||||
onPress={() => this.removeUser(item)}
|
||||
testID={`create-channel-view-item-${item.name}`}
|
||||
icon='check'
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderInvitedList = () => {
|
||||
const { users, theme } = this.props;
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={users}
|
||||
extraData={users}
|
||||
keyExtractor={item => item._id}
|
||||
style={[
|
||||
styles.list,
|
||||
sharedStyles.separatorVertical,
|
||||
{
|
||||
backgroundColor: themes[theme].focusedBackground,
|
||||
borderColor: themes[theme].separatorColor
|
||||
}
|
||||
]}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { channelName, isTeam } = this.state;
|
||||
const { users, theme } = this.props;
|
||||
const userCount = users.length;
|
||||
|
||||
return (
|
||||
<KeyboardView
|
||||
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
||||
contentContainerStyle={[sharedStyles.container, styles.container]}
|
||||
keyboardVerticalOffset={128}
|
||||
>
|
||||
<StatusBar />
|
||||
<SafeAreaView testID='create-channel-view'>
|
||||
<ScrollView {...scrollPersistTaps}>
|
||||
<View style={[sharedStyles.separatorVertical, { borderColor: themes[theme].separatorColor }]}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
style={[styles.input, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
value={channelName}
|
||||
onChangeText={this.onChangeText}
|
||||
placeholder={isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
|
||||
returnKeyType='done'
|
||||
testID='create-channel-name'
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
<List.Separator />
|
||||
{this.renderType()}
|
||||
<List.Separator />
|
||||
{this.renderReadOnly()}
|
||||
<List.Separator />
|
||||
{this.renderEncrypted()}
|
||||
<List.Separator />
|
||||
{this.renderBroadcast()}
|
||||
</View>
|
||||
<View style={styles.invitedHeader}>
|
||||
<Text style={[styles.invitedTitle, { color: themes[theme].titleText }]}>{I18n.t('Invite')}</Text>
|
||||
<Text style={[styles.invitedCount, { color: themes[theme].auxiliaryText }]}>
|
||||
{userCount === 1 ? I18n.t('1_user') : I18n.t('N_users', { n: userCount })}
|
||||
</Text>
|
||||
</View>
|
||||
{this.renderInvitedList()}
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</KeyboardView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: IApplicationState) => ({
|
||||
baseUrl: state.server.server,
|
||||
isFetching: state.createChannel.isFetching,
|
||||
encryptionEnabled: state.encryption.enabled,
|
||||
users: state.selectedUsers.users,
|
||||
user: getUserSelector(state),
|
||||
createPublicChannelPermission: state.permissions['create-c'],
|
||||
createPrivateChannelPermission: state.permissions['create-p']
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withTheme(CreateChannelView));
|
|
@ -0,0 +1,40 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
|
||||
import { SwitchItem } from './SwitchItem';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'flex-start',
|
||||
padding: 16
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
title: 'SwitchItem'
|
||||
};
|
||||
|
||||
const testSwitch = {
|
||||
id: 'switch-id',
|
||||
hint: 'Read_only_hint',
|
||||
label: 'Onboarding_title',
|
||||
onValueChange: () => {},
|
||||
value: false,
|
||||
testSwitchID: 'create-channel-switch-id',
|
||||
testLabelID: 'create-channel-switch-id-hint'
|
||||
};
|
||||
|
||||
export const Switch = () => (
|
||||
<>
|
||||
<View style={styles.container}>
|
||||
<SwitchItem
|
||||
hint={testSwitch.hint}
|
||||
id={testSwitch.id}
|
||||
label={testSwitch.label}
|
||||
onValueChange={() => testSwitch.onValueChange()}
|
||||
value={testSwitch.value}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
);
|
|
@ -0,0 +1,68 @@
|
|||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react-native';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import i18n from '../../../i18n';
|
||||
import { SwitchItem, ISwitch } from './SwitchItem';
|
||||
import { mockedStore as store } from '../../../reducers/mockedStore';
|
||||
|
||||
const onPressMock = jest.fn((value: boolean) => value);
|
||||
|
||||
const testSwitch = {
|
||||
id: 'switch-id',
|
||||
hint: 'Read_only_hint',
|
||||
label: 'Onboarding_title',
|
||||
onValueChange: onPressMock,
|
||||
value: false,
|
||||
testSwitchID: 'create-channel-switch-id',
|
||||
testLabelID: 'create-channel-switch-id-hint'
|
||||
};
|
||||
|
||||
const Render = ({ hint, id, label, onValueChange, value }: ISwitch) => (
|
||||
<Provider store={store}>
|
||||
<SwitchItem hint={hint} id={id} label={label} onValueChange={onValueChange} value={value} />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
describe('SwitchItemEncrypted', () => {
|
||||
it('should not render the Encrypted Switch component', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
hint={testSwitch.hint}
|
||||
id={testSwitch.id}
|
||||
label={testSwitch.label}
|
||||
onValueChange={value => testSwitch.onValueChange(value)}
|
||||
value={testSwitch.value}
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(testSwitch.testSwitchID);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
it('should change value of switch', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
hint={testSwitch.hint}
|
||||
id={testSwitch.id}
|
||||
label={testSwitch.label}
|
||||
onValueChange={value => testSwitch.onValueChange(value)}
|
||||
value={testSwitch.value}
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(testSwitch.testSwitchID);
|
||||
fireEvent(component, 'valueChange', { value: true });
|
||||
expect(onPressMock).toHaveReturnedWith({ value: !testSwitch.value });
|
||||
});
|
||||
it('check if hint exists and is the same from testSwitch object', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
hint={testSwitch.hint}
|
||||
id={testSwitch.id}
|
||||
label={testSwitch.label}
|
||||
onValueChange={value => testSwitch.onValueChange(value)}
|
||||
value={testSwitch.value}
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(testSwitch.testLabelID);
|
||||
expect(component.props.children).toBe(i18n.t(testSwitch.hint));
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, Switch, Text, View, SwitchProps } from 'react-native';
|
||||
|
||||
import I18n from '../../../i18n';
|
||||
import { SWITCH_TRACK_COLOR } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
import sharedStyles from '../../Styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
switchContainer: {
|
||||
minHeight: 54,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
flexDirection: 'row',
|
||||
maxHeight: 80,
|
||||
marginBottom: 12
|
||||
},
|
||||
switchTextContainer: {
|
||||
flex: 1,
|
||||
marginRight: 8
|
||||
},
|
||||
label: {
|
||||
fontSize: 14,
|
||||
...sharedStyles.textMedium
|
||||
},
|
||||
hint: {
|
||||
fontSize: 14,
|
||||
...sharedStyles.textRegular
|
||||
}
|
||||
});
|
||||
|
||||
export interface ISwitch extends SwitchProps {
|
||||
id: string;
|
||||
label: string;
|
||||
hint: string;
|
||||
onValueChange: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export const SwitchItem = ({ id, value, label, hint, onValueChange, disabled = false }: ISwitch) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={[styles.switchContainer, { backgroundColor: colors.backgroundColor }]}>
|
||||
<View style={styles.switchTextContainer}>
|
||||
<Text style={[styles.label, { color: colors.titleText }]}>{I18n.t(label)}</Text>
|
||||
<Text testID={`create-channel-${id}-hint`} style={[styles.hint, { color: colors.auxiliaryText }]}>
|
||||
{I18n.t(hint)}
|
||||
</Text>
|
||||
</View>
|
||||
<Switch
|
||||
value={value}
|
||||
onValueChange={onValueChange}
|
||||
testID={`create-channel-${id}`}
|
||||
trackColor={SWITCH_TRACK_COLOR}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,108 @@
|
|||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react-native';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { SwitchItemEncrypted, ISwitchItemEncrypted } from './SwitchItemEncrypted';
|
||||
import { mockedStore as store } from '../../../reducers/mockedStore';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
const onPressMock = jest.fn((value: boolean) => value);
|
||||
|
||||
const testEncrypted = {
|
||||
encrypted: false,
|
||||
encryptionEnabled: false,
|
||||
isTeam: false,
|
||||
onValueChangeEncrypted: onPressMock,
|
||||
type: false,
|
||||
testSwitchID: 'create-channel-encrypted',
|
||||
testLabelID: `create-channel-encrypted-hint`
|
||||
};
|
||||
|
||||
const Render = ({ encrypted, encryptionEnabled, isTeam, onValueChangeEncrypted, type }: ISwitchItemEncrypted) => (
|
||||
<Provider store={store}>
|
||||
<SwitchItemEncrypted
|
||||
encrypted={encrypted}
|
||||
encryptionEnabled={encryptionEnabled}
|
||||
isTeam={isTeam}
|
||||
onValueChangeEncrypted={onValueChangeEncrypted}
|
||||
type={type}
|
||||
/>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
describe('SwitchItemEncrypted', () => {
|
||||
it('should not render the Encrypted Switch component', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
encrypted={testEncrypted.encrypted}
|
||||
encryptionEnabled={testEncrypted.encryptionEnabled}
|
||||
isTeam={testEncrypted.isTeam}
|
||||
onValueChangeEncrypted={value => testEncrypted.onValueChangeEncrypted(value)}
|
||||
type={testEncrypted.type}
|
||||
/>
|
||||
);
|
||||
try {
|
||||
await findByTestId(testEncrypted.testSwitchID);
|
||||
} catch (e) {
|
||||
expect(e).toBeTruthy();
|
||||
}
|
||||
});
|
||||
it('should render the Encrypted Switch component', async () => {
|
||||
testEncrypted.encryptionEnabled = true;
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
encrypted={testEncrypted.encrypted}
|
||||
encryptionEnabled={testEncrypted.encryptionEnabled}
|
||||
isTeam={testEncrypted.isTeam}
|
||||
onValueChangeEncrypted={value => testEncrypted.onValueChangeEncrypted(value)}
|
||||
type={testEncrypted.type}
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(testEncrypted.testSwitchID);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
it('should change value of switch', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
encrypted={testEncrypted.encrypted}
|
||||
encryptionEnabled={testEncrypted.encryptionEnabled}
|
||||
isTeam={testEncrypted.isTeam}
|
||||
onValueChangeEncrypted={value => testEncrypted.onValueChangeEncrypted(value)}
|
||||
type={testEncrypted.type}
|
||||
/>
|
||||
);
|
||||
|
||||
const component = await findByTestId(testEncrypted.testSwitchID);
|
||||
fireEvent(component, 'valueChange', { value: true });
|
||||
expect(onPressMock).toHaveReturnedWith({ value: !testEncrypted.encrypted });
|
||||
});
|
||||
it('label when encrypted and isTeam are false and is a public channel', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
encrypted={testEncrypted.encrypted}
|
||||
encryptionEnabled={testEncrypted.encryptionEnabled}
|
||||
isTeam={testEncrypted.isTeam}
|
||||
onValueChangeEncrypted={value => testEncrypted.onValueChangeEncrypted(value)}
|
||||
type={testEncrypted.type}
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(testEncrypted.testLabelID);
|
||||
expect(component.props.children).toBe(i18n.t('Channel_hint_encrypted_not_available'));
|
||||
});
|
||||
it('label when encrypted and isTeam are true and is a private team', async () => {
|
||||
testEncrypted.isTeam = true;
|
||||
testEncrypted.type = true;
|
||||
testEncrypted.encrypted = true;
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
encrypted={testEncrypted.encrypted}
|
||||
encryptionEnabled={testEncrypted.encryptionEnabled}
|
||||
isTeam={testEncrypted.isTeam}
|
||||
onValueChangeEncrypted={value => testEncrypted.onValueChangeEncrypted(value)}
|
||||
type={testEncrypted.type}
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(testEncrypted.testLabelID);
|
||||
expect(component.props.children).toBe(i18n.t('Team_hint_encrypted'));
|
||||
});
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
import React from 'react';
|
||||
|
||||
import { SwitchItem } from './SwitchItem';
|
||||
|
||||
export interface ISwitchItemEncrypted {
|
||||
encryptionEnabled: boolean;
|
||||
isTeam: boolean;
|
||||
type: boolean;
|
||||
encrypted: boolean;
|
||||
onValueChangeEncrypted: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export const SwitchItemEncrypted = ({
|
||||
encryptionEnabled,
|
||||
isTeam,
|
||||
type,
|
||||
encrypted,
|
||||
onValueChangeEncrypted
|
||||
}: ISwitchItemEncrypted) => {
|
||||
if (!encryptionEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let hint = '';
|
||||
if (isTeam && type) {
|
||||
hint = 'Team_hint_encrypted';
|
||||
}
|
||||
if (isTeam && !type) {
|
||||
hint = 'Team_hint_encrypted_not_available';
|
||||
}
|
||||
if (!isTeam && type) {
|
||||
hint = 'Channel_hint_encrypted';
|
||||
}
|
||||
if (!isTeam && !type) {
|
||||
hint = 'Channel_hint_encrypted_not_available';
|
||||
}
|
||||
|
||||
return (
|
||||
<SwitchItem
|
||||
id={'encrypted'}
|
||||
value={encrypted}
|
||||
label={'Encrypted'}
|
||||
hint={hint}
|
||||
onValueChange={onValueChangeEncrypted}
|
||||
disabled={!type}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
import React from 'react';
|
||||
|
||||
import { SwitchItem } from './SwitchItem';
|
||||
|
||||
export const SwitchItemReadOnly = ({
|
||||
readOnly,
|
||||
isTeam,
|
||||
onValueChangeReadOnly,
|
||||
broadcast
|
||||
}: {
|
||||
readOnly: boolean;
|
||||
isTeam: boolean;
|
||||
onValueChangeReadOnly: (value: boolean) => void;
|
||||
broadcast: boolean;
|
||||
}) => {
|
||||
let hint = '';
|
||||
if (readOnly) {
|
||||
hint = 'Read_only_hint';
|
||||
}
|
||||
if (isTeam && !readOnly) {
|
||||
hint = 'Team_hint_not_read_only';
|
||||
}
|
||||
if (!isTeam && !readOnly) {
|
||||
hint = 'Channel_hint_not_read_only';
|
||||
}
|
||||
|
||||
return (
|
||||
<SwitchItem
|
||||
id={'readonly'}
|
||||
value={readOnly}
|
||||
label={'Read_Only'}
|
||||
hint={hint}
|
||||
onValueChange={onValueChangeReadOnly}
|
||||
disabled={broadcast}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
import React from 'react';
|
||||
|
||||
import { SwitchItem } from './SwitchItem';
|
||||
|
||||
export const SwitchItemType = ({
|
||||
isTeam,
|
||||
type,
|
||||
onValueChangeType,
|
||||
isDisabled
|
||||
}: {
|
||||
isTeam: boolean;
|
||||
type: boolean;
|
||||
onValueChangeType: (value: boolean) => void;
|
||||
isDisabled: boolean;
|
||||
}) => {
|
||||
let hint = '';
|
||||
if (isTeam && type) {
|
||||
hint = 'Team_hint_private';
|
||||
}
|
||||
if (isTeam && !type) {
|
||||
hint = 'Team_hint_public';
|
||||
}
|
||||
if (!isTeam && type) {
|
||||
hint = 'Channel_hint_private';
|
||||
}
|
||||
if (!isTeam && !type) {
|
||||
hint = 'Channel_hint_public';
|
||||
}
|
||||
|
||||
return (
|
||||
<SwitchItem id={'type'} value={type} disabled={isDisabled} label={'Private'} hint={hint} onValueChange={onValueChangeType} />
|
||||
);
|
||||
};
|
|
@ -0,0 +1,96 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import { UseFormSetValue } from 'react-hook-form';
|
||||
|
||||
import { useAppSelector } from '../../../lib/hooks';
|
||||
import { events, logEvent } from '../../../lib/methods/helpers/log';
|
||||
import { SwitchItem } from './SwitchItem';
|
||||
import { SwitchItemType } from './SwitchItemType';
|
||||
import { SwitchItemReadOnly } from './SwitchItemReadOnly';
|
||||
import { SwitchItemEncrypted } from './SwitchItemEncrypted';
|
||||
import { IFormData } from '..';
|
||||
|
||||
export const RoomSettings = ({
|
||||
isTeam,
|
||||
setValue,
|
||||
createChannelPermission,
|
||||
createPrivateChannelPermission
|
||||
}: {
|
||||
isTeam: boolean;
|
||||
setValue: UseFormSetValue<IFormData>;
|
||||
createChannelPermission: boolean;
|
||||
createPrivateChannelPermission: boolean;
|
||||
}) => {
|
||||
const [type, setType] = useState(true);
|
||||
const [readOnly, setReadOnly] = useState(false);
|
||||
const [encrypted, setEncrypted] = useState(false);
|
||||
const [broadcast, setBroadcast] = useState(false);
|
||||
|
||||
const { encryptionEnabled } = useAppSelector(state => ({
|
||||
encryptionEnabled: state.encryption.enabled
|
||||
}));
|
||||
|
||||
const onValueChangeType = useCallback(
|
||||
(value: boolean) => {
|
||||
logEvent(events.CR_TOGGLE_TYPE);
|
||||
// If we set the channel as public, encrypted status should be false
|
||||
setType(value);
|
||||
setValue('type', value);
|
||||
setEncrypted(value && encrypted);
|
||||
setValue('encrypted', value && encrypted);
|
||||
},
|
||||
[encrypted]
|
||||
);
|
||||
|
||||
const onValueChangeReadOnly = useCallback((value: boolean) => {
|
||||
logEvent(events.CR_TOGGLE_READ_ONLY);
|
||||
setReadOnly(value);
|
||||
setValue('readOnly', value);
|
||||
}, []);
|
||||
|
||||
const onValueChangeEncrypted = useCallback((value: boolean) => {
|
||||
logEvent(events.CR_TOGGLE_ENCRYPTED);
|
||||
setEncrypted(value);
|
||||
setValue('encrypted', value);
|
||||
}, []);
|
||||
|
||||
const onValueChangeBroadcast = (value: boolean) => {
|
||||
logEvent(events.CR_TOGGLE_BROADCAST);
|
||||
setBroadcast(value);
|
||||
setValue('broadcast', value);
|
||||
setReadOnly(value ? true : readOnly);
|
||||
setValue('readOnly', value ? true : readOnly);
|
||||
};
|
||||
|
||||
const isDisabled = [createChannelPermission, createPrivateChannelPermission].filter(r => r === true).length <= 1;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SwitchItemType
|
||||
isTeam={isTeam}
|
||||
type={createPrivateChannelPermission ? type : false}
|
||||
onValueChangeType={onValueChangeType}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
<SwitchItemReadOnly
|
||||
broadcast={broadcast}
|
||||
isTeam={isTeam}
|
||||
readOnly={readOnly}
|
||||
onValueChangeReadOnly={onValueChangeReadOnly}
|
||||
/>
|
||||
<SwitchItemEncrypted
|
||||
encryptionEnabled={encryptionEnabled}
|
||||
isTeam={isTeam}
|
||||
type={type}
|
||||
encrypted={encrypted}
|
||||
onValueChangeEncrypted={onValueChangeEncrypted}
|
||||
/>
|
||||
<SwitchItem
|
||||
id={'broadcast'}
|
||||
value={broadcast}
|
||||
label={'Broadcast'}
|
||||
hint={'Broadcast_hint'}
|
||||
onValueChange={onValueChangeBroadcast}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,212 @@
|
|||
import React, { useCallback, useEffect, useLayoutEffect } from 'react';
|
||||
import { shallowEqual, useDispatch } from 'react-redux';
|
||||
import { FlatList, ScrollView, StyleSheet, Text, View } from 'react-native';
|
||||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { useAppSelector, usePermissions } from '../../lib/hooks';
|
||||
import { sendLoadingEvent } from '../../containers/Loading';
|
||||
import { createChannelRequest } from '../../actions/createChannel';
|
||||
import { removeUser as removeUserAction } from '../../actions/selectedUsers';
|
||||
import KeyboardView from '../../containers/KeyboardView';
|
||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
||||
import I18n from '../../i18n';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import { useTheme } from '../../theme';
|
||||
import { Review } from '../../lib/methods/helpers/review';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import sharedStyles from '../Styles';
|
||||
import { ChatsStackParamList } from '../../stacks/types';
|
||||
import Button from '../../containers/Button';
|
||||
import { ControlledFormTextInput } from '../../containers/TextInput';
|
||||
import Chip from '../../containers/Chip';
|
||||
import { RoomSettings } from './RoomSettings';
|
||||
import { ISelectedUser } from '../../reducers/selectedUsers';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1
|
||||
},
|
||||
containerTextInput: {
|
||||
paddingHorizontal: 16,
|
||||
marginTop: 16
|
||||
},
|
||||
containerStyle: {
|
||||
marginBottom: 28
|
||||
},
|
||||
list: {
|
||||
width: '100%'
|
||||
},
|
||||
invitedHeader: {
|
||||
marginVertical: 12,
|
||||
marginHorizontal: 16,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
},
|
||||
invitedCount: {
|
||||
fontSize: 12,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
invitedList: {
|
||||
paddingHorizontal: 16
|
||||
},
|
||||
buttonCreate: {
|
||||
marginHorizontal: 16,
|
||||
marginTop: 24
|
||||
}
|
||||
});
|
||||
|
||||
export interface IFormData {
|
||||
channelName: string;
|
||||
type: boolean;
|
||||
readOnly: boolean;
|
||||
encrypted: boolean;
|
||||
broadcast: boolean;
|
||||
}
|
||||
|
||||
const CreateChannelView = () => {
|
||||
const [createChannelPermission, createPrivateChannelPermission] = usePermissions(['create-c', 'create-p']);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { isDirty },
|
||||
setValue
|
||||
} = useForm<IFormData>({
|
||||
defaultValues: { channelName: '', broadcast: false, encrypted: false, readOnly: false, type: createPrivateChannelPermission }
|
||||
});
|
||||
|
||||
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'CreateChannelView'>>();
|
||||
const { params } = useRoute<RouteProp<ChatsStackParamList, 'CreateChannelView'>>();
|
||||
const isTeam = params?.isTeam || false;
|
||||
const teamId = params?.teamId;
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { isFetching, useRealName, users } = useAppSelector(
|
||||
state => ({
|
||||
isFetching: state.createChannel.isFetching,
|
||||
users: state.selectedUsers.users,
|
||||
useRealName: state.settings.UI_Use_Real_Name as boolean
|
||||
}),
|
||||
shallowEqual
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
sendLoadingEvent({ visible: isFetching });
|
||||
}, [isFetching]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel')
|
||||
});
|
||||
}, [isTeam, navigation]);
|
||||
|
||||
const removeUser = useCallback(
|
||||
(user: ISelectedUser) => {
|
||||
dispatch(removeUserAction(user));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const submit = ({ channelName, broadcast, encrypted, readOnly, type }: IFormData) => {
|
||||
if (!channelName.trim() || isFetching) {
|
||||
return;
|
||||
}
|
||||
// transform users object into array of usernames
|
||||
const usersMapped = users.map(user => user.name);
|
||||
// create channel or team
|
||||
const data = {
|
||||
name: channelName,
|
||||
users: usersMapped,
|
||||
type,
|
||||
readOnly,
|
||||
broadcast,
|
||||
encrypted,
|
||||
isTeam,
|
||||
teamId
|
||||
};
|
||||
dispatch(createChannelRequest(data));
|
||||
Review.pushPositiveEvent();
|
||||
};
|
||||
|
||||
return (
|
||||
<KeyboardView
|
||||
style={{ backgroundColor: colors.backgroundColor }}
|
||||
contentContainerStyle={[sharedStyles.container, styles.container]}
|
||||
keyboardVerticalOffset={128}
|
||||
>
|
||||
<StatusBar />
|
||||
<SafeAreaView style={{ backgroundColor: colors.backgroundColor }} testID='create-channel-view'>
|
||||
<ScrollView {...scrollPersistTaps}>
|
||||
<View style={[styles.containerTextInput, { borderColor: colors.separatorColor }]}>
|
||||
<ControlledFormTextInput
|
||||
label={isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
|
||||
testID='create-channel-name'
|
||||
returnKeyType='done'
|
||||
containerStyle={styles.containerStyle}
|
||||
name={'channelName'}
|
||||
control={control}
|
||||
/>
|
||||
<RoomSettings
|
||||
createChannelPermission={createChannelPermission}
|
||||
createPrivateChannelPermission={createPrivateChannelPermission}
|
||||
isTeam={isTeam}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</View>
|
||||
{users.length > 0 ? (
|
||||
<>
|
||||
<View style={styles.invitedHeader}>
|
||||
<Text style={[styles.invitedCount, { color: colors.auxiliaryText }]}>
|
||||
{I18n.t('N_Selected_members', { n: users.length })}
|
||||
</Text>
|
||||
</View>
|
||||
<FlatList
|
||||
data={users}
|
||||
extraData={users}
|
||||
keyExtractor={item => item._id}
|
||||
style={[
|
||||
styles.list,
|
||||
{
|
||||
backgroundColor: colors.backgroundColor,
|
||||
borderColor: colors.separatorColor
|
||||
}
|
||||
]}
|
||||
contentContainerStyle={styles.invitedList}
|
||||
renderItem={({ item }) => {
|
||||
const name = useRealName && item.fname ? item.fname : item.name;
|
||||
const username = item.name;
|
||||
|
||||
return (
|
||||
<Chip
|
||||
text={name}
|
||||
avatar={username}
|
||||
onPress={() => removeUser(item)}
|
||||
testID={`create-channel-view-item-${item.name}`}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
keyboardShouldPersistTaps='always'
|
||||
horizontal
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
<Button
|
||||
title={isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel')}
|
||||
type='primary'
|
||||
onPress={handleSubmit(submit)}
|
||||
disabled={!isDirty}
|
||||
testID='create-channel-submit'
|
||||
loading={isFetching}
|
||||
style={styles.buttonCreate}
|
||||
/>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</KeyboardView>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateChannelView;
|
|
@ -1,75 +1,58 @@
|
|||
import React from 'react';
|
||||
import React, { useLayoutEffect, useState } from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import * as yup from 'yup';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
|
||||
import Button from '../containers/Button';
|
||||
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
||||
import { FormTextInput } from '../containers/TextInput';
|
||||
import { ControlledFormTextInput } from '../containers/TextInput';
|
||||
import I18n from '../i18n';
|
||||
import { themes } from '../lib/constants';
|
||||
import { Services } from '../lib/services';
|
||||
import { OutsideParamList } from '../stacks/types';
|
||||
import { withTheme } from '../theme';
|
||||
import { showErrorAlert, isValidEmail } from '../lib/methods/helpers';
|
||||
import { useTheme } from '../theme';
|
||||
import { showErrorAlert } from '../lib/methods/helpers';
|
||||
import { events, logEvent } from '../lib/methods/helpers/log';
|
||||
import { IBaseScreen } from '../definitions';
|
||||
import sharedStyles from './Styles';
|
||||
|
||||
interface IForgotPasswordViewState {
|
||||
const schema = yup.object().shape({
|
||||
email: yup.string().email().required()
|
||||
});
|
||||
|
||||
interface ISubmit {
|
||||
email: string;
|
||||
invalidEmail: boolean;
|
||||
isFetching: boolean;
|
||||
}
|
||||
|
||||
type IForgotPasswordViewProps = IBaseScreen<OutsideParamList, 'ForgotPasswordView'>;
|
||||
const ForgotPasswordView = (): React.ReactElement => {
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { isValid }
|
||||
} = useForm<ISubmit>({ mode: 'onChange', resolver: yupResolver(schema) });
|
||||
|
||||
class ForgotPasswordView extends React.Component<IForgotPasswordViewProps, IForgotPasswordViewState> {
|
||||
static navigationOptions = ({ route }: IForgotPasswordViewProps) => ({
|
||||
title: route.params?.title ?? 'Rocket.Chat'
|
||||
});
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
|
||||
state = {
|
||||
email: '',
|
||||
invalidEmail: true,
|
||||
isFetching: false
|
||||
};
|
||||
const navigation = useNavigation<StackNavigationProp<OutsideParamList, 'ForgotPasswordView'>>();
|
||||
const { params } = useRoute<RouteProp<OutsideParamList, 'ForgotPasswordView'>>();
|
||||
const { colors } = useTheme();
|
||||
|
||||
shouldComponentUpdate(nextProps: IForgotPasswordViewProps, nextState: IForgotPasswordViewState) {
|
||||
const { email, invalidEmail, isFetching } = this.state;
|
||||
const { theme } = this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.email !== email) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.invalidEmail !== invalidEmail) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.isFetching !== isFetching) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: params?.title ?? 'Rocket.Chat'
|
||||
});
|
||||
}, [navigation, params?.title]);
|
||||
|
||||
validate = (email: string) => {
|
||||
if (!isValidEmail(email)) {
|
||||
this.setState({ invalidEmail: true });
|
||||
return;
|
||||
}
|
||||
this.setState({ email, invalidEmail: false });
|
||||
};
|
||||
|
||||
resetPassword = async () => {
|
||||
logEvent(events.FP_FORGOT_PASSWORD);
|
||||
const { email, invalidEmail } = this.state;
|
||||
if (invalidEmail || !email) {
|
||||
const resetPassword = async ({ email }: ISubmit) => {
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.setState({ isFetching: true });
|
||||
logEvent(events.FP_FORGOT_PASSWORD);
|
||||
setIsFetching(true);
|
||||
const result = await Services.forgotPassword(email);
|
||||
if (result.success) {
|
||||
const { navigation } = this.props;
|
||||
navigation.pop();
|
||||
showErrorAlert(I18n.t('Forgot_password_If_this_email_is_registered'), I18n.t('Alert'));
|
||||
}
|
||||
|
@ -78,41 +61,38 @@ class ForgotPasswordView extends React.Component<IForgotPasswordViewProps, IForg
|
|||
const msg = (e.data && e.data.error) || I18n.t('There_was_an_error_while_action', { action: I18n.t('resetting_password') });
|
||||
showErrorAlert(msg, I18n.t('Alert'));
|
||||
}
|
||||
this.setState({ isFetching: false });
|
||||
setIsFetching(false);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { invalidEmail, isFetching } = this.state;
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<FormContainer testID='forgot-password-view'>
|
||||
<FormContainerInner>
|
||||
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: colors.titleText }]}>
|
||||
{I18n.t('Forgot_password')}
|
||||
</Text>
|
||||
<ControlledFormTextInput
|
||||
name='email'
|
||||
control={control}
|
||||
autoFocus
|
||||
placeholder={I18n.t('Email')}
|
||||
keyboardType='email-address'
|
||||
returnKeyType='send'
|
||||
iconLeft='mail'
|
||||
onSubmitEditing={handleSubmit(resetPassword)}
|
||||
testID='forgot-password-view-email'
|
||||
containerStyle={sharedStyles.inputLastChild}
|
||||
/>
|
||||
<Button
|
||||
title={I18n.t('Reset_password')}
|
||||
type='primary'
|
||||
onPress={handleSubmit(resetPassword)}
|
||||
testID='forgot-password-view-submit'
|
||||
loading={isFetching}
|
||||
disabled={!isValid}
|
||||
/>
|
||||
</FormContainerInner>
|
||||
</FormContainer>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormContainer testID='forgot-password-view'>
|
||||
<FormContainerInner>
|
||||
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>
|
||||
{I18n.t('Forgot_password')}
|
||||
</Text>
|
||||
<FormTextInput
|
||||
autoFocus
|
||||
placeholder={I18n.t('Email')}
|
||||
keyboardType='email-address'
|
||||
returnKeyType='send'
|
||||
onChangeText={email => this.validate(email)}
|
||||
onSubmitEditing={this.resetPassword}
|
||||
testID='forgot-password-view-email'
|
||||
containerStyle={sharedStyles.inputLastChild}
|
||||
/>
|
||||
<Button
|
||||
title={I18n.t('Reset_password')}
|
||||
type='primary'
|
||||
onPress={this.resetPassword}
|
||||
testID='forgot-password-view-submit'
|
||||
loading={isFetching}
|
||||
disabled={invalidEmail}
|
||||
/>
|
||||
</FormContainerInner>
|
||||
</FormContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(ForgotPasswordView);
|
||||
export default ForgotPasswordView;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { BackHandler, StyleSheet } from 'react-native';
|
||||
import JitsiMeet, { JitsiMeetView as RNJitsiMeetView } from 'react-native-jitsi-meet';
|
||||
import BackgroundTimer from 'react-native-background-timer';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -69,6 +69,7 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
}
|
||||
}, 1000);
|
||||
}
|
||||
BackHandler.addEventListener('hardwareBackPress', this.endCall);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -78,9 +79,17 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
this.jitsiTimeout = null;
|
||||
BackgroundTimer.stopBackgroundTimer();
|
||||
}
|
||||
JitsiMeet.endCall();
|
||||
BackHandler.removeEventListener('hardwareBackPress', this.endCall);
|
||||
if (isIOS) {
|
||||
JitsiMeet.endCall();
|
||||
}
|
||||
}
|
||||
|
||||
endCall = () => {
|
||||
JitsiMeet.endCall();
|
||||
return null;
|
||||
};
|
||||
|
||||
onConferenceWillJoin = () => {
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
|
|
|
@ -1,336 +0,0 @@
|
|||
import { Q } from '@nozbe/watermelondb';
|
||||
import { StackNavigationOptions } from '@react-navigation/stack';
|
||||
import { dequal } from 'dequal';
|
||||
import React from 'react';
|
||||
import { FlatList, StyleSheet, Text, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { createChannelRequest } from '../actions/createChannel';
|
||||
import { themes } from '../lib/constants';
|
||||
import * as HeaderButton from '../containers/HeaderButton';
|
||||
import * as List from '../containers/List';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import SearchBox from '../containers/SearchBox';
|
||||
import StatusBar from '../containers/StatusBar';
|
||||
import { IApplicationState, IBaseScreen, ISearch, TSubscriptionModel } from '../definitions';
|
||||
import I18n from '../i18n';
|
||||
import database from '../lib/database';
|
||||
import { CustomIcon, TIconsName } from '../containers/CustomIcon';
|
||||
import Navigation from '../lib/navigation/appNavigation';
|
||||
import UserItem from '../containers/UserItem';
|
||||
import { withTheme } from '../theme';
|
||||
import { goRoom, TGoRoomItem } from '../lib/methods/helpers/goRoom';
|
||||
import log, { events, logEvent } from '../lib/methods/helpers/log';
|
||||
import Touch from '../containers/Touch';
|
||||
import sharedStyles from './Styles';
|
||||
import { NewMessageStackParamList } from '../stacks/types';
|
||||
import { search } from '../lib/methods';
|
||||
import { hasPermission, compareServerVersion } from '../lib/methods/helpers';
|
||||
|
||||
const QUERY_SIZE = 50;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
height: 46,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
buttonIcon: {
|
||||
marginLeft: 18,
|
||||
marginRight: 16
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 17,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
buttonContainer: {
|
||||
paddingBottom: 16
|
||||
}
|
||||
});
|
||||
|
||||
interface IButton {
|
||||
onPress: () => void;
|
||||
testID: string;
|
||||
title: string;
|
||||
icon: TIconsName;
|
||||
first?: boolean;
|
||||
}
|
||||
|
||||
interface INewMessageViewState {
|
||||
search: (ISearch | TSubscriptionModel)[];
|
||||
chats: TSubscriptionModel[];
|
||||
permissions: boolean[];
|
||||
}
|
||||
|
||||
interface INewMessageViewProps extends IBaseScreen<NewMessageStackParamList, 'NewMessageView'> {
|
||||
maxUsers: number;
|
||||
isMasterDetail: boolean;
|
||||
serverVersion: string;
|
||||
createTeamPermission?: string[];
|
||||
createDirectMessagePermission?: string[];
|
||||
createPublicChannelPermission?: string[];
|
||||
createPrivateChannelPermission?: string[];
|
||||
createDiscussionPermission?: string[];
|
||||
}
|
||||
|
||||
class NewMessageView extends React.Component<INewMessageViewProps, INewMessageViewState> {
|
||||
static navigationOptions = ({ navigation }: INewMessageViewProps): StackNavigationOptions => ({
|
||||
headerLeft: () => <HeaderButton.CloseModal navigation={navigation} testID='new-message-view-close' />,
|
||||
title: I18n.t('New_Message')
|
||||
});
|
||||
|
||||
constructor(props: INewMessageViewProps) {
|
||||
super(props);
|
||||
this.init();
|
||||
this.state = {
|
||||
search: [],
|
||||
chats: [],
|
||||
permissions: []
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
init = async () => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const chats = await db
|
||||
.get('subscriptions')
|
||||
.query(Q.where('t', 'd'), Q.experimentalTake(QUERY_SIZE), Q.experimentalSortBy('room_updated_at', Q.desc))
|
||||
.fetch();
|
||||
|
||||
this.setState({ chats });
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.handleHasPermission();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: INewMessageViewProps) {
|
||||
const {
|
||||
createTeamPermission,
|
||||
createPublicChannelPermission,
|
||||
createPrivateChannelPermission,
|
||||
createDirectMessagePermission,
|
||||
createDiscussionPermission
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
!dequal(createTeamPermission, prevProps.createTeamPermission) ||
|
||||
!dequal(createPublicChannelPermission, prevProps.createPublicChannelPermission) ||
|
||||
!dequal(createPrivateChannelPermission, prevProps.createPrivateChannelPermission) ||
|
||||
!dequal(createDirectMessagePermission, prevProps.createDirectMessagePermission) ||
|
||||
!dequal(createDiscussionPermission, prevProps.createDiscussionPermission)
|
||||
) {
|
||||
this.handleHasPermission();
|
||||
}
|
||||
}
|
||||
|
||||
handleSearch = async (text: string) => {
|
||||
const result = (await search({ text, filterRooms: false })) as ISearch[];
|
||||
this.setState({
|
||||
search: result
|
||||
});
|
||||
};
|
||||
|
||||
onSearchChangeText(text: string) {
|
||||
this.handleSearch(text);
|
||||
}
|
||||
|
||||
dismiss = () => {
|
||||
const { navigation } = this.props;
|
||||
return navigation.pop();
|
||||
};
|
||||
|
||||
createChannel = () => {
|
||||
logEvent(events.NEW_MSG_CREATE_CHANNEL);
|
||||
const { navigation } = this.props;
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView') });
|
||||
};
|
||||
|
||||
createTeam = () => {
|
||||
logEvent(events.NEW_MSG_CREATE_TEAM);
|
||||
const { navigation } = this.props;
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', {
|
||||
nextAction: () => navigation.navigate('CreateChannelView', { isTeam: true })
|
||||
});
|
||||
};
|
||||
|
||||
createGroupChat = () => {
|
||||
logEvent(events.NEW_MSG_CREATE_GROUP_CHAT);
|
||||
const { dispatch, maxUsers, navigation } = this.props;
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', {
|
||||
nextAction: () => dispatch(createChannelRequest({ group: true })),
|
||||
buttonText: I18n.t('Create'),
|
||||
maxUsers
|
||||
});
|
||||
};
|
||||
|
||||
goRoom = (item: TGoRoomItem) => {
|
||||
logEvent(events.NEW_MSG_CHAT_WITH_USER);
|
||||
const { isMasterDetail, navigation } = this.props;
|
||||
if (isMasterDetail) {
|
||||
navigation.pop();
|
||||
}
|
||||
goRoom({ item, isMasterDetail });
|
||||
};
|
||||
|
||||
renderButton = ({ onPress, testID, title, icon, first }: IButton) => {
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<Touch onPress={onPress} style={{ backgroundColor: themes[theme].backgroundColor }} testID={testID}>
|
||||
<View
|
||||
style={[
|
||||
first ? sharedStyles.separatorVertical : sharedStyles.separatorBottom,
|
||||
styles.button,
|
||||
{ borderColor: themes[theme].separatorColor }
|
||||
]}
|
||||
>
|
||||
<CustomIcon name={icon} size={24} color={themes[theme].tintColor} style={styles.buttonIcon} />
|
||||
<Text style={[styles.buttonText, { color: themes[theme].tintColor }]}>{title}</Text>
|
||||
</View>
|
||||
</Touch>
|
||||
);
|
||||
};
|
||||
|
||||
createDiscussion = () => {
|
||||
logEvent(events.NEW_MSG_CREATE_DISCUSSION);
|
||||
Navigation.navigate('CreateDiscussionView');
|
||||
};
|
||||
|
||||
handleHasPermission = async () => {
|
||||
const {
|
||||
createTeamPermission,
|
||||
createDirectMessagePermission,
|
||||
createPublicChannelPermission,
|
||||
createPrivateChannelPermission,
|
||||
createDiscussionPermission
|
||||
} = this.props;
|
||||
const permissions = [
|
||||
createPublicChannelPermission,
|
||||
createPrivateChannelPermission,
|
||||
createTeamPermission,
|
||||
createDirectMessagePermission,
|
||||
createDiscussionPermission
|
||||
];
|
||||
const permissionsToCreate = await hasPermission(permissions);
|
||||
this.setState({ permissions: permissionsToCreate });
|
||||
};
|
||||
|
||||
renderHeader = () => {
|
||||
const { maxUsers, theme, serverVersion } = this.props;
|
||||
const { permissions } = this.state;
|
||||
|
||||
return (
|
||||
<View style={{ backgroundColor: themes[theme].auxiliaryBackground }}>
|
||||
<SearchBox onChangeText={(text: string) => this.onSearchChangeText(text)} testID='new-message-view-search' />
|
||||
<View style={styles.buttonContainer}>
|
||||
{permissions[0] || permissions[1]
|
||||
? this.renderButton({
|
||||
onPress: this.createChannel,
|
||||
title: I18n.t('Create_Channel'),
|
||||
icon: 'channel-public',
|
||||
testID: 'new-message-view-create-channel',
|
||||
first: true
|
||||
})
|
||||
: null}
|
||||
{compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '3.13.0') && permissions[2]
|
||||
? this.renderButton({
|
||||
onPress: this.createTeam,
|
||||
title: I18n.t('Create_Team'),
|
||||
icon: 'teams',
|
||||
testID: 'new-message-view-create-team'
|
||||
})
|
||||
: null}
|
||||
{maxUsers > 2 && permissions[3]
|
||||
? this.renderButton({
|
||||
onPress: this.createGroupChat,
|
||||
title: I18n.t('Create_Direct_Messages'),
|
||||
icon: 'message',
|
||||
testID: 'new-message-view-create-direct-message'
|
||||
})
|
||||
: null}
|
||||
{permissions[4]
|
||||
? this.renderButton({
|
||||
onPress: this.createDiscussion,
|
||||
title: I18n.t('Create_Discussion'),
|
||||
icon: 'discussions',
|
||||
testID: 'new-message-view-create-discussion'
|
||||
})
|
||||
: null}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderItem = ({ item, index }: { item: ISearch | TSubscriptionModel; index: number }) => {
|
||||
const { search, chats } = this.state;
|
||||
const { theme } = this.props;
|
||||
|
||||
let style = { borderColor: themes[theme].separatorColor };
|
||||
if (index === 0) {
|
||||
style = { ...style, ...sharedStyles.separatorTop };
|
||||
}
|
||||
if (search.length > 0 && index === search.length - 1) {
|
||||
style = { ...style, ...sharedStyles.separatorBottom };
|
||||
}
|
||||
if (search.length === 0 && index === chats.length - 1) {
|
||||
style = { ...style, ...sharedStyles.separatorBottom };
|
||||
}
|
||||
|
||||
const itemSearch = item as ISearch;
|
||||
const itemModel = item as TSubscriptionModel;
|
||||
|
||||
return (
|
||||
<UserItem
|
||||
name={itemSearch.search ? itemSearch.name : itemModel.fname || ''}
|
||||
username={itemSearch.search ? itemSearch.username : itemModel.name}
|
||||
onPress={() => this.goRoom(itemModel)}
|
||||
testID={`new-message-view-item-${item.name}`}
|
||||
style={style}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderList = () => {
|
||||
const { search, chats } = this.state;
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<FlatList
|
||||
data={search.length > 0 ? search : chats}
|
||||
extraData={this.state}
|
||||
keyExtractor={item => item._id || item.rid}
|
||||
ListHeaderComponent={this.renderHeader}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeAreaView testID='new-message-view'>
|
||||
<StatusBar />
|
||||
{this.renderList()}
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: IApplicationState) => ({
|
||||
serverVersion: state.server.version as string,
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
maxUsers: (state.settings.DirectMesssage_maxUsers as number) || 1,
|
||||
createTeamPermission: state.permissions['create-team'],
|
||||
createDirectMessagePermission: state.permissions['create-d'],
|
||||
createPublicChannelPermission: state.permissions['create-c'],
|
||||
createPrivateChannelPermission: state.permissions['create-p'],
|
||||
createDiscussionPermission: state.permissions['start-discussion']
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withTheme(NewMessageView));
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
|
||||
import * as List from '../../containers/List';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { CustomIcon, TIconsName } from '../../containers/CustomIcon';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
interface IButton {
|
||||
onPress: () => void;
|
||||
testID: string;
|
||||
title: string;
|
||||
icon: TIconsName;
|
||||
}
|
||||
|
||||
const ButtonCreate = ({ onPress, testID, title, icon }: IButton) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<>
|
||||
<List.Item
|
||||
onPress={onPress}
|
||||
testID={testID}
|
||||
left={() => <CustomIcon name={icon} size={24} color={themes[theme].bodyText} />}
|
||||
right={() => <CustomIcon name={'chevron-right'} size={24} color={themes[theme].bodyText} />}
|
||||
title={title}
|
||||
/>
|
||||
<List.Separator />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonCreate;
|
|
@ -0,0 +1,107 @@
|
|||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import React, { useCallback } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import { createChannelRequest } from '../../actions/createChannel';
|
||||
import { themes } from '../../lib/constants';
|
||||
import SearchBox from '../../containers/SearchBox';
|
||||
import I18n from '../../i18n';
|
||||
import Navigation from '../../lib/navigation/appNavigation';
|
||||
import { useTheme } from '../../theme';
|
||||
import { events, logEvent } from '../../lib/methods/helpers/log';
|
||||
import { NewMessageStackParamList } from '../../stacks/types';
|
||||
import { compareServerVersion } from '../../lib/methods/helpers';
|
||||
import { useAppSelector, usePermissions } from '../../lib/hooks';
|
||||
import ButtonCreate from './ButtonCreate';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
paddingTop: 16
|
||||
},
|
||||
buttonContainer: {
|
||||
paddingBottom: 16
|
||||
}
|
||||
});
|
||||
|
||||
const HeaderNewMessage = ({ maxUsers, onChangeText }: { maxUsers: number; onChangeText: (text: string) => void }) => {
|
||||
const navigation = useNavigation<StackNavigationProp<NewMessageStackParamList, 'NewMessageView'>>();
|
||||
const dispatch = useDispatch();
|
||||
const { theme } = useTheme();
|
||||
|
||||
const serverVersion = useAppSelector(state => state.server.version as string);
|
||||
|
||||
const [
|
||||
createPublicChannelPermission,
|
||||
createPrivateChannelPermission,
|
||||
createTeamPermission,
|
||||
createDirectMessagePermission,
|
||||
createDiscussionPermission
|
||||
] = usePermissions(['create-c', 'create-p', 'create-team', 'create-d', 'start-discussion']);
|
||||
|
||||
const createChannel = useCallback(() => {
|
||||
logEvent(events.NEW_MSG_CREATE_CHANNEL);
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView') });
|
||||
}, [navigation]);
|
||||
|
||||
const createTeam = useCallback(() => {
|
||||
logEvent(events.NEW_MSG_CREATE_TEAM);
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', {
|
||||
nextAction: () => navigation.navigate('CreateChannelView', { isTeam: true })
|
||||
});
|
||||
}, [navigation]);
|
||||
|
||||
const createGroupChat = useCallback(() => {
|
||||
logEvent(events.NEW_MSG_CREATE_GROUP_CHAT);
|
||||
navigation.navigate('SelectedUsersViewCreateChannel', {
|
||||
nextAction: () => dispatch(createChannelRequest({ group: true })),
|
||||
buttonText: I18n.t('Create'),
|
||||
maxUsers
|
||||
});
|
||||
}, [dispatch, maxUsers, navigation]);
|
||||
|
||||
const createDiscussion = useCallback(() => {
|
||||
logEvent(events.NEW_MSG_CREATE_DISCUSSION);
|
||||
Navigation.navigate('CreateDiscussionView');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={[styles.container, { backgroundColor: themes[theme].auxiliaryBackground }]}>
|
||||
<View style={styles.buttonContainer}>
|
||||
{createPublicChannelPermission || createPrivateChannelPermission ? (
|
||||
<ButtonCreate
|
||||
onPress={createChannel}
|
||||
title={'Channel'}
|
||||
icon={'channel-public'}
|
||||
testID={'new-message-view-create-channel'}
|
||||
/>
|
||||
) : null}
|
||||
{compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '3.13.0') && createTeamPermission ? (
|
||||
<ButtonCreate onPress={createTeam} title={'Team'} icon={'teams'} testID={'new-message-view-create-team'} />
|
||||
) : null}
|
||||
{maxUsers > 2 && createDirectMessagePermission ? (
|
||||
<ButtonCreate
|
||||
onPress={createGroupChat}
|
||||
title={'Direct_message'}
|
||||
icon={'message'}
|
||||
testID={'new-message-view-create-direct-message'}
|
||||
/>
|
||||
) : null}
|
||||
{createDiscussionPermission ? (
|
||||
<ButtonCreate
|
||||
onPress={createDiscussion}
|
||||
title={'Discussion'}
|
||||
icon={'discussions'}
|
||||
testID={'new-message-view-create-discussion'}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
<SearchBox onChangeText={(text: string) => onChangeText(text)} testID='new-message-view-search' />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderNewMessage;
|
|
@ -0,0 +1,115 @@
|
|||
import { Q } from '@nozbe/watermelondb';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react';
|
||||
import { FlatList } from 'react-native';
|
||||
import { shallowEqual } from 'react-redux';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
import * as HeaderButton from '../../containers/HeaderButton';
|
||||
import * as List from '../../containers/List';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import { ISearch, TSubscriptionModel } from '../../definitions';
|
||||
import I18n from '../../i18n';
|
||||
import database from '../../lib/database';
|
||||
import { useTheme } from '../../theme';
|
||||
import { goRoom as goRoomMethod, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
|
||||
import log, { events, logEvent } from '../../lib/methods/helpers/log';
|
||||
import { NewMessageStackParamList } from '../../stacks/types';
|
||||
import { search as searchMethod } from '../../lib/methods';
|
||||
import { useAppSelector } from '../../lib/hooks';
|
||||
import UserItem from '../../containers/UserItem';
|
||||
import HeaderNewMessage from './HeaderNewMessage';
|
||||
|
||||
const QUERY_SIZE = 50;
|
||||
|
||||
type TItem = ISearch | TSubscriptionModel;
|
||||
|
||||
const NewMessageView = () => {
|
||||
const [chats, setChats] = useState<TSubscriptionModel[]>([]);
|
||||
const [search, setSearch] = useState<TItem[]>([]);
|
||||
|
||||
const { colors } = useTheme();
|
||||
|
||||
const navigation = useNavigation<StackNavigationProp<NewMessageStackParamList, 'NewMessageView'>>();
|
||||
|
||||
const { isMasterDetail, maxUsers, useRealName } = useAppSelector(
|
||||
state => ({
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
maxUsers: (state.settings.DirectMesssage_maxUsers as number) || 1,
|
||||
useRealName: state.settings.UI_Use_Real_Name as boolean
|
||||
}),
|
||||
shallowEqual
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft: () => <HeaderButton.CloseModal navigation={navigation} testID='new-message-view-close' />,
|
||||
title: I18n.t('Create_New')
|
||||
});
|
||||
}, [navigation]);
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const c = await db
|
||||
.get('subscriptions')
|
||||
.query(Q.where('t', 'd'), Q.experimentalTake(QUERY_SIZE), Q.experimentalSortBy('room_updated_at', Q.desc))
|
||||
.fetch();
|
||||
setChats(c);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
init();
|
||||
}, []);
|
||||
|
||||
const handleSearch = useCallback(async (text: string) => {
|
||||
const result = (await searchMethod({ text, filterRooms: false })) as ISearch[];
|
||||
setSearch(result);
|
||||
}, []);
|
||||
|
||||
const goRoom = useCallback(
|
||||
(item: TGoRoomItem) => {
|
||||
logEvent(events.NEW_MSG_CHAT_WITH_USER);
|
||||
|
||||
if (isMasterDetail) {
|
||||
navigation.pop();
|
||||
}
|
||||
goRoomMethod({ item, isMasterDetail });
|
||||
},
|
||||
[isMasterDetail, navigation]
|
||||
);
|
||||
|
||||
return (
|
||||
<SafeAreaView testID='new-message-view'>
|
||||
<StatusBar />
|
||||
<FlatList
|
||||
data={search.length > 0 ? search : chats}
|
||||
keyExtractor={item => item._id || item.rid}
|
||||
ListHeaderComponent={<HeaderNewMessage maxUsers={maxUsers} onChangeText={handleSearch} />}
|
||||
renderItem={({ item }) => {
|
||||
const itemSearch = item as ISearch;
|
||||
const itemModel = item as TSubscriptionModel;
|
||||
|
||||
return (
|
||||
<UserItem
|
||||
name={useRealName && itemSearch.fname ? itemSearch.fname : itemModel.name}
|
||||
username={itemSearch.search ? itemSearch.username : itemModel.name}
|
||||
onPress={() => goRoom(itemModel)}
|
||||
testID={`new-message-view-item-${item.name}`}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
ListFooterComponent={List.Separator}
|
||||
contentContainerStyle={{ backgroundColor: colors.backgroundColor }}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewMessageView;
|
|
@ -127,22 +127,22 @@ class RegisterView extends React.Component<IProps, any> {
|
|||
const { dispatch, Accounts_EmailVerification, navigation, Accounts_ManuallyApproveNewUsers } = this.props;
|
||||
|
||||
try {
|
||||
await Services.register({
|
||||
const user = await Services.register({
|
||||
name,
|
||||
email,
|
||||
pass: password,
|
||||
username,
|
||||
...customFields
|
||||
username
|
||||
});
|
||||
|
||||
if (Accounts_EmailVerification) {
|
||||
await navigation.goBack();
|
||||
showErrorAlert(I18n.t('Verify_email_desc'), I18n.t('Registration_Succeeded'));
|
||||
} else if (Accounts_ManuallyApproveNewUsers) {
|
||||
await navigation.goBack();
|
||||
showErrorAlert(I18n.t('Wait_activation_warning'), I18n.t('Registration_Succeeded'));
|
||||
} else {
|
||||
dispatch(loginRequest({ user: email, password }));
|
||||
if (user.success) {
|
||||
if (Accounts_EmailVerification) {
|
||||
await navigation.goBack();
|
||||
showErrorAlert(I18n.t('Verify_email_desc'), I18n.t('Registration_Succeeded'));
|
||||
} else if (Accounts_ManuallyApproveNewUsers) {
|
||||
await navigation.goBack();
|
||||
showErrorAlert(I18n.t('Wait_activation_warning'), I18n.t('Registration_Succeeded'));
|
||||
} else {
|
||||
dispatch(loginRequest({ user: email, password }, false, false, customFields));
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.data?.errorType === 'username-invalid') {
|
||||
|
|
|
@ -64,10 +64,6 @@ interface IRoomActionsViewProps extends IActionSheetProvider, IBaseScreen<ChatsS
|
|||
encryptionEnabled: boolean;
|
||||
fontScale: number;
|
||||
serverVersion: string | null;
|
||||
addUserToJoinedRoomPermission?: string[];
|
||||
addUserToAnyCRoomPermission?: string[];
|
||||
addUserToAnyPRoomPermission?: string[];
|
||||
createInviteLinksPermission?: string[];
|
||||
editRoomPermission?: string[];
|
||||
toggleRoomE2EEncryptionPermission?: string[];
|
||||
viewBroadcastMemberListPermission?: string[];
|
||||
|
@ -94,13 +90,12 @@ interface IRoomActionsViewState {
|
|||
joined: boolean;
|
||||
canViewMembers: boolean;
|
||||
canAutoTranslate: boolean;
|
||||
canAddUser: boolean;
|
||||
canInviteUser: boolean;
|
||||
canEdit: boolean;
|
||||
canToggleEncryption: boolean;
|
||||
canCreateTeam: boolean;
|
||||
canAddChannelToTeam: boolean;
|
||||
canConvertTeam: boolean;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomActionsViewState> {
|
||||
|
@ -146,13 +141,12 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
joined: !!room,
|
||||
canViewMembers: false,
|
||||
canAutoTranslate: false,
|
||||
canAddUser: false,
|
||||
canInviteUser: false,
|
||||
canEdit: false,
|
||||
canToggleEncryption: false,
|
||||
canCreateTeam: false,
|
||||
canAddChannelToTeam: false,
|
||||
canConvertTeam: false
|
||||
canConvertTeam: false,
|
||||
loading: false
|
||||
};
|
||||
if (room && room.observe && room.rid) {
|
||||
this.roomObservable = room.observe();
|
||||
|
@ -206,8 +200,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
}
|
||||
|
||||
const canAutoTranslate = canAutoTranslateMethod();
|
||||
const canAddUser = await this.canAddUser();
|
||||
const canInviteUser = await this.canInviteUser();
|
||||
const canEdit = await this.canEdit();
|
||||
const canToggleEncryption = await this.canToggleEncryption();
|
||||
const canViewMembers = await this.canViewMembers();
|
||||
|
@ -217,8 +209,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
|
||||
this.setState({
|
||||
canAutoTranslate,
|
||||
canAddUser,
|
||||
canInviteUser,
|
||||
canEdit,
|
||||
canToggleEncryption,
|
||||
canViewMembers,
|
||||
|
@ -261,40 +251,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
}
|
||||
};
|
||||
|
||||
canAddUser = async () => {
|
||||
const { room, joined } = this.state;
|
||||
const { addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission } = this.props;
|
||||
const { rid, t } = room;
|
||||
let canAddUser = false;
|
||||
|
||||
const userInRoom = joined;
|
||||
const permissions = await hasPermission(
|
||||
[addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission],
|
||||
rid
|
||||
);
|
||||
|
||||
if (userInRoom && permissions[0]) {
|
||||
canAddUser = true;
|
||||
}
|
||||
if (t === 'c' && permissions[1]) {
|
||||
canAddUser = true;
|
||||
}
|
||||
if (t === 'p' && permissions[2]) {
|
||||
canAddUser = true;
|
||||
}
|
||||
return canAddUser;
|
||||
};
|
||||
|
||||
canInviteUser = async () => {
|
||||
const { room } = this.state;
|
||||
const { createInviteLinksPermission } = this.props;
|
||||
const { rid } = room;
|
||||
const permissions = await hasPermission([createInviteLinksPermission], rid);
|
||||
|
||||
const canInviteUser = permissions[0];
|
||||
return canInviteUser;
|
||||
};
|
||||
|
||||
canEdit = async () => {
|
||||
const { room } = this.state;
|
||||
const { editRoomPermission } = this.props;
|
||||
|
@ -558,6 +514,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
if (!room.teamId) {
|
||||
return;
|
||||
}
|
||||
this.setState({ loading: true });
|
||||
const result = await Services.teamListRoomsOfUser({ teamId: room.teamId, userId });
|
||||
|
||||
if (result.success) {
|
||||
|
@ -577,6 +534,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
this.convertTeamToChannelConfirmation();
|
||||
}
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
} catch (e) {
|
||||
this.convertTeamToChannelConfirmation();
|
||||
}
|
||||
|
@ -619,6 +577,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
if (!room.teamId) {
|
||||
return;
|
||||
}
|
||||
this.setState({ loading: true });
|
||||
const result = await Services.teamListRoomsOfUser({ teamId: room.teamId, userId });
|
||||
|
||||
if (result.success) {
|
||||
|
@ -644,6 +603,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
});
|
||||
}
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
} catch (e) {
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('You_are_leaving_the_team', { team: getRoomTitle(room) }),
|
||||
|
@ -705,7 +665,8 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
const data = teamRooms.map(team => ({
|
||||
rid: team.teamId as string,
|
||||
t: team.t,
|
||||
name: team.name
|
||||
name: team.name,
|
||||
teamMain: team.teamMain
|
||||
}));
|
||||
navigation.navigate('SelectListView', {
|
||||
title: 'Move_to_Team',
|
||||
|
@ -934,7 +895,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
};
|
||||
|
||||
renderLastSection = () => {
|
||||
const { room, joined } = this.state;
|
||||
const { room, joined, loading } = this.state;
|
||||
const { theme } = this.props;
|
||||
const { t, blocker } = room;
|
||||
|
||||
|
@ -968,6 +929,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
<List.Section>
|
||||
<List.Separator />
|
||||
<List.Item
|
||||
disabled={loading}
|
||||
title='Leave'
|
||||
onPress={() =>
|
||||
this.onPressTouchable({
|
||||
|
@ -1032,7 +994,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
};
|
||||
|
||||
teamToChannelActions = (t: string, room: ISubscription) => {
|
||||
const { canEdit, canConvertTeam } = this.state;
|
||||
const { canEdit, canConvertTeam, loading } = this.state;
|
||||
const canConvertTeamToChannel = canEdit && canConvertTeam && !!room?.teamMain;
|
||||
|
||||
return (
|
||||
|
@ -1041,6 +1003,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
<>
|
||||
<List.Item
|
||||
title='Convert_to_Channel'
|
||||
disabled={loading}
|
||||
onPress={() =>
|
||||
this.onPressTouchable({
|
||||
event: this.convertTeamToChannel
|
||||
|
@ -1135,7 +1098,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
};
|
||||
|
||||
render() {
|
||||
const { room, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate } = this.state;
|
||||
const { room, membersCount, canViewMembers, joined, canAutoTranslate } = this.state;
|
||||
const { rid, t, prid } = room;
|
||||
const isGroupChatHandler = isGroupChat(room);
|
||||
|
||||
|
@ -1154,7 +1117,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
<List.Item
|
||||
title='Members'
|
||||
subtitle={membersCount > 0 ? `${membersCount} ${I18n.t('members')}` : undefined}
|
||||
onPress={() => this.onPressTouchable({ route: 'RoomMembersView', params: { rid, room } })}
|
||||
onPress={() => this.onPressTouchable({ route: 'RoomMembersView', params: { rid, room, joined: this.joined } })}
|
||||
testID='room-actions-members'
|
||||
left={() => <List.Icon name='team' />}
|
||||
showActionIndicator
|
||||
|
@ -1164,45 +1127,6 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
</>
|
||||
) : null}
|
||||
|
||||
{['c', 'p'].includes(t) && canAddUser ? (
|
||||
<>
|
||||
<List.Item
|
||||
title='Add_users'
|
||||
onPress={() =>
|
||||
this.onPressTouchable({
|
||||
route: 'SelectedUsersView',
|
||||
params: {
|
||||
title: I18n.t('Add_users'),
|
||||
nextAction: this.addUser
|
||||
}
|
||||
})
|
||||
}
|
||||
testID='room-actions-add-user'
|
||||
left={() => <List.Icon name='add' />}
|
||||
showActionIndicator
|
||||
/>
|
||||
<List.Separator />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{['c', 'p'].includes(t) && canInviteUser ? (
|
||||
<>
|
||||
<List.Item
|
||||
title='Invite_users'
|
||||
onPress={() =>
|
||||
this.onPressTouchable({
|
||||
route: 'InviteUsersView',
|
||||
params: { rid }
|
||||
})
|
||||
}
|
||||
testID='room-actions-invite-user'
|
||||
left={() => <List.Icon name='user-add' />}
|
||||
showActionIndicator
|
||||
/>
|
||||
<List.Separator />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{['c', 'p', 'd'].includes(t) && !prid ? (
|
||||
<>
|
||||
<List.Item
|
||||
|
@ -1384,10 +1308,6 @@ const mapStateToProps = (state: IApplicationState) => ({
|
|||
encryptionEnabled: state.encryption.enabled,
|
||||
serverVersion: state.server.version,
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
addUserToJoinedRoomPermission: state.permissions['add-user-to-joined-room'],
|
||||
addUserToAnyCRoomPermission: state.permissions['add-user-to-any-c-room'],
|
||||
addUserToAnyPRoomPermission: state.permissions['add-user-to-any-p-room'],
|
||||
createInviteLinksPermission: state.permissions['create-invite-links'],
|
||||
editRoomPermission: state.permissions['edit-room'],
|
||||
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
|
||||
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
|
||||
|
|
|
@ -705,7 +705,7 @@ class RoomInfoEditView extends React.Component<IRoomInfoEditViewProps, IRoomInfo
|
|||
) : null}
|
||||
{room.broadcast
|
||||
? [
|
||||
<Text style={styles.broadcast}>{I18n.t('Broadcast_Channel')}</Text>,
|
||||
<Text style={styles.broadcast}>{I18n.t('Broadcast')}</Text>,
|
||||
<View style={[styles.divider, { borderColor: themes[theme].separatorColor }]} />
|
||||
]
|
||||
: null}
|
||||
|
|
|
@ -24,8 +24,8 @@ const Channel = ({ room }: { room: ISubscription }) => {
|
|||
testID='room-info-view-announcement'
|
||||
/>
|
||||
<Item
|
||||
label={I18n.t('Broadcast_Channel')}
|
||||
content={room.broadcast ? I18n.t('Broadcast_channel_Description') : ''}
|
||||
label={I18n.t('Broadcast')}
|
||||
content={room.broadcast ? I18n.t('Broadcast_hint') : ''}
|
||||
testID='room-info-view-broadcast'
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import { CompositeNavigationProp, useNavigation } from '@react-navigation/native';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { setLoading } from '../../../actions/selectedUsers';
|
||||
import * as List from '../../../containers/List';
|
||||
import { TSubscriptionModel } from '../../../definitions';
|
||||
import i18n from '../../../i18n';
|
||||
import { usePermissions } from '../../../lib/hooks';
|
||||
import log, { events, logEvent } from '../../../lib/methods/helpers/log';
|
||||
import { Services } from '../../../lib/services';
|
||||
import { MasterDetailInsideStackParamList } from '../../../stacks/MasterDetailStack/types';
|
||||
import { ChatsStackParamList } from '../../../stacks/types';
|
||||
|
||||
type TNavigation = CompositeNavigationProp<
|
||||
StackNavigationProp<ChatsStackParamList, 'RoomActionsView'>,
|
||||
StackNavigationProp<MasterDetailInsideStackParamList>
|
||||
>;
|
||||
|
||||
interface IActionsSection {
|
||||
rid: TSubscriptionModel['rid'];
|
||||
t: TSubscriptionModel['t'];
|
||||
joined: boolean;
|
||||
}
|
||||
|
||||
export default function ActionsSection({ rid, t, joined }: IActionsSection): React.ReactElement {
|
||||
const { navigate, pop } = useNavigation<TNavigation>();
|
||||
const dispatch = useDispatch();
|
||||
const [addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission, createInviteLinksPermission] =
|
||||
usePermissions(['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room', 'create-invite-links'], rid);
|
||||
|
||||
const canAddUser =
|
||||
(joined && addUserToJoinedRoomPermission) ||
|
||||
(t === 'c' && addUserToAnyCRoomPermission) ||
|
||||
(t === 'p' && addUserToAnyPRoomPermission) ||
|
||||
false;
|
||||
|
||||
const canInviteUser = createInviteLinksPermission;
|
||||
|
||||
const handleOnPress = ({
|
||||
route,
|
||||
params
|
||||
}: {
|
||||
route: keyof ChatsStackParamList;
|
||||
params: ChatsStackParamList[keyof ChatsStackParamList];
|
||||
}) => {
|
||||
navigate(route, params);
|
||||
// @ts-ignore
|
||||
logEvent(events[`RM_GO_${route.replace('View', '').toUpperCase()}`]);
|
||||
};
|
||||
|
||||
const addUser = async () => {
|
||||
try {
|
||||
dispatch(setLoading(true));
|
||||
await Services.addUsersToRoom(rid);
|
||||
pop();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
} finally {
|
||||
dispatch(setLoading(false));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ paddingTop: canAddUser || canInviteUser ? 16 : 0, paddingBottom: canAddUser || canInviteUser ? 8 : 0 }}>
|
||||
{['c', 'p'].includes(t) && canAddUser ? (
|
||||
<>
|
||||
<List.Separator />
|
||||
<List.Item
|
||||
title='Add_users'
|
||||
onPress={() =>
|
||||
handleOnPress({
|
||||
route: 'SelectedUsersView',
|
||||
params: {
|
||||
title: i18n.t('Add_users'),
|
||||
nextAction: addUser
|
||||
}
|
||||
})
|
||||
}
|
||||
testID='room-actions-add-user'
|
||||
left={() => <List.Icon name='add' />}
|
||||
showActionIndicator
|
||||
/>
|
||||
<List.Separator />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{['c', 'p'].includes(t) && canInviteUser ? (
|
||||
<>
|
||||
<List.Item
|
||||
title='Invite_users'
|
||||
onPress={() =>
|
||||
handleOnPress({
|
||||
route: 'InviteUsersView',
|
||||
params: { rid }
|
||||
})
|
||||
}
|
||||
testID='room-actions-invite-user'
|
||||
left={() => <List.Icon name='user-add' />}
|
||||
showActionIndicator
|
||||
/>
|
||||
<List.Separator />
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
import { Q } from '@nozbe/watermelondb';
|
||||
|
||||
import { LISTENER } from '../../containers/Toast';
|
||||
import { IUser, SubscriptionType, TSubscriptionModel, TUserModel } from '../../definitions';
|
||||
import I18n from '../../i18n';
|
||||
import { getRoomTitle, showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers';
|
||||
import EventEmitter from '../../lib/methods/helpers/events';
|
||||
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
|
||||
import log from '../../lib/methods/helpers/log';
|
||||
import appNavigation from '../../lib/navigation/appNavigation';
|
||||
import { Services } from '../../lib/services';
|
||||
import database from '../../lib/database';
|
||||
import { RoomTypes } from '../../lib/methods';
|
||||
|
||||
export type TRoomType = SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL;
|
||||
|
||||
const handleGoRoom = (item: TGoRoomItem, isMasterDetail: boolean): void => {
|
||||
if (isMasterDetail) {
|
||||
appNavigation.navigate('DrawerNavigator');
|
||||
} else {
|
||||
appNavigation.popToTop();
|
||||
}
|
||||
goRoom({ item, isMasterDetail });
|
||||
};
|
||||
|
||||
export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles: any): boolean => {
|
||||
const userRoleResult = roomRoles.find((r: any) => r.u._id === selectedUser._id);
|
||||
return userRoleResult?.roles.includes(role);
|
||||
};
|
||||
|
||||
export const fetchRoomMembersRoles = async (roomType: TRoomType, rid: string, updateState: any): Promise<void> => {
|
||||
try {
|
||||
const type = roomType;
|
||||
const result = await Services.getRoomRoles(rid, type);
|
||||
if (result?.success) {
|
||||
updateState({ roomRoles: result.roles });
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleMute = async (user: TUserModel, rid: string) => {
|
||||
try {
|
||||
await Services.toggleMuteUserInRoom(rid, user?.username, !user?.muted);
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t('User_has_been_key', { key: user?.muted ? I18n.t('unmuted') : I18n.t('muted') })
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleModerator = async (
|
||||
selectedUser: TUserModel,
|
||||
isModerator: boolean,
|
||||
room: TSubscriptionModel,
|
||||
username: string,
|
||||
callback: () => Promise<void>
|
||||
): Promise<void> => {
|
||||
try {
|
||||
await Services.toggleRoomModerator({
|
||||
roomId: room.rid,
|
||||
t: room.t,
|
||||
userId: selectedUser._id,
|
||||
isModerator
|
||||
});
|
||||
const message = isModerator
|
||||
? 'User__username__is_now_a_moderator_of__room_name_'
|
||||
: 'User__username__removed_from__room_name__moderators';
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t(message, {
|
||||
username,
|
||||
room_name: getRoomTitle(room)
|
||||
})
|
||||
});
|
||||
callback();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const navToDirectMessage = async (item: IUser, isMasterDetail: boolean): Promise<void> => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subsCollection = db.get('subscriptions');
|
||||
const query = await subsCollection.query(Q.where('name', item.username)).fetch();
|
||||
if (query.length) {
|
||||
const [room] = query;
|
||||
handleGoRoom(room, isMasterDetail);
|
||||
} else {
|
||||
const result = await Services.createDirectMessage(item.username);
|
||||
if (result.success) {
|
||||
handleGoRoom({ rid: result.room?._id as string, name: item.username, t: SubscriptionType.DIRECT }, isMasterDetail);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const removeFromTeam = async (
|
||||
selectedUser: IUser,
|
||||
updateState: Function,
|
||||
room: TSubscriptionModel,
|
||||
members: TUserModel[],
|
||||
selected?: any
|
||||
) => {
|
||||
try {
|
||||
const userId = selectedUser._id;
|
||||
const result = await Services.removeTeamMember({
|
||||
teamId: room.teamId,
|
||||
userId,
|
||||
...(selected && { rooms: selected })
|
||||
});
|
||||
if (result.success) {
|
||||
const message = I18n.t('User_has_been_removed_from_s', { s: getRoomTitle(room) });
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
const newMembers = members.filter(member => member._id !== userId);
|
||||
updateState({
|
||||
members: newMembers
|
||||
});
|
||||
appNavigation.navigate('RoomMembersView', { room });
|
||||
}
|
||||
} catch (e: any) {
|
||||
log(e);
|
||||
showErrorAlert(
|
||||
e.data.error ? I18n.t(e.data.error) : I18n.t('There_was_an_error_while_action', { action: I18n.t('removing_team') }),
|
||||
I18n.t('Cannot_remove')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleRemoveFromTeam = async (
|
||||
selectedUser: TUserModel,
|
||||
updateState: Function,
|
||||
room: TSubscriptionModel,
|
||||
members: TUserModel[]
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const result = await Services.teamListRoomsOfUser({ teamId: room.teamId as string, userId: selectedUser._id });
|
||||
|
||||
if (result.success) {
|
||||
if (result.rooms?.length) {
|
||||
const teamChannels = result.rooms.map((r: any) => ({
|
||||
rid: r._id,
|
||||
name: r.name,
|
||||
teamId: r.teamId,
|
||||
alert: r.isLastOwner
|
||||
}));
|
||||
appNavigation.navigate('SelectListView', {
|
||||
title: 'Remove_Member',
|
||||
infoText: 'Remove_User_Team_Channels',
|
||||
data: teamChannels,
|
||||
nextAction: (selected: any) => removeFromTeam(selectedUser, updateState, room, members, selected),
|
||||
showAlert: () => showErrorAlert(I18n.t('Last_owner_team_room'), I18n.t('Cannot_remove'))
|
||||
});
|
||||
} else {
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('Removing_user_from_this_team', { user: selectedUser.username }),
|
||||
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
|
||||
onPress: () => removeFromTeam(selectedUser, updateState, room, members)
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('Removing_user_from_this_team', { user: selectedUser.username }),
|
||||
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
|
||||
onPress: () => removeFromTeam(selectedUser, updateState, room, members)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const handleLeader = async (
|
||||
selectedUser: TUserModel,
|
||||
isLeader: boolean,
|
||||
room: TSubscriptionModel,
|
||||
username: string,
|
||||
callback: () => Promise<void>
|
||||
): Promise<void> => {
|
||||
try {
|
||||
await Services.toggleRoomLeader({
|
||||
roomId: room.rid,
|
||||
t: room.t,
|
||||
userId: selectedUser._id,
|
||||
isLeader
|
||||
});
|
||||
const message = isLeader
|
||||
? 'User__username__is_now_a_leader_of__room_name_'
|
||||
: 'User__username__removed_from__room_name__leaders';
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t(message, {
|
||||
username,
|
||||
room_name: getRoomTitle(room)
|
||||
})
|
||||
});
|
||||
callback();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleRemoveUserFromRoom = async (
|
||||
selectedUser: TUserModel,
|
||||
room: TSubscriptionModel,
|
||||
callback: Function
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const userId = selectedUser._id;
|
||||
await Services.removeUserFromRoom({ roomId: room.rid, t: room.t as RoomTypes, userId });
|
||||
const message = I18n.t('User_has_been_removed_from_s', { s: getRoomTitle(room) });
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
callback();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleIgnore = async (selectedUser: TUserModel, ignore: boolean, rid: string) => {
|
||||
try {
|
||||
await Services.ignoreUser({
|
||||
rid,
|
||||
userId: selectedUser._id,
|
||||
ignore
|
||||
});
|
||||
const message = I18n.t(ignore ? 'User_has_been_ignored' : 'User_has_been_unignored');
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleOwner = async (
|
||||
selectedUser: TUserModel,
|
||||
isOwner: boolean,
|
||||
username: string,
|
||||
room: TSubscriptionModel,
|
||||
callback: Function
|
||||
): Promise<void> => {
|
||||
try {
|
||||
await Services.toggleRoomOwner({
|
||||
roomId: room.rid,
|
||||
t: room.t,
|
||||
userId: selectedUser._id,
|
||||
isOwner
|
||||
});
|
||||
const message = isOwner ? 'User__username__is_now_a_owner_of__room_name_' : 'User__username__removed_from__room_name__owners';
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t(message, {
|
||||
username,
|
||||
room_name: getRoomTitle(room)
|
||||
})
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
callback();
|
||||
};
|
|
@ -1,311 +1,202 @@
|
|||
import { Q } from '@nozbe/watermelondb';
|
||||
import React from 'react';
|
||||
import { FlatList } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { NavigationProp, RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import React, { useEffect, useReducer } from 'react';
|
||||
import { FlatList, Text, View } from 'react-native';
|
||||
|
||||
import { themes } from '../../lib/constants';
|
||||
import { TActionSheetOptions, TActionSheetOptionsItem, withActionSheet } from '../../containers/ActionSheet';
|
||||
import { TActionSheetOptionsItem, useActionSheet } from '../../containers/ActionSheet';
|
||||
import ActivityIndicator from '../../containers/ActivityIndicator';
|
||||
import { CustomIcon } from '../../containers/CustomIcon';
|
||||
import * as HeaderButton from '../../containers/HeaderButton';
|
||||
import * as List from '../../containers/List';
|
||||
import { RadioButton } from '../../containers/RadioButton';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import SearchBox from '../../containers/SearchBox';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import { LISTENER } from '../../containers/Toast';
|
||||
import { IApplicationState, IBaseScreen, IUser, SubscriptionType, TSubscriptionModel, TUserModel } from '../../definitions';
|
||||
import I18n from '../../i18n';
|
||||
import database from '../../lib/database';
|
||||
import { CustomIcon } from '../../containers/CustomIcon';
|
||||
import UserItem from '../../containers/UserItem';
|
||||
import { getUserSelector } from '../../selectors/login';
|
||||
import { ModalStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||
import { TSupportedThemes, withTheme } from '../../theme';
|
||||
import EventEmitter from '../../lib/methods/helpers/events';
|
||||
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
|
||||
import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers/info';
|
||||
import { TSubscriptionModel, TUserModel } from '../../definitions';
|
||||
import I18n from '../../i18n';
|
||||
import { useAppSelector, usePermissions } from '../../lib/hooks';
|
||||
import { getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
|
||||
import { showConfirmationAlert } from '../../lib/methods/helpers/info';
|
||||
import log from '../../lib/methods/helpers/log';
|
||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
||||
import { TSupportedPermissions } from '../../reducers/permissions';
|
||||
import { RoomTypes } from '../../lib/methods';
|
||||
import { compareServerVersion, debounce, getRoomTitle, hasPermission, isGroupChat } from '../../lib/methods/helpers';
|
||||
import styles from './styles';
|
||||
import { Services } from '../../lib/services';
|
||||
import { TSupportedPermissions } from '../../reducers/permissions';
|
||||
import { getUserSelector } from '../../selectors/login';
|
||||
import { ModalStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||
import { useTheme } from '../../theme';
|
||||
import ActionsSection from './components/ActionsSection';
|
||||
import {
|
||||
fetchRole,
|
||||
fetchRoomMembersRoles,
|
||||
handleIgnore,
|
||||
handleLeader,
|
||||
handleModerator,
|
||||
handleMute,
|
||||
handleOwner,
|
||||
handleRemoveFromTeam,
|
||||
handleRemoveUserFromRoom,
|
||||
navToDirectMessage,
|
||||
TRoomType
|
||||
} from './helpers';
|
||||
import styles from './styles';
|
||||
|
||||
const PAGE_SIZE = 25;
|
||||
|
||||
interface IRoomMembersViewProps extends IBaseScreen<ModalStackParamList, 'RoomMembersView'> {
|
||||
rid: string;
|
||||
members: string[];
|
||||
baseUrl: string;
|
||||
room: TSubscriptionModel;
|
||||
user: {
|
||||
id: string;
|
||||
token: string;
|
||||
roles: string[];
|
||||
};
|
||||
showActionSheet: (params: TActionSheetOptions) => {};
|
||||
theme: TSupportedThemes;
|
||||
isMasterDetail: boolean;
|
||||
useRealName: boolean;
|
||||
muteUserPermission: string[];
|
||||
setLeaderPermission: string[];
|
||||
setOwnerPermission: string[];
|
||||
setModeratorPermission: string[];
|
||||
removeUserPermission: string[];
|
||||
editTeamMemberPermission: string[];
|
||||
viewAllTeamChannelsPermission: string[];
|
||||
viewAllTeamsPermission: string[];
|
||||
serverVersion: string;
|
||||
}
|
||||
|
||||
interface IRoomMembersViewState {
|
||||
isLoading: boolean;
|
||||
allUsers: boolean;
|
||||
filtering: string;
|
||||
rid: string;
|
||||
members: TUserModel[];
|
||||
membersFiltered: TUserModel[];
|
||||
room: TSubscriptionModel;
|
||||
end: boolean;
|
||||
roomRoles: any;
|
||||
filter: string;
|
||||
page: number;
|
||||
}
|
||||
|
||||
class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMembersViewState> {
|
||||
private mounted: boolean;
|
||||
private permissions: { [key in TSupportedPermissions]?: boolean };
|
||||
private roomObservable!: Observable<TSubscriptionModel>;
|
||||
private subscription!: Subscription;
|
||||
private roomRoles: any;
|
||||
const RightIcon = ({ check, label }: { check: boolean; label: string }) => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<CustomIcon
|
||||
testID={check ? `action-sheet-set-${label}-checked` : `action-sheet-set-${label}-unchecked`}
|
||||
name={check ? 'checkbox-checked' : 'checkbox-unchecked'}
|
||||
size={20}
|
||||
color={check ? colors.tintActive : colors.auxiliaryTintColor}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
constructor(props: IRoomMembersViewProps) {
|
||||
super(props);
|
||||
this.mounted = false;
|
||||
this.permissions = {};
|
||||
const rid = props.route.params?.rid;
|
||||
const room = props.route.params?.room;
|
||||
this.state = {
|
||||
const RoomMembersView = (): React.ReactElement => {
|
||||
const { showActionSheet } = useActionSheet();
|
||||
const { colors } = useTheme();
|
||||
|
||||
const { params } = useRoute<RouteProp<ModalStackParamList, 'RoomMembersView'>>();
|
||||
const navigation = useNavigation<NavigationProp<ModalStackParamList, 'RoomMembersView'>>();
|
||||
|
||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||
|
||||
const useRealName = useAppSelector(state => state.settings.UI_Use_Real_Name);
|
||||
const user = useAppSelector(state => getUserSelector(state));
|
||||
|
||||
const [state, updateState] = useReducer(
|
||||
(state: IRoomMembersViewState, newState: Partial<IRoomMembersViewState>) => ({ ...state, ...newState }),
|
||||
{
|
||||
isLoading: false,
|
||||
allUsers: false,
|
||||
filtering: '',
|
||||
rid,
|
||||
members: [],
|
||||
membersFiltered: [],
|
||||
room: room || ({} as TSubscriptionModel),
|
||||
room: params.room || ({} as TSubscriptionModel),
|
||||
end: false,
|
||||
roomRoles: null,
|
||||
filter: '',
|
||||
page: 0
|
||||
}
|
||||
);
|
||||
|
||||
const teamPermissions: TSupportedPermissions[] = state.room.teamMain
|
||||
? ['edit-team-member', 'view-all-team-channels', 'view-all-teams']
|
||||
: [];
|
||||
|
||||
const [
|
||||
muteUserPermission,
|
||||
setLeaderPermission,
|
||||
setOwnerPermission,
|
||||
setModeratorPermission,
|
||||
removeUserPermission,
|
||||
editTeamMemberPermission,
|
||||
viewAllTeamChannelsPermission,
|
||||
viewAllTeamsPermission
|
||||
] = usePermissions(['mute-user', 'set-leader', 'set-owner', 'set-moderator', 'remove-user', ...teamPermissions], params.rid);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = params?.room?.observe && params.room.observe().subscribe(changes => updateState({ room: changes }));
|
||||
setHeader(true);
|
||||
fetchMembers(true);
|
||||
return () => subscription?.unsubscribe();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchRoles = () => {
|
||||
if (isGroupChat(state.room)) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
muteUserPermission ||
|
||||
setLeaderPermission ||
|
||||
setOwnerPermission ||
|
||||
setModeratorPermission ||
|
||||
removeUserPermission ||
|
||||
editTeamMemberPermission ||
|
||||
viewAllTeamChannelsPermission ||
|
||||
viewAllTeamsPermission
|
||||
) {
|
||||
fetchRoomMembersRoles(state.room.t as any, state.room.rid, updateState);
|
||||
}
|
||||
};
|
||||
if (room && room.observe) {
|
||||
this.roomObservable = room.observe();
|
||||
this.subscription = this.roomObservable.subscribe(changes => {
|
||||
if (this.mounted) {
|
||||
this.setState({ room: changes });
|
||||
} else {
|
||||
this.setState({ room: changes });
|
||||
}
|
||||
});
|
||||
fetchRoles();
|
||||
}, [
|
||||
muteUserPermission,
|
||||
setLeaderPermission,
|
||||
setOwnerPermission,
|
||||
setModeratorPermission,
|
||||
removeUserPermission,
|
||||
editTeamMemberPermission,
|
||||
viewAllTeamChannelsPermission,
|
||||
viewAllTeamsPermission
|
||||
]);
|
||||
|
||||
const toggleStatus = (status: boolean) => {
|
||||
try {
|
||||
updateState({ members: [], allUsers: status, end: false });
|
||||
fetchMembers(status);
|
||||
setHeader(status);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
this.setHeader();
|
||||
}
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const { room } = this.state;
|
||||
this.mounted = true;
|
||||
this.fetchMembers();
|
||||
|
||||
if (isGroupChat(room)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
muteUserPermission,
|
||||
setLeaderPermission,
|
||||
setOwnerPermission,
|
||||
setModeratorPermission,
|
||||
removeUserPermission,
|
||||
editTeamMemberPermission,
|
||||
viewAllTeamChannelsPermission,
|
||||
viewAllTeamsPermission
|
||||
} = this.props;
|
||||
|
||||
const result = await hasPermission(
|
||||
[
|
||||
muteUserPermission,
|
||||
setLeaderPermission,
|
||||
setOwnerPermission,
|
||||
setModeratorPermission,
|
||||
removeUserPermission,
|
||||
...(room.teamMain ? [editTeamMemberPermission, viewAllTeamChannelsPermission, viewAllTeamsPermission] : [])
|
||||
],
|
||||
room.rid
|
||||
);
|
||||
|
||||
this.permissions = {
|
||||
'mute-user': result[0],
|
||||
'set-leader': result[1],
|
||||
'set-owner': result[2],
|
||||
'set-moderator': result[3],
|
||||
'remove-user': result[4],
|
||||
...(room.teamMain
|
||||
? {
|
||||
'edit-team-member': result[5],
|
||||
'view-all-team-channels': result[6],
|
||||
'view-all-teams': result[7]
|
||||
}
|
||||
: {})
|
||||
};
|
||||
|
||||
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
|
||||
if (hasSinglePermission) {
|
||||
this.fetchRoomMembersRoles();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.subscription && this.subscription.unsubscribe) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
setHeader = () => {
|
||||
const { allUsers } = this.state;
|
||||
const { navigation } = this.props;
|
||||
const toggleText = allUsers ? I18n.t('Online') : I18n.t('All');
|
||||
const setHeader = (allUsers: boolean) => {
|
||||
navigation.setOptions({
|
||||
title: I18n.t('Members'),
|
||||
headerRight: () => (
|
||||
<HeaderButton.Container>
|
||||
<HeaderButton.Item title={toggleText} onPress={this.toggleStatus} testID='room-members-view-toggle-status' />
|
||||
<HeaderButton.Item
|
||||
iconName='filter'
|
||||
onPress={() =>
|
||||
showActionSheet({
|
||||
options: [
|
||||
{
|
||||
title: I18n.t('Online'),
|
||||
onPress: () => toggleStatus(true),
|
||||
right: () => <RadioButton check={allUsers} />,
|
||||
testID: 'room-members-view-toggle-status-online'
|
||||
},
|
||||
{
|
||||
title: I18n.t('All'),
|
||||
onPress: () => toggleStatus(false),
|
||||
right: () => <RadioButton check={!allUsers} />,
|
||||
testID: 'room-members-view-toggle-status-all'
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
testID='room-members-view-filter'
|
||||
/>
|
||||
</HeaderButton.Container>
|
||||
)
|
||||
});
|
||||
};
|
||||
|
||||
get isServerVersionLowerThan3_16() {
|
||||
const { serverVersion } = this.props;
|
||||
return compareServerVersion(serverVersion, 'lowerThan', '3.16.0');
|
||||
}
|
||||
const getUserDisplayName = (user: TUserModel) => (useRealName ? user.name : user.username) || user.username;
|
||||
|
||||
onSearchChangeText = debounce((text: string) => {
|
||||
const { members } = this.state;
|
||||
text = text.trim();
|
||||
if (this.isServerVersionLowerThan3_16) {
|
||||
let membersFiltered: TUserModel[] = [];
|
||||
|
||||
if (members && members.length > 0 && text) {
|
||||
membersFiltered = members.filter(
|
||||
m => m.username.toLowerCase().match(text.toLowerCase()) || m.name?.toLowerCase().match(text.toLowerCase())
|
||||
);
|
||||
}
|
||||
return this.setState({ filtering: text, membersFiltered });
|
||||
}
|
||||
|
||||
this.setState({ filtering: text, page: 0, members: [], end: false }, () => {
|
||||
this.fetchMembers();
|
||||
});
|
||||
}, 500);
|
||||
|
||||
navToDirectMessage = async (item: IUser) => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subsCollection = db.get('subscriptions');
|
||||
const query = await subsCollection.query(Q.where('name', item.username)).fetch();
|
||||
if (query.length) {
|
||||
const [room] = query;
|
||||
this.goRoom(room);
|
||||
} else {
|
||||
const result = await Services.createDirectMessage(item.username);
|
||||
if (result.success) {
|
||||
this.goRoom({ rid: result.room?._id as string, name: item.username, t: SubscriptionType.DIRECT });
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
handleRemoveFromTeam = async (selectedUser: TUserModel) => {
|
||||
try {
|
||||
const { navigation } = this.props;
|
||||
const { room } = this.state;
|
||||
|
||||
const result = await Services.teamListRoomsOfUser({ teamId: room.teamId as string, userId: selectedUser._id });
|
||||
|
||||
if (result.success) {
|
||||
if (result.rooms?.length) {
|
||||
const teamChannels = result.rooms.map((r: any) => ({
|
||||
rid: r._id,
|
||||
name: r.name,
|
||||
teamId: r.teamId,
|
||||
alert: r.isLastOwner
|
||||
}));
|
||||
navigation.navigate('SelectListView', {
|
||||
title: 'Remove_Member',
|
||||
infoText: 'Remove_User_Team_Channels',
|
||||
data: teamChannels,
|
||||
nextAction: (selected: any) => this.removeFromTeam(selectedUser, selected),
|
||||
showAlert: () => showErrorAlert(I18n.t('Last_owner_team_room'), I18n.t('Cannot_remove'))
|
||||
});
|
||||
} else {
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('Removing_user_from_this_team', { user: selectedUser.username }),
|
||||
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
|
||||
onPress: () => this.removeFromTeam(selectedUser)
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('Removing_user_from_this_team', { user: selectedUser.username }),
|
||||
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
|
||||
onPress: () => this.removeFromTeam(selectedUser)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
removeFromTeam = async (selectedUser: IUser, selected?: any) => {
|
||||
try {
|
||||
const { members, membersFiltered, room } = this.state;
|
||||
const { navigation } = this.props;
|
||||
|
||||
const userId = selectedUser._id;
|
||||
const result = await Services.removeTeamMember({
|
||||
teamId: room.teamId,
|
||||
userId,
|
||||
...(selected && { rooms: selected })
|
||||
});
|
||||
if (result.success) {
|
||||
const message = I18n.t('User_has_been_removed_from_s', { s: getRoomTitle(room) });
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
const newMembers = members.filter(member => member._id !== userId);
|
||||
const newMembersFiltered = this.isServerVersionLowerThan3_16
|
||||
? membersFiltered.filter(member => member._id !== userId)
|
||||
: [];
|
||||
this.setState({
|
||||
members: newMembers,
|
||||
membersFiltered: newMembersFiltered
|
||||
});
|
||||
// @ts-ignore - This is just to force a reload
|
||||
navigation.navigate('RoomMembersView');
|
||||
}
|
||||
} catch (e: any) {
|
||||
log(e);
|
||||
showErrorAlert(
|
||||
e.data.error ? I18n.t(e.data.error) : I18n.t('There_was_an_error_while_action', { action: I18n.t('removing_team') }),
|
||||
I18n.t('Cannot_remove')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
onPressUser = (selectedUser: TUserModel) => {
|
||||
const { room } = this.state;
|
||||
const { showActionSheet, user, theme } = this.props;
|
||||
const onPressUser = (selectedUser: TUserModel) => {
|
||||
const { room, roomRoles, members } = state;
|
||||
|
||||
const options: TActionSheetOptionsItem[] = [
|
||||
{
|
||||
icon: 'message',
|
||||
title: I18n.t('Direct_message'),
|
||||
onPress: () => this.navToDirectMessage(selectedUser)
|
||||
onPress: () => navToDirectMessage(selectedUser, isMasterDetail)
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -316,12 +207,12 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
|||
options.push({
|
||||
icon: 'ignore',
|
||||
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
|
||||
onPress: () => this.handleIgnore(selectedUser, !isIgnored),
|
||||
onPress: () => handleIgnore(selectedUser, !isIgnored, room.rid),
|
||||
testID: 'action-sheet-ignore-user'
|
||||
});
|
||||
}
|
||||
|
||||
if (this.permissions['mute-user']) {
|
||||
if (muteUserPermission) {
|
||||
const { muted = [] } = room;
|
||||
const userIsMuted = muted.find?.(m => m === selectedUser.username);
|
||||
selectedUser.muted = !!userIsMuted;
|
||||
|
@ -334,7 +225,7 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
|||
roomName: getRoomTitle(room)
|
||||
}),
|
||||
confirmationText: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
|
||||
onPress: () => this.handleMute(selectedUser)
|
||||
onPress: () => handleMute(selectedUser, room.rid)
|
||||
});
|
||||
},
|
||||
testID: 'action-sheet-mute-user'
|
||||
|
@ -342,78 +233,63 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
|||
}
|
||||
|
||||
// Owner
|
||||
if (this.permissions['set-owner']) {
|
||||
const userRoleResult = this.roomRoles.find((r: any) => r.u._id === selectedUser._id);
|
||||
const isOwner = userRoleResult?.roles.includes('owner');
|
||||
if (setOwnerPermission) {
|
||||
const isOwner = fetchRole('owner', selectedUser, roomRoles);
|
||||
options.push({
|
||||
icon: 'shield-check',
|
||||
title: I18n.t('Owner'),
|
||||
onPress: () => this.handleOwner(selectedUser, !isOwner),
|
||||
right: () => (
|
||||
<CustomIcon
|
||||
testID={isOwner ? 'action-sheet-set-owner-checked' : 'action-sheet-set-owner-unchecked'}
|
||||
name={isOwner ? 'checkbox-checked' : 'checkbox-unchecked'}
|
||||
size={20}
|
||||
color={isOwner ? themes[theme].tintActive : themes[theme].auxiliaryTintColor}
|
||||
/>
|
||||
),
|
||||
onPress: () =>
|
||||
handleOwner(selectedUser, !isOwner, getUserDisplayName(selectedUser), room, () =>
|
||||
fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
|
||||
),
|
||||
right: () => <RightIcon check={isOwner} label='owner' />,
|
||||
testID: 'action-sheet-set-owner'
|
||||
});
|
||||
}
|
||||
|
||||
// Leader
|
||||
if (this.permissions['set-leader']) {
|
||||
const userRoleResult = this.roomRoles.find((r: any) => r.u._id === selectedUser._id);
|
||||
const isLeader = userRoleResult?.roles.includes('leader');
|
||||
if (setLeaderPermission) {
|
||||
const isLeader = fetchRole('leader', selectedUser, roomRoles);
|
||||
options.push({
|
||||
icon: 'shield-alt',
|
||||
title: I18n.t('Leader'),
|
||||
onPress: () => this.handleLeader(selectedUser, !isLeader),
|
||||
right: () => (
|
||||
<CustomIcon
|
||||
testID={isLeader ? 'action-sheet-set-leader-checked' : 'action-sheet-set-leader-unchecked'}
|
||||
name={isLeader ? 'checkbox-checked' : 'checkbox-unchecked'}
|
||||
size={20}
|
||||
color={isLeader ? themes[theme].tintActive : themes[theme].auxiliaryTintColor}
|
||||
/>
|
||||
),
|
||||
onPress: () =>
|
||||
handleLeader(selectedUser, !isLeader, room, getUserDisplayName(selectedUser), () =>
|
||||
fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
|
||||
),
|
||||
right: () => <RightIcon check={isLeader} label='leader' />,
|
||||
testID: 'action-sheet-set-leader'
|
||||
});
|
||||
}
|
||||
|
||||
// Moderator
|
||||
if (this.permissions['set-moderator']) {
|
||||
const userRoleResult = this.roomRoles.find((r: any) => r.u._id === selectedUser._id);
|
||||
const isModerator = userRoleResult?.roles.includes('moderator');
|
||||
if (setModeratorPermission) {
|
||||
const isModerator = fetchRole('moderator', selectedUser, roomRoles);
|
||||
options.push({
|
||||
icon: 'shield',
|
||||
title: I18n.t('Moderator'),
|
||||
onPress: () => this.handleModerator(selectedUser, !isModerator),
|
||||
right: () => (
|
||||
<CustomIcon
|
||||
testID={isModerator ? 'action-sheet-set-moderator-checked' : 'action-sheet-set-moderator-unchecked'}
|
||||
name={isModerator ? 'checkbox-checked' : 'checkbox-unchecked'}
|
||||
size={20}
|
||||
color={isModerator ? themes[theme].tintActive : themes[theme].auxiliaryTintColor}
|
||||
/>
|
||||
),
|
||||
onPress: () =>
|
||||
handleModerator(selectedUser, !isModerator, room, getUserDisplayName(selectedUser), () =>
|
||||
fetchRoomMembersRoles(room.t as TRoomType, room.rid, updateState)
|
||||
),
|
||||
right: () => <RightIcon check={isModerator} label='moderator' />,
|
||||
testID: 'action-sheet-set-moderator'
|
||||
});
|
||||
}
|
||||
|
||||
// Remove from team
|
||||
if (this.permissions['edit-team-member']) {
|
||||
if (editTeamMemberPermission) {
|
||||
options.push({
|
||||
icon: 'logout',
|
||||
danger: true,
|
||||
title: I18n.t('Remove_from_Team'),
|
||||
onPress: () => this.handleRemoveFromTeam(selectedUser),
|
||||
onPress: () => handleRemoveFromTeam(selectedUser, updateState, room, members),
|
||||
testID: 'action-sheet-remove-from-team'
|
||||
});
|
||||
}
|
||||
|
||||
// Remove from room
|
||||
if (this.permissions['remove-user'] && !room.teamMain) {
|
||||
if (removeUserPermission && !room.teamMain) {
|
||||
options.push({
|
||||
icon: 'logout',
|
||||
title: I18n.t('Remove_from_room'),
|
||||
|
@ -422,7 +298,13 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
|||
showConfirmationAlert({
|
||||
message: I18n.t('The_user_will_be_removed_from_s', { s: getRoomTitle(room) }),
|
||||
confirmationText: I18n.t('Yes_remove_user'),
|
||||
onPress: () => this.handleRemoveUserFromRoom(selectedUser)
|
||||
onPress: () => {
|
||||
handleRemoveUserFromRoom(selectedUser, room, () =>
|
||||
updateState({
|
||||
members: members.filter(member => member._id !== selectedUser._id)
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
testID: 'action-sheet-remove-from-room'
|
||||
|
@ -435,256 +317,83 @@ class RoomMembersView extends React.Component<IRoomMembersViewProps, IRoomMember
|
|||
});
|
||||
};
|
||||
|
||||
toggleStatus = () => {
|
||||
try {
|
||||
const { allUsers } = this.state;
|
||||
this.setState({ members: [], allUsers: !allUsers, end: false, page: 0 }, () => {
|
||||
this.fetchMembers();
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
fetchRoomMembersRoles = async () => {
|
||||
try {
|
||||
const { room } = this.state;
|
||||
const type = room.t as SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL;
|
||||
const result = await Services.getRoomRoles(room.rid, type);
|
||||
if (result?.success) {
|
||||
this.roomRoles = result.roles;
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
fetchMembers = async () => {
|
||||
const { rid, members, isLoading, allUsers, end, room, filtering, page } = this.state;
|
||||
const fetchMembers = async (status: boolean) => {
|
||||
const { members, isLoading, end, room, filter, page } = state;
|
||||
const { t } = room;
|
||||
|
||||
if (isLoading || end) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ isLoading: true });
|
||||
updateState({ isLoading: true });
|
||||
try {
|
||||
const membersResult = await Services.getRoomMembers({
|
||||
rid,
|
||||
rid: room.rid,
|
||||
roomType: t,
|
||||
type: allUsers ? 'all' : 'online',
|
||||
filter: filtering,
|
||||
type: !status ? 'all' : 'online',
|
||||
filter,
|
||||
skip: PAGE_SIZE * page,
|
||||
limit: PAGE_SIZE,
|
||||
allUsers
|
||||
allUsers: !status
|
||||
});
|
||||
const end = membersResult?.length < PAGE_SIZE;
|
||||
const membersResultFiltered = membersResult?.filter((member: TUserModel) => !members.some(m => m._id === member._id));
|
||||
this.setState({
|
||||
members: members.concat(membersResultFiltered || []),
|
||||
updateState({
|
||||
members: [...members, ...membersResultFiltered],
|
||||
isLoading: false,
|
||||
end,
|
||||
page: page + 1
|
||||
});
|
||||
this.setHeader();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
this.setState({ isLoading: false });
|
||||
updateState({ isLoading: false });
|
||||
}
|
||||
};
|
||||
|
||||
goRoom = (item: TGoRoomItem) => {
|
||||
const { navigation, isMasterDetail } = this.props;
|
||||
if (isMasterDetail) {
|
||||
// @ts-ignore
|
||||
navigation.navigate('DrawerNavigator');
|
||||
} else {
|
||||
navigation.popToTop();
|
||||
}
|
||||
goRoom({ item, isMasterDetail });
|
||||
};
|
||||
const filteredMembers =
|
||||
state.members && state.members.length > 0 && state.filter
|
||||
? state.members.filter(
|
||||
m =>
|
||||
m.username.toLowerCase().match(state.filter.toLowerCase()) || m.name?.toLowerCase().match(state.filter.toLowerCase())
|
||||
)
|
||||
: null;
|
||||
|
||||
getUserDisplayName = (user: TUserModel) => {
|
||||
const { useRealName } = this.props;
|
||||
return (useRealName ? user.name : user.username) || user.username;
|
||||
};
|
||||
|
||||
handleMute = async (user: TUserModel) => {
|
||||
const { rid } = this.state;
|
||||
try {
|
||||
await Services.toggleMuteUserInRoom(rid, user?.username, !user?.muted);
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t('User_has_been_key', { key: user?.muted ? I18n.t('unmuted') : I18n.t('muted') })
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
handleOwner = async (selectedUser: TUserModel, isOwner: boolean) => {
|
||||
try {
|
||||
const { room } = this.state;
|
||||
await Services.toggleRoomOwner({
|
||||
roomId: room.rid,
|
||||
t: room.t,
|
||||
userId: selectedUser._id,
|
||||
isOwner
|
||||
});
|
||||
const message = isOwner
|
||||
? 'User__username__is_now_a_owner_of__room_name_'
|
||||
: 'User__username__removed_from__room_name__owners';
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t(message, {
|
||||
username: this.getUserDisplayName(selectedUser),
|
||||
room_name: getRoomTitle(room)
|
||||
})
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
this.fetchRoomMembersRoles();
|
||||
};
|
||||
|
||||
handleLeader = async (selectedUser: TUserModel, isLeader: boolean) => {
|
||||
try {
|
||||
const { room } = this.state;
|
||||
await Services.toggleRoomLeader({
|
||||
roomId: room.rid,
|
||||
t: room.t,
|
||||
userId: selectedUser._id,
|
||||
isLeader
|
||||
});
|
||||
const message = isLeader
|
||||
? 'User__username__is_now_a_leader_of__room_name_'
|
||||
: 'User__username__removed_from__room_name__leaders';
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t(message, {
|
||||
username: this.getUserDisplayName(selectedUser),
|
||||
room_name: getRoomTitle(room)
|
||||
})
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
this.fetchRoomMembersRoles();
|
||||
};
|
||||
|
||||
handleModerator = async (selectedUser: TUserModel, isModerator: boolean) => {
|
||||
try {
|
||||
const { room } = this.state;
|
||||
await Services.toggleRoomModerator({
|
||||
roomId: room.rid,
|
||||
t: room.t,
|
||||
userId: selectedUser._id,
|
||||
isModerator
|
||||
});
|
||||
const message = isModerator
|
||||
? 'User__username__is_now_a_moderator_of__room_name_'
|
||||
: 'User__username__removed_from__room_name__moderators';
|
||||
EventEmitter.emit(LISTENER, {
|
||||
message: I18n.t(message, {
|
||||
username: this.getUserDisplayName(selectedUser),
|
||||
room_name: getRoomTitle(room)
|
||||
})
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
this.fetchRoomMembersRoles();
|
||||
};
|
||||
|
||||
handleIgnore = async (selectedUser: TUserModel, ignore: boolean) => {
|
||||
try {
|
||||
const { room } = this.state;
|
||||
await Services.ignoreUser({
|
||||
rid: room.rid,
|
||||
userId: selectedUser._id,
|
||||
ignore
|
||||
});
|
||||
const message = I18n.t(ignore ? 'User_has_been_ignored' : 'User_has_been_unignored');
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
handleRemoveUserFromRoom = async (selectedUser: TUserModel) => {
|
||||
try {
|
||||
const { room, members, membersFiltered } = this.state;
|
||||
const userId = selectedUser._id;
|
||||
// TODO: interface SubscriptionType on IRoom is wrong
|
||||
await Services.removeUserFromRoom({ roomId: room.rid, t: room.t as RoomTypes, userId });
|
||||
const message = I18n.t('User_has_been_removed_from_s', { s: getRoomTitle(room) });
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
this.setState({
|
||||
members: members.filter(member => member._id !== userId),
|
||||
membersFiltered: this.isServerVersionLowerThan3_16 ? membersFiltered.filter(member => member._id !== userId) : []
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
renderSearchBar = () => <SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='room-members-view-search' />;
|
||||
|
||||
renderItem = ({ item }: { item: TUserModel }) => {
|
||||
const { theme } = this.props;
|
||||
|
||||
return (
|
||||
<UserItem
|
||||
name={item.name as string}
|
||||
username={item.username}
|
||||
onPress={() => this.onPressUser(item)}
|
||||
testID={`room-members-view-item-${item.username}`}
|
||||
theme={theme}
|
||||
return (
|
||||
<SafeAreaView testID='room-members-view'>
|
||||
<StatusBar />
|
||||
<FlatList
|
||||
data={filteredMembers || state.members}
|
||||
renderItem={({ item }) => (
|
||||
<View style={{ backgroundColor: colors.backgroundColor }}>
|
||||
<UserItem
|
||||
name={item.name as string}
|
||||
username={item.username}
|
||||
onPress={() => onPressUser(item)}
|
||||
testID={`room-members-view-item-${item.username}`}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
ListHeaderComponent={
|
||||
<>
|
||||
<ActionsSection joined={params.joined as boolean} rid={state.room.rid} t={state.room.t} />
|
||||
<View style={{ backgroundColor: colors.backgroundColor }}>
|
||||
<SearchBox onChangeText={text => updateState({ filter: text.trim() })} testID='room-members-view-search' />
|
||||
</View>
|
||||
</>
|
||||
}
|
||||
ListFooterComponent={() => (state.isLoading ? <ActivityIndicator /> : null)}
|
||||
onEndReachedThreshold={0.1}
|
||||
onEndReached={() => fetchMembers(state.allUsers)}
|
||||
ListEmptyComponent={() =>
|
||||
state.end ? <Text style={[styles.noResult, { color: colors.titleText }]}>{I18n.t('No_members_found')}</Text> : null
|
||||
}
|
||||
{...scrollPersistTaps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { filtering, members, membersFiltered, isLoading } = this.state;
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<SafeAreaView testID='room-members-view'>
|
||||
<StatusBar />
|
||||
<FlatList
|
||||
data={!!filtering && this.isServerVersionLowerThan3_16 ? membersFiltered : members}
|
||||
renderItem={this.renderItem}
|
||||
style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
keyExtractor={item => item._id}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
ListHeaderComponent={this.renderSearchBar}
|
||||
ListFooterComponent={() => {
|
||||
if (isLoading) {
|
||||
return <ActivityIndicator />;
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
onEndReachedThreshold={0.1}
|
||||
onEndReached={this.fetchMembers}
|
||||
maxToRenderPerBatch={5}
|
||||
windowSize={10}
|
||||
{...scrollPersistTaps}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: IApplicationState) => ({
|
||||
baseUrl: state.server.server,
|
||||
user: getUserSelector(state),
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
useRealName: state.settings.UI_Use_Real_Name,
|
||||
muteUserPermission: state.permissions['mute-user'],
|
||||
setLeaderPermission: state.permissions['set-leader'],
|
||||
setOwnerPermission: state.permissions['set-owner'],
|
||||
setModeratorPermission: state.permissions['set-moderator'],
|
||||
removeUserPermission: state.permissions['remove-user'],
|
||||
editTeamMemberPermission: state.permissions['edit-team-member'],
|
||||
viewAllTeamChannelsPermission: state.permissions['view-all-team-channels'],
|
||||
viewAllTeamsPermission: state.permissions['view-all-teams'],
|
||||
serverVersion: state.server.version
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withTheme(withActionSheet(RoomMembersView)));
|
||||
export default RoomMembersView;
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import sharedStyles from '../Styles';
|
||||
|
||||
export default StyleSheet.create({
|
||||
list: {
|
||||
flex: 1
|
||||
},
|
||||
item: {
|
||||
flexDirection: 'row',
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 16,
|
||||
alignItems: 'center'
|
||||
},
|
||||
avatar: {
|
||||
marginRight: 16
|
||||
},
|
||||
separator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
marginLeft: 60
|
||||
noResult: {
|
||||
fontSize: 16,
|
||||
paddingVertical: 56,
|
||||
...sharedStyles.textSemibold,
|
||||
...sharedStyles.textAlignCenter
|
||||
}
|
||||
});
|
||||
|
|
|
@ -813,6 +813,10 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
|||
};
|
||||
|
||||
onReplyInit = (message: TAnyMessageModel, mention: boolean) => {
|
||||
// If there's a thread already, we redirect to it
|
||||
if (mention && !!message.tlm) {
|
||||
return this.onThreadPress(message);
|
||||
}
|
||||
this.setState({
|
||||
selectedMessage: message,
|
||||
replying: true,
|
||||
|
@ -833,6 +837,10 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
|||
};
|
||||
|
||||
onMessageLongPress = (message: TAnyMessageModel) => {
|
||||
// if it's a thread message on main room, we disable the long press
|
||||
if (message.tmid && !this.tmid) {
|
||||
return;
|
||||
}
|
||||
this.messagebox?.current?.closeEmojiAndAction(this.messageActions?.showMessageActions, message);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,296 +0,0 @@
|
|||
import { Q } from '@nozbe/watermelondb';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import React from 'react';
|
||||
import { FlatList, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { addUser, removeUser, reset } from '../actions/selectedUsers';
|
||||
import { themes } from '../lib/constants';
|
||||
import * as HeaderButton from '../containers/HeaderButton';
|
||||
import * as List from '../containers/List';
|
||||
import { sendLoadingEvent } from '../containers/Loading';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import SearchBox from '../containers/SearchBox';
|
||||
import StatusBar from '../containers/StatusBar';
|
||||
import { IApplicationState, IBaseScreen, ISearch, ISearchLocal, IUser } from '../definitions';
|
||||
import I18n from '../i18n';
|
||||
import database from '../lib/database';
|
||||
import UserItem from '../containers/UserItem';
|
||||
import { ISelectedUser } from '../reducers/selectedUsers';
|
||||
import { getUserSelector } from '../selectors/login';
|
||||
import { ChatsStackParamList } from '../stacks/types';
|
||||
import { withTheme } from '../theme';
|
||||
import { showErrorAlert } from '../lib/methods/helpers/info';
|
||||
import log, { events, logEvent } from '../lib/methods/helpers/log';
|
||||
import sharedStyles from './Styles';
|
||||
import { search } from '../lib/methods';
|
||||
import { isGroupChat } from '../lib/methods/helpers';
|
||||
|
||||
const ITEM_WIDTH = 250;
|
||||
const getItemLayout = (_: any, index: number) => ({ length: ITEM_WIDTH, offset: ITEM_WIDTH * index, index });
|
||||
|
||||
interface ISelectedUsersViewState {
|
||||
maxUsers?: number;
|
||||
search: (ISearch | ISearchLocal)[];
|
||||
chats: ISelectedUser[];
|
||||
}
|
||||
|
||||
interface ISelectedUsersViewProps extends IBaseScreen<ChatsStackParamList, 'SelectedUsersView'> {
|
||||
users: ISelectedUser[];
|
||||
loading: boolean;
|
||||
user: IUser;
|
||||
baseUrl: string;
|
||||
}
|
||||
|
||||
class SelectedUsersView extends React.Component<ISelectedUsersViewProps, ISelectedUsersViewState> {
|
||||
private flatlist?: FlatList;
|
||||
|
||||
private querySubscription?: Subscription;
|
||||
|
||||
constructor(props: ISelectedUsersViewProps) {
|
||||
super(props);
|
||||
this.init();
|
||||
const maxUsers = props.route.params?.maxUsers;
|
||||
this.state = {
|
||||
maxUsers,
|
||||
search: [],
|
||||
chats: []
|
||||
};
|
||||
const { user, dispatch } = this.props;
|
||||
if (this.isGroupChat()) {
|
||||
dispatch(addUser({ _id: user.id, name: user.username, fname: user.name as string }));
|
||||
}
|
||||
this.setHeader(props.route.params?.showButton);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ISelectedUsersViewProps) {
|
||||
const { users, loading } = this.props;
|
||||
if (this.isGroupChat()) {
|
||||
if (prevProps.users.length !== users.length) {
|
||||
this.setHeader(users.length > 0);
|
||||
}
|
||||
}
|
||||
if (loading !== prevProps.loading) {
|
||||
sendLoadingEvent({ visible: loading });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(reset());
|
||||
if (this.querySubscription && this.querySubscription.unsubscribe) {
|
||||
this.querySubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
// showButton can be sent as route params or updated by the component
|
||||
setHeader = (showButton?: boolean) => {
|
||||
const { navigation, route } = this.props;
|
||||
const title = route.params?.title ?? I18n.t('Select_Users');
|
||||
const buttonText = route.params?.buttonText ?? I18n.t('Next');
|
||||
const maxUsers = route.params?.maxUsers;
|
||||
const nextAction = route.params?.nextAction ?? (() => {});
|
||||
const options = {
|
||||
title,
|
||||
headerRight: () =>
|
||||
(!maxUsers || showButton) && (
|
||||
<HeaderButton.Container>
|
||||
<HeaderButton.Item title={buttonText} onPress={nextAction} testID='selected-users-view-submit' />
|
||||
</HeaderButton.Container>
|
||||
)
|
||||
};
|
||||
navigation.setOptions(options);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
init = async () => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const observable = await db.get('subscriptions').query(Q.where('t', 'd')).observeWithColumns(['room_updated_at']);
|
||||
|
||||
this.querySubscription = observable.subscribe(data => {
|
||||
const chats = orderBy(data, ['roomUpdatedAt'], ['desc']) as ISelectedUser[];
|
||||
this.setState({ chats });
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
onSearchChangeText(text: string) {
|
||||
this.handleSearch(text);
|
||||
}
|
||||
|
||||
handleSearch = async (text: string) => {
|
||||
const result = await search({ text, filterRooms: false });
|
||||
this.setState({
|
||||
search: result
|
||||
});
|
||||
};
|
||||
|
||||
isGroupChat = () => {
|
||||
const { maxUsers } = this.state;
|
||||
return maxUsers && maxUsers > 2;
|
||||
};
|
||||
|
||||
isChecked = (username: string) => {
|
||||
const { users } = this.props;
|
||||
return users.findIndex(el => el.name === username) !== -1;
|
||||
};
|
||||
|
||||
toggleUser = (user: ISelectedUser) => {
|
||||
const { maxUsers } = this.state;
|
||||
const {
|
||||
dispatch,
|
||||
users,
|
||||
user: { username }
|
||||
} = this.props;
|
||||
|
||||
// Disallow removing self user from the direct message group
|
||||
if (this.isGroupChat() && username === user.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isChecked(user.name)) {
|
||||
if (this.isGroupChat() && users.length === maxUsers) {
|
||||
return showErrorAlert(I18n.t('Max_number_of_users_allowed_is_number', { maxUsers }), I18n.t('Oops'));
|
||||
}
|
||||
logEvent(events.SELECTED_USERS_ADD_USER);
|
||||
dispatch(addUser(user));
|
||||
} else {
|
||||
logEvent(events.SELECTED_USERS_REMOVE_USER);
|
||||
dispatch(removeUser(user));
|
||||
}
|
||||
};
|
||||
|
||||
_onPressItem = (id: string, item = {} as ISelectedUser) => {
|
||||
if (item.search) {
|
||||
this.toggleUser({ _id: item._id, name: item.username as string, fname: item.name });
|
||||
} else {
|
||||
this.toggleUser({ _id: item._id, name: item.name, fname: item.fname });
|
||||
}
|
||||
};
|
||||
|
||||
_onPressSelectedItem = (item: ISelectedUser) => this.toggleUser(item);
|
||||
|
||||
renderHeader = () => {
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<View style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
<SearchBox onChangeText={(text: string) => this.onSearchChangeText(text)} testID='select-users-view-search' />
|
||||
{this.renderSelected()}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
setFlatListRef = (ref: FlatList) => (this.flatlist = ref);
|
||||
|
||||
onContentSizeChange = () => this.flatlist?.scrollToEnd({ animated: true });
|
||||
|
||||
renderSelected = () => {
|
||||
const { users, theme } = this.props;
|
||||
|
||||
if (users.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={users}
|
||||
ref={this.setFlatListRef}
|
||||
onContentSizeChange={this.onContentSizeChange}
|
||||
getItemLayout={getItemLayout}
|
||||
keyExtractor={item => item._id}
|
||||
style={[sharedStyles.separatorTop, { borderColor: themes[theme].separatorColor }]}
|
||||
contentContainerStyle={{ marginVertical: 5 }}
|
||||
renderItem={this.renderSelectedItem}
|
||||
keyboardShouldPersistTaps='always'
|
||||
horizontal
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderSelectedItem = ({ item }: { item: ISelectedUser }) => {
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<UserItem
|
||||
name={item.fname}
|
||||
username={item.name}
|
||||
onPress={() => this._onPressSelectedItem(item)}
|
||||
testID={`selected-user-${item.name}`}
|
||||
style={{ paddingRight: 15 }}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderItem = ({ item, index }: { item: ISelectedUser; index: number }) => {
|
||||
const { search, chats } = this.state;
|
||||
const { theme } = this.props;
|
||||
|
||||
const name = item.search ? item.name : item.fname;
|
||||
const username = item.search ? (item.username as string) : item.name;
|
||||
let style = { borderColor: themes[theme].separatorColor };
|
||||
if (index === 0) {
|
||||
style = { ...style, ...sharedStyles.separatorTop };
|
||||
}
|
||||
if (search.length > 0 && index === search.length - 1) {
|
||||
style = { ...style, ...sharedStyles.separatorBottom };
|
||||
}
|
||||
if (search.length === 0 && index === chats.length - 1) {
|
||||
style = { ...style, ...sharedStyles.separatorBottom };
|
||||
}
|
||||
return (
|
||||
<UserItem
|
||||
name={name}
|
||||
username={username}
|
||||
onPress={() => this._onPressItem(item._id, item)}
|
||||
testID={`select-users-view-item-${item.name}`}
|
||||
icon={this.isChecked(username) ? 'check' : null}
|
||||
style={style}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderList = () => {
|
||||
const { search, chats } = this.state;
|
||||
const { theme } = this.props;
|
||||
|
||||
const searchOrChats = (search.length > 0 ? search : chats) as ISelectedUser[];
|
||||
// filter DM between multiple users
|
||||
const data = searchOrChats.filter(sub => !isGroupChat(sub));
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={data}
|
||||
extraData={this.props}
|
||||
keyExtractor={item => item._id}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
ListHeaderComponent={this.renderHeader}
|
||||
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeAreaView testID='select-users-view'>
|
||||
<StatusBar />
|
||||
{this.renderList()}
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: IApplicationState) => ({
|
||||
baseUrl: state.server.server,
|
||||
users: state.selectedUsers.users,
|
||||
loading: state.selectedUsers.loading,
|
||||
user: getUserSelector(state)
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withTheme(SelectedUsersView));
|
|
@ -0,0 +1,74 @@
|
|||
import React, { useRef } from 'react';
|
||||
import { FlatList, View, Text, StyleSheet } from 'react-native';
|
||||
|
||||
import { themes } from '../../lib/constants';
|
||||
import SearchBox from '../../containers/SearchBox';
|
||||
import I18n from '../../i18n';
|
||||
import { ISelectedUser } from '../../reducers/selectedUsers';
|
||||
import { useTheme } from '../../theme';
|
||||
import sharedStyles from '../Styles';
|
||||
import { useAppSelector } from '../../lib/hooks';
|
||||
import Chip from '../../containers/Chip';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
selectedText: {
|
||||
marginLeft: 16,
|
||||
marginBottom: 12,
|
||||
fontSize: 12,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
contentContainerList: {
|
||||
paddingHorizontal: 16,
|
||||
marginBottom: 16
|
||||
}
|
||||
});
|
||||
|
||||
const Header = ({
|
||||
onChangeText,
|
||||
useRealName,
|
||||
onPressItem
|
||||
}: {
|
||||
useRealName: boolean;
|
||||
onChangeText: (text: string) => void;
|
||||
onPressItem: (userItem: ISelectedUser) => void;
|
||||
}) => {
|
||||
const flatlist = useRef<FlatList>();
|
||||
const { theme } = useTheme();
|
||||
const { users } = useAppSelector(state => ({
|
||||
users: state.selectedUsers.users
|
||||
}));
|
||||
|
||||
const onContentSizeChange = () => flatlist?.current?.scrollToEnd({ animated: true });
|
||||
|
||||
return (
|
||||
<View style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
<SearchBox onChangeText={(text: string) => onChangeText(text)} testID='select-users-view-search' />
|
||||
{users.length === 0 ? null : (
|
||||
<View>
|
||||
<Text style={[styles.selectedText, { color: themes[theme].auxiliaryTintColor }]}>
|
||||
{I18n.t('N_Selected_members', { n: users.length })}
|
||||
</Text>
|
||||
<FlatList
|
||||
data={users}
|
||||
ref={(ref: FlatList) => (flatlist.current = ref)}
|
||||
onContentSizeChange={onContentSizeChange}
|
||||
keyExtractor={item => item._id}
|
||||
renderItem={({ item }) => {
|
||||
const name = useRealName && item.fname ? item.fname : item.name;
|
||||
const username = item.search ? (item.username as string) : item.name;
|
||||
|
||||
return (
|
||||
<Chip text={name} avatar={username} onPress={() => onPressItem(item)} testID={`selected-user-${item.name}`} />
|
||||
);
|
||||
}}
|
||||
keyboardShouldPersistTaps='always'
|
||||
contentContainerStyle={styles.contentContainerList}
|
||||
horizontal
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue