/*
* 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 type React from 'react';
import * as ContextMenuActionCreators from '~/actions/ContextMenuActionCreators';
import {GuildMemberContextMenu} from '~/components/uikit/ContextMenu/GuildMemberContextMenu';
import {UserContextMenu} from '~/components/uikit/ContextMenu/UserContextMenu';
import i18n from '~/i18n';
import type {MuteConfig} from '~/records/UserGuildSettingsRecord';
import GuildMemberStore from '~/stores/GuildMemberStore';
import UserStore from '~/stores/UserStore';
import {isLegacyDocument} from '~/types/browser';
import {getFormattedDateTime} from '~/utils/DateUtils';
function getSelectionText(): string {
let text = '';
if (window.getSelection) {
text = window.getSelection()?.toString() || '';
} else if (isLegacyDocument(document) && document.selection && document.selection.type !== 'Control') {
text = document.selection.createRange().text;
}
return text;
}
function findUserData(element: HTMLElement): {userId?: string; guildId?: string; channelId?: string} {
let current: HTMLElement | null = element;
while (current) {
const userId = current.dataset.userId || current.getAttribute('data-user-id');
const guildId = current.dataset.guildId || current.getAttribute('data-guild-id');
const channelId = current.dataset.channelId || current.getAttribute('data-channel-id');
if (userId) {
return {userId, guildId: guildId || undefined, channelId: channelId || undefined};
}
current = current.parentElement;
}
return {};
}
export function handleContextMenu(e: MouseEvent): void {
const target = e.target as HTMLElement;
const {userId, guildId, channelId} = findUserData(target);
if (userId) {
const user = UserStore.getUser(userId);
if (user) {
e.preventDefault();
e.stopPropagation();
const isGuildMember = guildId ? GuildMemberStore.getMember(guildId, user.id) : null;
const view = (e.view ?? null) as unknown as React.MouseEvent['view'];
const reactEvent = {
nativeEvent: e,
currentTarget: target,
target: target,
pageX: e.pageX,
pageY: e.pageY,
preventDefault: () => e.preventDefault(),
stopPropagation: () => e.stopPropagation(),
altKey: e.altKey,
button: e.button,
buttons: e.buttons,
clientX: e.clientX,
clientY: e.clientY,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
screenX: e.screenX,
screenY: e.screenY,
detail: e.detail,
bubbles: e.bubbles,
cancelable: e.cancelable,
defaultPrevented: e.defaultPrevented,
eventPhase: e.eventPhase,
isTrusted: e.isTrusted,
movementX: e.movementX,
movementY: e.movementY,
relatedTarget: e.relatedTarget,
timeStamp: e.timeStamp,
type: e.type,
view,
getModifierState: e.getModifierState.bind(e),
isDefaultPrevented: () => e.defaultPrevented,
isPropagationStopped: () => false,
persist: () => {},
} satisfies React.MouseEvent;
ContextMenuActionCreators.openFromEvent(reactEvent, ({onClose}) =>
guildId && isGuildMember ? (
) : (
),
);
return;
}
}
const selectedText = getSelectionText();
let href: string | null = null;
let src: string | null = null;
let node: HTMLElement | null = target;
while (node) {
if (node instanceof HTMLAnchorElement) {
href = node.href;
}
if (node instanceof HTMLImageElement) {
src = node.src;
}
node = node.parentElement;
}
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
return;
}
if (selectedText) {
return;
}
if (href || src) {
return;
}
e.preventDefault();
}
export function getMutedText(isMuted: boolean, muteConfig?: MuteConfig): string | undefined {
if (!isMuted) return;
const now = Date.now();
if (muteConfig?.end_time && new Date(muteConfig.end_time).getTime() <= now) {
return;
}
if (muteConfig?.end_time) {
return i18n._(msg`Muted until ${getFormattedDateTime(new Date(muteConfig.end_time))}`);
}
return i18n._(msg`Muted`);
}
export function getNotificationSettingsLabel(currentNotificationLevel: number): string | undefined {
switch (currentNotificationLevel) {
case 0:
return i18n._(msg`All Messages`);
case 1:
return i18n._(msg`Only @mentions`);
case 2:
return i18n._(msg`Nothing`);
case 3:
return i18n._(msg`Use Category Default`);
default:
return;
}
}