Rewrite: Room Header (#19808)
parent
8b6bfbb10b
commit
08059e4e18
@ -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, |
||||
}); |
||||
}); |
||||
}); |
||||
@ -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]); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
}); |
||||
}); |
||||
@ -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]); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
}); |
||||
@ -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<FC>, |
||||
order: 7, |
||||
}); |
||||
@ -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'), |
||||
}); |
||||
}); |
||||
@ -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<FC>, |
||||
full: true, |
||||
order: 1, |
||||
} : null), [discussionEnabled]); |
||||
}); |
||||
@ -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(); |
||||
}, |
||||
}); |
||||
@ -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'); |
||||
} |
||||
}); |
||||
}); |
||||
@ -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]); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
@ -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, |
||||
}); |
||||
}); |
||||
}); |
||||
@ -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]); |
||||
}); |
||||
@ -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; |
||||
}); |
||||
@ -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, |
||||
}); |
||||
@ -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', |
||||
}); |
||||
} |
||||
}); |
||||
}); |
||||
@ -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 => <Header.ToolBoxAction {...props}> |
||||
{isLive ? <Header.Badge title={t('Livestream_live_now')} variant='danger'>!</Header.Badge> : null} |
||||
</Header.ToolBoxAction>, |
||||
renderOption: ({ label: { title, icon }, ...props }: any): React.ReactNode => <Option label={title} title={title} icon={icon} {...props}> |
||||
{isLive ? <Badge title={t('Livestream_live_now')} variant='danger'>!</Badge> : null } |
||||
</Option>, |
||||
} : null), [enabled, isLive, t]); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
@ -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'); |
||||
} |
||||
}); |
||||
}); |
||||
@ -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]); |
||||
}); |
||||
@ -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(); |
||||
}, |
||||
], |
||||
}); |
||||
|
||||
@ -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'); |
||||
} |
||||
}); |
||||
}); |
||||
@ -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]); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
@ -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'); |
||||
} |
||||
}); |
||||
}); |
||||
@ -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<FC>, |
||||
order: 13, |
||||
full: true, |
||||
} : null), [shouldAddAction]); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
}); |
||||
@ -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, |
||||
}); |
||||
@ -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, |
||||
}); |
||||
}); |
||||
@ -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<FC>; |
||||
|
||||
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 <Header.ToolBoxAction {...props} > |
||||
{ unread > 0 && <Header.Badge variant={variant}>{unread}</Header.Badge> } |
||||
</Header.ToolBoxAction>; |
||||
}, |
||||
order: 2, |
||||
} : null), [threadsEnabled, room.tunread?.length, room.tunreadUser?.length, room.tunreadGroup?.length]); |
||||
}); |
||||
@ -1,3 +0,0 @@ |
||||
<template name="threads"> |
||||
{{> ThreadsList threads=threads rid=rid onClose=close }} |
||||
</template> |
||||
@ -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')), |
||||
}); |
||||
}); |
||||
@ -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]); |
||||
}); |
||||
@ -1,101 +0,0 @@ |
||||
<template name="flexTabBar"> |
||||
{{#unless embeddedVersion}} |
||||
<div class="flex-tab-container border-component-color {{opened}}"> |
||||
<section class="flex-tab border-component-color content-background-color"> |
||||
<div class="content flex-tab__content"> |
||||
<header class="contextual-bar__header {{#unless headerData}}contextual-bar__header--empty{{/unless}}"> |
||||
{{#with headerData}} |
||||
<div class="contextual-bar__header-data"> |
||||
{{> icon block="contextual-bar__header-icon" icon=icon}} |
||||
<h1 class="contextual-bar__header-title">{{_ label}}</h1> |
||||
</div> |
||||
{{/with}} |
||||
<button class="contextual-bar__header-close js-close close-flex-tab" aria-label="{{_ "Close"}}"> |
||||
{{> icon block="contextual-bar__header-close-icon" icon="plus"}} |
||||
</button> |
||||
</header> |
||||
{{> Template.dynamic template=template data=flexData}} |
||||
</div> |
||||
</section> |
||||
<div class="flex-tab-bar content-background-color" role="toolbar"> |
||||
{{#each buttons}} |
||||
<div class="tab-button {{active}} {{visible}} {{class}}" title="{{title}}"> |
||||
<button aria-label="{{title}}"> |
||||
{{> icon block="tab-button-icon" icon=icon }} |
||||
</button> |
||||
</div> |
||||
{{/each}} |
||||
</div> |
||||
</div> |
||||
{{/unless}} |
||||
</template> |
||||
|
||||
<template name="RoomsActionTab"> |
||||
{{# with postButtons}} |
||||
<div class="rc-room-actions iframe-toolbar"> |
||||
{{#each .}} |
||||
<div class="rc-room-actions__action tab-button {{active}} {{visible}} {{class}} js-iframe-action" data-id="{{id}}"> |
||||
<button class="rc-room-actions__button" title="{{title}}"> |
||||
{{> icon block="tab-button-icon" icon=icon }} |
||||
</button> |
||||
</div> |
||||
{{/each}} |
||||
</div> |
||||
{{/with}} |
||||
<div class="rc-room-actions"> |
||||
{{#each buttons}} |
||||
<div class="rc-room-actions__action tab-button {{active}} {{visible}} {{class}} js-action" data-id="{{id}}"> |
||||
{{#with badge}} |
||||
<span class="rc-badge {{class}}">{{body}}</span> |
||||
{{/with}} |
||||
<button class="rc-room-actions__button" title="{{title}}"> |
||||
{{> icon block="tab-button-icon" icon=icon }} |
||||
</button> |
||||
</div> |
||||
{{/each}} |
||||
{{# with moreButtons}} |
||||
<div class="rc-room-actions__action {{opened}}"> |
||||
<button class="rc-room-actions__button js-more" title="{{_ 'More'}}"> |
||||
{{> icon block="tab-button-icon" icon="menu" }} |
||||
</button> |
||||
</div> |
||||
{{/with}} |
||||
</div> |
||||
</template> |
||||
|
||||
<template name="RoomsActionMore"> |
||||
<div class="rc-popover__header"> |
||||
<div class="rc-popover__title"> |
||||
{{_"More actions"}} |
||||
</div> |
||||
{{> icon block="rc-popover__close js-close" icon="plus" }} |
||||
</div> |
||||
<div class="rc-popover__container rc-room-actions__more-container"> |
||||
<!-- <div class=""> --> |
||||
{{#each buttons}} |
||||
<div class="rc-room-actions__more-action tab-button {{active}} {{visible}} {{class}} js-action" title="{{title}}"> |
||||
<button class="rc-room-actions__button"> |
||||
{{> icon block="tab-button-icon" icon=icon }} |
||||
</button> |
||||
<span class="rc-room-actions__description">{{title}}</span> |
||||
</div> |
||||
{{/each}} |
||||
<!-- </div> --> |
||||
</div> |
||||
</template> |
||||
|
||||
<!-- <template name="RoomsActionContent"> |
||||
{{#if template}} |
||||
<div class="rc-room-actions__content {{opened}}"> |
||||
<section class="flex-tab border-component-color"> |
||||
<header class="rc-room-actions__content-header"> |
||||
{{> icon block="rc-room-actions__content-header-icon" icon="clip"}} |
||||
<h1 class="rc-room-actions__content-header-title">Title</h1> |
||||
</header> |
||||
<main class="rc-room-actions__content-main"> |
||||
{{> Template.dynamic template=template data=flexData}} |
||||
</main> |
||||
</section> |
||||
</div> |
||||
{{/if}} |
||||
</template> --> |
||||
@ -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; |
||||
}, |
||||
}); |
||||
@ -1,3 +0,0 @@ |
||||
import './flexTabBar.html'; |
||||
import './flexTabBar'; |
||||
// import './tabs/uploadedFilesList';
|
||||
@ -1 +0,0 @@ |
||||
export * from './client/index'; |
||||
@ -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(); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
}(); |
||||
@ -1,28 +0,0 @@ |
||||
<template name="contextualBar"> |
||||
{{#if template}} |
||||
{{#if full}} |
||||
{{> Template.dynamic template=template data=flexData}} |
||||
{{else}} |
||||
<div class="contextual-bar"> |
||||
<div class="contextual-bar-wrap"> |
||||
<header class="contextual-bar__header"> |
||||
{{#with headerData}} |
||||
<div class="contextual-bar__header-data"> |
||||
{{> icon block="contextual-bar__header-icon" icon=icon}} |
||||
<h1 class="contextual-bar__header-title">{{_ label}} |
||||
<sub class="contextual-bar__header-description">{{ description }}</sub> |
||||
</h1> |
||||
</div> |
||||
{{/with}} |
||||
<button class="contextual-bar__header-close js-close" aria-label="{{_ "Close"}}"> |
||||
{{> icon block="contextual-bar__header-close-icon" icon="plus"}} |
||||
</button> |
||||
</header> |
||||
<section class="contextual-bar__content flex-tab {{id}}"> |
||||
{{> Template.dynamic template=template data=flexData}} |
||||
</section> |
||||
</div> |
||||
</div> |
||||
{{/if}} |
||||
{{/if}} |
||||
</template> |
||||
@ -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, |
||||
}; |
||||
}, |
||||
}); |
||||
@ -1,75 +0,0 @@ |
||||
<template name="headerRoom"> |
||||
<header class="rc-header rc-header--room" > |
||||
<div class="rc-header__wrap"> |
||||
|
||||
<div class="rc-header__block rc-header--burger"> |
||||
{{> burger}} |
||||
</div> |
||||
|
||||
{{#if isDiscussion}} |
||||
<div class="rc-header__block rc-header__block--action js-open-parent-channel" title={{_ "Back_to_room"}}> |
||||
<span class="rc-header__first-icon">{{> icon block="rc-header__icon rc-header__icon" icon="back"}}</span> |
||||
</div> |
||||
{{/if}} |
||||
|
||||
{{#if isToggleFavoriteButtonVisible}} |
||||
<div class="rc-header__block rc-header__favorite rc-header__block--action"> |
||||
<button title="{{toggleFavoriteButtonIconLabel}}" type="button" class="rc-header__toggle-favorite {{#if isToggleFavoriteButtonChecked}}rc-header__toggle-favorite--checked{{/if}} rc-header__first-icon js-favorite"> |
||||
{{> icon block="rc-header__icon" icon=toggleFavoriteButtonIcon}} |
||||
</button> |
||||
</div> |
||||
{{/if}} |
||||
|
||||
<!-- TODO: fix it style and helper --> |
||||
{{#if tokenAccessChannel}} |
||||
<i class="icon-tokenpass" aria-label="{{_ "Tokenpass_Channel_Label"}}"></i> |
||||
{{/if}} |
||||
|
||||
<div class="rc-header__content rc-header__block"> |
||||
<div class="rc-header__block"> |
||||
<div class="rc-header__image"> |
||||
{{> avatar url=avatarBackground}} |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="rc-header__data"> |
||||
{{#unless secondaryName}} |
||||
<div class="rc-header__name">{{> icon block="rc-header__icon" icon=roomIcon}}{{roomName}}</div> |
||||
{{else}} |
||||
<div class="rc-header__name">{{roomName}} <div class="rc-header__username">@{{secondaryName}}</div></div> |
||||
{{/unless}} |
||||
|
||||
{{#if hasPresence}} |
||||
{{# userPresence uid=uid}}<span class="rc-header__status"> |
||||
<div class="rc-header__status-bullet rc-header__status-bullet--{{userStatus}}" title="{{_ userStatus}}"></div> |
||||
<div class="rc-header__visual-status">{{userStatusText}}</div> |
||||
</span>{{/userPresence}} |
||||
{{else}} |
||||
{{#if roomTopic}}<span class="rc-header__topic">{{{roomTopic}}}</span>{{/if}} |
||||
{{/if}} |
||||
</div> |
||||
|
||||
{{#if sentimentSmile}} |
||||
<span class="sentiment">{{sentimentSmile}}</span> |
||||
{{/if}} |
||||
{{#if isTranslated}} |
||||
<i class="icon-language" title="{{_ "Translated"}}"></i> |
||||
{{/if}} |
||||
</div> |
||||
|
||||
{{#if isSection}} |
||||
<span class="rc-header__block">{{_ sectionName}}</span> |
||||
{{/if}} |
||||
{{#if encryptionState}} |
||||
<div class="rc-header__block rc-header__block-action rc-header__encryption"> |
||||
<div class="rc-room-actions"> |
||||
<button title="{{_ 'E2E_Enabled'}}" class="rc-room-actions__action tab-button rc-header__toggle-encryption js-toggle-encryption {{encryptionState}}">{{> icon icon="key"}}</button> |
||||
</div> |
||||
</div> |
||||
{{/if}} |
||||
{{#if Template.contentBlock}} |
||||
{{> Template.contentBlock}} |
||||
{{/if}} |
||||
</div> |
||||
</header> |
||||
</template> |
||||
@ -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(); |
||||
}); |
||||
@ -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); |
||||
}, |
||||
}); |
||||
} |
||||
}, |
||||
}); |
||||
File diff suppressed because it is too large
Load Diff
@ -1,90 +0,0 @@ |
||||
import { Meteor } from 'meteor/meteor'; |
||||
import { Tracker } from 'meteor/tracker'; |
||||
import { Session } from 'meteor/session'; |
||||
|
||||
import { settings } from '../../settings'; |
||||
import { TabBar } from '../../ui-utils'; |
||||
import { Rooms } from '../../models'; |
||||
|
||||
Meteor.startup(function() { |
||||
Tracker.autorun(function() { |
||||
if (!settings.get('bigbluebutton_Enabled')) { |
||||
return TabBar.removeButton('bbb_video'); |
||||
} |
||||
const live = Rooms.findOne({ _id: Session.get('openedRoom'), 'streamingOptions.type': 'call' }, { fields: { streamingOptions: 1 } }); |
||||
|
||||
const groups = []; |
||||
|
||||
if (settings.get('bigbluebutton_enable_d')) { |
||||
groups.push('direct'); |
||||
} |
||||
if (settings.get('bigbluebutton_enable_p')) { |
||||
groups.push('group'); |
||||
} |
||||
if (settings.get('bigbluebutton_enable_c')) { |
||||
groups.push('channel'); |
||||
} |
||||
|
||||
TabBar.addButton({ |
||||
groups, |
||||
id: 'bbb_video', |
||||
i18nTitle: 'BBB Video Call', |
||||
icon: 'phone', |
||||
iconColor: 'red', |
||||
template: 'videoFlexTabBbb', |
||||
width: 600, |
||||
order: live ? -1 : 0, |
||||
class: () => live && 'live', |
||||
}); |
||||
}); |
||||
|
||||
Tracker.autorun(function() { |
||||
if (settings.get('Jitsi_Enabled')) { |
||||
TabBar.addButton({ |
||||
groups: ['direct', 'group'], |
||||
id: 'video', |
||||
i18nTitle: 'Call', |
||||
icon: 'phone', |
||||
iconColor: 'red', |
||||
template: 'videoFlexTab', |
||||
width: 600, |
||||
order: 0, |
||||
}); |
||||
} else { |
||||
TabBar.removeButton('video'); |
||||
} |
||||
}); |
||||
|
||||
Tracker.autorun(function() { |
||||
if (settings.get('Jitsi_Enabled') && settings.get('Jitsi_Enable_Channels')) { |
||||
TabBar.addGroup('video', ['channel']); |
||||
} else { |
||||
TabBar.removeGroup('video', ['channel']); |
||||
} |
||||
}); |
||||
|
||||
Tracker.autorun(function() { |
||||
if (settings.get('Jitsi_Enabled')) { |
||||
// Load from the jitsi meet instance.
|
||||
if (typeof JitsiMeetExternalAPI === 'undefined') { |
||||
const prefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ''; |
||||
$.getScript(`${ prefix }/packages/rocketchat_videobridge/client/public/external_api.js`); |
||||
} |
||||
|
||||
// Compare current time to call started timeout. If its past then call is probably over.
|
||||
if (Session.get('openedRoom')) { |
||||
const rid = Session.get('openedRoom'); |
||||
|
||||
const room = Rooms.findOne({ _id: rid }); |
||||
const currentTime = new Date().getTime(); |
||||
const jitsiTimeout = new Date((room && room.jitsiTimeout) || currentTime).getTime(); |
||||
|
||||
if (jitsiTimeout > currentTime) { |
||||
TabBar.updateButton('video', { class: 'attention' }); |
||||
} else { |
||||
TabBar.updateButton('video', { class: '' }); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
}); |
||||
@ -0,0 +1,71 @@ |
||||
import React, { useMemo } from 'react'; |
||||
import { useStableArray } from '@rocket.chat/fuselage-hooks'; |
||||
import { Option, Badge } from '@rocket.chat/fuselage'; |
||||
|
||||
import { useSetting } from '../../../client/contexts/SettingsContext'; |
||||
import { addAction, ToolboxActionConfig } from '../../../client/views/room/lib/Toolbox'; |
||||
import { useTranslation } from '../../../client/contexts/TranslationContext'; |
||||
import Header from '../../../client/components/Header'; |
||||
|
||||
addAction('bbb_video', ({ room }) => { |
||||
const enabled = useSetting('bigbluebutton_Enabled'); |
||||
const t = useTranslation(); |
||||
|
||||
const live = room && room.streamingOptions && room.streamingOptions.type === 'call'; |
||||
|
||||
const enabledDirect = useSetting('bigbluebutton_enable_d'); |
||||
const enabledGroup = useSetting('bigbluebutton_enable_p'); |
||||
const enabledChannel = useSetting('bigbluebutton_enable_c'); |
||||
|
||||
const groups = useStableArray([ |
||||
enabledDirect && 'direct', |
||||
enabledGroup && 'group', |
||||
enabledChannel && 'channel', |
||||
].filter(Boolean) as ToolboxActionConfig['groups']); |
||||
|
||||
return useMemo(() => (enabled ? { |
||||
groups, |
||||
id: 'bbb_video', |
||||
title: 'BBB Video Call', |
||||
icon: 'phone', |
||||
template: 'videoFlexTabBbb', |
||||
order: live ? -1 : 0, |
||||
renderAction: (props): React.ReactNode => <Header.ToolBoxAction {...props}> |
||||
{live ? <Header.Badge title={t('Started_a_video_call')} variant='primary'>!</Header.Badge> : null} |
||||
</Header.ToolBoxAction>, |
||||
renderOption: ({ label: { title, icon }, ...props }: any): React.ReactNode => <Option label={title} title={title} icon={icon} {...props}><Badge title={t('Started_a_video_call')} variant='primary'>!</Badge></Option>, |
||||
} : null), [enabled, groups, live, t]); |
||||
}); |
||||
|
||||
addAction('video', ({ room }) => { |
||||
const enabled = useSetting('Jitsi_Enabled'); |
||||
const t = useTranslation(); |
||||
|
||||
const enabledChannel = useSetting('Jitsi_Enable_Channels'); |
||||
|
||||
const groups = useStableArray([ |
||||
'direct', |
||||
'group', |
||||
'live', |
||||
enabledChannel && 'channel', |
||||
].filter(Boolean) as ToolboxActionConfig['groups']); |
||||
|
||||
const currentTime = new Date().getTime(); |
||||
const jitsiTimeout = new Date((room && room.jitsiTimeout) || currentTime).getTime(); |
||||
const live = jitsiTimeout > currentTime || null; |
||||
|
||||
return useMemo(() => (enabled ? { |
||||
groups, |
||||
id: 'video', |
||||
title: 'Call', |
||||
icon: 'phone', |
||||
template: 'videoFlexTab', |
||||
order: live ? -1 : 0, |
||||
renderAction: (props): React.ReactNode => <Header.ToolBoxAction {...props}> |
||||
{live && <Header.Badge title={t('Started_a_video_call')} variant='primary'>!</Header.Badge>} |
||||
</Header.ToolBoxAction>, |
||||
renderOption: ({ label: { title, icon }, ...props }: any): React.ReactNode => <Option label={title} title={title} icon={icon} {...props}> |
||||
{ live && <Badge title={t('Started_a_video_call')} variant='primary'>!</Badge> } |
||||
</Option>, |
||||
} : null), [enabled, groups, live, t]); |
||||
}); |
||||
@ -0,0 +1,41 @@ |
||||
import { Box, Skeleton } from '@rocket.chat/fuselage'; |
||||
import React, { useMemo } from 'react'; |
||||
|
||||
import { useTranslation } from '../../contexts/TranslationContext'; |
||||
import CounterSet from '../../components/data/CounterSet'; |
||||
import { usePolledMethodData } from '../../hooks/usePolledMethodData'; |
||||
import { AsyncStatePhase } from '../../hooks/useAsyncState'; |
||||
|
||||
function OverviewSection() { |
||||
const t = useTranslation(); |
||||
const { value: overviewData, phase: overviewStatus } = usePolledMethodData('federation:getOverviewData', useMemo(() => [], []), 10000); |
||||
|
||||
const eventCount = (overviewStatus === AsyncStatePhase.LOADING && <Skeleton variant='text' />) |
||||
|| (overviewStatus === AsyncStatePhase.REJECTED && <Box color='danger'>Error</Box>) |
||||
|| overviewData?.data[0]?.value; |
||||
const userCount = (overviewStatus === AsyncStatePhase.LOADING && <Skeleton variant='text' />) |
||||
|| (overviewStatus === AsyncStatePhase.REJECTED && <Box color='danger'>Error</Box>) |
||||
|| overviewData?.data[1]?.value; |
||||
const serverCount = (overviewStatus === AsyncStatePhase.LOADING && <Skeleton variant='text' />) |
||||
|| (overviewStatus === AsyncStatePhase.REJECTED && <Box color='danger'>Error</Box>) |
||||
|| overviewData?.data[2]?.value; |
||||
|
||||
return <CounterSet |
||||
counters={[ |
||||
{ |
||||
count: eventCount, |
||||
description: t('Number_of_events'), |
||||
}, |
||||
{ |
||||
count: userCount, |
||||
description: t('Number_of_federated_users'), |
||||
}, |
||||
{ |
||||
count: serverCount, |
||||
description: t('Number_of_federated_servers'), |
||||
}, |
||||
]} |
||||
/>; |
||||
} |
||||
|
||||
export default OverviewSection; |
||||
@ -0,0 +1,153 @@ |
||||
import React from 'react'; |
||||
|
||||
import { SettingsContext } from '../../contexts/SettingsContext'; |
||||
import Header from './Header'; |
||||
import RoomAvatar from '../avatar/RoomAvatar'; |
||||
import ToolBox from '../../views/room/Header/ToolBox'; |
||||
import { ToolboxProvider } from '../../views/room/providers/ToolboxProvider'; |
||||
import { addAction } from '../../views/room/lib/Toolbox'; |
||||
import { useRoomIcon } from '../../hooks/useRoomIcon'; |
||||
|
||||
export default { |
||||
title: 'Chat Header', |
||||
component: Header, |
||||
}; |
||||
|
||||
const room = { |
||||
t: 'c', |
||||
name: 'general general general general general general general general general general general general general general general general general general general', |
||||
_id: 'GENERAL', |
||||
encrypted: true, |
||||
autoTranslate: true, |
||||
autoTranslateLanguage: 'pt-BR', |
||||
}; |
||||
|
||||
const settings = { |
||||
Favorite_Rooms: true, |
||||
AutoTranslate_Enabled: true, |
||||
E2E_Enable: true, |
||||
}; |
||||
|
||||
|
||||
const settingContextValue = { |
||||
hasPrivateAccess: true, |
||||
isLoading: false, |
||||
querySetting: (setting) => ({ |
||||
getCurrentValue: () => settings[setting], |
||||
subscribe: () => () => undefined, |
||||
}), |
||||
querySettings: () => ({ |
||||
getCurrentValue: () => [], |
||||
subscribe: () => () => undefined, |
||||
}), |
||||
dispatch: async () => undefined, |
||||
}; |
||||
|
||||
export const ChatHeader = () => { |
||||
const icon = useRoomIcon(room); |
||||
const avatar = <RoomAvatar room={room}/>; |
||||
return <SettingsContext.Provider value={settingContextValue}> |
||||
<Header> |
||||
<Header.Avatar>{avatar}</Header.Avatar> |
||||
<Header.Content> |
||||
<Header.Content.Row> |
||||
{ icon && <Header.Icon icon={icon}/> } |
||||
<Header.Title>{room.name}</Header.Title> |
||||
<Header.State onClick icon='star'/> |
||||
<Header.State icon='key'/> |
||||
<Header.State icon='language'/> |
||||
</Header.Content.Row> |
||||
<Header.Content.Row> |
||||
<Header.Subtitle>{room.name}</Header.Subtitle> |
||||
</Header.Content.Row> |
||||
</Header.Content> |
||||
<Header.ToolBox> |
||||
<Header.ToolBoxAction icon='magnifier'/> |
||||
<Header.ToolBoxAction icon='key'/> |
||||
<Header.ToolBoxAction icon='kebab'/> |
||||
</Header.ToolBox> |
||||
</Header> |
||||
</SettingsContext.Provider>; |
||||
}; |
||||
|
||||
const toolboxRoom = { |
||||
...room, |
||||
msgs: 2, |
||||
u: { username: 'rocket.cat' }, |
||||
usersCount: 2, |
||||
}; |
||||
|
||||
// const renderWithBadge = createHeaderActionRenderer(<Header.Badge variant='primary'>1</Header.Badge>);
|
||||
// const renderWithRedBadge = createHeaderActionRenderer(<Header.Badge variant='danger'>2</Header.Badge>);
|
||||
// const renderWithOrangeBadge = createHeaderActionRenderer(<Header.Badge variant='warning'>99</Header.Badge>);
|
||||
|
||||
const renderWithBadge = (props, index) => <Header.ToolBoxAction index={index} {...props} > |
||||
<Header.Badge variant='primary'>1</Header.Badge> |
||||
</Header.ToolBoxAction>; |
||||
|
||||
const renderWithRedBadge = (props, index) => <Header.ToolBoxAction index={index} {...props} > |
||||
<Header.Badge variant='danger'>2</Header.Badge> |
||||
</Header.ToolBoxAction>; |
||||
|
||||
const renderWithOrangeBadge = (props, index) => <Header.ToolBoxAction index={index} {...props} > |
||||
<Header.Badge variant='warning'>99</Header.Badge> |
||||
</Header.ToolBoxAction>; |
||||
|
||||
|
||||
addAction('render-action-example-badge', { |
||||
groups: ['channel'], |
||||
id: 'render-action-example', |
||||
title: 'Example', |
||||
icon: 'phone', |
||||
template: 'b', |
||||
order: 0, |
||||
renderAction: renderWithBadge, |
||||
}); |
||||
|
||||
addAction('render-action-example-badge-warning', { |
||||
groups: ['channel'], |
||||
id: 'render-action-example', |
||||
title: 'Example', |
||||
icon: 'thread', |
||||
template: 'a', |
||||
order: 1, |
||||
renderAction: renderWithOrangeBadge, |
||||
}); |
||||
|
||||
|
||||
addAction('render-action-example-badge-danger', { |
||||
groups: ['channel'], |
||||
id: 'render-action-example', |
||||
title: 'Example', |
||||
icon: 'discussion', |
||||
template: 'c', |
||||
order: 2, |
||||
renderAction: renderWithRedBadge, |
||||
}); |
||||
|
||||
export const WithToolboxContext = () => { |
||||
const icon = useRoomIcon(room); |
||||
const avatar = <RoomAvatar room={room}/>; |
||||
return <SettingsContext.Provider value={settingContextValue}> |
||||
<Header> |
||||
<Header.Avatar>{avatar}</Header.Avatar> |
||||
<Header.Content> |
||||
<Header.Content.Row> |
||||
{ icon && <Header.Icon icon={icon}/> } |
||||
<Header.Title>{room.name}</Header.Title> |
||||
<Header.State onClick icon='star'/> |
||||
<Header.State icon='key'/> |
||||
<Header.State icon='language'/> |
||||
</Header.Content.Row> |
||||
<Header.Content.Row> |
||||
<Header.Subtitle>{room.name}</Header.Subtitle> |
||||
</Header.Content.Row> |
||||
</Header.Content> |
||||
<Header.ToolBox> |
||||
<ToolboxProvider room={toolboxRoom}> |
||||
<ToolBox /> |
||||
</ToolboxProvider> |
||||
</Header.ToolBox> |
||||
</Header> |
||||
</SettingsContext.Provider>; |
||||
}; |
||||
@ -0,0 +1,69 @@ |
||||
import React, { FC } from 'react'; |
||||
import { Box, Icon, Divider, ButtonGroup, ActionButton, Badge, BadgeProps } from '@rocket.chat/fuselage'; |
||||
import { css } from '@rocket.chat/css-in-js'; |
||||
|
||||
const Title: FC = (props: any) => <Box color='default' mi='x4' fontScale='s2' withTruncatedText {...props}/>; |
||||
const Subtitle: FC = (props: any) => <Box color='hint' fontScale='p1' withTruncatedText {...props}/>; |
||||
|
||||
const Row: FC = (props: any) => <Box alignItems='center' flexShrink={1} flexGrow={1} display='flex' {...props}/>; |
||||
|
||||
const HeaderIcon: FC<{ icon: JSX.Element | { name: string; color?: string } | null}> = ({ icon }) => icon && <Box display='flex' flexShrink={0} alignItems='center' size={18} overflow='hidden' justifyContent='center'>{React.isValidElement(icon) ? icon : <Icon color='info' size='x18' { ...{ name: (icon as any).name }} />}</Box>; |
||||
|
||||
const ToolBox: FC = (props: any) => <ButtonGroup small {...props}/>; |
||||
|
||||
const ToolBoxAction: FC = ({ id, icon, title, action, className, tabId, index, ...props }: any) => <ActionButton |
||||
className={className} |
||||
primary={tabId === id} |
||||
onClick={action} |
||||
title={title} |
||||
data-toolbox={index} |
||||
key={id} |
||||
icon={icon} |
||||
position='relative' |
||||
ghost |
||||
tiny |
||||
overflow='visible' |
||||
{...props} |
||||
/>; |
||||
|
||||
const ToolBoxActionBadge: FC<BadgeProps> = (props) => <Box position='absolute' className={css`top: 0; right: 0; transform: translate(30%, -30%);`}><Badge {...props}/></Box>; |
||||
|
||||
const State: FC = (props: any) => (props.onClick ? <ActionButton ghost mini {...props}/> : <Icon size={16} name={props.icon} {...props}/>); |
||||
|
||||
const Content: FC = (props: any) => <Box flexGrow={1} width={1} flexShrink={1} mi='x4' display='flex' justifyContent='center' flexDirection='column' {...props}/>; |
||||
|
||||
const Button: FC = (props: any) => <Box mi='x4' display='flex' alignItems='center' {...props}/>; |
||||
const Avatar: FC = (props: any) => <Button width='x36' {...props}/>; |
||||
|
||||
const HeaderDivider: FC = () => <Divider { ...{ mbs: 'neg-x2', mbe: 0 } as any} />; |
||||
|
||||
const Header: FC & { ToolBoxAction: FC; Badge: FC<BadgeProps> } = (props: any) => <Box rcx-room-header is='header' height='x64' display='flex' justifyContent='center' flexDirection='column' overflow='hidden' flexShrink={0}> |
||||
<Box height='x64' pi='x24' display='flex' flexGrow={1} justifyContent='center' alignItems='center' overflow='hidden' flexDirection='row' {...props}/> |
||||
<HeaderDivider/> |
||||
</Box>; |
||||
|
||||
Header.ToolBoxAction = ToolBoxAction; |
||||
Header.Badge = ToolBoxActionBadge; |
||||
|
||||
export default Header; |
||||
|
||||
Object.assign(Content, { |
||||
Row, |
||||
}); |
||||
|
||||
Object.assign(ToolBoxAction, { |
||||
Badge: ToolBoxActionBadge, |
||||
}); |
||||
|
||||
Object.assign(Header, { |
||||
Button, |
||||
State, |
||||
Avatar, |
||||
Content, |
||||
Title, |
||||
Subtitle, |
||||
ToolBox, |
||||
ToolBoxAction, |
||||
Divider: HeaderDivider, |
||||
Icon: HeaderIcon, |
||||
}); |
||||
@ -0,0 +1,3 @@ |
||||
import Header from './Header'; |
||||
|
||||
export default Header; |
||||
@ -0,0 +1,32 @@ |
||||
import { createContext, useContext } from 'react'; |
||||
|
||||
export type SizeLayout = { |
||||
sidebar: string; |
||||
contextualBar: string; |
||||
} |
||||
|
||||
export type LayoutContextValue = { |
||||
isEmbedded: boolean; |
||||
showTopNavbarEmbeddedLayout: boolean; |
||||
isMobile: boolean; |
||||
sidebar: any; |
||||
size: SizeLayout; |
||||
contextualBarExpanded: boolean; |
||||
contextualBarPosition: 'absolute' | 'relative' | 'fixed'; |
||||
} |
||||
|
||||
export const LayoutContext = createContext<LayoutContextValue>({ |
||||
isEmbedded: false, |
||||
showTopNavbarEmbeddedLayout: false, |
||||
isMobile: false, |
||||
sidebar: {}, |
||||
size: { |
||||
sidebar: '380px', |
||||
contextualBar: '380px', |
||||
}, |
||||
contextualBarPosition: 'relative', |
||||
contextualBarExpanded: false, |
||||
}); |
||||
|
||||
export const useLayout = (): LayoutContextValue => |
||||
useContext(LayoutContext); |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue