From 9a34802c973fa3de33342a6e2e49fa4ca22df379 Mon Sep 17 00:00:00 2001
From: Aleksander Nicacio da Silva
Date: Sun, 7 Dec 2025 15:56:12 -0300
Subject: [PATCH 1/7] feat: added invitation date to UserInfo
---
.../client/components/UserInfo/UserInfo.tsx | 9 +++++++++
.../RoomMembers/RoomMembersItem.tsx | 12 ++++++++++-
.../RoomMembers/RoomMembersWithData.tsx | 20 +++++++++++++------
.../UserInfo/UserInfoWithData.tsx | 11 ++++++++--
packages/i18n/src/locales/en.i18n.json | 1 +
5 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/apps/meteor/client/components/UserInfo/UserInfo.tsx b/apps/meteor/client/components/UserInfo/UserInfo.tsx
index 28a82c3835da1..59ddb7710a921 100644
--- a/apps/meteor/client/components/UserInfo/UserInfo.tsx
+++ b/apps/meteor/client/components/UserInfo/UserInfo.tsx
@@ -52,6 +52,7 @@ type UserInfoProps = UserInfoDataProps & {
actions: ReactElement;
roles: ReactElement[];
reason?: string;
+ invitationDate?: string;
};
const UserInfo = ({
@@ -75,6 +76,7 @@ const UserInfo = ({
reason,
freeSwitchExtension,
abacAttributes,
+ invitationDate,
...props
}: UserInfoProps): ReactElement => {
const { t } = useTranslation();
@@ -207,6 +209,13 @@ const UserInfo = ({
),
)}
+ {invitationDate && (
+
+ {t('Invitation_date')}
+ {timeAgo(invitationDate)}
+
+ )}
+
{createdAt && (
{t('Created_at')}
diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
index 52e84a3f04350..3953146428e54 100644
--- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
+++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
@@ -37,12 +37,15 @@ const RoomMembersItem = ({
freeSwitchExtension,
onClickView,
rid,
+ subscription,
reload,
useRealName,
subscription,
}: RoomMembersItemProps): ReactElement => {
const [showButton, setShowButton] = useState();
const isReduceMotionEnabled = usePrefersReducedMotion();
+ const isInvited = subscription?.status === 'INVITED';
+ const invitationDate = isInvited ? subscription?.ts : undefined;
const handleMenuEvent = {
[isReduceMotionEnabled ? 'onMouseEnter' : 'onTransitionEnd']: setShowButton,
};
@@ -52,7 +55,14 @@ const RoomMembersItem = ({
const [nameOrUsername, displayUsername] = getUserDisplayNames(name, username, useRealName);
return (
-
+
diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersWithData.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersWithData.tsx
index 81f5503045406..c44c31f5e9cbd 100644
--- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersWithData.tsx
+++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersWithData.tsx
@@ -48,9 +48,9 @@ const RoomMembersWithData = ({ rid }: { rid: IRoom['_id'] }): ReactElement => {
? Federation.canCreateInviteLinks(user, room, subscription)
: hasPermissionToCreateInviteLinks;
- const [state, setState] = useState<{ tab: ROOM_MEMBERS_TABS; userId?: IUser['_id'] }>({
+ const [state, setState] = useState<{ tab: ROOM_MEMBERS_TABS; user?: { id?: IUser['_id']; invitationDate?: string } }>({
tab: ROOM_MEMBERS_TABS.LIST,
- userId: undefined,
+ user: undefined,
});
const debouncedText = useDebouncedValue(text, 800);
@@ -74,10 +74,10 @@ const RoomMembersWithData = ({ rid }: { rid: IRoom['_id'] }): ReactElement => {
}, []);
const openUserInfo = useEffectEvent((e: MouseEvent) => {
- const { userid } = e.currentTarget.dataset;
+ const { userid: userId, invitationdate: invitationDate } = e.currentTarget.dataset;
setState({
tab: ROOM_MEMBERS_TABS.INFO,
- userId: userid,
+ user: { id: userId, invitationDate },
});
});
@@ -93,8 +93,16 @@ const RoomMembersWithData = ({ rid }: { rid: IRoom['_id'] }): ReactElement => {
setState({ tab: ROOM_MEMBERS_TABS.LIST });
});
- if (state.tab === ROOM_MEMBERS_TABS.INFO && state.userId) {
- return ;
+ if (state.tab === ROOM_MEMBERS_TABS.INFO && state.user?.id) {
+ return (
+
+ );
}
if (state.tab === ROOM_MEMBERS_TABS.INVITE) {
diff --git a/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx b/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx
index 7e28c0b4eada7..cc0f5bb01bd09 100644
--- a/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx
+++ b/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx
@@ -28,11 +28,12 @@ type UserInfoWithDataProps = {
uid?: IUser['_id'];
username?: IUser['username'];
rid: IRoom['_id'];
+ invitationDate?: string;
onClose: () => void;
onClickBack?: () => void;
};
-const UserInfoWithData = ({ uid, username, rid, onClose, onClickBack }: UserInfoWithDataProps): ReactElement => {
+const UserInfoWithData = ({ uid, username, rid, invitationDate, onClose, onClickBack }: UserInfoWithDataProps): ReactElement => {
const { t } = useTranslation();
const getRoles = useRolesDescription();
@@ -113,7 +114,13 @@ const UserInfoWithData = ({ uid, username, rid, onClose, onClickBack }: UserInfo
)}
- {!isPending && user && } />}
+ {!isPending && user && (
+ }
+ />
+ )}
);
};
diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index 616d58b289ea9..cc9f21e7d6233 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -2751,6 +2751,7 @@
"Invalid_username": "The username entered is invalid",
"Invisible": "Invisible",
"Invitation": "Invitation",
+ "Invitation_date": "Invitation date",
"Invitation_Email_Description": "You may use the following placeholders: \n - `[email]` for the recipient email. \n - `[Site_Name]` and `[Site_URL]` for the Application Name and URL respectively. ",
"Invitation_HTML": "Invitation HTML",
"Invitation_HTML_Default": "You have been invited to [Site_Name] Go to [Site_URL] and try the best open source chat solution available today!
",
From 69537cf304a6a51fc62a86c09c9a35577b77058d Mon Sep 17 00:00:00 2001
From: Aleksander Nicacio da Silva
Date: Sun, 7 Dec 2025 16:04:51 -0300
Subject: [PATCH 2/7] feat: added revoke invitation to user actions
---
.../RoomMembers/RoomMembersActions.tsx | 14 ++++++-
.../RoomMembers/RoomMembersItem.tsx | 10 ++++-
.../UserInfo/UserInfoActions.tsx | 6 ++-
.../actions/useRemoveUserAction.tsx | 39 ++++++++++++-------
.../useUserInfoActions/useUserInfoActions.ts | 7 +++-
packages/i18n/src/locales/en.i18n.json | 1 +
6 files changed, 57 insertions(+), 20 deletions(-)
diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersActions.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersActions.tsx
index ba5c7fc6644c6..c53892d42252d 100644
--- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersActions.tsx
+++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersActions.tsx
@@ -7,10 +7,19 @@ import { useUserInfoActions } from '../../hooks/useUserInfoActions';
type RoomMembersActionsProps = Pick & {
rid: IRoom['_id'];
+ isInvited?: boolean;
reload: () => void;
};
-const RoomMembersActions = ({ username, _id, name, rid, freeSwitchExtension, reload }: RoomMembersActionsProps): ReactElement | null => {
+const RoomMembersActions = ({
+ username,
+ _id,
+ name,
+ rid,
+ freeSwitchExtension,
+ isInvited,
+ reload,
+}: RoomMembersActionsProps): ReactElement | null => {
const { t } = useTranslation();
const { menuActions: menuOptions } = useUserInfoActions({
@@ -18,7 +27,8 @@ const RoomMembersActions = ({ username, _id, name, rid, freeSwitchExtension, rel
user: { _id, username, name, freeSwitchExtension },
reload,
size: 0,
- isMember: true,
+ isMember: !isInvited,
+ isInvited,
});
if (!menuOptions) {
diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
index 3953146428e54..6b1c73d874c2b 100644
--- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
+++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
@@ -77,7 +77,15 @@ const RoomMembersItem = ({
)}
{showButton ? (
-
+
) : (
)}
diff --git a/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoActions.tsx b/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoActions.tsx
index 59bd612c4c72f..ca7c6b0c88531 100644
--- a/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoActions.tsx
+++ b/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoActions.tsx
@@ -13,10 +13,11 @@ import { useUserInfoActions } from '../../hooks/useUserInfoActions';
type UserInfoActionsProps = {
user: Pick;
rid: IRoom['_id'];
+ isInvited?: boolean;
backToList?: () => void;
};
-const UserInfoActions = ({ user, rid, backToList }: UserInfoActionsProps): ReactElement => {
+const UserInfoActions = ({ user, rid, isInvited, backToList }: UserInfoActionsProps): ReactElement => {
const { t } = useTranslation();
const {
data: isMemberData,
@@ -31,8 +32,9 @@ const UserInfoActions = ({ user, rid, backToList }: UserInfoActionsProps): React
const { actions: actionsDefinition, menuActions: menuOptions } = useUserInfoActions({
rid,
user: { _id: userId, username, name, freeSwitchExtension },
- size: 3,
+ size: 2,
isMember,
+ isInvited,
reload: () => {
backToList?.();
refetch();
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx
index 75a3ca69bb746..a6b97deef5038 100644
--- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx
@@ -25,6 +25,7 @@ export const useRemoveUserAction = (
user: Pick,
rid: IRoom['_id'],
reload?: () => void,
+ invited?: boolean,
): UserInfoAction | undefined => {
const room = useUserRoom(rid);
@@ -110,19 +111,31 @@ export const useRemoveUserAction = (
);
});
- const removeUserOption = useMemo(
- () =>
- roomCanRemove && userCanRemove
- ? {
- content: room?.teamMain ? t('Remove_from_team') : t('Remove_from_room'),
- icon: 'cross' as const,
- onClick: removeUserOptionAction,
- type: 'moderation' as const,
- variant: 'danger' as const,
- }
- : undefined,
- [room, roomCanRemove, userCanRemove, removeUserOptionAction, t],
- );
+ const content = useMemo(() => {
+ if (invited) {
+ return t('Revoke_invitation');
+ }
+
+ if (room?.teamMain) {
+ return t('Remove_from_team');
+ }
+
+ return t('Remove_from_room');
+ }, [invited, room?.teamMain, t]);
+
+ const removeUserOption = useMemo(() => {
+ if (!roomCanRemove || !userCanRemove) {
+ return undefined;
+ }
+
+ return {
+ content,
+ icon: 'cross' as const,
+ onClick: removeUserOptionAction,
+ type: 'moderation' as const,
+ variant: 'danger' as const,
+ };
+ }, [roomCanRemove, userCanRemove, removeUserOptionAction, content]);
return removeUserOption;
};
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/useUserInfoActions.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/useUserInfoActions.ts
index 6540fd26e9373..f9baa1f736680 100644
--- a/apps/meteor/client/views/room/hooks/useUserInfoActions/useUserInfoActions.ts
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/useUserInfoActions.ts
@@ -56,6 +56,7 @@ type UserInfoActionsParams = {
reload?: () => void;
size?: number;
isMember?: boolean;
+ isInvited?: boolean;
};
export const useUserInfoActions = ({
@@ -64,6 +65,7 @@ export const useUserInfoActions = ({
reload,
size = 2,
isMember,
+ isInvited,
}: UserInfoActionsParams): { actions: [string, UserInfoAction][]; menuActions: any | undefined } => {
const addUser = useAddUserAction(user, rid, reload);
const blockUser = useBlockUserAction(user, rid);
@@ -74,7 +76,7 @@ export const useUserInfoActions = ({
const openDirectMessage = useDirectMessageAction(user, rid);
const ignoreUser = useIgnoreUserAction(user, rid);
const muteUser = useMuteUserAction(user, rid);
- const removeUser = useRemoveUserAction(user, rid, reload);
+ const removeUser = useRemoveUserAction(user, rid, reload, isInvited);
const videoCall = useVideoCallAction(user);
const reportUserOption = useReportUser(user);
const isLayoutEmbedded = useEmbeddedLayout();
@@ -94,8 +96,8 @@ export const useUserInfoActions = ({
...(isMember && ignoreUser && { ignoreUser }),
...(isMember && muteUser && { muteUser }),
...(blockUser && { toggleBlock: blockUser }),
+ ...((isMember || isInvited) && removeUser && { removeUser }),
...(reportUserOption && { reportUser: reportUserOption }),
- ...(isMember && removeUser && { removeUser }),
}),
[
openDirectMessage,
@@ -113,6 +115,7 @@ export const useUserInfoActions = ({
openModerationConsole,
addUser,
isMember,
+ isInvited,
],
);
diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json
index cc9f21e7d6233..bb33465f6e6b3 100644
--- a/packages/i18n/src/locales/en.i18n.json
+++ b/packages/i18n/src/locales/en.i18n.json
@@ -4568,6 +4568,7 @@
"Review": "Review",
"Review_contact": "Review contact",
"Review_devices": "Review when and where devices are connecting from",
+ "Revoke_invitation": "Revoke invitation",
"Right": "Right",
"Ringing": "Ringing",
"Ringtones_and_visual_indicators_notify_people_of_incoming_calls": "Ringtones and visual indicators notify people of incoming calls.",
From f13d00da1247cd551de61b4362253a71bfde4e54 Mon Sep 17 00:00:00 2001
From: Aleksander Nicacio da Silva
Date: Sun, 7 Dec 2025 18:07:33 -0300
Subject: [PATCH 3/7] chore: updated storybook
---
apps/meteor/client/components/UserInfo/UserInfo.stories.tsx | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/apps/meteor/client/components/UserInfo/UserInfo.stories.tsx b/apps/meteor/client/components/UserInfo/UserInfo.stories.tsx
index 81e32c7f6cd31..65b94ed0d4445 100644
--- a/apps/meteor/client/components/UserInfo/UserInfo.stories.tsx
+++ b/apps/meteor/client/components/UserInfo/UserInfo.stories.tsx
@@ -53,3 +53,8 @@ WithABACAttributes.args = {
},
],
};
+
+export const InvitedUser = Template.bind({});
+InvitedUser.args = {
+ invitationDate: '2025-01-01T12:00:00Z',
+};
From 24f3080457b0a8d65aee2b8b99427ceb247e6aa2 Mon Sep 17 00:00:00 2001
From: Aleksander Nicacio da Silva
Date: Sun, 7 Dec 2025 22:12:50 -0300
Subject: [PATCH 4/7] test: updated snapshots
---
.../__snapshots__/UserInfo.spec.tsx.snap | 275 ++++++++++++++++++
1 file changed, 275 insertions(+)
diff --git a/apps/meteor/client/components/UserInfo/__snapshots__/UserInfo.spec.tsx.snap b/apps/meteor/client/components/UserInfo/__snapshots__/UserInfo.spec.tsx.snap
index 3625392b362c2..e009dbb16862c 100644
--- a/apps/meteor/client/components/UserInfo/__snapshots__/UserInfo.spec.tsx.snap
+++ b/apps/meteor/client/components/UserInfo/__snapshots__/UserInfo.spec.tsx.snap
@@ -261,6 +261,281 @@ exports[`renders Default without crashing 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ guilherme.gazzo
+
+
+
+
+
+ currently working on User Card
+
+
+
+
+
+
+ Nickname
+
+
+ gazzo
+
+
+
+
+ Roles
+
+
+
+
+
+
+ admin
+
+
+
+
+
+
+ user
+
+
+
+
+
+
+
+
+ Username
+
+
+ guilherme.gazzo
+
+
+
+
+ Bio
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla tempus, eros convallis vulputate cursus, nisi neque eleifend libero, eget lacinia justo purus nec est. In at sodales ipsum. Sed lacinia quis purus eget pulvinar. Aenean eu pretium nunc, at aliquam magna. Praesent dignissim, tortor sed volutpat mattis, mauris diam pulvinar leo, porta commodo risus est non purus.
+
+
+
+
+
+
+ Invitation_date
+
+
+ January 1, 2025
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From aab969096d7b23d009c3450da2559b406c08ae55 Mon Sep 17 00:00:00 2001
From: Aleksander Nicacio da Silva
Date: Tue, 16 Dec 2025 10:43:25 -0300
Subject: [PATCH 5/7] feat: changed confirm text for revoke invitation
---
.../hooks/useUserInfoActions/actions/useRemoveUserAction.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx
index a6b97deef5038..19b1078f8eece 100644
--- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx
+++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRemoveUserAction.tsx
@@ -101,7 +101,7 @@ export const useRemoveUserAction = (
setModal(
=> handleRemoveFromRoom(rid, uid)}
From 8fcbe660c6740ba8c81f6216ac72bc50b59f0c91 Mon Sep 17 00:00:00 2001
From: Aleksander Nicacio da Silva
Date: Fri, 19 Dec 2025 16:21:20 -0300
Subject: [PATCH 6/7] chore: updated snapshot
---
.../__snapshots__/UserInfo.spec.tsx.snap | 32 +++++++++----------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/apps/meteor/client/components/UserInfo/__snapshots__/UserInfo.spec.tsx.snap b/apps/meteor/client/components/UserInfo/__snapshots__/UserInfo.spec.tsx.snap
index e009dbb16862c..ad214a8b86be0 100644
--- a/apps/meteor/client/components/UserInfo/__snapshots__/UserInfo.spec.tsx.snap
+++ b/apps/meteor/client/components/UserInfo/__snapshots__/UserInfo.spec.tsx.snap
@@ -271,7 +271,7 @@ exports[`renders InvitedUser without crashing 1`] = `
@@ -279,7 +279,7 @@ exports[`renders InvitedUser without crashing 1`] = `
class="rcx-box rcx-box--full rcx-css-1svuzur"
>
Nickname
gazzo
@@ -383,7 +383,7 @@ exports[`renders InvitedUser without crashing 1`] = `
class="rcx-box rcx-box--full rcx-css-1jagyun"
>
Roles
@@ -391,7 +391,7 @@ exports[`renders InvitedUser without crashing 1`] = `
class="rcx-box rcx-box--full rcx-css-9vcs0o"
>
Username
guilherme.gazzo
@@ -441,12 +441,12 @@ exports[`renders InvitedUser without crashing 1`] = `
class="rcx-box rcx-box--full rcx-css-1jagyun"
>
Bio
Email
Invitation_date
January 1, 2025
From faf1bebe7ee6a75cbb752ba0a3068f60d4f18c49 Mon Sep 17 00:00:00 2001
From: Diego Sampaio
Date: Fri, 19 Dec 2025 21:21:03 -0300
Subject: [PATCH 7/7] fix duplicated param
---
.../views/room/contextualBar/RoomMembers/RoomMembersItem.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
index 6b1c73d874c2b..26e09d4ef4621 100644
--- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
+++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembersItem.tsx
@@ -40,7 +40,6 @@ const RoomMembersItem = ({
subscription,
reload,
useRealName,
- subscription,
}: RoomMembersItemProps): ReactElement => {
const [showButton, setShowButton] = useState();
const isReduceMotionEnabled = usePrefersReducedMotion();