/* * 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 {Trans, useLingui} from '@lingui/react/macro'; import {observer} from 'mobx-react-lite'; import type React from 'react'; import {ChannelTypes, getStatusTypeLabel, StatusTypes} from '~/Constants'; import {GroupDMAvatar} from '~/components/common/GroupDMAvatar'; import { SettingsTabContainer, SettingsTabContent, SettingsTabSection, } from '~/components/modals/shared/SettingsTabLayout'; import {AvatarStack} from '~/components/uikit/avatars/AvatarStack'; import {MentionBadge} from '~/components/uikit/MentionBadge'; import {MockAvatar} from '~/components/uikit/MockAvatar'; import {ChannelRecord} from '~/records/ChannelRecord'; import type {UserPartial} from '~/records/UserRecord'; import styles from './IndicatorsTab.module.css'; import {SubsectionTitle} from './shared'; const AVATAR_SIZES_WITH_STATUS: Array<16 | 24 | 32 | 36 | 40 | 48 | 80> = [16, 24, 32, 36, 40, 48, 80]; const AVATAR_STATUSES: Array = [ StatusTypes.ONLINE, StatusTypes.IDLE, StatusTypes.DND, StatusTypes.INVISIBLE, StatusTypes.OFFLINE, ]; const createMockRecipient = (id: string): UserPartial => ({ id, username: id, discriminator: '0000', avatar: null, flags: 0, }); const createMockGroupDMChannel = (id: string, recipientIds: Array): ChannelRecord => new ChannelRecord({ id, type: ChannelTypes.GROUP_DM, recipients: recipientIds.map(createMockRecipient), }); const getMockGroupDMChannels = (): Array => [ createMockGroupDMChannel('1000000000000000001', ['1000000000000000002', '1000000000000000003']), createMockGroupDMChannel('1000000000000000004', [ '1000000000000000005', '1000000000000000006', '1000000000000000007', ]), ]; export const IndicatorsTab: React.FC = observer(() => { const {i18n} = useLingui(); const mockGroupDMChannels = getMockGroupDMChannels(); return ( Status Indicators} description={ Visual indicators showing user status, rendered using the same masked status badges as avatars: online, idle, do not disturb, invisible, and offline. } > Single User (All Statuses)
{AVATAR_STATUSES.map((status) => (
{getStatusTypeLabel(i18n, status) ?? status}
))}
Mobile Online Status on Avatars
{AVATAR_SIZES_WITH_STATUS.map((size) => (
{size}px
))}
Different Sizes (Status Supported)
{AVATAR_SIZES_WITH_STATUS.map((size) => (
{size}px
))}
Mention Badges} description={Notification badges showing unread mention counts in different sizes.} > Medium Size (Default)
Small Size
Mock Avatars} description={Mock user avatars in various sizes and all status permutations.} > Different Sizes (Online)
{AVATAR_SIZES_WITH_STATUS.map((size) => (
{size}px
))}
All Status Types
{AVATAR_STATUSES.map((status) => (
{getStatusTypeLabel(i18n, status) ?? status}
))}
Typing State
{AVATAR_STATUSES.map((status) => (
{getStatusTypeLabel(i18n, status) ?? status}
))}
Group DM Avatars} description={ Group DM avatars using the same status masks as regular avatars, including stacked layouts and typing states. } > Different Sizes & Member Counts
32px · 2 members
40px · 3 members
Group Online Status
2 members (online)
3 members (online)
Group Typing States
2 members (typing)
3 members (typing)
Avatar Stacks} description={Overlapping avatar groups showing multiple users with automatic overflow counts.} > Different Sizes
{[1, 2, 3, 4].map((i) => ( ))} 24px
{[1, 2, 3, 4].map((i) => ( ))} 32px
{[1, 2, 3, 4].map((i) => ( ))} 40px
Max Visible Count
{[1, 2, 3, 4, 5, 6, 7, 8].map((i) => ( ))} Show max 3 (+5 badge)
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((i) => ( ))} Show max 5 (+5 badge)
); });