Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/components/Dialog/hooks/useDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@ export const useDialog = ({
}: UseDialogParams) => {
const { dialogManager } = useDialogManager({ dialogManagerId });

useEffect(
() => () => {
useEffect(() => {
dialogManager.cancelPendingRemoval(id);

return () => {
// Since this cleanup can run even if the component is still mounted
// and dialog id is unchanged (e.g. in <StrictMode />), it's safer to
// mark state as unused and only remove it after a timeout, rather than
// to remove it immediately.
dialogManager.markForRemoval(id);
},
[dialogManager, id],
);
};
}, [dialogManager, id]);

return dialogManager.getOrCreate({ closeOnClickOutside, id });
};
Expand Down
47 changes: 33 additions & 14 deletions src/components/Dialog/service/DialogManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ export class DialogManager {
);
}

get(id: DialogId) {
get(id: DialogId): Dialog | undefined {
return this.state.getLatestValue().dialogsById[id];
}

getOrCreate({ closeOnClickOutside, id }: GetOrCreateDialogParams) {
let dialog = this.state.getLatestValue().dialogsById[id];
let dialog = this.get(id);
if (!dialog) {
dialog = {
close: () => {
Expand All @@ -97,7 +97,7 @@ export class DialogManager {
};
this.state.next((current) => ({
...current,
dialogsById: { ...current.dialogsById, [id]: dialog },
dialogsById: { ...current.dialogsById, [id]: dialog as Dialog },
}));
}

Expand All @@ -106,16 +106,15 @@ export class DialogManager {

if (shouldUpdateDialogSettings) {
if (dialog.removalTimeout) clearTimeout(dialog.removalTimeout);
dialog = {
...dialog,
closeOnClickOutside,
removalTimeout: undefined,
};
this.state.next((current) => ({
...current,
dialogsById: {
...current.dialogsById,
[id]: dialog,
[id]: {
...current.dialogsById[id],
closeOnClickOutside,
removalTimeout: undefined,
},
},
}));
}
Expand Down Expand Up @@ -158,9 +157,8 @@ export class DialogManager {
}
}

remove(id: DialogId) {
const state = this.state.getLatestValue();
const dialog = state.dialogsById[id];
remove = (id: DialogId) => {
const dialog = this.get(id);
if (!dialog) return;

if (dialog.removalTimeout) {
Expand All @@ -175,15 +173,15 @@ export class DialogManager {
dialogsById: newDialogs,
};
});
}
};

/**
* Marks the dialog state as unused. If the dialog id is referenced again quickly,
* the state will not be removed. Otherwise, the state will be removed after
* a short timeout.
*/
markForRemoval(id: DialogId) {
const dialog = this.state.getLatestValue().dialogsById[id];
const dialog = this.get(id);

if (!dialog) {
return;
Expand All @@ -202,4 +200,25 @@ export class DialogManager {
},
}));
}

cancelPendingRemoval(id: DialogId) {
const dialog = this.get(id);

if (!dialog?.removalTimeout) {
return;
}

clearTimeout(dialog.removalTimeout);

this.state.next((current) => ({
...current,
dialogsById: {
...current.dialogsById,
[id]: {
...current.dialogsById[id],
removalTimeout: undefined,
},
},
}));
}
}
26 changes: 26 additions & 0 deletions src/components/Icons/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,32 @@ export const IconEmoji = createIcon(
</>,
);

export const IconEmojiAdd = createIcon(
'IconEmojiAdd',
<>
<path
d='M1.75 10C1.75 5.44365 5.44365 1.75 10 1.75C10.4142 1.75 10.75 2.08579 10.75 2.5C10.75 2.91421 10.4142 3.25 10 3.25C6.27208 3.25 3.25 6.27208 3.25 10C3.25 13.7279 6.27208 16.75 10 16.75C13.7279 16.75 16.75 13.7279 16.75 10C16.75 9.58579 17.0858 9.25 17.5 9.25C17.9142 9.25 18.25 9.58579 18.25 10C18.25 14.5563 14.5563 18.25 10 18.25C5.44365 18.25 1.75 14.5563 1.75 10Z'
fill='currentColor'
/>
<path
d='M7.1875 9.375C7.70527 9.375 8.125 8.95527 8.125 8.4375C8.125 7.91973 7.70527 7.5 7.1875 7.5C6.66973 7.5 6.25 7.91973 6.25 8.4375C6.25 8.95527 6.66973 9.375 7.1875 9.375Z'
fill='currentColor'
/>
<path
d='M12.8125 9.375C13.3303 9.375 13.75 8.95527 13.75 8.4375C13.75 7.91973 13.3303 7.5 12.8125 7.5C12.2947 7.5 11.875 7.91973 11.875 8.4375C11.875 8.95527 12.2947 9.375 12.8125 9.375Z'
fill='currentColor'
/>
<path
d='M12.4756 11.499C12.683 11.1407 13.1425 11.0182 13.501 11.2256C13.8593 11.433 13.9818 11.8925 13.7744 12.251C13.0125 13.568 11.6947 14.5 10 14.5C8.30531 14.5 6.98748 13.568 6.22559 12.251C6.01825 11.8925 6.14067 11.433 6.49902 11.2256C6.85749 11.0182 7.31695 11.1407 7.52441 11.499C8.05942 12.424 8.91824 13 10 13C11.0818 13 11.9406 12.424 12.4756 11.499Z'
fill='currentColor'
/>
<path
d='M15.083 6.87524V4.91626H13.125C12.7108 4.91626 12.375 4.58047 12.375 4.16626C12.3752 3.7522 12.7109 3.41626 13.125 3.41626H15.083V1.45825C15.083 1.04415 15.4189 0.708427 15.833 0.708252C16.2472 0.708252 16.583 1.04404 16.583 1.45825V3.41626H18.542C18.9559 3.41644 19.2918 3.7523 19.292 4.16626C19.292 4.58036 18.9561 4.91608 18.542 4.91626H16.583V6.87524C16.5828 7.28931 16.2471 7.62524 15.833 7.62524C15.4191 7.62507 15.0832 7.2892 15.083 6.87524Z'
fill='currentColor'
/>
</>,
);

// was: IconExclamation
export const IconExclamationMarkFill = createIcon(
'IconExclamationMarkFill',
Expand Down
8 changes: 2 additions & 6 deletions src/components/Message/styling/Message.scss
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,7 @@
var(--str-chat__message-reactions-host-offset-x) * -1
);

&:has(.str-chat__message-reactions--flipped-horizontally) {
margin-inline-end: var(--str-chat__message-reactions-host-offset-x);
}
margin-inline-end: var(--str-chat__message-reactions-host-offset-x);
}

.str-chat__message-reactions.str-chat__message-reactions--segmented.str-chat__message-reactions--bottom
Expand Down Expand Up @@ -323,9 +321,7 @@
&:has(.str-chat__message-reactions--top) {
padding-inline-end: calc(var(--str-chat__message-reactions-host-offset-x) * -1);

&:has(.str-chat__message-reactions--flipped-horizontally) {
margin-inline-start: var(--str-chat__message-reactions-host-offset-x);
}
margin-inline-start: var(--str-chat__message-reactions-host-offset-x);
}

.str-chat__message-reactions.str-chat__message-reactions--segmented.str-chat__message-reactions--bottom
Expand Down
18 changes: 14 additions & 4 deletions src/components/Reactions/MessageReactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
capLimit: { clustered: capLimitClustered = 5, segmented: capLimitSegmented = 4 } = {},
flipHorizontalPosition = false,
handleFetchReactions,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
reactionDetailsSort,
verticalPosition = 'top',
visualStyle = 'clustered',
Expand All @@ -89,7 +88,9 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
const { isMyMessage, message } = useMessageContext('MessageReactions');

const divRef = useRef<ComponentRef<'div'>>(null);
const dialogId = `message-reactions-detail-${message.id}`;
const dialogId = DefaultMessageReactionsDetail.getDialogId({
messageId: message.id,
});
const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId });
const isDialogOpen = useDialogIsOpen(dialogId, dialogManager?.id);

Expand Down Expand Up @@ -158,6 +159,7 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
aria-pressed={isDialogOpen}
buttonIf={visualStyle === 'clustered'}
className='str-chat__message-reactions__list-button'
data-testid='message-reactions-list-button'
onClick={() => handleReactionButtonClick(null)}
>
<ul className='str-chat__message-reactions__list'>
Expand All @@ -166,14 +168,18 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
EmojiComponent && (
<li
className='str-chat__message-reactions__list-item'
data-testid='message-reactions-list-item'
key={reactionType}
>
<FragmentOrButton
buttonIf={visualStyle === 'segmented'}
className='str-chat__message-reactions__list-item-button'
onClick={() => handleReactionButtonClick(reactionType)}
>
<span className='str-chat__message-reactions__list-item-icon'>
<span
className='str-chat__message-reactions__list-item-icon'
data-testid='message-reactions-list-item-icon'
>
<EmojiComponent />
</span>
{visualStyle === 'segmented' && reactionCount > 1 && (
Expand Down Expand Up @@ -206,7 +212,10 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
)}
</ul>
{visualStyle === 'clustered' && (
<span className='str-chat__message-reactions__total-count'>
<span
className='str-chat__message-reactions__total-count'
data-testid='message-reactions-total-count'
>
{totalReactionCount}
</span>
)}
Expand All @@ -225,6 +234,7 @@ const UnMemoizedMessageReactions = (props: MessageReactionsProps) => {
<MessageReactionsDetail
handleFetchReactions={handleFetchReactions}
onSelectedReactionTypeChange={setSelectedReactionType}
reactionDetailsSort={reactionDetailsSort}
reactionGroups={reactionGroups}
reactions={existingReactions}
selectedReactionType={selectedReactionType}
Expand Down
Loading
Loading