/*
* Wire
* Copyright (C) 2023 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 {MemberLeaveReason} from '@wireapp/api-client/lib/conversation/data/';
import {CONVERSATION_EVENT} from '@wireapp/api-client/lib/event/';
import {Config} from 'src/script/Config';
import {MemberMessage as MemberMessageEntity} from 'src/script/entity/message/MemberMessage';
import {User} from 'src/script/entity/User';
import {ClientEvent} from 'src/script/event/Client';
import {SystemMessageType} from 'src/script/message/SystemMessageType';
import {Declension, joinNames, replaceLink, t} from 'Util/LocalizerUtil';
import {replaceReactComponents} from 'Util/LocalizerUtil/ReactLocalizerUtil';
import {matchQualifiedIds} from 'Util/QualifiedId';
export const CONFIG = {
MAX_USERS_VISIBLE: 17,
MAX_WHOLE_TEAM_USERS_VISIBLE: 10,
REDUCED_USERS_COUNT: 15,
} as const;
function generateNames(users: User[], declension = Declension.ACCUSATIVE, hasExtra = false) {
const visibleUsers = hasExtra ? getVisibleUsers(users) : users;
return joinNames(visibleUsers, declension, hasExtra, true);
}
function getVisibleUsers(users: User[]) {
return users.slice(0, CONFIG.REDUCED_USERS_COUNT);
}
function ShowMoreButton({children, onClick}: {children: React.ReactNode; onClick: () => void}) {
return (
);
}
function getContent(message: MemberMessageEntity) {
if (!message.hasUsers()) {
return '';
}
/** the users that are impacted by the member event */
const targetedUsers = message.targetedUsers();
/** the user that triggered the action */
const actor = message.user();
const exceedsMaxVisibleUsers = targetedUsers.length > CONFIG.MAX_USERS_VISIBLE;
/** the number of users that will not be displayed on screen */
const hiddenUsersCount = exceedsMaxVisibleUsers ? targetedUsers.length - CONFIG.REDUCED_USERS_COUNT : 0;
const dativeUsers = generateNames(targetedUsers, Declension.DATIVE, exceedsMaxVisibleUsers);
const accusativeUsers = generateNames(targetedUsers, Declension.ACCUSATIVE, exceedsMaxVisibleUsers);
const name = message.senderName();
switch (message.memberMessageType) {
case SystemMessageType.CONVERSATION_CREATE: {
if (message.name().length) {
const exceedsMaxTeam = targetedUsers.length > CONFIG.MAX_WHOLE_TEAM_USERS_VISIBLE;
if (message.allTeamMembers && exceedsMaxTeam) {
const guestCount = targetedUsers.filter(userEntity => userEntity.isGuest()).length;
if (!guestCount) {
return t('conversationCreateTeam', {});
}
const hasSingleGuest = guestCount === 1;
return hasSingleGuest ? t('conversationCreateTeamGuest', {}) : t('conversationCreateTeamGuests', guestCount);
}
return exceedsMaxVisibleUsers
? t('conversationCreateWithMore', {count: hiddenUsersCount.toString(), users: dativeUsers}, {}, true)
: t('conversationCreateWith', dativeUsers, {}, true);
}
if (actor.isMe) {
return exceedsMaxVisibleUsers
? t('conversationCreatedYouMore', {count: hiddenUsersCount.toString(), users: dativeUsers}, {}, true)
: t('conversationCreatedYou', dativeUsers, {}, true);
}
return exceedsMaxVisibleUsers
? t('conversationCreatedMore', {count: hiddenUsersCount.toString(), name, users: dativeUsers}, {}, true)
: t('conversationCreated', {name, users: dativeUsers}, {}, true);
}
case SystemMessageType.CONVERSATION_RESUME: {
return t('conversationResume', generateNames(targetedUsers, Declension.DATIVE, false), {}, true);
}
default:
break;
}
switch (message.type) {
case CONVERSATION_EVENT.MEMBER_JOIN: {
const senderJoined = matchQualifiedIds(message.otherUser(), actor);
if (senderJoined) {
return message.user().isMe
? t('conversationMemberJoinedSelfYou')
: t('conversationMemberJoinedSelf', message.senderName(), {}, true);
}
if (message.user().isMe) {
return exceedsMaxVisibleUsers
? t('conversationMemberJoinedYouMore', {count: hiddenUsersCount.toString(), users: accusativeUsers}, {}, true)
: t('conversationMemberJoinedYou', accusativeUsers, {}, true);
}
return exceedsMaxVisibleUsers
? t(
'conversationMemberJoinedMore',
{count: hiddenUsersCount.toString(), name, users: accusativeUsers},
{},
true,
)
: t('conversationMemberJoined', {name, users: accusativeUsers}, {}, true);
}
case CONVERSATION_EVENT.MEMBER_LEAVE: {
if (message.reason === MemberLeaveReason.LEGAL_HOLD_POLICY_CONFLICT) {
const replaceLinkLegalHold = replaceLink(
Config.getConfig().URL.SUPPORT.LEGAL_HOLD_BLOCK,
'',
'read-more-legal-hold',
);
if (message.userEntities().some(user => user.isMe)) {
return t('conversationYouRemovedMissingLegalHoldConsent', {}, replaceLinkLegalHold);
}
const users = generateNames(targetedUsers);
if (message.userEntities().length === 1) {
return t('conversationMemberRemovedMissingLegalHoldConsent', users, replaceLinkLegalHold);
}
if (exceedsMaxVisibleUsers) {
return t(
'conversationMultipleMembersRemovedMissingLegalHoldConsentMore',
{
count: hiddenUsersCount.toString(10),
users,
},
replaceLinkLegalHold,
true,
);
}
return t('conversationMultipleMembersRemovedMissingLegalHoldConsent', users, replaceLinkLegalHold);
}
const temporaryGuestRemoval = message.otherUser().isMe && message.otherUser().isTemporaryGuest();
if (temporaryGuestRemoval) {
return t('temporaryGuestLeaveMessage');
}
const senderLeft = matchQualifiedIds(message.otherUser(), actor);
if (senderLeft) {
return message.user().isMe ? t('conversationMemberLeftYou') : t('conversationMemberLeft', name, {}, true);
}
const allUsers = generateNames(targetedUsers);
if (!actor.id) {
return t('conversationMemberWereRemoved', allUsers, {}, true);
}
return actor.isMe
? t('conversationMemberRemovedYou', allUsers, {}, true)
: t('conversationMemberRemoved', {name, users: allUsers}, {}, true);
}
case ClientEvent.CONVERSATION.TEAM_MEMBER_LEAVE: {
return t('conversationTeamLeft', name, {}, true);
}
default:
break;
}
return '';
}
export function MessageContent({
message,
onClickParticipants,
}: {
message: MemberMessageEntity;
onClickParticipants: (participants: User[]) => void;
}) {
const htmlCaption = getContent(message);
const content = replaceReactComponents(htmlCaption, [
{start: '', end: '', render: text => {text}},
{
start: '[showmore]',
end: '[/showmore]',
render: text => (
{content}
; }