/* * Wire * Copyright (C) 2022 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * */ import {cloneElement, FC, ReactNode, useCallback, useEffect, useState} from 'react'; import {amplify} from 'amplify'; import {CSSTransition, TransitionGroup} from 'react-transition-group'; import {container} from 'tsyringe'; import {WebAppEvents} from '@wireapp/webapp-events'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {AddParticipants} from './AddParticipants'; import {ConversationDetails} from './ConversationDetails'; import {ConversationParticipants} from './ConversationParticipants'; import {GroupParticipantService} from './GroupParticipantService'; import {GroupParticipantUser} from './GroupParticipantUser'; import {GuestServicesOptions} from './GuestServicesOptions'; import {MessageDetails} from './MessageDetails'; import {Notifications} from './Notifications'; import {ParticipantDevices} from './ParticipantDevices'; import {TimedMessages} from './TimedMessages'; import {ConversationState} from '../../conversation/ConversationState'; import {Conversation} from '../../entity/Conversation'; import {Message} from '../../entity/message/Message'; import {User} from '../../entity/User'; import {isReadableMessage} from '../../guards/Message'; import {isUserEntity, isUserServiceEntity} from '../../guards/Panel'; import {isServiceEntity} from '../../guards/Service'; import {ServiceEntity} from '../../integration/ServiceEntity'; import {Core} from '../../service/CoreSingleton'; import {TeamState} from '../../team/TeamState'; import {UserState} from '../../user/UserState'; import {ActionsViewModel} from '../../view_model/ActionsViewModel'; import {ViewModelRepositories} from '../../view_model/MainViewModel'; import {RightSidebarParams} from '../AppMain'; import {useAppMainState} from '../state'; import {ContentState} from '../useAppState'; export const OPEN_CONVERSATION_DETAILS = 'OPEN_CONVERSATION_DETAILS'; export const rightPanelAnimationTimeout = 350; // ms const Animated: FC<{children: ReactNode}> = ({children, ...rest}) => ( {children} ); export enum PanelState { ADD_PARTICIPANTS = 'ADD_PARTICIPANTS', CONVERSATION_DETAILS = 'CONVERSATION_DETAILS', CONVERSATION_PARTICIPANTS = 'CONVERSATION_PARTICIPANTS', GROUP_PARTICIPANT_SERVICE = 'GROUP_PARTICIPANT_SERVICE', GROUP_PARTICIPANT_USER = 'GROUP_PARTICIPANT_USER', GUEST_OPTIONS = 'GUEST_OPTIONS', MESSAGE_DETAILS = 'MESSAGE_DETAILS', NOTIFICATIONS = 'NOTIFICATIONS', PARTICIPANT_DEVICES = 'DEVICES', SERVICES_OPTIONS = 'SERVICES_OPTIONS', TIMED_MESSAGES = 'TIMED_MESSAGES', } export type PanelEntity = Conversation | User | Message | ServiceEntity; interface RightSidebarProps { currentEntity?: RightSidebarParams['entity']; actionsViewModel: ActionsViewModel; repositories: ViewModelRepositories; teamState: TeamState; userState: UserState; selfUser: User; isFederated: boolean; lastViewedMessageDetailsEntity: Message | null; core?: Core; } const RightSidebar: FC = ({ currentEntity, actionsViewModel, repositories, teamState, userState, selfUser, isFederated, lastViewedMessageDetailsEntity, core = container.resolve(Core), }) => { const { conversation: conversationRepository, integration: integrationRepository, search: searchRepository, team: teamRepository, user: userRepository, } = repositories; const {conversationRoleRepository} = conversationRepository; const conversationState = container.resolve(ConversationState); const {activeConversation} = useKoSubscribableChildren(conversationState, ['activeConversation']); const [animatePanelToLeft, setAnimatePanelToLeft] = useState(true); const {rightSidebar} = useAppMainState.getState(); const lastItem = rightSidebar.history.length - 1; const currentState = rightSidebar.history[lastItem]; const userEntity = currentEntity && isUserEntity(currentEntity) ? currentEntity : null; const userServiceEntity = currentEntity && isUserServiceEntity(currentEntity) ? currentEntity : null; const messageEntity = currentEntity && isReadableMessage(currentEntity) ? currentEntity : null; const serviceEntity = currentEntity && isServiceEntity(currentEntity) ? currentEntity : null; const goToRoot = () => rightSidebar.goToRoot(activeConversation || null); const closePanel = () => rightSidebar.close(); const togglePanel = (newState: PanelState, entity: PanelEntity | null, isAddMode: boolean = false) => { setAnimatePanelToLeft(true); rightSidebar.goTo(newState, {entity, isAddMode}); }; const onBackClick = (entity: PanelEntity | null = activeConversation || null) => { const previousHistory = rightSidebar.history.slice(0, -1); const hasPreviousHistory = !!previousHistory.length; setAnimatePanelToLeft(false); if (hasPreviousHistory && previousHistory.length === 1 && previousHistory[0] === PanelState.MESSAGE_DETAILS) { rightSidebar.goBack(lastViewedMessageDetailsEntity); return; } if (hasPreviousHistory) { rightSidebar.goBack(entity); return; } rightSidebar.goTo(PanelState.CONVERSATION_DETAILS, {entity}); }; const showDevices = (entity: User) => { rightSidebar.goTo(PanelState.PARTICIPANT_DEVICES, {entity}); setAnimatePanelToLeft(true); }; const switchContent = (newContentState: string) => { const isCollectionState = newContentState === ContentState.COLLECTION; if (isCollectionState && currentState) { closePanel(); } }; useEffect(() => { amplify.subscribe(WebAppEvents.CONTENT.SWITCH, switchContent); amplify.subscribe(OPEN_CONVERSATION_DETAILS, goToRoot); amplify.subscribe(WebAppEvents.CONVERSATION.MESSAGE.UPDATED, (oldId: string, updatedMessageEntity: Message) => { if (currentState === PanelState.MESSAGE_DETAILS && oldId === currentEntity?.id) { rightSidebar.updateEntity(updatedMessageEntity); } }); }, []); const containerRef = useCallback((element: HTMLDivElement | null) => element?.focus(), [currentState]); if (!activeConversation) { return null; } return ( cloneElement(child, { classNames: animatePanelToLeft ? 'right-to-left' : 'left-to-right', timeout: rightPanelAnimationTimeout, }) } > <> {currentState === PanelState.CONVERSATION_DETAILS && ( )} {currentState === PanelState.GROUP_PARTICIPANT_USER && userEntity && ( )} {currentState === PanelState.NOTIFICATIONS && ( )} {currentState === PanelState.PARTICIPANT_DEVICES && userEntity && ( )} {currentState === PanelState.TIMED_MESSAGES && ( )} {(currentState === PanelState.GUEST_OPTIONS || currentState === PanelState.SERVICES_OPTIONS) && ( )} {currentState === PanelState.GROUP_PARTICIPANT_SERVICE && serviceEntity && userServiceEntity && ( )} {currentState === PanelState.ADD_PARTICIPANTS && ( )} {currentState === PanelState.MESSAGE_DETAILS && messageEntity && ( )} {currentState === PanelState.CONVERSATION_PARTICIPANTS && ( )} ); }; export {RightSidebar};