mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 15:36:18 +00:00
feat(mentions): overhaul, refactor, notification bubble & window update
Chat tagging: - Any @user is a visible tag in chat bubbles (the .mention-tag CSS never existed, so highlighting was invisible); self/alias mentions get a gold emphasis. Fixes cross-room tags not being highlighted. Mentions window: - Redesigned: unread count in the header, restyled filter chips + a refresh button, CSS-driven list/date-groups, adaptive height (compact when few, capped + scroll when many), polished empty state. - Rows: framed avatar (friends-list head crop so the face is never clipped), per-row unread dot, type marker, icon action buttons (goto / remove). - Re-requests from the server each time it opens. Autocomplete: - Never suggests the viewer themselves; suggests room users + online friends + aliases. Notifications: - Mention toast removed; mentions flow through the client's standard notification stream via a dedicated mention bubble (avatar + actions) in the default position. EVERY received mention surfaces (independent of the generic info-feed toggle, gated only by mentions_ui.enabled). Refactor (behaviour-preserving): - Centralised @-token classification in api/mentions/mentionTokens. - Moved mentionsFormat -> api/mentions, useMentionActions -> hooks/mentions. - Extracted ChatInputView @-autocomplete into a tested useChatMentions hook + pure helper; removed the dead duplicate useMentionAutocomplete.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { NotificationBubbleItem, NotificationBubbleType } from '../../../../api';
|
||||
import { MentionNotificationBubbleItem, NotificationBubbleItem, NotificationBubbleType } from '../../../../api';
|
||||
import { NotificationBadgeReceivedBubbleView } from './NotificationBadgeReceivedBubbleView';
|
||||
import { NotificationClubGiftBubbleView } from './NotificationClubGiftBubbleView';
|
||||
import { NotificationDefaultBubbleView } from './NotificationDefaultBubbleView';
|
||||
import { NotificationMentionBubbleView } from './NotificationMentionBubbleView';
|
||||
|
||||
export const GetBubbleLayout = (item: NotificationBubbleItem, onClose: () => void) =>
|
||||
{
|
||||
@@ -15,6 +16,8 @@ export const GetBubbleLayout = (item: NotificationBubbleItem, onClose: () => voi
|
||||
return <NotificationBadgeReceivedBubbleView key={ item.id } { ...props } />;
|
||||
case NotificationBubbleType.CLUBGIFT:
|
||||
return <NotificationClubGiftBubbleView key={ item.id } { ...props } />;
|
||||
case NotificationBubbleType.MENTION:
|
||||
return <NotificationMentionBubbleView key={ item.id } item={ item as MentionNotificationBubbleItem } onClose={ onClose } />;
|
||||
default:
|
||||
return <NotificationDefaultBubbleView key={ item.id } { ...props } />;
|
||||
}
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
import { CreateLinkEvent, MarkMentionsReadComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, MouseEvent } from 'react';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
import { formatMentionTime, LocalizeText, MentionNotificationBubbleItem, MentionType, SendMessageComposer } from '../../../../api';
|
||||
import { Flex, LayoutAvatarImageView, LayoutNotificationBubbleView, LayoutNotificationBubbleViewProps, Text } from '../../../../common';
|
||||
import { markRead } from '../../../../hooks/mentions/mentionsStore';
|
||||
import { useUserDataSnapshot } from '../../../../hooks/session/useSessionSnapshots';
|
||||
import { MentionMessageView } from '../../../mentions/MentionMessageView';
|
||||
|
||||
export interface NotificationMentionBubbleViewProps extends LayoutNotificationBubbleViewProps
|
||||
{
|
||||
item: MentionNotificationBubbleItem;
|
||||
}
|
||||
|
||||
export const NotificationMentionBubbleView: FC<NotificationMentionBubbleViewProps> = props =>
|
||||
{
|
||||
const { item = null, onClose = null, ...rest } = props;
|
||||
const { userName: ownUsername = '' } = useUserDataSnapshot();
|
||||
|
||||
const mention = item.mention;
|
||||
const isRoom = (mention.mentionType === MentionType.ROOM);
|
||||
const time = formatMentionTime(mention.timestamp);
|
||||
|
||||
const markReadOnServer = () =>
|
||||
{
|
||||
markRead(mention.mentionId);
|
||||
SendMessageComposer(new MarkMentionsReadComposer(1, mention.mentionId));
|
||||
};
|
||||
|
||||
// Whole-bubble click opens the mentions panel (and dismisses the bubble).
|
||||
const open = () =>
|
||||
{
|
||||
CreateLinkEvent('mentions/toggle');
|
||||
onClose();
|
||||
};
|
||||
|
||||
const goto = () =>
|
||||
{
|
||||
markReadOnServer();
|
||||
if(mention.roomId > 0) CreateLinkEvent(`navigator/goto/${ mention.roomId }`);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const act = (event: MouseEvent, fn: () => void) =>
|
||||
{
|
||||
event.stopPropagation();
|
||||
fn();
|
||||
};
|
||||
|
||||
return (
|
||||
<LayoutNotificationBubbleView
|
||||
alignItems="start"
|
||||
gap={ 2 }
|
||||
classNames={ [ 'w-[330px]', 'max-w-[92vw]', 'cursor-pointer' ] }
|
||||
onClick={ open }
|
||||
onClose={ onClose }
|
||||
{ ...rest }>
|
||||
<div className="mention-toast-avatar">
|
||||
<LayoutAvatarImageView headOnly direction={ 2 } figure={ mention.senderFigure } />
|
||||
</div>
|
||||
<Flex column gap={ 0 } className="min-w-0 flex-1">
|
||||
<Flex alignItems="center" gap={ 1 } className="min-w-0">
|
||||
<Text bold truncate variant="white">{ mention.senderUsername }</Text>
|
||||
<span className={ `mention-toast-chip ${ isRoom ? 'is-room' : 'is-direct' }` }>
|
||||
{ LocalizeText(isRoom ? 'mentions.type.room' : 'mentions.type.direct') }
|
||||
</span>
|
||||
<span className="mention-toast-spacer" />
|
||||
{ (time.length > 0) &&
|
||||
<span className="mention-toast-time">{ time }</span> }
|
||||
<button type="button" className="mention-toast-dismiss" title={ LocalizeText('generic.cancel') } onClick={ event => act(event, onClose) }>
|
||||
<FaTimes />
|
||||
</button>
|
||||
</Flex>
|
||||
{ (mention.roomName && (mention.roomName.length > 0)) &&
|
||||
<span className="mention-toast-room">· { mention.roomName }</span> }
|
||||
<MentionMessageView className="mention-toast-message" ownUsername={ ownUsername } text={ mention.message } />
|
||||
<Flex gap={ 1 } className="mention-toast-actions">
|
||||
<button type="button" className="mention-toast-btn" onClick={ event => act(event, open) }>
|
||||
{ LocalizeText('mentions.window.title') }
|
||||
</button>
|
||||
{ (mention.roomId > 0) &&
|
||||
<button type="button" className="mention-toast-btn" onClick={ event => act(event, goto) }>
|
||||
{ LocalizeText('mentions.action.goto') }
|
||||
</button> }
|
||||
</Flex>
|
||||
</Flex>
|
||||
</LayoutNotificationBubbleView>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user