import React from 'react'; import initMatrix from '../../../client/initMatrix'; import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil'; function getTimelineJSXMessages() { return { join(user) { return ( <> {user} {' joined the room'} ); }, leave(user, reason) { const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : ''; return ( <> {user} {' left the room'} {reasonMsg} ); }, invite(inviter, user) { return ( <> {inviter} {' invited '} {user} ); }, cancelInvite(inviter, user) { return ( <> {inviter} {' canceled '} {user} {'\'s invite'} ); }, rejectInvite(user) { return ( <> {user} {' rejected the invitation'} ); }, kick(actor, user, reason) { const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : ''; return ( <> {actor} {' kicked '} {user} {reasonMsg} ); }, ban(actor, user, reason) { const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : ''; return ( <> {actor} {' banned '} {user} {reasonMsg} ); }, unban(actor, user) { return ( <> {actor} {' unbanned '} {user} ); }, avatarSets(user) { return ( <> {user} {' set the avatar'} ); }, avatarChanged(user) { return ( <> {user} {' changed the avatar'} ); }, avatarRemoved(user) { return ( <> {user} {' removed the avatar'} ); }, nameSets(user, newName) { return ( <> {user} {' set the display name to '} {newName} ); }, nameChanged(user, newName) { return ( <> {user} {' changed the display name to '} {newName} ); }, nameRemoved(user, lastName) { return ( <> {user} {' removed the display name '} {lastName} ); }, }; } function getUsersActionJsx(roomId, userIds, actionStr) { const room = initMatrix.matrixClient.getRoom(roomId); const getUserDisplayName = (userId) => { if (room?.getMember(userId)) return getUsernameOfRoomMember(room.getMember(userId)); return getUsername(userId); }; const getUserJSX = (userId) => {getUserDisplayName(userId)}; if (!Array.isArray(userIds)) return 'Idle'; if (userIds.length === 0) return 'Idle'; const MAX_VISIBLE_COUNT = 3; const u1Jsx = getUserJSX(userIds[0]); // eslint-disable-next-line react/jsx-one-expression-per-line if (userIds.length === 1) return <>{u1Jsx} is {actionStr}; const u2Jsx = getUserJSX(userIds[1]); // eslint-disable-next-line react/jsx-one-expression-per-line if (userIds.length === 2) return <>{u1Jsx} and {u2Jsx} are {actionStr}; const u3Jsx = getUserJSX(userIds[2]); if (userIds.length === 3) { // eslint-disable-next-line react/jsx-one-expression-per-line return <>{u1Jsx}, {u2Jsx} and {u3Jsx} are {actionStr}; } const othersCount = userIds.length - MAX_VISIBLE_COUNT; // eslint-disable-next-line react/jsx-one-expression-per-line return <>{u1Jsx}, {u2Jsx}, {u3Jsx} and {othersCount} other are {actionStr}; } function parseReply(rawContent) { if (rawContent.indexOf('>') !== 0) return null; let content = rawContent.slice(rawContent.indexOf('<') + 1); const user = content.slice(0, content.indexOf('>')); content = content.slice(content.indexOf('>') + 2); const replyContent = content.slice(0, content.indexOf('\n\n')); content = content.slice(content.indexOf('\n\n') + 2); if (user === '') return null; const isUserId = user.match(/^@.+:.+/); return { userId: isUserId ? user : null, displayName: isUserId ? null : user, replyContent, content, }; } function parseTimelineChange(mEvent) { const tJSXMsgs = getTimelineJSXMessages(); const makeReturnObj = (variant, content) => ({ variant, content, }); const content = mEvent.getContent(); const prevContent = mEvent.getPrevContent(); const sender = mEvent.getSender(); const senderName = getUsername(sender); const userName = getUsername(mEvent.getStateKey()); switch (content.membership) { case 'invite': return makeReturnObj('invite', tJSXMsgs.invite(senderName, userName)); case 'ban': return makeReturnObj('leave', tJSXMsgs.ban(senderName, userName, content.reason)); case 'join': if (prevContent.membership === 'join') { if (content.displayname !== prevContent.displayname) { if (typeof content.displayname === 'undefined') return makeReturnObj('avatar', tJSXMsgs.nameRemoved(sender, prevContent.displayname)); if (typeof prevContent.displayname === 'undefined') return makeReturnObj('avatar', tJSXMsgs.nameSets(sender, content.displayname)); return makeReturnObj('avatar', tJSXMsgs.nameChanged(prevContent.displayname, content.displayname)); } if (content.avatar_url !== prevContent.avatar_url) { if (typeof content.avatar_url === 'undefined') return makeReturnObj('avatar', tJSXMsgs.avatarRemoved(content.displayname)); if (typeof prevContent.avatar_url === 'undefined') return makeReturnObj('avatar', tJSXMsgs.avatarSets(content.displayname)); return makeReturnObj('avatar', tJSXMsgs.avatarChanged(content.displayname)); } return null; } return makeReturnObj('join', tJSXMsgs.join(senderName)); case 'leave': if (sender === mEvent.getStateKey()) { switch (prevContent.membership) { case 'invite': return makeReturnObj('invite-cancel', tJSXMsgs.rejectInvite(senderName)); default: return makeReturnObj('leave', tJSXMsgs.leave(senderName, content.reason)); } } switch (prevContent.membership) { case 'invite': return makeReturnObj('invite-cancel', tJSXMsgs.cancelInvite(senderName, userName)); case 'ban': return makeReturnObj('other', tJSXMsgs.unban(senderName, userName)); // sender is not target and made the target leave, // if not from invite/ban then this is a kick default: return makeReturnObj('leave', tJSXMsgs.kick(senderName, userName, content.reason)); } default: return null; } } function scrollToBottom(ref) { const maxScrollTop = ref.current.scrollHeight - ref.current.offsetHeight; // eslint-disable-next-line no-param-reassign ref.current.scrollTop = maxScrollTop; } function isAtBottom(ref) { const { scrollHeight, scrollTop, offsetHeight } = ref.current; const scrollUptoBottom = scrollTop + offsetHeight; // scroll view have to div inside div which contains messages const lastMessage = ref.current.lastElementChild.lastElementChild.lastElementChild; const lastChildHeight = lastMessage.offsetHeight; // auto scroll to bottom even if user has EXTRA_SPACE left to scroll const EXTRA_SPACE = 48; if (scrollHeight - scrollUptoBottom <= lastChildHeight + EXTRA_SPACE) { return true; } return false; } function autoScrollToBottom(ref) { if (isAtBottom(ref)) scrollToBottom(ref); } export { getTimelineJSXMessages, getUsersActionJsx, parseReply, parseTimelineChange, scrollToBottom, isAtBottom, autoScrollToBottom, };