/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see .
*/
import {msg} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
import {DotsThreeIcon, FlagCheckeredIcon, SparkleIcon, XIcon} from '@phosphor-icons/react';
import {clsx} from 'clsx';
import {autorun} from 'mobx';
import {observer} from 'mobx-react-lite';
import React from 'react';
import * as ContextMenuActionCreators from '~/actions/ContextMenuActionCreators';
import * as RecentMentionActionCreators from '~/actions/RecentMentionActionCreators';
import {MessagePreviewContext} from '~/Constants';
import {Message} from '~/components/channel/Message';
import styles from '~/components/popouts/RecentMentionsContent.module.css';
import {MessageContextPrefix} from '~/components/shared/MessageContextPrefix/MessageContextPrefix';
import previewStyles from '~/components/shared/MessagePreview.module.css';
import {MenuGroup} from '~/components/uikit/ContextMenu/MenuGroup';
import {MenuItemCheckbox} from '~/components/uikit/ContextMenu/MenuItemCheckbox';
import FocusRing from '~/components/uikit/FocusRing/FocusRing';
import {Scroller} from '~/components/uikit/Scroller';
import ChannelStore from '~/stores/ChannelStore';
import ContextMenuStore from '~/stores/ContextMenuStore';
import RecentMentionsStore from '~/stores/RecentMentionsStore';
import {goToMessage} from '~/utils/MessageNavigator';
const FilterMenuContent = observer(() => {
const filters = RecentMentionsStore.getFilters();
return (
{
RecentMentionActionCreators.updateFilters({
includeEveryone: checked,
});
RecentMentionActionCreators.fetch();
}}
>
Include @everyone mentions {
RecentMentionActionCreators.updateFilters({
includeRoles: checked,
});
RecentMentionActionCreators.fetch();
}}
>
Include @role mentions {
RecentMentionActionCreators.updateFilters({
includeGuilds: checked,
});
RecentMentionActionCreators.fetch();
}}
>
Include all community mentions
);
});
export const RecentMentionsContent = observer(
({onHeaderActionsChange}: {onHeaderActionsChange?: (actions: React.ReactNode) => void}) => {
const {i18n} = useLingui();
const fetched = RecentMentionsStore.fetched;
const hasMore = RecentMentionsStore.getHasMore();
const isLoadingMore = RecentMentionsStore.getIsLoadingMore();
const filterButtonRef = React.useRef(null);
const [isFilterMenuOpen, setIsFilterMenuOpen] = React.useState(false);
const accessibleMentions = RecentMentionsStore.getAccessibleMentions();
React.useEffect(() => {
if (!fetched) {
RecentMentionActionCreators.fetch();
}
}, [fetched]);
React.useEffect(() => {
const handleContextMenuChange = () => {
const contextMenu = ContextMenuStore.contextMenu;
const isOpen =
!!contextMenu && !!filterButtonRef.current && contextMenu.target.target === filterButtonRef.current;
setIsFilterMenuOpen(isOpen);
};
const disposer = autorun(handleContextMenuChange);
return () => disposer();
}, []);
const handleFilterPointerDown = React.useCallback((event: React.PointerEvent) => {
const contextMenu = ContextMenuStore.contextMenu;
const isOpen = !!contextMenu && contextMenu.target.target === filterButtonRef.current;
if (isOpen) {
event.stopPropagation();
event.preventDefault();
ContextMenuActionCreators.close();
}
}, []);
const handleFilterClick = React.useCallback((event: React.MouseEvent) => {
const contextMenu = ContextMenuStore.contextMenu;
const isOpen = !!contextMenu && contextMenu.target.target === event.currentTarget;
if (isOpen) {
return;
}
ContextMenuActionCreators.openFromEvent(event, () => );
}, []);
React.useEffect(() => {
onHeaderActionsChange?.(
,
);
return () => onHeaderActionsChange?.(null);
}, [onHeaderActionsChange, handleFilterPointerDown, handleFilterClick, isFilterMenuOpen, i18n]);
const handleScroll = React.useCallback(
(event: React.UIEvent) => {
const target = event.currentTarget;
const scrollPercentage = (target.scrollTop + target.offsetHeight) / target.scrollHeight;
if (scrollPercentage > 0.8 && hasMore && !isLoadingMore) {
RecentMentionActionCreators.loadMore();
}
},
[hasMore, isLoadingMore],
);
const handleJumpToMessage = React.useCallback((channelId: string, messageId: string) => {
goToMessage(channelId, messageId);
}, []);
if (accessibleMentions.length === 0) {
return (