feat(app): show instance branding in switchers and pickers (#3289)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-08 17:34:56 +02:00
1 parent 6dc698ff5e
commit d6e3254a16
63 files changed
+929 -587

No files matched your search

@@ -31,6 +31,25 @@
line-height: 0.875rem;
}
.instanceHeadingMark {
display: inline-flex;
flex: 0 0 auto;
width: 0.75rem;
height: 0.75rem;
align-items: center;
justify-content: center;
overflow: hidden;
border-radius: 0.1875rem;
}
.instanceHeadingMark svg,
.instanceHeadingMark img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.instanceHeadingLabel {
min-width: 0;
overflow: hidden;
@@ -9,6 +9,9 @@ import {
} from '@app/features/auth/components/accounts/AccountIdentity';
import {getAccountMentionCount} from '@app/features/auth/components/accounts/account_switcher_modal/AccountMentionCounts';
import {SESSION_EXPIRED_DESCRIPTOR} from '@app/features/auth/components/accounts/account_switcher_modal/AccountPickerShared';
import {InstanceBrandMark} from '@app/features/auth/components/InstanceBrandMark';
import {resolveInstanceLabel} from '@app/features/auth/flow/instance_selector/InstanceDirectoryStorage';
import {resolveInstanceBrandIconUrl, resolveInstanceProductName} from '@app/features/auth/InstanceBranding';
import {getAccountKey} from '@app/features/auth/state/AccountStorageKey';
import {ACTIVE_ACCOUNT_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
import type {Account} from '@app/features/platform/state/AuthSession';
@@ -18,6 +21,7 @@ import {MentionBadge} from '@app/features/ui/components/MentionBadge';
import {MockAvatar} from '@app/features/ui/components/MockAvatar';
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
import {flxElementClassName} from '@app/lib/react';
import {isOfficialInstanceHost} from '@fluxer/instance_bootstrap/src/OfficialInstance';
import {Trans, useLingui} from '@lingui/react/macro';
import {CheckIcon, GearIcon} from '@phosphor-icons/react';
import clsx from 'clsx';
@@ -30,6 +34,8 @@ const UNKNOWN_INSTANCE_GROUP_KEY = 'unknown';
interface AccountInstanceGroup {
readonly groupKey: string;
readonly instanceLabel: string | null;
readonly isOfficial: boolean;
readonly iconUrl: string | null;
readonly accounts: Array<Account>;
}
@@ -46,7 +52,13 @@ function createAccountInstanceGroups(accounts: ReadonlyArray<Account>): Array<Ac
const instanceKey = resolveAccountInstanceKey(account);
if (instanceKey == null) {
if (unknownGroup == null) {
unknownGroup = {groupKey: UNKNOWN_INSTANCE_GROUP_KEY, instanceLabel: null, accounts: []};
unknownGroup = {
groupKey: UNKNOWN_INSTANCE_GROUP_KEY,
instanceLabel: null,
isOfficial: false,
iconUrl: null,
accounts: [],
};
}
unknownGroup.accounts.push(account);
continue;
@@ -54,7 +66,16 @@ function createAccountInstanceGroups(accounts: ReadonlyArray<Account>): Array<Ac
const groupKey = `instance:${instanceKey}`;
let group = groupsByKey.get(groupKey);
if (group == null) {
group = {groupKey, instanceLabel: resolveAccountInstanceLabel(account), accounts: []};
const domain = resolveAccountInstanceLabel(account);
const isOfficial = isOfficialInstanceHost(instanceKey);
const productName = isOfficial ? null : resolveInstanceProductName(account.instance);
group = {
groupKey,
instanceLabel: domain == null ? null : resolveInstanceLabel(productName, domain),
isOfficial,
iconUrl: isOfficial ? null : resolveInstanceBrandIconUrl(account.instance),
accounts: [],
};
groupsByKey.set(groupKey, group);
groups.push(group);
}
@@ -202,6 +223,17 @@ export const UserAreaAccountSwitchMenu = observer(
className={flxElementClassName(styles.instanceHeading)}
data-flx="app.floating.user-area-account-switch-menu.instance-heading"
>
<span
className={styles.instanceHeadingMark}
data-flx="app.floating.user-area-account-switch-menu.instance-heading-mark"
>
<InstanceBrandMark
isOfficial={group.isOfficial}
iconUrl={group.iconUrl}
size={12}
data-flx="app.floating.user-area-account-switch-menu.instance-brand-mark"
/>
</span>
<span
className={styles.instanceHeadingLabel}
data-flx="app.floating.user-area-account-switch-menu.instance-heading-label"
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {PRODUCT_NAME} from '@app/features/app/config/ProductConstants';
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
export function getActiveInstanceProductName(): string {
return RuntimeConfig.getSnapshotOrNull()?.appPublic.branding.product_name?.trim() || PRODUCT_NAME;
}