/*
* 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};