From 08059e4e1819e5ac2c13abe63115d4c67b809269 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 21 Dec 2020 21:45:10 -0300 Subject: [PATCH] Rewrite: Room Header (#19808) --- app/apps/client/gameCenter/gameCenter.js | 1 + app/apps/client/gameCenter/tabBar.js | 31 - app/apps/client/gameCenter/tabBar.ts | 28 + app/autotranslate/client/lib/tabBar.js | 28 - app/autotranslate/client/lib/tabBar.ts | 19 + app/channel-settings/client/startup/tabBar.js | 16 - app/channel-settings/client/startup/tabBar.ts | 14 + app/discussion/client/index.js | 1 - app/discussion/client/tabBar.js | 17 - app/discussion/client/tabBar.ts | 18 + .../client/views/DiscussionTabbar.js | 11 - app/e2e/client/tabbar.js | 37 - app/e2e/client/tabbar.ts | 28 + app/lib/client/defaultTabBars.js | 92 -- app/lib/client/index.js | 1 - app/livechat/client/externalFrame/tabBar.js | 23 - app/livechat/client/externalFrame/tabBar.ts | 18 + .../client/hooks/onCreateRoomTabBar.js | 29 - app/livechat/client/index.js | 2 +- app/livechat/client/tabBar.ts | 19 + app/livechat/client/ui.js | 26 +- .../views/app/tabbar/contactChatHistory.js | 5 - app/livechat/lib/LivechatRoomType.js | 28 +- app/livestream/client/tabBar.js | 30 - app/livestream/client/tabBar.tsx | 29 + app/mentions-flextab/client/actionButton.js | 4 +- app/mentions-flextab/client/tabBar.js | 14 - app/mentions-flextab/client/tabBar.ts | 10 + .../client/views/mentionsFlexTab.js | 2 + app/message-pin/client/tabBar.js | 22 - app/message-pin/client/tabBar.ts | 16 + .../client/views/pinnedMessages.js | 4 + app/message-snippet/client/router.js | 12 +- app/message-snippet/client/tabBar/tabBar.js | 22 - app/message-snippet/client/tabBar/tabBar.ts | 16 + app/message-star/client/tabBar.js | 14 - app/message-star/client/tabBar.ts | 10 + .../client/views/starredMessages.js | 3 + app/otr/client/rocketchat.otr.js | 1 + app/otr/client/tabBar.js | 27 - app/otr/client/tabBar.ts | 31 + app/push-notifications/client/tabBar.js | 15 - app/push-notifications/client/tabBar.ts | 12 + app/reactions/client/init.js | 12 +- app/search/client/provider/result.js | 2 + app/search/client/style/style.css | 4 - .../imports/components/contextual-bar.css | 2 +- .../client/imports/components/header.css | 268 ---- app/theme/client/imports/general/base.css | 33 - app/theme/client/imports/general/base_old.css | 270 ---- app/theme/client/imports/general/rtl.css | 12 - .../client/imports/general/theme_old.css | 29 - .../client/imports/general/variables.css | 4 - .../client/components/ThreadComponent.tsx | 17 +- .../client/components/ThreadSkeleton.tsx | 4 +- app/threads/client/components/ThreadView.tsx | 23 +- app/threads/client/flextab/thread.js | 2 + app/threads/client/flextab/threadlist.js | 37 - app/threads/client/flextab/threadlist.tsx | 40 + app/threads/client/flextab/threads.html | 3 - app/threads/client/flextab/threads.js | 16 - .../client/messageAction/replyInThread.js | 4 +- app/threads/client/threads.css | 3 + app/ui-clean-history/client/lib/startup.js | 18 - app/ui-clean-history/client/lib/startup.ts | 18 + app/ui-flextab/README.md | 0 app/ui-flextab/client/flexTabBar.html | 101 -- app/ui-flextab/client/flexTabBar.js | 260 ---- app/ui-flextab/client/index.js | 3 - app/ui-flextab/index.js | 1 - app/ui-sidenav/client/roomList.js | 8 +- app/ui-utils/client/index.js | 2 - app/ui-utils/client/lib/Layout.js | 2 +- app/ui-utils/client/lib/MessageAction.js | 12 +- .../client/lib/ReactionListContent.js | 44 +- app/ui-utils/client/lib/RocketChatTabBar.js | 86 -- app/ui-utils/client/lib/TabBar.js | 104 -- app/ui-utils/client/lib/popover.js | 59 +- app/ui/client/components/contextualBar.html | 28 - app/ui/client/components/contextualBar.js | 31 - app/ui/client/components/header/header.js | 4 +- .../client/components/header/headerRoom.html | 75 - app/ui/client/components/header/headerRoom.js | 251 ---- app/ui/client/index.js | 3 - app/ui/client/lib/iframeCommands.js | 13 +- .../views/app/lib/getCommonRoomEvents.js | 317 +++++ app/ui/client/views/app/room.html | 17 - app/ui/client/views/app/room.js | 1233 ++++++----------- app/videobridge/client/tabBar.js | 90 -- app/videobridge/client/tabBar.tsx | 71 + app/videobridge/client/views/videoFlexTab.js | 10 +- .../federationDashboard/OverviewSection.js | 41 + client/components/Header/Header.stories.js | 153 ++ client/components/Header/Header.tsx | 69 + client/components/Header/index.js | 3 + .../components/ScrollableContentWrapper.tsx | 9 +- .../admin/users => components}/Skeleton.js | 0 client/components/VerticalBar.js | 78 +- client/contexts/AvatarUrlContext.ts | 2 +- client/contexts/LayoutContext.ts | 32 + client/hooks/useRoomIcon.tsx | 34 + client/importPackages.js | 1 - client/providers/LayoutProvider.tsx | 37 + client/providers/MeteorProvider.js | 29 +- .../startup/contextualBar/exportMessages.js | 19 - .../startup/contextualBar/exportMessages.ts | 18 + client/types/fuselage.d.ts | 58 +- .../admin/customEmoji/CustomEmojiRoute.js | 2 +- .../admin/customSounds/AdminSoundsRoute.js | 2 +- .../customUserStatus/CustomUserStatusRoute.js | 2 +- .../permissions/PermissionsContextBar.js | 2 +- client/views/admin/rooms/RoomsPage.js | 2 +- client/views/admin/users/EditUser.js | 2 +- client/views/admin/users/UserInfo.js | 2 +- client/views/admin/users/UsersPage.js | 2 +- client/views/omnichannel/agents/AgentEdit.js | 2 +- client/views/omnichannel/agents/AgentInfo.js | 2 +- .../views/omnichannel/agents/AgentsRoute.js | 2 +- client/views/omnichannel/agents/Skeleton.js | 11 - .../omnichannel/departments/DepartmentEdit.js | 2 +- .../views/omnichannel/departments/Skeleton.js | 11 - .../omnichannel/triggers/TriggersPage.js | 2 +- client/views/room/Header/Burger.js | 15 + client/views/room/Header/Header.js | 60 + client/views/room/Header/ToolBox/ToolBox.tsx | 94 ++ client/views/room/Header/ToolBox/index.js | 4 + client/views/room/Header/icons/Encrypted.js | 21 + client/views/room/Header/icons/Favorite.js | 24 + client/views/room/Header/icons/Translate.js | 16 + client/views/room/Header/index.js | 3 + client/views/room/MemberListRouter.js | 25 + .../NotificationPreferences.js | 143 ++ .../NotificationsPreferences.stories.js | 68 + .../components/NotificationByDevice.js | 13 + .../components/NotificationToogle.js | 17 + .../components/Preferences.js | 14 + .../room/NotificationPreferences/index.js | 3 + client/views/room/Room.stories.js | 18 +- client/views/room/adapters.js | 23 + client/views/room/components/BlazeTemplate.js | 28 + client/views/room/components/RoomTemplate.js | 39 + .../room/components/VerticalBarOldActions.js | 22 + .../views/room/contexts/OpenedRoomContext.js | 0 client/views/room/contexts/RoomContext.ts | 13 + .../AutoTranslate/AutoTranslate.js | 20 +- .../AutoTranslate/AutoTranslate.stories.js | 4 +- .../Discussions/components/Message.js | 2 +- .../room/contextualBar/Discussions/index.js | 7 +- .../contextualBar/ExportMessages/index.js | 41 +- .../Info/EditRoomInfo/EditRoomInfo.js | 10 +- .../contextualBar/Info/RoomInfo/RoomInfo.js | 8 +- client/views/room/contextualBar/Info/index.js | 11 + .../room/contextualBar/RoomFiles/RoomFiles.js | 44 +- .../RoomMembers/AddUsers/AddUsers.js | 4 +- .../RoomMembers/EditInvite/EditInvite.js | 4 +- .../RoomMembers/InviteUsers/InviteUsers.js | 7 +- .../RoomMembers/List/RoomMembers.js | 50 +- .../Threads/components/Message.js | 2 +- .../views/room/contextualBar/Threads/index.js | 59 +- .../room/contextualBar/UserInfo/index.js | 8 +- client/views/room/index.js | 87 +- .../views/room/lib/Toolbox/IframeButtons.tsx | 10 + .../views/room/lib/Toolbox/ToolboxContext.tsx | 25 + .../views/room/lib/Toolbox/defaultActions.ts | 63 + client/views/room/lib/Toolbox/generator.tsx | 38 + client/views/room/lib/Toolbox/index.tsx | 41 + client/views/room/providers/RoomProvider.tsx | 41 + .../views/room/providers/ToolboxProvider.tsx | 133 ++ definition/IRoom.ts | 20 +- ee/app/canned-responses/client/index.js | 2 +- .../client/stylesheets/cannedResponses.css | 4 - ee/app/canned-responses/client/tabBar.ts | 19 + ee/app/canned-responses/client/ui.js | 28 - .../omnichannel/priorities/EditPriority.js | 2 +- .../omnichannel/priorities/PrioritiesRoute.js | 2 +- ee/client/omnichannel/priorities/Skeleton.js | 11 - ee/client/omnichannel/tags/EditTag.js | 3 +- ee/client/omnichannel/tags/Skeleton.js | 11 - ee/client/omnichannel/tags/TagsRoute.js | 2 +- ee/client/omnichannel/units/EditUnit.js | 2 +- ee/client/omnichannel/units/Skeleton.js | 11 - ee/client/omnichannel/units/UnitsRoute.js | 2 +- package-lock.json | 96 +- package.json | 18 +- packages/rocketchat-i18n/i18n/en.i18n.json | 3 + 185 files changed, 3049 insertions(+), 3632 deletions(-) delete mode 100644 app/apps/client/gameCenter/tabBar.js create mode 100644 app/apps/client/gameCenter/tabBar.ts delete mode 100644 app/autotranslate/client/lib/tabBar.js create mode 100644 app/autotranslate/client/lib/tabBar.ts delete mode 100644 app/channel-settings/client/startup/tabBar.js create mode 100644 app/channel-settings/client/startup/tabBar.ts delete mode 100644 app/discussion/client/tabBar.js create mode 100644 app/discussion/client/tabBar.ts delete mode 100644 app/discussion/client/views/DiscussionTabbar.js delete mode 100644 app/e2e/client/tabbar.js create mode 100644 app/e2e/client/tabbar.ts delete mode 100644 app/lib/client/defaultTabBars.js delete mode 100644 app/livechat/client/externalFrame/tabBar.js create mode 100644 app/livechat/client/externalFrame/tabBar.ts delete mode 100644 app/livechat/client/hooks/onCreateRoomTabBar.js create mode 100644 app/livechat/client/tabBar.ts delete mode 100644 app/livestream/client/tabBar.js create mode 100644 app/livestream/client/tabBar.tsx delete mode 100644 app/mentions-flextab/client/tabBar.js create mode 100644 app/mentions-flextab/client/tabBar.ts delete mode 100644 app/message-pin/client/tabBar.js create mode 100644 app/message-pin/client/tabBar.ts delete mode 100644 app/message-snippet/client/tabBar/tabBar.js create mode 100644 app/message-snippet/client/tabBar/tabBar.ts delete mode 100644 app/message-star/client/tabBar.js create mode 100644 app/message-star/client/tabBar.ts delete mode 100644 app/otr/client/tabBar.js create mode 100644 app/otr/client/tabBar.ts delete mode 100644 app/push-notifications/client/tabBar.js create mode 100644 app/push-notifications/client/tabBar.ts delete mode 100644 app/threads/client/flextab/threadlist.js create mode 100644 app/threads/client/flextab/threadlist.tsx delete mode 100644 app/threads/client/flextab/threads.html delete mode 100644 app/ui-clean-history/client/lib/startup.js create mode 100644 app/ui-clean-history/client/lib/startup.ts delete mode 100644 app/ui-flextab/README.md delete mode 100644 app/ui-flextab/client/flexTabBar.html delete mode 100644 app/ui-flextab/client/flexTabBar.js delete mode 100644 app/ui-flextab/client/index.js delete mode 100644 app/ui-flextab/index.js delete mode 100644 app/ui-utils/client/lib/RocketChatTabBar.js delete mode 100644 app/ui-utils/client/lib/TabBar.js delete mode 100644 app/ui/client/components/contextualBar.html delete mode 100644 app/ui/client/components/contextualBar.js delete mode 100644 app/ui/client/components/header/headerRoom.html delete mode 100644 app/ui/client/components/header/headerRoom.js create mode 100644 app/ui/client/views/app/lib/getCommonRoomEvents.js delete mode 100644 app/videobridge/client/tabBar.js create mode 100644 app/videobridge/client/tabBar.tsx create mode 100644 client/admin/federationDashboard/OverviewSection.js create mode 100644 client/components/Header/Header.stories.js create mode 100644 client/components/Header/Header.tsx create mode 100644 client/components/Header/index.js rename client/{views/admin/users => components}/Skeleton.js (100%) create mode 100644 client/contexts/LayoutContext.ts create mode 100644 client/hooks/useRoomIcon.tsx create mode 100644 client/providers/LayoutProvider.tsx delete mode 100644 client/startup/contextualBar/exportMessages.js create mode 100644 client/startup/contextualBar/exportMessages.ts delete mode 100644 client/views/omnichannel/agents/Skeleton.js delete mode 100644 client/views/omnichannel/departments/Skeleton.js create mode 100644 client/views/room/Header/Burger.js create mode 100644 client/views/room/Header/Header.js create mode 100644 client/views/room/Header/ToolBox/ToolBox.tsx create mode 100644 client/views/room/Header/ToolBox/index.js create mode 100644 client/views/room/Header/icons/Encrypted.js create mode 100644 client/views/room/Header/icons/Favorite.js create mode 100644 client/views/room/Header/icons/Translate.js create mode 100644 client/views/room/Header/index.js create mode 100644 client/views/room/MemberListRouter.js create mode 100644 client/views/room/NotificationPreferences/NotificationPreferences.js create mode 100644 client/views/room/NotificationPreferences/NotificationsPreferences.stories.js create mode 100644 client/views/room/NotificationPreferences/components/NotificationByDevice.js create mode 100644 client/views/room/NotificationPreferences/components/NotificationToogle.js create mode 100644 client/views/room/NotificationPreferences/components/Preferences.js create mode 100644 client/views/room/NotificationPreferences/index.js create mode 100644 client/views/room/components/BlazeTemplate.js create mode 100644 client/views/room/components/RoomTemplate.js create mode 100644 client/views/room/components/VerticalBarOldActions.js delete mode 100644 client/views/room/contexts/OpenedRoomContext.js create mode 100644 client/views/room/contexts/RoomContext.ts create mode 100644 client/views/room/contextualBar/Info/index.js create mode 100644 client/views/room/lib/Toolbox/IframeButtons.tsx create mode 100644 client/views/room/lib/Toolbox/ToolboxContext.tsx create mode 100644 client/views/room/lib/Toolbox/defaultActions.ts create mode 100644 client/views/room/lib/Toolbox/generator.tsx create mode 100644 client/views/room/lib/Toolbox/index.tsx create mode 100644 client/views/room/providers/RoomProvider.tsx create mode 100644 client/views/room/providers/ToolboxProvider.tsx create mode 100644 ee/app/canned-responses/client/tabBar.ts delete mode 100644 ee/app/canned-responses/client/ui.js delete mode 100644 ee/client/omnichannel/priorities/Skeleton.js delete mode 100644 ee/client/omnichannel/tags/Skeleton.js delete mode 100644 ee/client/omnichannel/units/Skeleton.js diff --git a/app/apps/client/gameCenter/gameCenter.js b/app/apps/client/gameCenter/gameCenter.js index c7eef85de98..8ffee03cd9c 100644 --- a/app/apps/client/gameCenter/gameCenter.js +++ b/app/apps/client/gameCenter/gameCenter.js @@ -3,6 +3,7 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { modal } from '../../../ui-utils/client'; import { APIClient, t, handleError } from '../../../utils/client'; +import './gameCenter.html'; const getExternalComponents = async (instance) => { try { diff --git a/app/apps/client/gameCenter/tabBar.js b/app/apps/client/gameCenter/tabBar.js deleted file mode 100644 index fe5883119c9..00000000000 --- a/app/apps/client/gameCenter/tabBar.js +++ /dev/null @@ -1,31 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { APIClient } from '../../../utils/client'; -import { TabBar } from '../../../ui-utils/client'; -import { settings } from '../../../settings/client'; - -import './gameCenter.html'; - -Meteor.startup(function() { - Tracker.autorun(async function() { - if (!settings.get('Apps_Game_Center_enabled')) { - return TabBar.removeButton('gameCenter'); - } - - const { externalComponents } = await APIClient.get('apps/externalComponents'); - - if (!externalComponents.length) { - return TabBar.removeButton('gameCenter'); - } - - TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'gameCenter', - i18nTitle: 'Apps_Game_Center', - icon: 'game', - template: 'GameCenter', - order: -1, - }); - }); -}); diff --git a/app/apps/client/gameCenter/tabBar.ts b/app/apps/client/gameCenter/tabBar.ts new file mode 100644 index 00000000000..e7b452c66eb --- /dev/null +++ b/app/apps/client/gameCenter/tabBar.ts @@ -0,0 +1,28 @@ +import { useMemo } from 'react'; + +import { useSetting } from '../../../../client/contexts/SettingsContext'; +import { addAction } from '../../../../client/views/room/lib/Toolbox'; +import { useEndpointData } from '../../../../client/hooks/useEndpointData'; +import { AsyncStatePhase } from '../../../../client/hooks/useAsyncState'; + +addAction('game-center', () => { + const enabled = useSetting('Apps_Game_Center_enabled'); + + const { value = { externalComponents: [] }, phase: state, error } = useEndpointData('/apps/externalComponents'); + + const hasExternalComponents = value && value.externalComponents.length > 0; + const hasError = !!error; + return useMemo(() => + (enabled + && state === AsyncStatePhase.LOADING + && !hasError + && hasExternalComponents + ? { + groups: ['channel', 'group', 'direct'], + id: 'game-center', + title: 'Apps_Game_Center', + icon: 'game', + template: 'GameCenter', + order: -1, + } : null), [enabled, hasError, hasExternalComponents, state]); +}); diff --git a/app/autotranslate/client/lib/tabBar.js b/app/autotranslate/client/lib/tabBar.js deleted file mode 100644 index 875cd9804ed..00000000000 --- a/app/autotranslate/client/lib/tabBar.js +++ /dev/null @@ -1,28 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { hasPermission } from '../../../authorization'; -import { settings } from '../../../settings'; -import { TabBar } from '../../../ui-utils'; - - -Meteor.startup(() => { - Tracker.autorun(() => { - const isEnabled = settings.get('AutoTranslate_Enabled') && hasPermission('auto-translate'); - - if (!isEnabled) { - TabBar.removeButton('autotranslate'); - return; - } - - TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'autotranslate', - i18nTitle: 'Auto_Translate', - icon: 'language', - template: 'AutoTranslate', - full: true, - order: 20, - }); - }); -}); diff --git a/app/autotranslate/client/lib/tabBar.ts b/app/autotranslate/client/lib/tabBar.ts new file mode 100644 index 00000000000..eb65fabc828 --- /dev/null +++ b/app/autotranslate/client/lib/tabBar.ts @@ -0,0 +1,19 @@ +import { lazy, useMemo } from 'react'; + +import { addAction } from '../../../../client/views/room/lib/Toolbox'; +import { usePermission } from '../../../../client/contexts/AuthorizationContext'; +import { useSetting } from '../../../../client/contexts/SettingsContext'; + +addAction('autotranslate', () => { + const hasPermission = usePermission('auto-translate'); + const autoTranslateEnabled = useSetting('AutoTranslate_Enabled'); + return useMemo(() => (hasPermission && autoTranslateEnabled ? { + groups: ['channel', 'group', 'direct'], + id: 'autotranslate', + title: 'Auto_Translate', + icon: 'language', + template: lazy(() => import('../../../../client/views/room/contextualBar/AutoTranslate')), + order: 20, + full: true, + } : null), [autoTranslateEnabled, hasPermission]); +}); diff --git a/app/channel-settings/client/startup/tabBar.js b/app/channel-settings/client/startup/tabBar.js deleted file mode 100644 index d2de4441a9f..00000000000 --- a/app/channel-settings/client/startup/tabBar.js +++ /dev/null @@ -1,16 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { TabBar } from '../../../ui-utils'; - -Meteor.startup(() => { - TabBar.addButton({ - groups: ['channel', 'group'], - id: 'channel-settings', - anonymous: true, - i18nTitle: 'Room_Info', - icon: 'info-circled', - template: 'RoomInfo', - order: 7, - full: true, - }); -}); diff --git a/app/channel-settings/client/startup/tabBar.ts b/app/channel-settings/client/startup/tabBar.ts new file mode 100644 index 00000000000..e655852ae58 --- /dev/null +++ b/app/channel-settings/client/startup/tabBar.ts @@ -0,0 +1,14 @@ +import { FC, lazy, LazyExoticComponent } from 'react'; + +import { addAction } from '../../../../client/views/room/lib/Toolbox'; + +addAction('channel-settings', { + groups: ['channel', 'group'], + id: 'channel-settings', + anonymous: true, + full: true, + title: 'Room_Info', + icon: 'info-circled', + template: lazy(() => import('../../../../client/views/room/contextualBar/Info')) as LazyExoticComponent, + order: 7, +}); diff --git a/app/discussion/client/index.js b/app/discussion/client/index.js index 6d7642ac1b6..fcd3eb9a9d8 100644 --- a/app/discussion/client/index.js +++ b/app/discussion/client/index.js @@ -1,6 +1,5 @@ // Templates import './views/creationDialog/CreateDiscussion'; -import './views/DiscussionTabbar'; // Other UI extensions import './lib/messageTypes/discussionMessage'; diff --git a/app/discussion/client/tabBar.js b/app/discussion/client/tabBar.js deleted file mode 100644 index 5b5873d2a41..00000000000 --- a/app/discussion/client/tabBar.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { TabBar } from '../../ui-utils/client'; -import { settings } from '../../settings'; - -Meteor.startup(function() { - return TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'discussions', - i18nTitle: 'Discussions', - icon: 'discussion', - template: 'discussionsTabbar', - full: true, - order: 1, - condition: () => settings.get('Discussion_enabled'), - }); -}); diff --git a/app/discussion/client/tabBar.ts b/app/discussion/client/tabBar.ts new file mode 100644 index 00000000000..4eedbeed662 --- /dev/null +++ b/app/discussion/client/tabBar.ts @@ -0,0 +1,18 @@ +import { useMemo, lazy, LazyExoticComponent, FC } from 'react'; + +import { addAction } from '../../../client/views/room/lib/Toolbox'; +import { useSetting } from '../../../client/contexts/SettingsContext'; + +addAction('discussions', () => { + const discussionEnabled = useSetting('Discussion_enabled'); + + return useMemo(() => (discussionEnabled ? { + groups: ['channel', 'group', 'direct'], + id: 'discussions', + title: 'Discussions', + icon: 'discussion', + template: lazy(() => import('../../../client/views/room/contextualBar/Discussions')) as LazyExoticComponent, + full: true, + order: 1, + } : null), [discussionEnabled]); +}); diff --git a/app/discussion/client/views/DiscussionTabbar.js b/app/discussion/client/views/DiscussionTabbar.js deleted file mode 100644 index acbae6d694b..00000000000 --- a/app/discussion/client/views/DiscussionTabbar.js +++ /dev/null @@ -1,11 +0,0 @@ -import { Template } from 'meteor/templating'; - -import './DiscussionTabbar.html'; - -Template.discussionsTabbar.helpers({ - close() { - const { data } = Template.instance(); - const { tabBar } = data; - return () => tabBar.close(); - }, -}); diff --git a/app/e2e/client/tabbar.js b/app/e2e/client/tabbar.js deleted file mode 100644 index d90550732f0..00000000000 --- a/app/e2e/client/tabbar.js +++ /dev/null @@ -1,37 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Session } from 'meteor/session'; -import { Tracker } from 'meteor/tracker'; - -import { hasAllPermission } from '../../authorization'; -import { call, TabBar } from '../../ui-utils'; -import { ChatRoom } from '../../models'; -import { settings } from '../../settings'; - -Meteor.startup(() => { - Tracker.autorun(() => { - if (settings.get('E2E_Enable')) { - TabBar.addButton({ - groups: ['direct', 'group'], - id: 'e2e', - i18nTitle: 'E2E', - icon: 'key', - class: () => (ChatRoom.findOne(Session.get('openedRoom')) || {}).encrypted && 'enabled', - action: () => { - const room = ChatRoom.findOne(Session.get('openedRoom')); - call('saveRoomSettings', room._id, 'encrypted', !room.encrypted); - }, - order: 13, - condition: () => { - const session = Session.get('openedRoom'); - const room = ChatRoom.findOne(session); - if (room && room.t === 'd') { - return true; - } - return hasAllPermission('edit-room', session); - }, - }); - } else { - TabBar.removeButton('e2e'); - } - }); -}); diff --git a/app/e2e/client/tabbar.ts b/app/e2e/client/tabbar.ts new file mode 100644 index 00000000000..b3bdfa7c8cb --- /dev/null +++ b/app/e2e/client/tabbar.ts @@ -0,0 +1,28 @@ +import { useMemo } from 'react'; +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import { addAction } from '../../../client/views/room/lib/Toolbox'; +import { useSetting } from '../../../client/contexts/SettingsContext'; +import { usePermission } from '../../../client/contexts/AuthorizationContext'; +import { useMethod } from '../../../client/contexts/ServerContext'; + +addAction('e2e', ({ room }) => { + const e2eEnabled = useSetting('E2E_Enable'); + const hasPermission = usePermission('edit-room', room._id); + const toggleE2E = useMethod('saveRoomSettings'); + + const action = useMutableCallback(() => { + toggleE2E(room._id, 'encrypted', !room.encrypted); + }); + + const enabledOnRoom = !!room.encrypted; + + return useMemo(() => (e2eEnabled && hasPermission ? { + groups: ['direct', 'group'], + id: 'e2e', + title: enabledOnRoom ? 'E2E_disable' : 'E2E_enable', + icon: 'key', + order: 13, + action, + } : null), [action, e2eEnabled, enabledOnRoom, hasPermission]); +}); diff --git a/app/lib/client/defaultTabBars.js b/app/lib/client/defaultTabBars.js deleted file mode 100644 index 852df0e08dc..00000000000 --- a/app/lib/client/defaultTabBars.js +++ /dev/null @@ -1,92 +0,0 @@ -import { Session } from 'meteor/session'; - -import { TabBar } from '../../ui-utils'; -import { Rooms } from '../../models'; -import { hasAllPermission } from '../../authorization'; -import { roomTypes } from '../../utils/client'; - -TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'rocket-search', - i18nTitle: 'Search_Messages', - icon: 'magnifier', - template: 'RocketSearch', - order: 4, -}); - -TabBar.addButton({ - groups: ['direct'], - id: 'user-info', - i18nTitle: 'User_Info', - icon: 'user', - full: true, - template: 'UserInfoWithData', - order: 5, - condition() { - const rid = Session.get('openedRoom'); - const room = Rooms.findOne({ - _id: rid, - }); - - return room && !roomTypes.getConfig(room.t).isGroupChat(room); - }, -}); - -TabBar.addButton({ - groups: ['direct'], - id: 'user-info-group', - i18nTitle: 'Members', - icon: 'team', - template: 'membersList', - order: 5, - condition() { - const rid = Session.get('openedRoom'); - const room = Rooms.findOne({ - _id: rid, - }); - - return room && roomTypes.getConfig(room.t).isGroupChat(room); - }, -}); - -TabBar.addButton({ - groups: ['channel', 'group'], - id: 'members-list', - i18nTitle: 'Members', - icon: 'team', - template: 'membersList', - order: 5, - full: 1, - condition() { - const rid = Session.get('openedRoom'); - const room = Rooms.findOne({ - _id: rid, - }); - - if (!room || !room.broadcast) { - return true; - } - - return hasAllPermission('view-broadcast-member-list', rid); - }, -}); - -TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'uploaded-files-list', - i18nTitle: 'Files', - icon: 'clip', - template: 'channelFilesList', - order: 6, - full: true, -}); - -TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'keyboard-shortcut-list', - i18nTitle: 'Keyboard_Shortcuts_Title', - icon: 'keyboard', - template: 'KeyboardShortcuts', - full: true, - order: 99, -}); diff --git a/app/lib/client/index.js b/app/lib/client/index.js index f67950d96ca..64ed16e27b1 100644 --- a/app/lib/client/index.js +++ b/app/lib/client/index.js @@ -1,7 +1,6 @@ import '../lib/startup/settingsOnLoadSiteUrl'; import '../lib/MessageTypes'; import './CustomTranslations'; -import './defaultTabBars'; import './OAuthProxy'; import './UserDeleted'; import './lib/startup/commands'; diff --git a/app/livechat/client/externalFrame/tabBar.js b/app/livechat/client/externalFrame/tabBar.js deleted file mode 100644 index 2907b3d6718..00000000000 --- a/app/livechat/client/externalFrame/tabBar.js +++ /dev/null @@ -1,23 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { TabBar } from '../../../ui-utils/client'; -import { settings } from '../../../settings/client'; - - -Meteor.startup(function() { - Tracker.autorun(function() { - if (!settings.get('Omnichannel_External_Frame_Enabled')) { - return TabBar.removeButton('omnichannelExternalFrame'); - } - - TabBar.addButton({ - groups: ['live'], - id: 'omnichannelExternalFrame', - i18nTitle: 'Omnichannel_External_Frame', - icon: 'cube', - template: 'ExternalFrameContainer', - order: -1, - }); - }); -}); diff --git a/app/livechat/client/externalFrame/tabBar.ts b/app/livechat/client/externalFrame/tabBar.ts new file mode 100644 index 00000000000..61c12de0743 --- /dev/null +++ b/app/livechat/client/externalFrame/tabBar.ts @@ -0,0 +1,18 @@ +import { useMemo } from 'react'; + +import { useSetting } from '../../../../client/contexts/SettingsContext'; +import { addAction } from '../../../../client/views/room/lib/Toolbox'; + +addAction('omnichannel-external-frame', () => { + const enabled = useSetting('Omnichannel_External_Frame_Enabled'); + + return useMemo(() => (enabled + ? { + groups: ['live'], + id: 'omnichannel-external-frame', + title: 'Omnichannel_External_Frame', + icon: 'cube', + template: 'ExternalFrameContainer', + order: -1, + } : null), [enabled]); +}); diff --git a/app/livechat/client/hooks/onCreateRoomTabBar.js b/app/livechat/client/hooks/onCreateRoomTabBar.js deleted file mode 100644 index 011122ef0d8..00000000000 --- a/app/livechat/client/hooks/onCreateRoomTabBar.js +++ /dev/null @@ -1,29 +0,0 @@ -import { callbacks } from '../../../callbacks'; - -callbacks.add('onCreateRoomTabBar', (info) => { - const { tabBar, room } = info; - - if (!tabBar) { - return info; - } - - if (!room || !room.t || room.t !== 'l') { - return info; - } - - const button = tabBar.getButtons().find((button) => button.id === 'visitor-info'); - if (!button) { - return info; - } - - const { template, i18nTitle: label, icon } = button; - tabBar.setTemplate(template); - tabBar.setData({ - label, - icon, - }); - - tabBar.open(); - - return info; -}); diff --git a/app/livechat/client/index.js b/app/livechat/client/index.js index f7746487ac3..2432ca73a6f 100644 --- a/app/livechat/client/index.js +++ b/app/livechat/client/index.js @@ -2,7 +2,7 @@ import '../lib/messageTypes'; import './roomType'; import './route'; import './ui'; -import './hooks/onCreateRoomTabBar'; +import './tabBar'; import './startup/notifyUnreadRooms'; import './views/app/dialog/closeRoom'; import './stylesheets/livechat.css'; diff --git a/app/livechat/client/tabBar.ts b/app/livechat/client/tabBar.ts new file mode 100644 index 00000000000..c5558a897d9 --- /dev/null +++ b/app/livechat/client/tabBar.ts @@ -0,0 +1,19 @@ +import { addAction } from '../../../client/views/room/lib/Toolbox'; + +addAction('visitor-info', { + groups: ['live'], + id: 'visitor-info', + title: 'Visitor_Info', + icon: 'info-circled', + template: 'visitorInfo', + order: 0, +}); + +addAction('contact-chat-history', { + groups: ['live'], + id: 'contact-chat-history', + title: 'Contact_Chat_History', + icon: 'clock', + template: 'contactChatHistory', + order: 11, +}); diff --git a/app/livechat/client/ui.js b/app/livechat/client/ui.js index 1da170bdf38..3f534e1ddb6 100644 --- a/app/livechat/client/ui.js +++ b/app/livechat/client/ui.js @@ -2,7 +2,7 @@ import { Tracker } from 'meteor/tracker'; import { settings } from '../../settings'; import { hasAllPermission } from '../../authorization'; -import { AccountBox, TabBar, MessageTypes } from '../../ui-utils'; +import { AccountBox, MessageTypes } from '../../ui-utils'; Tracker.autorun((c) => { // import omnichannel tabbar templates right away if omnichannel enabled @@ -21,30 +21,6 @@ AccountBox.addItem({ condition: () => settings.get('Livechat_enabled') && hasAllPermission('view-livechat-manager'), }); -TabBar.addButton({ - groups: ['live'], - id: 'visitor-info', - i18nTitle: 'Visitor_Info', - icon: 'info-circled', - template: 'visitorInfo', - order: 0, -}); - -TabBar.addButton({ - groups: ['live'], - id: 'contact-chat-history', - i18nTitle: 'Contact_Chat_History', - icon: 'clock', - template: 'contactChatHistory', - order: 11, -}); - -TabBar.addGroup('message-search', ['live']); -TabBar.addGroup('starred-messages', ['live']); -TabBar.addGroup('uploaded-files-list', ['live']); -TabBar.addGroup('push-notifications', ['live']); -TabBar.addGroup('video', ['live']); - MessageTypes.registerType({ id: 'livechat-close', system: true, diff --git a/app/livechat/client/views/app/tabbar/contactChatHistory.js b/app/livechat/client/views/app/tabbar/contactChatHistory.js index 87ff3b1cee5..67abad838fc 100644 --- a/app/livechat/client/views/app/tabbar/contactChatHistory.js +++ b/app/livechat/client/views/app/tabbar/contactChatHistory.js @@ -60,11 +60,6 @@ Template.contactChatHistory.onCreated(async function() { this.returnChatHistoryList = () => { this.showChatHistoryMessages.set(false); this.chatHistoryMessagesContext.set(); - - this.tabBar.setData({ - label: 'Contact_Chat_History', - icon: 'clock', - }); }; this.autorun(async () => { diff --git a/app/livechat/lib/LivechatRoomType.js b/app/livechat/lib/LivechatRoomType.js index 1bcbe8bebc3..4c05ce72145 100644 --- a/app/livechat/lib/LivechatRoomType.js +++ b/app/livechat/lib/LivechatRoomType.js @@ -17,7 +17,7 @@ class LivechatRoomRoute extends RoomTypeRouteConfig { constructor() { super({ name: 'live', - path: '/live/:id', + path: '/live/:id/:tab?/:context?', }); } @@ -122,19 +122,19 @@ export default class LivechatRoomType extends RoomTypeConfig { if (!room || !room.v || room.v.username !== username) { return false; } - const button = instance.tabBar.getButtons().find((button) => button.id === 'visitor-info'); - if (!button) { - return false; - } - - const { template, i18nTitle: label, icon } = button; - instance.tabBar.setTemplate(template); - instance.tabBar.setData({ - label, - icon, - }); - - instance.tabBar.open(); + // const button = instance.tabBar.getButtons({ room }).find((button) => button.id === 'visitor-info'); + // if (!button) { + // return false; + // } + + // const { template, i18nTitle: label, icon } = button; + // instance.tabBar.setTemplate(template); + // instance.tabBar.setData({ + // label, + // icon, + // }); + + instance.tabBar.openUserInfo(); return true; } } diff --git a/app/livestream/client/tabBar.js b/app/livestream/client/tabBar.js deleted file mode 100644 index a1041cac525..00000000000 --- a/app/livestream/client/tabBar.js +++ /dev/null @@ -1,30 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; -import { Session } from 'meteor/session'; - -import { TabBar } from '../../ui-utils'; -import { Rooms } from '../../models'; -import { settings } from '../../settings'; - -Meteor.startup(function() { - Tracker.autorun(function() { - TabBar.removeButton('livestream'); - if (settings.get('Livestream_enabled')) { - const live = Rooms.findOne({ - _id: Session.get('openedRoom'), - 'streamingOptions.type': 'livestream', - 'streamingOptions.id': { $exists: 1 }, - }, { fields: { streamingOptions: 1 } }); - - return TabBar.addButton({ - groups: ['channel', 'group'], - id: 'livestream', - i18nTitle: 'Livestream', - icon: 'podcast', - template: 'liveStreamTab', - order: live ? -1 : 15, - class: () => live && 'live', - }); - } - }); -}); diff --git a/app/livestream/client/tabBar.tsx b/app/livestream/client/tabBar.tsx new file mode 100644 index 00000000000..6020b8f6daf --- /dev/null +++ b/app/livestream/client/tabBar.tsx @@ -0,0 +1,29 @@ +import React, { useMemo } from 'react'; +import { Option, Badge } from '@rocket.chat/fuselage'; + +import { useSetting } from '../../../client/contexts/SettingsContext'; +import { useTranslation } from '../../../client/contexts/TranslationContext'; +import { addAction } from '../../../client/views/room/lib/Toolbox'; +import Header from '../../../client/components/Header'; + +addAction('livestream', ({ room }) => { + const enabled = useSetting('Livestream_enabled'); + const t = useTranslation(); + + const isLive = room && room.streamingOptions && room.streamingOptions.id && room.streamingOptions.type === 'livestream'; + + return useMemo(() => (enabled ? { + groups: ['channel', 'group'], + id: 'livestream', + title: 'Livestream', + icon: 'podcast', + template: 'liveStreamTab', + order: isLive ? -1 : 15, + renderAction: (props): React.ReactNode => + {isLive ? ! : null} + , + renderOption: ({ label: { title, icon }, ...props }: any): React.ReactNode => , + } : null), [enabled, isLive, t]); +}); diff --git a/app/mentions-flextab/client/actionButton.js b/app/mentions-flextab/client/actionButton.js index f1e41680c82..d6b86681f58 100644 --- a/app/mentions-flextab/client/actionButton.js +++ b/app/mentions-flextab/client/actionButton.js @@ -12,7 +12,9 @@ Meteor.startup(function() { icon: 'jump', label: 'Jump_to_message', context: ['mentions', 'threads'], - action() { + action(e) { + e.preventDefault(); + e.stopPropagation(); const { msg: message } = messageArgs(this); if (window.matchMedia('(max-width: 500px)').matches) { Template.instance().tabBar.close(); diff --git a/app/mentions-flextab/client/tabBar.js b/app/mentions-flextab/client/tabBar.js deleted file mode 100644 index 2b2a219c670..00000000000 --- a/app/mentions-flextab/client/tabBar.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { TabBar } from '../../ui-utils'; - -Meteor.startup(function() { - return TabBar.addButton({ - groups: ['channel', 'group'], - id: 'mentions', - i18nTitle: 'Mentions', - icon: 'at', - template: 'mentionsFlexTab', - order: 9, - }); -}); diff --git a/app/mentions-flextab/client/tabBar.ts b/app/mentions-flextab/client/tabBar.ts new file mode 100644 index 00000000000..5c47f6bd0ca --- /dev/null +++ b/app/mentions-flextab/client/tabBar.ts @@ -0,0 +1,10 @@ +import { addAction } from '../../../client/views/room/lib/Toolbox'; + +addAction('mentions', { + groups: ['channel', 'group'], + id: 'mentions', + title: 'Mentions', + icon: 'at', + template: 'mentionsFlexTab', + order: 9, +}); diff --git a/app/mentions-flextab/client/views/mentionsFlexTab.js b/app/mentions-flextab/client/views/mentionsFlexTab.js index b1b09030ba9..6a1afb8ca9f 100644 --- a/app/mentions-flextab/client/views/mentionsFlexTab.js +++ b/app/mentions-flextab/client/views/mentionsFlexTab.js @@ -8,6 +8,7 @@ import { messageContext } from '../../../ui-utils/client/lib/messageContext'; import { upsertMessageBulk } from '../../../ui-utils/client/lib/RoomHistoryManager'; import { APIClient } from '../../../utils/client'; import { Messages, Users } from '../../../models/client'; +import { getCommonRoomEvents } from '../../../ui/client/views/app/lib/getCommonRoomEvents'; const LIMIT_DEFAULT = 50; @@ -73,6 +74,7 @@ Template.mentionsFlexTab.onDestroyed(function() { }); Template.mentionsFlexTab.events({ + ...getCommonRoomEvents(), 'scroll .js-list': _.throttle(function(e, instance) { if (e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight && instance.hasMore.get()) { return instance.limit.set(instance.limit.get() + 50); diff --git a/app/message-pin/client/tabBar.js b/app/message-pin/client/tabBar.js deleted file mode 100644 index b1fd452707d..00000000000 --- a/app/message-pin/client/tabBar.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { settings } from '../../settings'; -import { TabBar } from '../../ui-utils'; - -Meteor.startup(function() { - return Tracker.autorun(function() { - if (settings.get('Message_AllowPinning')) { - TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'pinned-messages', - i18nTitle: 'Pinned_Messages', - icon: 'pin', - template: 'pinnedMessages', - order: 11, - }); - } else { - TabBar.removeButton('pinned-messages'); - } - }); -}); diff --git a/app/message-pin/client/tabBar.ts b/app/message-pin/client/tabBar.ts new file mode 100644 index 00000000000..8fcbbc1b8b5 --- /dev/null +++ b/app/message-pin/client/tabBar.ts @@ -0,0 +1,16 @@ +import { useMemo } from 'react'; + +import { addAction } from '../../../client/views/room/lib/Toolbox'; +import { useSetting } from '../../../client/contexts/SettingsContext'; + +addAction('pinned-messages', () => { + const pinningAllowed = useSetting('Message_AllowPinning'); + return useMemo(() => (pinningAllowed ? { + groups: ['channel', 'group', 'direct'], + id: 'pinned-messages', + title: 'Pinned_Messages', + icon: 'pin', + template: 'pinnedMessages', + order: 11, + } : null), [pinningAllowed]); +}); diff --git a/app/message-pin/client/views/pinnedMessages.js b/app/message-pin/client/views/pinnedMessages.js index fea54f0e2f4..5c6305fe430 100644 --- a/app/message-pin/client/views/pinnedMessages.js +++ b/app/message-pin/client/views/pinnedMessages.js @@ -7,9 +7,12 @@ import { upsertMessageBulk } from '../../../ui-utils/client/lib/RoomHistoryManag import { messageContext } from '../../../ui-utils/client/lib/messageContext'; import { APIClient } from '../../../utils/client'; import { Messages } from '../../../models/client'; +import { getCommonRoomEvents } from '../../../ui/client/views/app/lib/getCommonRoomEvents'; const LIMIT_DEFAULT = 50; +Template.pinnedMessages.events(getCommonRoomEvents()); + Template.pinnedMessages.helpers({ hasMessages() { return Template.instance().messages.find().count(); @@ -74,6 +77,7 @@ Template.mentionsFlexTab.onDestroyed(function() { }); Template.pinnedMessages.events({ + ...getCommonRoomEvents(), 'scroll .js-list': _.throttle(function(e, instance) { if (e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight && instance.hasMore.get()) { return instance.limit.set(instance.limit.get() + 50); diff --git a/app/message-snippet/client/router.js b/app/message-snippet/client/router.js index a93b5593088..79c4ab0acc1 100644 --- a/app/message-snippet/client/router.js +++ b/app/message-snippet/client/router.js @@ -1,19 +1,9 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { BlazeLayout } from 'meteor/kadira:blaze-layout'; -import { TabBar } from '../../ui-utils'; - FlowRouter.route('/snippet/:snippetId/:snippetName', { name: 'snippetView', action() { - BlazeLayout.render('main', { center: 'snippetPage', flexTabBar: null }); + BlazeLayout.render('main', { center: 'snippetPage' }); }, - triggersEnter: [function() { - TabBar.hide(); - }], - triggersExit: [ - function() { - TabBar.show(); - }, - ], }); diff --git a/app/message-snippet/client/tabBar/tabBar.js b/app/message-snippet/client/tabBar/tabBar.js deleted file mode 100644 index 465518bd424..00000000000 --- a/app/message-snippet/client/tabBar/tabBar.js +++ /dev/null @@ -1,22 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { settings } from '../../../settings'; -import { TabBar } from '../../../ui-utils'; - -Meteor.startup(function() { - Tracker.autorun(function() { - if (settings.get('Message_AllowSnippeting')) { - TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'snippeted-messages', - i18nTitle: 'snippet-message', - icon: 'code', - template: 'snippetedMessages', - order: 20, - }); - } else { - TabBar.removeButton('snippeted-messages'); - } - }); -}); diff --git a/app/message-snippet/client/tabBar/tabBar.ts b/app/message-snippet/client/tabBar/tabBar.ts new file mode 100644 index 00000000000..4139a842512 --- /dev/null +++ b/app/message-snippet/client/tabBar/tabBar.ts @@ -0,0 +1,16 @@ +import { useMemo } from 'react'; + +import { addAction } from '../../../../client/views/room/lib/Toolbox'; +import { useSetting } from '../../../../client/contexts/SettingsContext'; + +addAction('snippeted-messages', () => { + const snippetingEnabled = useSetting('Message_AllowSnippeting'); + return useMemo(() => (snippetingEnabled ? { + groups: ['channel', 'group', 'direct'], + id: 'snippeted-messages', + title: 'snippet-message', + icon: 'code', + template: 'snippetedMessages', + order: 20, + } : null), [snippetingEnabled]); +}); diff --git a/app/message-star/client/tabBar.js b/app/message-star/client/tabBar.js deleted file mode 100644 index cff05f61cc0..00000000000 --- a/app/message-star/client/tabBar.js +++ /dev/null @@ -1,14 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { TabBar } from '../../ui-utils'; - -Meteor.startup(function() { - TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'starred-messages', - i18nTitle: 'Starred_Messages', - icon: 'star', - template: 'starredMessages', - order: 10, - }); -}); diff --git a/app/message-star/client/tabBar.ts b/app/message-star/client/tabBar.ts new file mode 100644 index 00000000000..904186ba5d5 --- /dev/null +++ b/app/message-star/client/tabBar.ts @@ -0,0 +1,10 @@ +import { addAction } from '../../../client/views/room/lib/Toolbox'; + +addAction('starred-messages', { + groups: ['channel', 'group', 'direct', 'live'], + id: 'starred-messages', + title: 'Starred_Messages', + icon: 'star', + template: 'starredMessages', + order: 10, +}); diff --git a/app/message-star/client/views/starredMessages.js b/app/message-star/client/views/starredMessages.js index 7b97d7f63bc..e4c862e22ae 100644 --- a/app/message-star/client/views/starredMessages.js +++ b/app/message-star/client/views/starredMessages.js @@ -8,9 +8,11 @@ import { messageContext } from '../../../ui-utils/client/lib/messageContext'; import { Messages } from '../../../models/client'; import { upsertMessageBulk } from '../../../ui-utils/client/lib/RoomHistoryManager'; import { APIClient } from '../../../utils/client'; +import { getCommonRoomEvents } from '../../../ui/client/views/app/lib/getCommonRoomEvents'; const LIMIT_DEFAULT = 50; +Template.starredMessages.events(getCommonRoomEvents()); Template.starredMessages.helpers({ hasMessages() { return Template.instance().messages.find().count(); @@ -73,6 +75,7 @@ Template.mentionsFlexTab.onDestroyed(function() { }); Template.starredMessages.events({ + ...getCommonRoomEvents(), 'scroll .js-list': _.throttle(function(e, instance) { if (e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight) { return instance.limit.set(instance.limit.get() + 50); diff --git a/app/otr/client/rocketchat.otr.js b/app/otr/client/rocketchat.otr.js index e2f9645a245..76d48a1bf4e 100644 --- a/app/otr/client/rocketchat.otr.js +++ b/app/otr/client/rocketchat.otr.js @@ -11,6 +11,7 @@ class OTRClass { constructor() { this.enabled = new ReactiveVar(false); this.instancesByRoomId = {}; + this.crypto = null; } isEnabled() { diff --git a/app/otr/client/tabBar.js b/app/otr/client/tabBar.js deleted file mode 100644 index e044b3ff909..00000000000 --- a/app/otr/client/tabBar.js +++ /dev/null @@ -1,27 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { OTR } from './rocketchat.otr'; -import { settings } from '../../settings'; -import { TabBar } from '../../ui-utils'; - -Meteor.startup(function() { - Tracker.autorun(function() { - if (settings.get('OTR_Enable') && window.crypto) { - OTR.crypto = window.crypto.subtle || window.crypto.webkitSubtle; - OTR.enabled.set(true); - TabBar.addButton({ - groups: ['direct'], - id: 'otr', - i18nTitle: 'OTR', - icon: 'key', - template: 'OTR', - order: 13, - full: true, - }); - } else { - OTR.enabled.set(false); - TabBar.removeButton('otr'); - } - }); -}); diff --git a/app/otr/client/tabBar.ts b/app/otr/client/tabBar.ts new file mode 100644 index 00000000000..2cfdc6dcdef --- /dev/null +++ b/app/otr/client/tabBar.ts @@ -0,0 +1,31 @@ +import { useMemo, lazy, LazyExoticComponent, FC, useEffect } from 'react'; + +import { OTR } from './rocketchat.otr'; +import { useSetting } from '../../../client/contexts/SettingsContext'; +import { addAction } from '../../../client/views/room/lib/Toolbox'; + +addAction('otr', () => { + const enabled = useSetting('OTR_Enable'); + + const shouldAddAction = enabled && window.crypto; + + useEffect(() => { + if (shouldAddAction) { + OTR.crypto = window.crypto.subtle; + OTR.enabled.set(true); + } else { + OTR.enabled.set(false); + } + }, [shouldAddAction]); + + return useMemo(() => (shouldAddAction + ? { + groups: ['direct'], + id: 'otr', + title: 'OTR', + icon: 'key', + template: lazy(() => import('../../../client/views/room/contextualBar/OTR')) as LazyExoticComponent, + order: 13, + full: true, + } : null), [shouldAddAction]); +}); diff --git a/app/push-notifications/client/tabBar.js b/app/push-notifications/client/tabBar.js deleted file mode 100644 index 2989bcee8b0..00000000000 --- a/app/push-notifications/client/tabBar.js +++ /dev/null @@ -1,15 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { TabBar } from '../../ui-utils'; - -Meteor.startup(function() { - TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'push-notifications', - i18nTitle: 'Notifications_Preferences', - icon: 'bell', - template: 'NotificationsPreferences', - full: true, - order: 8, - }); -}); diff --git a/app/push-notifications/client/tabBar.ts b/app/push-notifications/client/tabBar.ts new file mode 100644 index 00000000000..3fd640652c0 --- /dev/null +++ b/app/push-notifications/client/tabBar.ts @@ -0,0 +1,12 @@ +import { lazy } from 'react'; + +import { addAction } from '../../../client/views/room/lib/Toolbox'; + +addAction('push-notifications', { + groups: ['channel', 'group', 'direct', 'live'], + id: 'push-notifications', + title: 'Notifications_Preferences', + icon: 'bell', + template: lazy(() => import('../../../client/views/room/contextualBar/NotificationPreferences')), + order: 8, +}); diff --git a/app/reactions/client/init.js b/app/reactions/client/init.js index c396c38cd52..8e9d5420eba 100644 --- a/app/reactions/client/init.js +++ b/app/reactions/client/init.js @@ -3,14 +3,14 @@ import { Blaze } from 'meteor/blaze'; import { Template } from 'meteor/templating'; import { roomTypes } from '../../utils/client'; -import { Rooms } from '../../models'; +import { Rooms, Subscriptions } from '../../models'; import { MessageAction } from '../../ui-utils'; import { messageArgs } from '../../ui-utils/client/lib/messageArgs'; import { EmojiPicker } from '../../emoji'; import { tooltip } from '../../ui/client/components/tooltip'; -Template.roomOld.events({ - 'click .add-reaction'(event, instance) { +export const EmojiEvents = { + 'click .add-reaction'(event) { event.preventDefault(); event.stopPropagation(); const data = Blaze.getData(event.currentTarget); @@ -22,7 +22,7 @@ Template.roomOld.events({ return false; } - if (!instance.subscription.get()) { + if (!Subscriptions.findOne({ rid })) { return false; } @@ -58,7 +58,9 @@ Template.roomOld.events({ event.stopPropagation(); tooltip.hide(); }, -}); +}; + +Template.roomOld.events(EmojiEvents); Meteor.startup(function() { MessageAction.addButton({ diff --git a/app/search/client/provider/result.js b/app/search/client/provider/result.js index 2e7455bbf4a..00a6eaee78a 100644 --- a/app/search/client/provider/result.js +++ b/app/search/client/provider/result.js @@ -10,6 +10,7 @@ import { messageContext } from '../../../ui-utils/client/lib/messageContext'; import { MessageAction, RoomHistoryManager } from '../../../ui-utils'; import { messageArgs } from '../../../ui-utils/client/lib/messageArgs'; import { Rooms } from '../../../models/client'; +import { getCommonRoomEvents } from '../../../ui/client/views/app/lib/getCommonRoomEvents'; Meteor.startup(function() { MessageAction.addButton({ @@ -84,6 +85,7 @@ Template.DefaultSearchResultTemplate.onCreated(function() { }); Template.DefaultSearchResultTemplate.events({ + ...getCommonRoomEvents(), 'change #global-search'(e, t) { t.data.parentPayload.searchAll = e.target.checked; t.data.payload.limit = t.pageSize; diff --git a/app/search/client/style/style.css b/app/search/client/style/style.css index 6fce863ca6c..87dadc8c355 100644 --- a/app/search/client/style/style.css +++ b/app/search/client/style/style.css @@ -6,10 +6,6 @@ padding: 0 !important; } -.defaultProvider { - padding: 24px; -} - .rocket-search-tab { display: flex; flex-direction: column; diff --git a/app/theme/client/imports/components/contextual-bar.css b/app/theme/client/imports/components/contextual-bar.css index 2e5afe8de0e..e27c96fefae 100644 --- a/app/theme/client/imports/components/contextual-bar.css +++ b/app/theme/client/imports/components/contextual-bar.css @@ -8,7 +8,7 @@ overflow: hidden; flex-direction: column; - flex: 0 0 var(--flex-tab-width); + flex: 1 0 var(--flex-tab-width); width: var(--flex-tab-width); height: 100%; diff --git a/app/theme/client/imports/components/header.css b/app/theme/client/imports/components/header.css index c17daf2bcbb..51ddd7f6009 100644 --- a/app/theme/client/imports/components/header.css +++ b/app/theme/client/imports/components/header.css @@ -1,60 +1,6 @@ .rc-header { font-size: var(--text-heading-size); - .rc-badge { - position: absolute; - z-index: 1; - top: -2px; - left: var(--badge-size); - - display: flex; - - min-width: var(--badge-size); - height: var(--badge-size); - - padding: 0 5px; - - text-align: center; - - color: white; - border-radius: calc(4 * var(--badge-font-size)); - background-color: var(--rc-color-button-primary); - box-shadow: 0 0 0 2px #ffffff; - - font-size: var(--badge-font-size); - font-weight: 600; - align-items: center; - justify-content: center; - - &--user-mentions { - background-color: var(--badge-user-mentions-background); - } - - &--group-mentions { - background-color: var(--badge-group-mentions-background); - } - } - - &__first-icon { - display: flex; - - width: 48px; - - padding: 0 0.25rem; - - cursor: pointer; - - justify-content: center; - } - - &--room { - padding: 1.25rem; - - border-bottom: 2px solid var(--color-gray-lightest); - - font-size: var(--header-title-font-size); - } - &__wrap { z-index: 2; @@ -212,203 +158,7 @@ text-overflow: ellipsis; } - - &__status { - display: flex; - - align-items: center; - - &-bullet { - width: var(--header-title-status-bullet-size); - height: var(--header-title-status-bullet-size); - margin-right: 0.25rem; - - border-radius: var(--header-title-status-bullet-radius); - - &--online { - background-color: var(--rc-status-online); - } - - &--away { - background-color: var(--rc-status-away); - } - - &--busy { - background-color: var(--rc-status-busy); - } - - &--invisible { - background-color: var(--rc-status-invisible); - } - - &--offline { - background-color: var(--rc-status-invisible); - } - } - } - - &__toggle-favorite { - - padding: 0 0.25rem; - - color: var(--header-toggle-favorite-color); - - & > .rc-header__icon { - font-size: 2rem; - } - - &:hover { - color: var(--header-toggle-favorite-star-color); - } - - &--checked { - color: var(--header-toggle-favorite-star-color); - } - } - - &__toggle-encryption { - color: var(--header-toggle-encryption-on-color); - - &.empty { - color: var(--header-toggle-encryption-off-color); - - & .rc-header__icon { - fill: none; - } - } - - &:hover { - color: var(--header-toggle-encryption-on-color); - } - } - - &__icon { - font-size: 1rem; - } - - &__image { - width: 32px; - height: 32px; - flex-shrink: 0; - } - - .rc-button { - height: 36px; - min-height: 36px; - - margin: 0 0.25rem; - } -} - -.rc-room-actions { - display: flex; - - &__action, - &__more-action { - position: relative; - - display: flex; - flex-direction: column; - - margin: 0 6px; - - cursor: pointer; - transition: all 0.3s; - - font-size: 20px; - align-items: center; - - &.active, - &:hover { - color: var(--rc-color-link-active); - } - - &.enabled { - color: var(--header-toggle-encryption-on-color); - } - - &.live { - position: relative; - } - - &.live::before { - - position: absolute; - - z-index: 1; - right: -2px; - bottom: -1px; - - display: block; - - width: 10px; - width: var(--sidebar-account-status-bullet-size); - height: 10px; - height: var(--sidebar-account-status-bullet-size); - - content: ''; - - animation: blink 1.5s ease-in-out infinite; - - border-radius: 50%; - border-radius: var(--sidebar-account-status-bullet-radius); - background-color: #f5455c; - } - } - - &__more { - &-action { - flex: 0 0 80px; - - max-width: 80px; - margin: 8px; - } - - &-container { - display: flex; - - max-width: 480px; - margin: 0 -8px; - flex-wrap: wrap; - justify-content: space-between; - } - } - - &__button { - color: inherit; - - font-size: inherit; - } - - &__description { - display: inline-block; - overflow: hidden; - - width: 100%; - padding: 8px 0; - - text-align: center; - white-space: nowrap; - text-overflow: ellipsis; - - font-size: 12px; - font-weight: 600; - } - - & + & { - border-left: 1px var(--color-gray) solid; - - .rtl & { - border-right: 1px var(--color-gray) solid; - border-left: 0; - } - } } - -.tab-button-icon--star { - fill: none; -} - .tab-bugtton-icon--team { font-size: 28px; } @@ -432,25 +182,11 @@ } } - &__favorite { - order: 1; - } - &__data { display: flex; flex-direction: row; align-items: center; } - - &__status { - margin: 0 0.5rem; - } - - &__image { - width: 20px; - height: 20px; - } - &--burger { display: flex; @@ -460,10 +196,6 @@ } } -.embedded-view .room-container .rc-header--burger { - display: none; -} - .burger { position: relative; diff --git a/app/theme/client/imports/general/base.css b/app/theme/client/imports/general/base.css index e35353061bc..e130222bc94 100644 --- a/app/theme/client/imports/general/base.css +++ b/app/theme/client/imports/general/base.css @@ -105,39 +105,6 @@ button { } } -.flex-tab-bar { - & .tab-button { - position: relative; - - cursor: pointer; - } - - & .tab-button-icon { - color: var(--rc-color-primary-dark); - - font-size: 20px; - - &--star { - width: 17px; - height: 16px; - fill: none; - } - - &--language { - width: 16px; - height: 16px; - - fill: currentColor; - stroke: none; - } - - &--hubot { - width: 14px; - height: 16px; - } - } -} - .rc-icon { overflow: hidden; diff --git a/app/theme/client/imports/general/base_old.css b/app/theme/client/imports/general/base_old.css index 81a29d52af2..b48cdd16ebd 100644 --- a/app/theme/client/imports/general/base_old.css +++ b/app/theme/client/imports/general/base_old.css @@ -2427,272 +2427,6 @@ flex-grow: 1; } -.rc-old .flex-tab-container { - z-index: 2; - - display: flex; - - flex: 0 0 auto; - - transform: box-shadow 0.3s; - - box-shadow: -1px 0 0 1px #cccccc26; - - &.opened { - box-shadow: -1px 0 5px 2px #cccccc26; - - & > .flex-tab-bar { - box-shadow: -1px 0 5px 2px #cccccc26; - } - } - - & .flex-tab { - position: relative; - - display: none; - overflow-x: visible; - - width: var(--flex-tab-width); - - & .control { - & .header { - margin: 5px 0 15px; - padding: 5px 30px 20px; - - text-align: center; - - & h2 { - font-size: 20px; - font-weight: 300; - line-height: 25px; - } - } - - & .button { - min-height: 36px; - margin: 0 1px; - } - - & .more { - position: absolute; - top: 0; - left: 0; - - width: 30px; - height: 60px; - - cursor: pointer; - transition: transform 0.25s ease-out 0.475s, background 0.075s ease-out 0.5s; - transform: translateX(-27px); - - border-width: 0 0 1px; - - & i { - height: 12.5px; - margin-top: 1px; - - transition: transform 0.3s ease-out; - transform-origin: 50%, 50%, 0; - vertical-align: top; - } - } - - & .search-form { - width: 100%; - - & .icon-plus { - position: absolute; - top: 11px; - left: 8px; - - font-size: 13px; - } - } - - & .info-tabs { - position: absolute; - top: 0; - right: 20px; - - height: 60px; - - text-align: right; - - & a { - display: inline-block; - float: left; - - height: 60px; - padding: 0 15px; - - vertical-align: middle; - - border-width: 0 0 0 1px; - - line-height: 60px; - - &:last-child { - border-width: 0 1px 0 0; - } - } - } - } - - & .content { - overflow-x: hidden; - overflow-y: auto; - - width: 100%; - height: 100%; - -webkit-overflow-scrolling: touch; - - & > div { - overflow-y: auto; - - transition: transform 0.45s cubic-bezier(0.5, 0, 0, 1), opacity 0.125s ease-out 0.1s; - } - - & > .animated-hidden { - transform: translateX(calc(100% + 40px)); - - opacity: 0; - } - - & .section { - margin: 20px; - padding: 20px; - - border: 1px solid #dddddd; - border-radius: var(--border-radius); - background-color: #ffffff; - } - - & > .animated { - position: absolute; - top: 0; - left: 0; - - width: 100%; - height: 100%; - } - - & > .title { - height: var(--header-min-height); - - & h2 { - padding: 0 20px; - - font-size: 20px; - font-weight: 300; - line-height: var(--header-min-height); - } - } - } - - & .channel-settings .button { - visibility: initial; - } - - & footer { - position: absolute; - z-index: 100; - bottom: 0; - left: 0; - - width: 100%; - height: var(--footer-min-height); - padding: 9px 15px 0; - - text-align: right; - } - - & .social { - text-align: center; - - & h4 { - position: absolute; - top: -12px; - left: 0; - - width: 100%; - - font-size: 13px; - font-weight: 300; - } - - & .share { - min-height: 40px; - - border-radius: 50%; - - line-height: 20px; - - &::before { - border-radius: 50%; - } - - & span { - display: none; - } - } - } - } - - & .flex-tab-bar { - z-index: 1; - - min-width: 40px; - - transition: box-shadow 0.3s; - - box-shadow: -1px 0 0 1px #cccccc26; - - & .tab-button { - position: relative; - - cursor: pointer; - text-align: center; - - & button { - height: 38px; - } - - & .counter { - position: absolute; - top: 4px; - right: 4px; - - width: 13px; - height: 13px; - - text-align: center; - - border-radius: 50%; - - font-size: 9px; - font-weight: bold; - line-height: 13px; - } - - &.active { - border-width: 0 3px 0 0; - - & button { - margin-left: 3px; - } - - & .counter { - margin-right: -3px; - } - } - } - } - - &.opened .flex-tab { - display: block; - } -} - .rc-old .list-view { & .list { position: relative; @@ -3919,10 +3653,6 @@ & .messages-container { border-width: 0; - & .flex-tab-container { - display: none; - } - & .messages-box { margin-top: 0; } diff --git a/app/theme/client/imports/general/rtl.css b/app/theme/client/imports/general/rtl.css index e5ac1ee97e8..436f2305e2b 100644 --- a/app/theme/client/imports/general/rtl.css +++ b/app/theme/client/imports/general/rtl.css @@ -100,18 +100,6 @@ } } - & .flex-tab-container { - border-width: 0 1px 0 0; - } - - & .flex-tab-bar .tab-button.active { - margin-right: -1px; - margin-left: auto; - - border-right: unset; - border-left: 3px solid #ff0000; - } - & .flex-tab .control { padding: 12px 30px; diff --git a/app/theme/client/imports/general/theme_old.css b/app/theme/client/imports/general/theme_old.css index d1b72c3bc16..bb6840f8afe 100644 --- a/app/theme/client/imports/general/theme_old.css +++ b/app/theme/client/imports/general/theme_old.css @@ -312,10 +312,6 @@ textarea { } } -.toggle-favorite { - color: var(--component-color); -} - .upload-progress-progress { background-color: var(--success-background); } @@ -377,31 +373,6 @@ textarea { } } -.flex-tab-bar { - .tab-button { - &:hover { - background-color: var(--secondary-background-color); - } - - &.active { - border-right-color: var(--selection-color); - background-color: var(--secondary-background-color); - } - - &.attention { - animation-name: blink; - animation-duration: 1000ms; - animation-iteration-count: infinite; - animation-direction: alternate; - } - } - - .counter { - color: white; - background: var(--secondary-font-color); - } -} - i.status-online { color: var(--rc-status-online); } diff --git a/app/theme/client/imports/general/variables.css b/app/theme/client/imports/general/variables.css index 8d07ba5b8a0..79ff802069a 100644 --- a/app/theme/client/imports/general/variables.css +++ b/app/theme/client/imports/general/variables.css @@ -346,10 +346,6 @@ */ --header-height: 77px; --header-padding: 16px; - --header-toggle-favorite-color: var(--color-gray-medium); - --header-toggle-favorite-star-color: var(--rc-color-alert-light); - --header-toggle-encryption-off-color: var(--color-gray-medium); - --header-toggle-encryption-on-color: var(--rc-color-alert-message-secondary); --header-title-username-color-darker: var(--color-dark); --header-title-font-size: var(--text-default-size); --header-title-font-size--subtitle: var(--text-small-size); diff --git a/app/threads/client/components/ThreadComponent.tsx b/app/threads/client/components/ThreadComponent.tsx index f300d07d24b..8d77308605e 100644 --- a/app/threads/client/components/ThreadComponent.tsx +++ b/app/threads/client/components/ThreadComponent.tsx @@ -8,13 +8,16 @@ import { ChatMessage } from '../../../models/client'; import { useRoute } from '../../../../client/contexts/RouterContext'; import { roomTypes } from '../../../utils/client'; import { normalizeThreadTitle } from '../lib/normalizeThreadTitle'; -import { useUserId } from '../../../../client/contexts/UserContext'; +import { useUserId, useUserSubscription } from '../../../../client/contexts/UserContext'; import { useEndpoint, useMethod } from '../../../../client/contexts/ServerContext'; import { useToastMessageDispatch } from '../../../../client/contexts/ToastMessagesContext'; import ThreadSkeleton from './ThreadSkeleton'; import ThreadView from './ThreadView'; import { IMessage } from '../../../../definition/IMessage'; import { IRoom } from '../../../../definition/IRoom'; +import { useTabBarOpenUserInfo } from '../../../../client/views/room/providers/ToolboxProvider'; + +const subscriptionFields = {}; const useThreadMessage = (tmid: string): IMessage => { const [message, setMessage] = useState(() => Tracker.nonreactive(() => ChatMessage.findOne({ _id: tmid }))); @@ -56,16 +59,17 @@ const ThreadComponent: FC<{ mid: string; jump: unknown; room: IRoom; - subscription: unknown; }> = ({ mid, jump, room, - subscription, }) => { + const subscription = useUserSubscription(room._id, subscriptionFields); const channelRoute = useRoute(roomTypes.getConfig(room.t).route.name); const threadMessage = useThreadMessage(mid); + const open = useTabBarOpenUserInfo(); + const ref = useRef(null); const uid = useUserId(); @@ -102,6 +106,8 @@ const ThreadComponent: FC<{ jump, following, subscription, + rid: room._id, + openProfileTab: open, })); useEffect(() => { @@ -115,15 +121,16 @@ const ThreadComponent: FC<{ jump, following, subscription, + rid: room._id, + openProfileTab: open, }; }); - }, [following, jump, subscription, threadMessage]); + }, [following, jump, open, room._id, subscription, threadMessage]); useEffect(() => { if (!ref.current || !viewData.mainMessage) { return; } - const view = Blaze.renderWithData(Template.thread, viewData, ref.current); return (): void => { diff --git a/app/threads/client/components/ThreadSkeleton.tsx b/app/threads/client/components/ThreadSkeleton.tsx index b6086fa8255..7d0cd9e1d1c 100644 --- a/app/threads/client/components/ThreadSkeleton.tsx +++ b/app/threads/client/components/ThreadSkeleton.tsx @@ -21,13 +21,13 @@ const ThreadSkeleton: FC = ({ expanded, onClose }) => { return <> {expanded && } - + (({ onToggleFollow, onClose, }, ref) => { + const hasExpand = useLayoutContextualBarExpanded(); + const style = useMemo(() => (document.dir === 'rtl' ? { left: 0, @@ -48,29 +51,29 @@ const ThreadView = forwardRef(({ }, [following, onToggleFollow]); return <> - {expanded && } + {hasExpand && expanded && } - + - - - + {hasExpand && } + + + + { const { CheckBox } = await import('@rocket.chat/fuselage'); @@ -38,6 +39,7 @@ createTemplateForComponent('ThreadComponent', () => import('../components/Thread Template.thread.events({ ...dropzoneEvents, + ...getCommonRoomEvents(), 'click .js-close'(e) { e.preventDefault(); e.stopPropagation(); diff --git a/app/threads/client/flextab/threadlist.js b/app/threads/client/flextab/threadlist.js deleted file mode 100644 index ea7653a861e..00000000000 --- a/app/threads/client/flextab/threadlist.js +++ /dev/null @@ -1,37 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Session } from 'meteor/session'; - -import { TabBar } from '../../../ui-utils/client'; -import { Subscriptions } from '../../../models/client'; - -Meteor.startup(function() { - return TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'thread', - full: true, - i18nTitle: 'Threads', - icon: 'thread', - template: 'threads', - badge: () => { - const subscription = Subscriptions.findOne({ rid: Session.get('openedRoom') }, { fields: { tunread: 1, tunreadUser: 1, tunreadGroup: 1 } }); - if (!subscription?.tunread?.length) { - return; - } - - const badgeClass = (() => { - if (subscription.tunreadUser?.length > 0) { - return 'rc-badge--user-mentions'; - } - if (subscription.tunreadGroup?.length > 0) { - return 'rc-badge--group-mentions'; - } - })(); - - return { - body: subscription.tunread.length > 99 ? '99+' : subscription.tunread.length, - class: badgeClass, - }; - }, - order: 2, - }); -}); diff --git a/app/threads/client/flextab/threadlist.tsx b/app/threads/client/flextab/threadlist.tsx new file mode 100644 index 00000000000..1fe2ab0b93e --- /dev/null +++ b/app/threads/client/flextab/threadlist.tsx @@ -0,0 +1,40 @@ +import React, { useMemo, lazy, LazyExoticComponent, FC } from 'react'; +import { BadgeProps } from '@rocket.chat/fuselage'; + +import { addAction } from '../../../../client/views/room/lib/Toolbox'; +import { useSetting } from '../../../../client/contexts/SettingsContext'; +import Header from '../../../../client/components/Header'; +import { ISubscription } from '../../../../definition/ISubscription'; + +const getVariant = (tunreadUser: number, tunreadGroup: number): BadgeProps['variant'] => { + if (tunreadUser > 0) { + return 'danger'; + } + if (tunreadGroup > 0) { + return 'warning'; + } + return 'primary'; +}; + +const template = lazy(() => import('../../../../client/views/room/contextualBar/Threads')) as LazyExoticComponent; + +addAction('thread', (options) => { + const room = options.room as unknown as ISubscription; + const threadsEnabled = useSetting('Threads_enabled'); + return useMemo(() => (threadsEnabled ? { + groups: ['channel', 'group', 'direct'], + id: 'thread', + full: true, + title: 'Threads', + icon: 'thread', + template, + renderAction: (props) => { + const unread = room.tunread?.length > 99 ? '99+' : room.tunread?.length; + const variant = getVariant(room.tunreadUser?.length, room.tunreadGroup?.length); + return + { unread > 0 && {unread} } + ; + }, + order: 2, + } : null), [threadsEnabled, room.tunread?.length, room.tunreadUser?.length, room.tunreadGroup?.length]); +}); diff --git a/app/threads/client/flextab/threads.html b/app/threads/client/flextab/threads.html deleted file mode 100644 index 356ddb6191c..00000000000 --- a/app/threads/client/flextab/threads.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/app/threads/client/flextab/threads.js b/app/threads/client/flextab/threads.js index c302a5f00ad..19a5c0fc47b 100644 --- a/app/threads/client/flextab/threads.js +++ b/app/threads/client/flextab/threads.js @@ -1,17 +1 @@ - -import { Template } from 'meteor/templating'; - -import './threads.html'; import '../threads.css'; - -Template.threads.helpers({ - rid() { - const { rid } = Template.instance().data; - return rid; - }, - close() { - const { data } = Template.instance(); - const { tabBar } = data; - return () => tabBar.close(); - }, -}); diff --git a/app/threads/client/messageAction/replyInThread.js b/app/threads/client/messageAction/replyInThread.js index d5c463dd0fa..f0df4fb6e7b 100644 --- a/app/threads/client/messageAction/replyInThread.js +++ b/app/threads/client/messageAction/replyInThread.js @@ -16,9 +16,9 @@ Meteor.startup(function() { icon: 'thread', label: 'Reply_in_thread', context: ['message', 'message-mobile'], - action() { + action(e) { const { msg: message } = messageArgs(this); - + e.stopPropagation(); FlowRouter.setParams({ tab: 'thread', context: message.tmid || message._id, diff --git a/app/threads/client/threads.css b/app/threads/client/threads.css index d6d81e046fe..8b3537eeaeb 100644 --- a/app/threads/client/threads.css +++ b/app/threads/client/threads.css @@ -214,6 +214,9 @@ } .contextual-bar__content.thread, +.contextual-bar__content.discussions, +.contextual-bar__content.channel-settings, +.contextual-bar__content.keyboard-shortcut-list, .contextual-bar__content.threads { padding: 0; } diff --git a/app/ui-clean-history/client/lib/startup.js b/app/ui-clean-history/client/lib/startup.js deleted file mode 100644 index 2c7b1ffaa12..00000000000 --- a/app/ui-clean-history/client/lib/startup.js +++ /dev/null @@ -1,18 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Session } from 'meteor/session'; - -import { TabBar } from '../../../ui-utils'; -import { hasAllPermission } from '../../../authorization'; - -Meteor.startup(() => { - TabBar.addButton({ - groups: ['channel', 'group', 'direct'], - id: 'clean-history', - anonymous: true, - i18nTitle: 'Prune_Messages', - icon: 'eraser', - template: 'cleanHistory', - order: 250, - condition: () => hasAllPermission('clean-channel-history', Session.get('openedRoom')), - }); -}); diff --git a/app/ui-clean-history/client/lib/startup.ts b/app/ui-clean-history/client/lib/startup.ts new file mode 100644 index 00000000000..56575338c67 --- /dev/null +++ b/app/ui-clean-history/client/lib/startup.ts @@ -0,0 +1,18 @@ + +import { useMemo } from 'react'; + +import { addAction } from '../../../../client/views/room/lib/Toolbox'; +import { usePermission } from '../../../../client/contexts/AuthorizationContext'; + +addAction('clean-history', ({ room }) => { + const hasPermission = usePermission('clean-channel-history', room._id); + return useMemo(() => (hasPermission ? { + groups: ['channel', 'group', 'direct'], + id: 'clean-history', + anonymous: true, + title: 'Prune_Messages', + icon: 'eraser', + template: 'cleanHistory', + order: 250, + } : null), [hasPermission]); +}); diff --git a/app/ui-flextab/README.md b/app/ui-flextab/README.md deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/app/ui-flextab/client/flexTabBar.html b/app/ui-flextab/client/flexTabBar.html deleted file mode 100644 index 1f801674746..00000000000 --- a/app/ui-flextab/client/flexTabBar.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - diff --git a/app/ui-flextab/client/flexTabBar.js b/app/ui-flextab/client/flexTabBar.js deleted file mode 100644 index aebdceea11f..00000000000 --- a/app/ui-flextab/client/flexTabBar.js +++ /dev/null @@ -1,260 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Session } from 'meteor/session'; -import { Template } from 'meteor/templating'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import _ from 'underscore'; - -import { hasAllPermission } from '../../authorization'; -import { popover, TabBar, Layout } from '../../ui-utils'; -import { t } from '../../utils'; -import { settings } from '../../settings'; - -const commonHelpers = { - title() { - return t(this.i18nTitle) || this.title; - }, - active() { - if (this.template === Template.instance().tabBar.getTemplate() && Template.instance().tabBar.getState() === 'opened') { - return 'active'; - } - }, -}; -function canShowAddUsersButton(rid) { - const canAddToChannel = hasAllPermission( - 'add-user-to-any-c-room', rid, - ); - const canAddToGroup = hasAllPermission( - 'add-user-to-any-p-room', rid, - ); - const canAddToJoinedRoom = hasAllPermission( - 'add-user-to-joined-room', rid, - ); - if ( - !canAddToJoinedRoom - && !canAddToChannel - && Template.instance().tabBar.currentGroup() === 'channel' - ) { - return false; - } - if ( - !canAddToJoinedRoom - && !canAddToGroup - && Template.instance().tabBar.currentGroup() === 'group' - ) { - return false; - } - return true; -} -const filterButtons = (button, anonymous, rid) => { - if (!Meteor.userId() && !anonymous) { - return false; - } - if (button.groups.indexOf(Template.instance().tabBar.currentGroup()) === -1) { - return false; - } - if (button.id === 'addUsers' && !canShowAddUsersButton(rid)) { - return false; - } - if (button.id === 'thread' && !settings.get('Threads_enabled')) { - return false; - } - if (button.id === 'gameCenter' && !settings.get('Apps_Game_Center_enabled')) { - return false; - } - return true; -}; -Template.flexTabBar.helpers({ - headerData() { - return Template.instance().tabBar.getData(); - }, - ...commonHelpers, - buttons() { - return TabBar.getButtons().filter((button) => - filterButtons(button, this.anonymous, this.data && this.data.rid), - ); - }, - opened() { - return Template.instance().tabBar.getState(); - }, - - template() { - return Template.instance().tabBar.getTemplate(); - }, - flexData() { - return Object.assign(Template.currentData().data || {}, { - tabBar: Template.instance().tabBar, - }); - }, - - embeddedVersion() { - return Layout.isEmbedded(); - }, -}); - -const commonEvents = { - 'click .js-action'(e, t) { - $('button', e.currentTarget).blur(); - e.preventDefault(); - const $flexTab = $('.flex-tab-container .flex-tab'); - - if (t.tabBar.getState() === 'opened' && t.tabBar.getTemplate() === this.template) { - $flexTab.attr('template', ''); - return t.tabBar.close(); - } - - if (this.action) { - return this.action(); - } - - $flexTab.attr('template', this.template); - t.tabBar.setData({ - ...this, - label: this.i18nTitle, - icon: this.icon, - }); - t.tabBar.open(this); - - popover.close(); - }, -}; -const action = function(e, t) { - $('button', e.currentTarget).blur(); - e.preventDefault(); - const $flexTab = $('.flex-tab-container .flex-tab'); - - if (this.actionDefault) { - return this.actionDefault(); - } - if (t.tabBar.getState() === 'opened' && t.tabBar.getTemplate() === this.template) { - $flexTab.attr('template', ''); - return t.tabBar.close(); - } - $flexTab.attr('template', this.template); - t.tabBar.setData({ - ...this, - label: this.i18nTitle, - icon: this.icon, - }); - t.tabBar.open(this); - - popover.close(); -}; - -Template.flexTabBar.events({ - 'click .tab-button'(e, t) { - e.preventDefault(); - const $flexTab = $('.flex-tab-container .flex-tab'); - - if (t.tabBar.getState() === 'opened' && t.tabBar.getTemplate() === this.template) { - $flexTab.attr('template', ''); - return t.tabBar.close(); - } - - $flexTab.attr('template', this.template); - - t.tabBar.setData({ - label: this.i18nTitle, - icon: this.icon, - }); - t.tabBar.open(this.id); - }, - - 'click .close-flex-tab'(event, t) { - t.tabBar.close(); - }, -}); - -Template.flexTabBar.onCreated(function() { - this.tabBar = Template.currentData().tabBar; -}); - - -Template.RoomsActionMore.events({ - ...commonEvents, -}); - - -Template.RoomsActionMore.helpers({ - ...commonHelpers, -}); - -Template.RoomsActionMore.onCreated(function() { - this.tabBar = Template.currentData().tabBar; -}); - -Template.RoomsActionTab.events({ - ...commonEvents, - 'click .js-more'(e, t) { - $(e.currentTarget).blur(); - e.preventDefault(); - const buttons = TabBar.getButtons().filter((button) => filterButtons(button, t.anonymous, t.data.rid)); - const groups = [{ items: (t.small.get() ? buttons : buttons.slice(TabBar.size)).map((item) => ({ - ...item, - name: TAPi18n.__(item.i18nTitle), - actionDefault: item.action !== action && item.action, - action, - })) }]; - const columns = [groups]; - columns[0] = { groups }; - const config = { - columns, - popoverClass: 'message-box', - data: { - rid: this._id, - buttons: t.small.get() ? buttons : buttons.slice(TabBar.size), - tabBar: t.tabBar, - }, - currentTarget: e.currentTarget, - offsetHorizontal: -e.currentTarget.clientWidth, - offsetVertical: e.currentTarget.clientHeight + 10, - }; - - popover.open(config); - }, -}); - -Template.RoomsActionTab.onDestroyed(function() { - $(window).off('resize', this.refresh); -}); -Template.RoomsActionTab.onCreated(function() { - this.small = new ReactiveVar(window.matchMedia('(max-width: 500px)').matches); - this.refresh = _.throttle(() => { - this.small.set(window.matchMedia('(max-width: 500px)').matches); - }, 100); - $(window).on('resize', this.refresh); - this.tabBar = Template.currentData().tabBar; -}); - -Template.RoomsActionTab.helpers({ - ...commonHelpers, - - postButtons() { - const toolbar = Session.get('toolbarButtons') || {}; - return Object.keys(toolbar.buttons || []).map((key) => ({ id: key, ...toolbar.buttons[key] })); - }, - - active() { - if (this.template === Template.instance().tabBar.getTemplate() && Template.instance().tabBar.getState() === 'opened') { - return 'active'; - } - }, - - buttons() { - if (Template.instance().small.get()) { - return []; - } - const buttons = TabBar.getButtons() - .filter((button) => filterButtons(button, Template.instance().anonymous, Template.instance().data.rid)); - return buttons.length <= TabBar.size ? buttons : buttons.slice(0, TabBar.size); - }, - - moreButtons() { - if (Template.instance().small.get()) { - return true; - } - const buttons = TabBar.getButtons() - .filter((button) => filterButtons(button, Template.instance().anonymous, Template.instance().data.rid)); - return buttons.length > TabBar.size; - }, -}); diff --git a/app/ui-flextab/client/index.js b/app/ui-flextab/client/index.js deleted file mode 100644 index bf1b5a811cf..00000000000 --- a/app/ui-flextab/client/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import './flexTabBar.html'; -import './flexTabBar'; -// import './tabs/uploadedFilesList'; diff --git a/app/ui-flextab/index.js b/app/ui-flextab/index.js deleted file mode 100644 index 40a7340d388..00000000000 --- a/app/ui-flextab/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './client/index'; diff --git a/app/ui-sidenav/client/roomList.js b/app/ui-sidenav/client/roomList.js index 5d7604d7472..475c1107a76 100644 --- a/app/ui-sidenav/client/roomList.js +++ b/app/ui-sidenav/client/roomList.js @@ -150,8 +150,11 @@ const mergeSubRoom = (subscription) => { v: 1, streamingOptions: 1, usernames: 1, - description: 1, topic: 1, + encrypted: 1, + // autoTranslate: 1, + // autoTranslateLanguage: 1, + description: 1, announcement: 1, broadcast: 1, archived: 1, @@ -177,7 +180,7 @@ const mergeSubRoom = (subscription) => { subscription.lm = subscription.lr ? new Date(Math.max(subscription.lr, lastRoomUpdate)) : lastRoomUpdate; subscription.streamingOptions = room.streamingOptions; - + subscription.encrypted = room.encrypted; subscription.description = room.description; subscription.cl = room.cl; subscription.topic = room.topic; @@ -197,6 +200,7 @@ const mergeRoomSub = (room) => { rid: room._id, }, { $set: { + encrypted: room.encrypted, description: room.description, cl: room.cl, topic: room.topic, diff --git a/app/ui-utils/client/index.js b/app/ui-utils/client/index.js index dd40b69ac68..0cf0b4975ec 100644 --- a/app/ui-utils/client/index.js +++ b/app/ui-utils/client/index.js @@ -15,8 +15,6 @@ export { Layout } from './lib/Layout'; export { IframeLogin, iframeLogin } from './lib/IframeLogin'; export { fireGlobalEvent } from './lib/fireGlobalEvent'; export { getAvatarAsPng } from './lib/avatar'; -export { TabBar, TABBAR_DEFAULT_VISIBLE_ICON_COUNT } from './lib/TabBar'; -export { RocketChatTabBar } from './lib/RocketChatTabBar'; export { popout } from './lib/popout'; export { messageProperties } from '../lib/MessageProperties'; export { MessageTypes } from '../lib/MessageTypes'; diff --git a/app/ui-utils/client/lib/Layout.js b/app/ui-utils/client/lib/Layout.js index 73dc3f46ec1..758f58a99fa 100644 --- a/app/ui-utils/client/lib/Layout.js +++ b/app/ui-utils/client/lib/Layout.js @@ -9,6 +9,6 @@ export const Layout = new class RocketChatLayout { } isEmbedded() { - return this.layout === 'embedded'; + return FlowRouter.getQueryParam('layout') === 'embedded'; } }(); diff --git a/app/ui-utils/client/lib/MessageAction.js b/app/ui-utils/client/lib/MessageAction.js index 5b22ccc55e6..6e87cc6a583 100644 --- a/app/ui-utils/client/lib/MessageAction.js +++ b/app/ui-utils/client/lib/MessageAction.js @@ -363,14 +363,12 @@ Meteor.startup(async function() { icon: 'emoji', label: 'Reactions', context: ['message', 'message-mobile', 'threads'], - action(_, roomInstance) { + action(_, { tabBar, rid }) { const { msg: { reactions } } = messageArgs(this); modal.open({ - template: createTemplateForComponent('reactionList', () => import('./ReactionListContent'), { - renderContainerView: () => HTML.DIV({ style: 'margin: -16px; height: 100%; display: flex; flex-direction: column; overflow: hidden;' }), // eslint-disable-line new-cap - }), - data: { reactions, roomInstance, onClose: () => modal.close() }, + template: 'reactionList', + data: { reactions, tabBar, rid, onClose: () => modal.close() }, }); }, condition({ msg: { reactions } }) { @@ -380,3 +378,7 @@ Meteor.startup(async function() { group: 'menu', }); }); + +createTemplateForComponent('reactionList', () => import('./ReactionListContent'), { + renderContainerView: () => HTML.DIV({ style: 'margin: -16px; height: 100%; display: flex; flex-direction: column; overflow: hidden;' }), // eslint-disable-line new-cap +}); diff --git a/app/ui-utils/client/lib/ReactionListContent.js b/app/ui-utils/client/lib/ReactionListContent.js index 1ef0b47b891..3358d944338 100644 --- a/app/ui-utils/client/lib/ReactionListContent.js +++ b/app/ui-utils/client/lib/ReactionListContent.js @@ -4,15 +4,12 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '../../../../client/contexts/TranslationContext'; import { useSetting } from '../../../../client/contexts/SettingsContext'; -import { useSession } from '../../../../client/contexts/SessionContext'; import Emoji from '../../../../client/components/Emoji'; import ScrollableContentWrapper from '../../../../client/components/ScrollableContentWrapper'; import { openUserCard } from '../../../ui/client/lib/UserCard'; -import { openProfileTabOrOpenDM } from '../../../ui/client/views/app/room'; -export function Reactions({ reactions, roomInstance, onClose }) { +export function Reactions({ reactions, onClick }) { const useRealName = useSetting('UI_Use_Real_Name'); - return {Object.entries(reactions).map(([reaction, { names = [], usernames }]) => @@ -23,8 +20,7 @@ export function Reactions({ reactions, roomInstance, onClose }) { key={username} displayName={useRealName ? names[i] || username : username} username={username} - roomInstance={roomInstance} - onClose={onClose} + onClick={onClick} />)} @@ -33,28 +29,32 @@ export function Reactions({ reactions, roomInstance, onClose }) { ; } -export function Username({ username, displayName, roomInstance, onClose }) { - const openedRoom = useSession('openedRoom'); - const handleUserCard = useMutableCallback((e) => openUserCard({ - username, - rid: openedRoom, - target: e.currentTarget, - open: (e) => { - e.preventDefault(); - onClose(); - openProfileTabOrOpenDM(e, roomInstance, username); - }, - })); - +export function Username({ username, onClick, displayName }) { return ( - + {displayName} ); } -export default function ReactionListContent({ reactions, roomInstance, onClose }) { +export default function ReactionListContent({ rid, reactions, tabBar, onClose }) { const t = useTranslation(); + const onClick = useMutableCallback((e) => { + const { username } = e.currentTarget.dataset; + if (!username) { + return; + } + openUserCard({ + username, + rid, + target: e.currentTarget, + open: (e) => { + e.preventDefault(); + onClose(); + tabBar.openUserInfo(username); + }, + }); + }); return <> @@ -62,7 +62,7 @@ export default function ReactionListContent({ reactions, roomInstance, onClose } - + diff --git a/app/ui-utils/client/lib/RocketChatTabBar.js b/app/ui-utils/client/lib/RocketChatTabBar.js deleted file mode 100644 index 1974bc91989..00000000000 --- a/app/ui-utils/client/lib/RocketChatTabBar.js +++ /dev/null @@ -1,86 +0,0 @@ -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; - -import { TabBar } from './TabBar'; - -export class RocketChatTabBar { - constructor() { - this.template = new ReactiveVar(); - this.id = new ReactiveVar(); - this.group = new ReactiveVar(); - this.state = new ReactiveVar(); - this.data = new ReactiveVar(); - } - - getTemplate() { - return this.template.get(); - } - - getId() { - return this.id.get(); - } - - setTemplate(template) { - this.template.set(template); - } - - currentGroup() { - return this.group.get(); - } - - showGroup(group) { - this.group.set(group); - } - - extendsData(data) { - this.data.set({ ...this.data.get(), ...data }); - } - - setData(d) { - this.data.set(d); - } - - getData() { - return this.data.get(); - } - - getButtons() { - return TabBar.getButtons(); - } - - getState() { - return this.state.get(); - } - - open(button) { - this.state.set('opened'); - Tracker.afterFlush(() => { - $('.contextual-bar__container').scrollTop(0).find('input[type=text]:first').focus(); - }); - - const current = FlowRouter.current(); - FlowRouter.go(current.route.name, { ...current.params, tab: null, context: null }); - - if (!button) { - return; - } - if (typeof button !== 'object' || !button.id) { - button = TabBar.getButton(button); - } - - $('.flex-tab, .contextual-bar').css('width', button.width ? `${ button.width }px` : ''); - this.template.set(button.template); - this.id.set(button.id); - return button; - } - - close() { - this.state.set(''); - - $('.flex-tab, .contextual-bar').css('width', ''); - - this.template.set(); - this.id.set(); - } -} diff --git a/app/ui-utils/client/lib/TabBar.js b/app/ui-utils/client/lib/TabBar.js deleted file mode 100644 index 5117af3ad6b..00000000000 --- a/app/ui-utils/client/lib/TabBar.js +++ /dev/null @@ -1,104 +0,0 @@ -import _ from 'underscore'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; - -export const TABBAR_DEFAULT_VISIBLE_ICON_COUNT = 6; - -export const TabBar = new class TabBar { - get size() { - return this._size.get(); - } - - set size(s) { - this._size.set(s); - } - - constructor() { - this.buttons = new ReactiveVar({}); - this._size = new ReactiveVar(TABBAR_DEFAULT_VISIBLE_ICON_COUNT); - this.extraGroups = {}; - } - - show() { - $('.flex-tab-bar').show(); - } - - hide() { - $('.flex-tab-bar').hide(); - } - - addButton(config) { - if (!config || !config.id) { - return false; - } - - const btns = this.buttons.curValue; - btns[config.id] = config; - - if (this.extraGroups[config.id]) { - btns[config.id].groups = _.union(btns[config.id].groups || [], this.extraGroups[config.id]); - } - - // When you add a button with an order value of -1 - // we assume you want to force the visualization of your button - // so we increase the number of buttons that are shown so you - // don't end up hiding any of the default ones - if (config.order === -1) { - Tracker.nonreactive(() => this.size++); - } - - this.buttons.set(btns); - } - - removeButton(id) { - const btns = this.buttons.curValue; - - // Here we decrease the shown count as your - // button is no longer present - if (btns[id] && btns[id].order === -1) { - Tracker.nonreactive(() => this.size--); - } - - delete btns[id]; - - this.buttons.set(btns); - } - - updateButton(id, config) { - const btns = this.buttons.curValue; - if (btns[id]) { - btns[id] = _.extend(btns[id], config); - this.buttons.set(btns); - } - } - - getButtons() { - const buttons = _.toArray(this.buttons.get()).filter((button) => !button.condition || button.condition()); - - return _.sortBy(buttons, 'order'); - } - - getButton(id) { - return _.findWhere(this.buttons.get(), { id }); - } - - addGroup(id, groups) { - const btns = this.buttons.curValue; - if (btns[id]) { - btns[id].groups = _.union(btns[id].groups || [], groups); - this.buttons.set(btns); - } else { - this.extraGroups[id] = _.union(this.extraGroups[id] || [], groups); - } - } - - removeGroup(id, groups) { - const btns = this.buttons.curValue; - if (btns[id]) { - btns[id].groups = _.difference(btns[id].groups || [], groups); - this.buttons.set(btns); - } else { - this.extraGroups[id] = _.difference(this.extraGroups[id] || [], groups); - } - } -}(); diff --git a/app/ui-utils/client/lib/popover.js b/app/ui-utils/client/lib/popover.js index 2aaaadc799a..014212e10a7 100644 --- a/app/ui-utils/client/lib/popover.js +++ b/app/ui-utils/client/lib/popover.js @@ -1,16 +1,11 @@ import './popover.html'; -import { Meteor } from 'meteor/meteor'; import { Blaze } from 'meteor/blaze'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Template } from 'meteor/templating'; import _ from 'underscore'; -import { hide, leave } from './ChannelActions'; import { messageBox } from './messageBox'; import { MessageAction } from './MessageAction'; -import { RoomManager } from './RoomManager'; -import { ChatSubscription } from '../../../models/client'; -import { isRtl, handleError } from '../../../utils/client'; +import { isRtl } from '../../../utils/client'; export const popover = { renderedPopover: null, @@ -174,58 +169,14 @@ Template.popover.events({ 'click [data-type="message-action"]'(e, t) { const button = MessageAction.getButtonById(e.currentTarget.dataset.id); if ((button != null ? button.action : undefined) != null) { - button.action.call(t.data.data, e, t.data.instance); + e.stopPropagation(); + e.preventDefault(); + const { tabBar, rid } = t.data.instance; + button.action.call(t.data.data, e, { tabBar, rid }); popover.close(); return false; } }, - 'click [data-type="sidebar-item"]'(e, instance) { - popover.close(); - const { rid, name, template } = instance.data.data; - const action = e.currentTarget.dataset.id; - - if (action === 'hide') { - hide(template, rid, name); - } - - if (action === 'leave') { - leave(template, rid, name); - } - - if (action === 'read') { - Meteor.call('readMessages', rid); - return false; - } - - if (action === 'unread') { - Meteor.call('unreadMessages', null, rid, function(error) { - if (error) { - return handleError(error); - } - - const subscription = ChatSubscription.findOne({ rid }); - if (subscription == null) { - return; - } - RoomManager.close(subscription.t + subscription.name); - - FlowRouter.go('home'); - }); - - return false; - } - - if (action === 'favorite') { - Meteor.call('toggleFavorite', rid, !$(e.currentTarget).hasClass('rc-popover__item--star-filled'), function(err) { - popover.close(); - if (err) { - handleError(err); - } - }); - - return false; - } - }, }); Template.popover.helpers({ diff --git a/app/ui/client/components/contextualBar.html b/app/ui/client/components/contextualBar.html deleted file mode 100644 index e3ed4bcea6a..00000000000 --- a/app/ui/client/components/contextualBar.html +++ /dev/null @@ -1,28 +0,0 @@ - diff --git a/app/ui/client/components/contextualBar.js b/app/ui/client/components/contextualBar.js deleted file mode 100644 index b96e6b61894..00000000000 --- a/app/ui/client/components/contextualBar.js +++ /dev/null @@ -1,31 +0,0 @@ -import { Template } from 'meteor/templating'; - -Template.contextualBar.events({ - 'click .js-close'(e, t) { - t.tabBar.close(); - }, -}); - -Template.contextualBar.onCreated(function() { - this.tabBar = Template.currentData().tabBar; -}); - -Template.contextualBar.helpers({ - id() { - return Template.instance().tabBar.getId(); - }, - template() { - return Template.instance().tabBar.getTemplate(); - }, - headerData() { - return Template.instance().tabBar.getData(); - }, - flexData() { - const { tabBar } = Template.instance(); - return { - tabBar, - ...tabBar.getData(), - ...Template.currentData().data, - }; - }, -}); diff --git a/app/ui/client/components/header/header.js b/app/ui/client/components/header/header.js index 380a80bdacd..205d52a227f 100644 --- a/app/ui/client/components/header/header.js +++ b/app/ui/client/components/header/header.js @@ -1,6 +1,6 @@ import { Template } from 'meteor/templating'; -import { TabBar, fireGlobalEvent } from '../../../../ui-utils'; +import { fireGlobalEvent } from '../../../../ui-utils'; import './header.html'; Template.header.helpers({ @@ -8,7 +8,7 @@ Template.header.helpers({ return Template.instance().data.back; }, buttons() { - return TabBar.getButtons(); + console.log('asdasd'); }, }); diff --git a/app/ui/client/components/header/headerRoom.html b/app/ui/client/components/header/headerRoom.html deleted file mode 100644 index a83271ff41d..00000000000 --- a/app/ui/client/components/header/headerRoom.html +++ /dev/null @@ -1,75 +0,0 @@ - diff --git a/app/ui/client/components/header/headerRoom.js b/app/ui/client/components/header/headerRoom.js deleted file mode 100644 index 701400f0c5e..00000000000 --- a/app/ui/client/components/header/headerRoom.js +++ /dev/null @@ -1,251 +0,0 @@ -import toastr from 'toastr'; -import { Meteor } from 'meteor/meteor'; -import { ReactiveDict } from 'meteor/reactive-dict'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Session } from 'meteor/session'; -import { Template } from 'meteor/templating'; -import { FlowRouter } from 'meteor/kadira:flow-router'; - -import { t, roomTypes, handleError } from '../../../../utils'; -import { TabBar, fireGlobalEvent, call } from '../../../../ui-utils'; -import { ChatSubscription, Rooms, ChatRoom } from '../../../../models'; -import { settings } from '../../../../settings'; -import { emoji } from '../../../../emoji'; -import { Markdown } from '../../../../markdown/client'; -import { hasAllPermission } from '../../../../authorization'; -import { getUidDirectMessage } from '../../../../ui-utils/client/lib/getUidDirectMessage'; - -import './headerRoom.html'; - -const getUserStatus = (id) => { - const roomData = Session.get(`roomData${ id }`); - return roomTypes.getUserStatus(roomData.t, id); -}; - -const getUserStatusText = (id) => { - const roomData = Session.get(`roomData${ id }`); - return roomTypes.getUserStatusText(roomData.t, id); -}; - -Template.headerRoom.helpers({ - isDiscussion: () => Template.instance().state.get('discussion'), - hasPresence() { - const room = Rooms.findOne(this._id); - return !roomTypes.getConfig(room.t).isGroupChat(room); - }, - isToggleFavoriteButtonVisible: () => Template.instance().state.get('favorite') !== null, - isToggleFavoriteButtonChecked: () => Template.instance().state.get('favorite'), - toggleFavoriteButtonIconLabel: () => (Template.instance().state.get('favorite') ? t('Unfavorite') : t('Favorite')), - toggleFavoriteButtonIcon: () => (Template.instance().state.get('favorite') ? 'star-filled' : 'star'), - uid() { - return getUidDirectMessage(this._id); - }, - back() { - return Template.instance().data.back; - }, - avatarBackground() { - const roomData = Session.get(`roomData${ this._id }`); - if (!roomData) { return ''; } - return roomTypes.getConfig(roomData.t).getAvatarPath(roomData); - }, - buttons() { - return TabBar.getButtons(); - }, - - isTranslated() { - const sub = ChatSubscription.findOne({ rid: this._id }, { fields: { autoTranslate: 1, autoTranslateLanguage: 1 } }); - return settings.get('AutoTranslate_Enabled') && ((sub != null ? sub.autoTranslate : undefined) === true) && (sub.autoTranslateLanguage != null); - }, - roomName() { - const roomData = Session.get(`roomData${ this._id }`); - if (!roomData) { return ''; } - - return roomTypes.getRoomName(roomData.t, roomData); - }, - - secondaryName() { - const roomData = Session.get(`roomData${ this._id }`); - if (!roomData) { return ''; } - - return roomTypes.getSecondaryRoomName(roomData.t, roomData); - }, - - roomTopic() { - const roomData = Session.get(`roomData${ this._id }`); - if (!roomData || !roomData.topic) { return ''; } - - let roomTopic = Markdown.parse(roomData.topic.replace(/\n/mg, ' ')); - - // ' to apostrophe (') for emojis such as :') - roomTopic = roomTopic.replace(/'/g, '\''); - - roomTopic = Object.keys(emoji.packages).reduce((topic, emojiPackage) => emoji.packages[emojiPackage].render(topic), roomTopic); - - // apostrophe (') back to ' - return roomTopic.replace(/\'/g, '''); - }, - - roomIcon() { - const roomData = Session.get(`roomData${ this._id }`); - if (!(roomData != null ? roomData.t : undefined)) { return ''; } - - return roomTypes.getIcon(roomData); - }, - - tokenAccessChannel() { - return Template.instance().hasTokenpass.get(); - }, - encryptionState() { - const room = ChatRoom.findOne(this._id); - return settings.get('E2E_Enable') && room && room.encrypted && 'encrypted'; - }, - - userStatus() { - return getUserStatus(this._id) || 'offline'; - }, - - userStatusText() { - const statusText = getUserStatusText(this._id); - if (statusText) { - return statusText; - } - - const presence = getUserStatus(this._id); - if (presence) { - return t(presence); - } - - const oldStatusText = Template.instance().userOldStatusText.get(); - if (oldStatusText) { - return oldStatusText; - } - - return t('offline'); - }, - - fixedHeight() { - return Template.instance().data.fixedHeight; - }, - - fullpage() { - return Template.instance().data.fullpage; - }, - - isChannel() { - return Template.instance().currentChannel != null; - }, - - isSection() { - return Template.instance().data.sectionName != null; - }, -}); - -Template.headerRoom.events({ - 'click .iframe-toolbar .js-iframe-action'(e) { - fireGlobalEvent('click-toolbar-button', { id: this.id }); - e.currentTarget.querySelector('button').blur(); - return false; - }, - - 'click .js-favorite'(event, instance) { - event.stopPropagation(); - event.preventDefault(); - event.currentTarget.blur(); - - return Meteor.call( - 'toggleFavorite', - this._id, - !instance.state.get('favorite'), - (err) => err && handleError(err), - ); - }, - - 'click .js-open-parent-channel'(event, t) { - event.preventDefault(); - const { prid } = t.currentChannel; - FlowRouter.goToRoomById(prid); - }, - 'click .js-toggle-encryption'(event) { - event.stopPropagation(); - event.preventDefault(); - const room = ChatRoom.findOne(this._id); - if (hasAllPermission('edit-room', this._id) || (room && room.t)) { - call('saveRoomSettings', this._id, 'encrypted', !(room && room.encrypted)).then(() => { - toastr.success( - t('Encrypted_setting_changed_successfully'), - ); - }); - } - }, - -}); - -const loadUserStatusText = () => { - const instance = Template.instance(); - - if (!instance || !instance.data || !instance.data._id) { - return; - } - - const id = instance.data._id; - - if (Rooms.findOne(id).t !== 'd') { - return; - } - - const userId = getUidDirectMessage(id); - - // If the user is already on the local collection, the method call is not necessary - const found = Meteor.users.findOne(userId, { fields: { _id: 1 } }); - if (found) { - return; - } - - Meteor.call('getUserStatusText', userId, (error, result) => { - if (!error) { - instance.userOldStatusText.set(result); - } - }); -}; - -Template.headerRoom.onCreated(function() { - this.state = new ReactiveDict(); - - const isFavoritesEnabled = () => settings.get('Favorite_Rooms'); - - const isDiscussion = (rid) => { - const room = ChatRoom.findOne({ _id: rid }); - return !!(room && room.prid); - }; - - this.autorun(() => { - const { _id: rid } = Template.currentData(); - - this.state.set({ - rid, - discussion: isDiscussion(rid), - }); - - if (!this.state.get('discussion') && isFavoritesEnabled()) { - const subscription = ChatSubscription.findOne({ rid }, { fields: { f: 1 } }); - this.state.set('favorite', !!(subscription && subscription.f)); - } else { - this.state.set('favorite', null); - } - }); - - this.currentChannel = (this.data && this.data._id && Rooms.findOne(this.data._id)) || undefined; - - this.hasTokenpass = new ReactiveVar(false); - this.userOldStatusText = new ReactiveVar(null); - - if (settings.get('API_Tokenpass_URL') !== '') { - Meteor.call('getChannelTokenpass', this.data._id, (error, result) => { - if (!error) { - this.hasTokenpass.set(!!(result && result.tokens && result.tokens.length > 0)); - } - }); - } - - loadUserStatusText(); -}); diff --git a/app/ui/client/index.js b/app/ui/client/index.js index a3b53193f2b..2e23faa91ac 100644 --- a/app/ui/client/index.js +++ b/app/ui/client/index.js @@ -48,9 +48,6 @@ import './components/popupList'; import './components/selectDropdown.html'; import './components/header/header'; -import './components/header/headerRoom'; -import './components/contextualBar.html'; -import './components/contextualBar'; import './components/tooltip'; import './lib/Tooltip'; diff --git a/app/ui/client/lib/iframeCommands.js b/app/ui/client/lib/iframeCommands.js index d4d3048b118..5ad9003470b 100644 --- a/app/ui/client/lib/iframeCommands.js +++ b/app/ui/client/lib/iframeCommands.js @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Session } from 'meteor/session'; import { ServiceConfiguration } from 'meteor/service-configuration'; import s from 'underscore.string'; @@ -8,6 +7,7 @@ import { AccountBox } from '../../../ui-utils'; import { settings } from '../../../settings'; import { callbacks } from '../../../callbacks'; import { baseURI } from '../../../utils/client/lib/baseuri.js'; +import { add, remove } from '../../../../client/views/room/lib/Toolbox/IframeButtons'; const commands = { go(data) { @@ -69,16 +69,11 @@ const commands = { }); }, - 'set-toolbar-button'({ id, icon, label: i18nTitle }) { - const toolbar = Session.get('toolbarButtons') || { buttons: {} }; - toolbar.buttons[id] = { icon, i18nTitle }; - Session.set('toolbarButtons', toolbar); + 'set-toolbar-button'({ id, icon, label }) { + add(id, { id, icon, label }); }, - 'remove-toolbar-button'({ id }) { - const toolbar = Session.get('toolbarButtons') || { buttons: {} }; - delete toolbar.buttons[id]; - Session.set('toolbarButtons', toolbar); + remove(id); }, }; diff --git a/app/ui/client/views/app/lib/getCommonRoomEvents.js b/app/ui/client/views/app/lib/getCommonRoomEvents.js new file mode 100644 index 00000000000..d984f79831e --- /dev/null +++ b/app/ui/client/views/app/lib/getCommonRoomEvents.js @@ -0,0 +1,317 @@ +import Clipboard from 'clipboard'; +import { Meteor } from 'meteor/meteor'; +import { Random } from 'meteor/random'; +import { FlowRouter } from 'meteor/kadira:flow-router'; + +import { + fireGlobalEvent, + popover, + Layout, + MessageAction, +} from '../../../../../ui-utils/client'; +import { call } from '../../../../../ui-utils/client/lib/callMethod'; +import { promises } from '../../../../../promises/client'; +import { isURL } from '../../../../../utils/lib/isURL'; +import { openUserCard } from '../../../lib/UserCard'; +import { messageArgs } from '../../../../../ui-utils/client/lib/messageArgs'; +import { ChatMessage, Rooms } from '../../../../../models'; +import { t, roomTypes } from '../../../../../utils/client'; +import { chatMessages } from '../room'; +import { EmojiEvents } from '../../../../../reactions/client/init'; + +const mountPopover = (e, i, outerContext) => { + let context = $(e.target).parents('.message').data('context'); + if (!context) { + context = 'message'; + } + + const messageContext = messageArgs(outerContext); + + let menuItems = MessageAction.getButtons(messageContext, context, 'menu').map((item) => ({ + icon: item.icon, + name: t(item.label), + type: 'message-action', + id: item.id, + modifier: item.color, + })); + + if (window.matchMedia('(max-width: 500px)').matches) { + const messageItems = MessageAction.getButtons(messageContext, context, 'message').map((item) => ({ + icon: item.icon, + name: t(item.label), + type: 'message-action', + id: item.id, + modifier: item.color, + })); + + menuItems = menuItems.concat(messageItems); + } + + const [items, deleteItem] = menuItems.reduce((result, value) => { result[value.id === 'delete-message' ? 1 : 0].push(value); return result; }, [[], []]); + const groups = [{ items }]; + + if (deleteItem.length) { + groups.push({ items: deleteItem }); + } + + const config = { + columns: [ + { + groups, + }, + ], + instance: i, + currentTarget: e.currentTarget, + data: outerContext, + activeElement: $(e.currentTarget).parents('.message')[0], + onRendered: () => new Clipboard('.rc-popover__item'), + }; + + popover.open(config); +}; + +export const getCommonRoomEvents = () => ({ + ...(() => { + let touchMoved = false; + let lastTouchX = null; + let lastTouchY = null; + + let touchtime = null; + return { + ...EmojiEvents, + 'click .message img'(e) { + clearTimeout(touchtime); + if (touchMoved === true) { + e.preventDefault(); + e.stopPropagation(); + } + }, + + 'touchstart .message'(e) { + const { touches } = e.originalEvent; + if (touches && touches.length) { + lastTouchX = touches[0].pageX; + lastTouchY = touches[0].pagey; + } + touchMoved = false; + if (e.originalEvent.touches.length !== 1) { + return; + } + + if ($(e.currentTarget).hasClass('system')) { + return; + } + + if (e.target && (e.target.nodeName === 'AUDIO')) { + return; + } + + if (e.target && (e.target.nodeName === 'A') && isURL(e.target.getAttribute('href'))) { + e.preventDefault(); + e.stopPropagation(); + } + + const doLongTouch = () => { + mountPopover(e, t, this); + }; + + clearTimeout(touchtime); + touchtime = setTimeout(doLongTouch, 500); + }, + + 'touchend .message'(e) { + clearTimeout(touchtime); + if (e.target && (e.target.nodeName === 'A') && isURL(e.target.getAttribute('href'))) { + if (touchMoved === true) { + e.preventDefault(); + e.stopPropagation(); + return; + } + + window.open(e.target.href); + } + }, + + 'touchmove .message'(e) { + const { touches } = e.originalEvent; + if (touches && touches.length) { + const deltaX = Math.abs(lastTouchX - touches[0].pageX); + const deltaY = Math.abs(lastTouchY - touches[0].pageY); + if (deltaX > 5 || deltaY > 5) { + touchMoved = true; + } + } + clearTimeout(touchtime); + }, + + 'touchcancel .message'() { + clearTimeout(touchtime); + }, + }; + })(), + 'click [data-message-action]'(event, template) { + const button = MessageAction.getButtonById(event.currentTarget.dataset.messageAction); + if ((button != null ? button.action : undefined) != null) { + button.action.call(this, event, { tabBar: template.tabBar, rid: template.data.rid }); + } + }, + 'click .js-follow-thread'(e) { + e.preventDefault(); + e.stopPropagation(); + const { msg } = messageArgs(this); + call('followMessage', { mid: msg._id }); + }, + 'click .js-unfollow-thread'(e) { + e.preventDefault(); + e.stopPropagation(); + const { msg } = messageArgs(this); + call('unfollowMessage', { mid: msg._id }); + }, + 'click .js-open-thread'(event) { + event.preventDefault(); + event.stopPropagation(); + + const { msg: { rid, _id, tmid } } = messageArgs(this); + const room = Rooms.findOne({ _id: rid }); + + FlowRouter.go(FlowRouter.getRouteName(), { + rid, + name: room.name, + tab: 'thread', + context: tmid || _id, + }, { + jump: tmid && tmid !== _id && _id && _id, + }); + }, + 'click .js-reply-broadcast'() { + const msg = messageArgs(this); + roomTypes.openRouteLink('d', { name: msg.u.username }, { ...FlowRouter.current().queryParams, reply: msg._id }); + }, + + 'click .image-to-download'(event) { + const { msg } = messageArgs(this); + ChatMessage.update({ _id: msg._id, 'urls.url': $(event.currentTarget).data('url') }, { $set: { 'urls.$.downloadImages': true } }); + ChatMessage.update({ _id: msg._id, 'attachments.image_url': $(event.currentTarget).data('url') }, { $set: { 'attachments.$.downloadImages': true } }); + }, + 'click .user-card-message'(e, instance) { + const { msg } = messageArgs(this); + if (!Meteor.userId()) { + return; + } + + const { username } = msg.u; + + if (username) { + openUserCard({ + username, + rid: instance.data.rid, + target: e.currentTarget, + open: (e) => { + e.preventDefault(); + instance.data.openProfileTab(username); + }, + }); + } + }, + 'click .js-actionButton-respondWithMessage'(event, instance) { + const { rid } = instance.data; + const msg = event.currentTarget.value; + if (!msg) { + return; + } + + const { input } = chatMessages[rid]; + input.value = msg; + input.focus(); + }, + async 'click .js-actionButton-sendMessage'(event, instance) { + const { rid } = instance.data; + const msg = event.currentTarget.value; + let msgObject = { _id: Random.id(), rid, msg }; + if (!msg) { + return; + } + + msgObject = await promises.run('onClientBeforeSendMessage', msgObject); + + const _chatMessages = chatMessages[rid]; + if (_chatMessages && await _chatMessages.processSlashCommand(msgObject)) { + return; + } + + await call('sendMessage', msgObject); + }, + 'click .message-actions__menu'(e, template) { + const messageContext = messageArgs(this); + const { msg: message, context: ctx } = messageContext; + const context = ctx || message.context || message.actionContext || 'message'; + + const allItems = MessageAction.getButtons(messageContext, context, 'menu').map((item) => ({ + icon: item.icon, + name: t(item.label), + type: 'message-action', + id: item.id, + modifier: item.color, + })); + + const itemsBelowDivider = [ + 'delete-message', + 'report-message', + ]; + const [items, alertsItem] = allItems.reduce((result, value) => { result[itemsBelowDivider.includes(value.id) ? 1 : 0].push(value); return result; }, [[], []]); + const groups = [{ items }]; + + if (alertsItem.length) { + groups.push({ items: alertsItem }); + } + const config = { + columns: [ + { + groups, + }, + ], + instance: template, + rid: template.data.rid, + data: this, + currentTarget: e.currentTarget, + activeElement: $(e.currentTarget).parents('.message')[0], + onRendered: () => new Clipboard('.rc-popover__item'), + }; + + popover.open(config); + }, + 'click .mention-link'(e, instance) { + e.stopPropagation(); + e.preventDefault(); + + if (!Meteor.userId()) { + return; + } + + const { currentTarget: { dataset: { channel, group, username } } } = e; + + if (channel) { + if (Layout.isEmbedded()) { + fireGlobalEvent('click-mention-link', { path: FlowRouter.path('channel', { name: channel }), channel }); + } + FlowRouter.goToRoomById(channel); + return; + } + + if (group) { + return; + } + + if (username) { + openUserCard({ + username, + rid: instance.data.rid, + target: e.currentTarget, + open: (e) => { + e.preventDefault(); + instance.data.openProfileTab(username); + }, + }); + } + }, +}); diff --git a/app/ui/client/views/app/room.html b/app/ui/client/views/app/room.html index 09b9ad9e2cf..89281c46c17 100644 --- a/app/ui/client/views/app/room.html +++ b/app/ui/client/views/app/room.html @@ -1,15 +1,6 @@