/* * 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 {forwardRef, useEffect, useMemo, useState} from 'react'; import {RECEIPT_MODE} from '@wireapp/api-client/lib/conversation/data/'; import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; import {FadingScrollbar} from 'Components/FadingScrollbar'; import * as Icon from 'Components/Icon'; import {ConversationProtocolDetails} from 'Components/panel/ConversationProtocolDetails/ConversationProtocolDetails'; import {EnrichedFields} from 'Components/panel/EnrichedFields'; import {ServiceDetails} from 'Components/panel/ServiceDetails'; import {UserDetails} from 'Components/panel/UserDetails'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {t} from 'Util/LocalizerUtil'; import {sortUsersByPriority} from 'Util/StringUtil'; import {formatDuration} from 'Util/TimeUtil'; import {ConversationDetailsHeader} from './components/ConversationDetailsHeader'; import {ConversationDetailsOptions} from './components/ConversationDetailsOptions'; import {ConversationDetailsParticipants} from './components/ConversationDetailsParticipants'; import {ConversationRepository} from '../../../conversation/ConversationRepository'; import {ConversationVerificationState} from '../../../conversation/ConversationVerificationState'; import {getNotificationText} from '../../../conversation/NotificationSetting'; import {Conversation} from '../../../entity/Conversation'; import {User} from '../../../entity/User'; import {isServiceEntity} from '../../../guards/Service'; import {IntegrationRepository} from '../../../integration/IntegrationRepository'; import {ServiceEntity} from '../../../integration/ServiceEntity'; import {TeamRepository} from '../../../team/TeamRepository'; import {TeamState} from '../../../team/TeamState'; import {Shortcut} from '../../../ui/Shortcut'; import {ShortcutType} from '../../../ui/ShortcutType'; import {ActionsViewModel} from '../../../view_model/ActionsViewModel'; import {PanelHeader} from '../PanelHeader'; import {PanelEntity, PanelState} from '../RightSidebar'; const CONFIG = { MAX_USERS_VISIBLE: 7, REDUCED_USERS_COUNT: 5, }; interface ConversationDetailsProps { onClose?: () => void; togglePanel?: (panel: PanelState, entity: PanelEntity, addMode?: boolean, direction?: 'left' | 'right') => void; actionsViewModel: ActionsViewModel; activeConversation: Conversation; conversationRepository: ConversationRepository; integrationRepository: IntegrationRepository; teamRepository: TeamRepository; teamState: TeamState; selfUser: User; isFederated?: boolean; } const ConversationDetails = forwardRef( ( { onClose = () => {}, togglePanel = () => {}, actionsViewModel, activeConversation, conversationRepository, integrationRepository, teamRepository, teamState, selfUser, isFederated = false, }, ref, ) => { const [selectedService, setSelectedService] = useState(); const roleRepository = conversationRepository.conversationRoleRepository; const { isMutable, showNotificationsNothing, verification_state: verificationState, isGroup, isSelfUserRemoved, notificationState, hasGlobalMessageTimer, globalMessageTimer, isTeamOnly: isConversationTeamOnly, isServicesRoom: isConversationServicesRoomOnly, isGuestAndServicesRoom, is1to1, isRequest, participating_user_ets: participatingUserEts, firstUserEntity: firstParticipant, } = useKoSubscribableChildren(activeConversation, [ 'isMutable', 'showNotificationsNothing', 'verification_state', 'isGroup', 'isSelfUserRemoved', 'notificationState', 'hasGlobalMessageTimer', 'globalMessageTimer', 'isTeamOnly', 'isServicesRoom', 'isGuestAndServicesRoom', 'receiptMode', 'is1to1', 'isRequest', 'participating_user_ets', 'firstUserEntity', ]); const {isTemporaryGuest} = useKoSubscribableChildren(firstParticipant!, ['isTemporaryGuest']); const {isTeam, classifiedDomains, team, isSelfDeletingMessagesEnforced, getEnforcedSelfDeletingMessagesTimeout} = useKoSubscribableChildren(teamState, [ 'isTeam', 'classifiedDomains', 'isSelfDeletingMessagesEnforced', 'getEnforcedSelfDeletingMessagesTimeout', 'team', ]); const isActiveGroupParticipant = isGroup && !isSelfUserRemoved; const showActionAddParticipants = isActiveGroupParticipant && roleRepository.canAddParticipants(activeConversation); const hasTimer = hasGlobalMessageTimer; const isTeamOnly = isConversationTeamOnly || isConversationServicesRoomOnly; const isServicesRoom = isConversationServicesRoomOnly || isGuestAndServicesRoom; const guestOptionsText = isTeamOnly ? t('conversationDetailsOff') : t('conversationDetailsOn'); const servicesOptionsText = isServicesRoom ? t('conversationDetailsOn') : t('conversationDetailsOff'); const notificationStatusText = getNotificationText(notificationState); const timedMessagesText = isSelfDeletingMessagesEnforced ? formatDuration(getEnforcedSelfDeletingMessagesTimeout).text : hasTimer && globalMessageTimer ? formatDuration(globalMessageTimer).text : t('ephemeralUnitsNone'); const showActionMute = isMutable && !isTeam; const isVerified = verificationState === ConversationVerificationState.VERIFIED; const canRenameGroup = roleRepository.canRenameGroup(activeConversation); const userParticipants = useMemo(() => { const filteredUsers: User[] = participatingUserEts.flatMap(user => { const isUser = !isServiceEntity(user); return isUser ? [user] : []; }); if (!isSelfUserRemoved) { return [...filteredUsers, selfUser].sort(sortUsersByPriority); } return filteredUsers; }, [participatingUserEts, isSelfUserRemoved, selfUser]); const usersCount = userParticipants.length; const exceedsMaxUserCount = usersCount > CONFIG.MAX_USERS_VISIBLE; const allUsersCount = exceedsMaxUserCount ? usersCount : 0; const serviceParticipants: ServiceEntity[] = participatingUserEts.flatMap(service => { const isService = isServiceEntity(service); return isService ? [service] : []; }); const toggleMute = () => actionsViewModel.toggleMuteConversation(activeConversation); const updateConversationName = (conversationName: string) => conversationRepository.renameConversation(activeConversation, conversationName); const openAddParticipants = () => togglePanel(PanelState.ADD_PARTICIPANTS, activeConversation); const showUser = (userEntity: User) => togglePanel(PanelState.GROUP_PARTICIPANT_USER, userEntity); const showService = async (entity: ServiceEntity) => { const serviceEntity = await integrationRepository.getServiceFromUser(entity); if (serviceEntity) { togglePanel(PanelState.GROUP_PARTICIPANT_SERVICE, serviceEntity); } }; const showAllParticipants = () => togglePanel(PanelState.CONVERSATION_PARTICIPANTS, activeConversation); const updateConversationReceiptMode = (receiptMode: RECEIPT_MODE) => conversationRepository.updateConversationReceiptMode(activeConversation, {receipt_mode: receiptMode}); const isSingleUserMode = is1to1 || isRequest; const isServiceMode = isSingleUserMode && firstParticipant!.isService; useEffect(() => { void conversationRepository.refreshUnavailableParticipants(activeConversation); }, [activeConversation, conversationRepository]); useEffect(() => { if (team.id && isSingleUserMode) { void teamRepository.updateTeamMembersByIds(team.id, [firstParticipant!.id], true); } }, [firstParticipant, isSingleUserMode, team, teamRepository]); useEffect(() => { const getService = async () => { if (firstParticipant) { const serviceEntity = await integrationRepository.getServiceFromUser(firstParticipant); if (serviceEntity) { setSelectedService(serviceEntity); await integrationRepository.addProviderNameToParticipant(serviceEntity); } } }; void getService(); }, [firstParticipant, integrationRepository]); return (

{t('tooltipConversationInfo')}

{isSingleUserMode && isServiceMode && selectedService && } {isSingleUserMode && !isServiceMode && firstParticipant && ( <> )} {!isSingleUserMode && ( <> {showActionAddParticipants && (
)} {isGroup && (!!userParticipants.length || !!serviceParticipants.length) && ( )} )}
); }, ); ConversationDetails.displayName = 'ConversationDetails'; export {ConversationDetails};