/*
* 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 * as ContextMenuActionCreators from '@app/actions/ContextMenuActionCreators';
import * as ModalActionCreators from '@app/actions/ModalActionCreators';
import {modal} from '@app/actions/ModalActionCreators';
import * as NavigationActionCreators from '@app/actions/NavigationActionCreators';
import * as PrivateChannelActionCreators from '@app/actions/PrivateChannelActionCreators';
import * as TextCopyActionCreators from '@app/actions/TextCopyActionCreators';
import * as UserNoteActionCreators from '@app/actions/UserNoteActionCreators';
import * as UserProfileActionCreators from '@app/actions/UserProfileActionCreators';
import {UserTag} from '@app/components/channel/UserTag';
import {CustomStatusDisplay} from '@app/components/common/custom_status_display/CustomStatusDisplay';
import {GroupDMAvatar} from '@app/components/common/GroupDMAvatar';
import {ConfirmModal} from '@app/components/modals/ConfirmModal';
import type {IARContext} from '@app/components/modals/IARModal';
import {IARModal} from '@app/components/modals/IARModal';
import * as Modal from '@app/components/modals/Modal';
import userProfileModalStyles from '@app/components/modals/UserProfileModal.module.css';
import {UserSettingsModal} from '@app/components/modals/UserSettingsModal';
import {GuildIcon} from '@app/components/popouts/GuildIcon';
import {UserProfileBadges} from '@app/components/popouts/UserProfileBadges';
import {UserProfileDataWarning} from '@app/components/popouts/UserProfileDataWarning';
import {
UserProfileBio,
UserProfileConnections,
UserProfileMembershipInfo,
UserProfileRoles,
} from '@app/components/popouts/UserProfileShared';
import {VoiceActivitySection} from '@app/components/profile/VoiceActivitySection';
import {Button} from '@app/components/uikit/button/Button';
import {GroupDMContextMenu} from '@app/components/uikit/context_menu/GroupDMContextMenu';
import {GuildContextMenu} from '@app/components/uikit/context_menu/GuildContextMenu';
import {GuildMemberContextMenu} from '@app/components/uikit/context_menu/GuildMemberContextMenu';
import {MenuGroup} from '@app/components/uikit/context_menu/MenuGroup';
import {MenuItem} from '@app/components/uikit/context_menu/MenuItem';
import {MenuItemRadio} from '@app/components/uikit/context_menu/MenuItemRadio';
import {UserContextMenu} from '@app/components/uikit/context_menu/UserContextMenu';
import FocusRing from '@app/components/uikit/focus_ring/FocusRing';
import {Scroller} from '@app/components/uikit/Scroller';
import {Spinner} from '@app/components/uikit/Spinner';
import {StatusAwareAvatar} from '@app/components/uikit/StatusAwareAvatar';
import {Tabs} from '@app/components/uikit/tabs/Tabs';
import {Tooltip} from '@app/components/uikit/tooltip/Tooltip';
import {useAutoplayExpandedProfileAnimations} from '@app/hooks/useAutoplayExpandedProfileAnimations';
import {Logger} from '@app/lib/Logger';
import {TextareaAutosize} from '@app/lib/TextareaAutosize';
import type {ChannelRecord} from '@app/records/ChannelRecord';
import type {GuildRecord} from '@app/records/GuildRecord';
import type {ProfileRecord} from '@app/records/ProfileRecord';
import {UserRecord} from '@app/records/UserRecord';
import AuthenticationStore from '@app/stores/AuthenticationStore';
import ChannelStore from '@app/stores/ChannelStore';
import type {ContextMenuTargetElement} from '@app/stores/ContextMenuStore';
import ContextMenuStore, {isContextMenuNodeTarget} from '@app/stores/ContextMenuStore';
import DeveloperOptionsStore from '@app/stores/DeveloperOptionsStore';
import GuildMemberStore from '@app/stores/GuildMemberStore';
import GuildStore from '@app/stores/GuildStore';
import MemberPresenceSubscriptionStore from '@app/stores/MemberPresenceSubscriptionStore';
import ModalStore from '@app/stores/ModalStore';
import PermissionStore from '@app/stores/PermissionStore';
import RelationshipStore from '@app/stores/RelationshipStore';
import SelectedChannelStore from '@app/stores/SelectedChannelStore';
import UserNoteStore from '@app/stores/UserNoteStore';
import UserProfileStore from '@app/stores/UserProfileStore';
import UserStore from '@app/stores/UserStore';
import {getUserAccentColor} from '@app/utils/AccentColorUtils';
import * as CallUtils from '@app/utils/CallUtils';
import * as ChannelUtils from '@app/utils/ChannelUtils';
import * as NicknameUtils from '@app/utils/NicknameUtils';
import * as ProfileDisplayUtils from '@app/utils/ProfileDisplayUtils';
import {createMockProfile} from '@app/utils/ProfileUtils';
import * as RelationshipActionUtils from '@app/utils/RelationshipActionUtils';
import {ME} from '@fluxer/constants/src/AppConstants';
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
import {
MEDIA_PROXY_AVATAR_SIZE_PROFILE,
MEDIA_PROXY_PROFILE_BANNER_SIZE_MODAL,
} from '@fluxer/constants/src/MediaProxyAssetSizes';
import {PublicUserFlags, RelationshipTypes} from '@fluxer/constants/src/UserConstants';
import type {UserPartial} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
import {Trans, useLingui} from '@lingui/react/macro';
import {
CaretDownIcon,
ChatTeardropIcon,
CheckCircleIcon,
ClockCounterClockwiseIcon,
CopyIcon,
DotsThreeIcon,
FlagIcon,
GlobeIcon,
IdentificationCardIcon,
PencilIcon,
ProhibitIcon,
UserMinusIcon,
UserPlusIcon,
UsersThreeIcon,
VideoCameraIcon,
} from '@phosphor-icons/react';
import {clsx} from 'clsx';
import {autorun} from 'mobx';
import {observer} from 'mobx-react-lite';
import type React from 'react';
import {useCallback, useEffect, useId, useMemo, useRef, useState} from 'react';
import type {PressEvent} from 'react-aria-components';
const logger = new Logger('UserProfileModal');
export interface UserProfileModalProps {
userId: string;
guildId?: string;
autoFocusNote?: boolean;
disableEditProfile?: boolean;
previewOverrides?: ProfileDisplayUtils.ProfilePreviewOverrides;
previewUser?: UserRecord;
}
interface UserInfoProps {
user: UserRecord;
profile: ProfileRecord;
guildId?: string;
showProfileDataWarning?: boolean;
}
interface UserNoteEditorProps {
userId: string;
initialNote: string | null;
autoFocus?: boolean;
noteRef?: React.RefObject;
}
interface ProfileContentProps {
profile: ProfileRecord;
user: UserRecord;
userNote: string | null;
autoFocusNote?: boolean;
noteRef?: React.RefObject;
}
type UserProfileModalComponent = React.FC;
interface ProfileModalContentProps {
profile: ProfileRecord;
user: UserRecord;
userNote: string | null;
autoFocusNote?: boolean;
noteRef?: React.RefObject;
renderActionButtons: () => React.ReactNode;
previewOverrides?: ProfileDisplayUtils.ProfilePreviewOverrides;
showProfileDataWarning?: boolean;
}
const UserInfo: React.FC = observer(({user, profile, guildId, showProfileDataWarning}) => {
const displayName = NicknameUtils.getNickname(user, guildId);
const effectiveProfile = profile?.getEffectiveProfile() ?? null;
const shouldAutoplayProfileAnimations = useAutoplayExpandedProfileAnimations();
return (