mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 13:35:42 +09:00
feat(app): show instance branding in switchers and pickers (#3289)
This commit is contained in:
1 parent
6dc698ff5e
commit
d6e3254a16
63 files changed
+929
-587
No files matched your search
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
|
||||
import FluxerLogoAsset from '@app/media/images/fluxer-logo-color.svg?react';
|
||||
import {GlobeIcon} from '@phosphor-icons/react';
|
||||
import type React from 'react';
|
||||
import {useState} from 'react';
|
||||
|
||||
interface InstanceBrandMarkProps {
|
||||
readonly isOfficial: boolean;
|
||||
readonly iconUrl: string | null;
|
||||
readonly size: number;
|
||||
readonly globeWeight?: 'regular' | 'bold';
|
||||
readonly 'data-flx'?: string;
|
||||
}
|
||||
|
||||
function InstanceBrandImage({
|
||||
url,
|
||||
size,
|
||||
fallback,
|
||||
}: {
|
||||
readonly url: string;
|
||||
readonly size: number;
|
||||
readonly fallback: React.ReactElement;
|
||||
}): React.ReactElement {
|
||||
const [failed, setFailed] = useState(false);
|
||||
if (failed) {
|
||||
return fallback;
|
||||
}
|
||||
return (
|
||||
<img
|
||||
src={url}
|
||||
alt=""
|
||||
width={size}
|
||||
height={size}
|
||||
draggable={false}
|
||||
decoding="async"
|
||||
referrerPolicy="no-referrer"
|
||||
onError={() => setFailed(true)}
|
||||
data-flx="auth.components.instance-brand-mark.instance-brand-image.image"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function InstanceBrandMark({
|
||||
isOfficial,
|
||||
iconUrl,
|
||||
size,
|
||||
globeWeight = 'regular',
|
||||
'data-flx': dataFlx,
|
||||
}: InstanceBrandMarkProps): React.ReactElement {
|
||||
if (isOfficial) {
|
||||
return <FluxerLogoAsset aria-hidden="true" data-flx={dataFlx} />;
|
||||
}
|
||||
const globe = <GlobeIcon size={remFromPx(size)} weight={globeWeight} aria-hidden="true" data-flx={dataFlx} />;
|
||||
if (iconUrl == null) {
|
||||
return globe;
|
||||
}
|
||||
return <InstanceBrandImage key={iconUrl} url={iconUrl} size={size} fallback={globe} />;
|
||||
}
|
||||
+15
-14
@@ -2,11 +2,10 @@
|
||||
|
||||
import styles from '@app/features/auth/components/accounts/account_switcher_modal/AccountProfilePicker.module.css';
|
||||
import type {AccountInstanceBadgeInfo} from '@app/features/auth/components/accounts/account_switcher_modal/useAccountInstanceBadge';
|
||||
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
|
||||
import {InstanceBrandMark} from '@app/features/auth/components/InstanceBrandMark';
|
||||
import {instanceDomainHost} from '@app/features/auth/flow/instance_selector/InstanceDirectoryStorage';
|
||||
import {VerifiedConnectionIcon} from '@app/features/ui/components/icons/VerifiedConnectionIcon';
|
||||
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
|
||||
import FluxerLogoAsset from '@app/media/images/fluxer-logo-color.svg?react';
|
||||
import {GlobeIcon} from '@phosphor-icons/react';
|
||||
import type React from 'react';
|
||||
|
||||
interface AccountInstanceBadgeProps {
|
||||
@@ -22,11 +21,15 @@ export function AccountInstanceBadge({
|
||||
}: AccountInstanceBadgeProps): React.ReactElement {
|
||||
let domainLabel = fallbackLabel;
|
||||
let isOfficial = false;
|
||||
let iconUrl: string | null = null;
|
||||
let instanceName: string | null = null;
|
||||
if (badge != null) {
|
||||
domainLabel = badge.title;
|
||||
isOfficial = badge.isOfficial;
|
||||
iconUrl = badge.iconUrl;
|
||||
instanceName = badge.label !== instanceDomainHost(badge.title) ? badge.label : null;
|
||||
}
|
||||
const ariaLabels = [domainLabel];
|
||||
const ariaLabels = instanceName == null ? [domainLabel] : [instanceName, domainLabel];
|
||||
if (isOfficial) {
|
||||
ariaLabels.push(officialLabel);
|
||||
}
|
||||
@@ -39,7 +42,7 @@ export function AccountInstanceBadge({
|
||||
className={styles.instanceTooltipLabel}
|
||||
data-flx="auth.accounts.account-switcher-modal.account-instance-badge.render-tooltip.instance-tooltip-label"
|
||||
>
|
||||
{domainLabel}
|
||||
{instanceName == null ? domainLabel : `${instanceName} (${domainLabel})`}
|
||||
</span>
|
||||
{isOfficial && (
|
||||
<span
|
||||
@@ -69,15 +72,13 @@ export function AccountInstanceBadge({
|
||||
aria-label={ariaLabels.join(', ')}
|
||||
data-flx="auth.accounts.account-switcher-modal.account-instance-badge.instance-badge"
|
||||
>
|
||||
{isOfficial ? (
|
||||
<FluxerLogoAsset data-flx="auth.accounts.account-switcher-modal.account-instance-badge.fluxer-logo-asset" />
|
||||
) : (
|
||||
<GlobeIcon
|
||||
size={remFromPx(15)}
|
||||
weight="bold"
|
||||
data-flx="auth.accounts.account-switcher-modal.account-instance-badge.globe-icon"
|
||||
/>
|
||||
)}
|
||||
<InstanceBrandMark
|
||||
isOfficial={isOfficial}
|
||||
iconUrl={iconUrl}
|
||||
size={15}
|
||||
globeWeight="bold"
|
||||
data-flx="auth.accounts.account-switcher-modal.account-instance-badge.instance-brand-mark"
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
+8
-2
@@ -281,14 +281,20 @@
|
||||
box-shadow: 0 0.25rem 0.75rem rgb(0 0 0 / 0.28);
|
||||
}
|
||||
|
||||
.instanceBadge svg,
|
||||
.instanceBadge img {
|
||||
.instanceBadge svg {
|
||||
display: block;
|
||||
width: 72%;
|
||||
height: 72%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.instanceBadge img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.instanceTooltip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
+9
-2
@@ -6,6 +6,7 @@ import {
|
||||
normalizeInstanceName,
|
||||
resolveInstanceLabel,
|
||||
} from '@app/features/auth/flow/instance_selector/InstanceDirectoryStorage';
|
||||
import {resolveInstanceBrandIconUrl} from '@app/features/auth/InstanceBranding';
|
||||
import type {Account} from '@app/features/platform/state/AuthSession';
|
||||
import {isOfficialInstanceHost, OFFICIAL_INSTANCE_NAME} from '@fluxer/instance_bootstrap/src/OfficialInstance';
|
||||
import {useMemo} from 'react';
|
||||
@@ -14,6 +15,7 @@ export interface AccountInstanceBadgeInfo {
|
||||
readonly label: string;
|
||||
readonly title: string;
|
||||
readonly isOfficial: boolean;
|
||||
readonly iconUrl: string | null;
|
||||
}
|
||||
|
||||
function findKnownInstance(instanceKey: string, knownInstances: ReadonlyArray<InstanceInfo>): InstanceInfo | null {
|
||||
@@ -44,10 +46,15 @@ function getAccountInstanceBadgeInfo(
|
||||
}
|
||||
const isOfficial = isOfficialInstanceHost(instanceKey);
|
||||
if (isOfficial) {
|
||||
return {label: OFFICIAL_INSTANCE_NAME, title: domain, isOfficial};
|
||||
return {label: OFFICIAL_INSTANCE_NAME, title: domain, isOfficial, iconUrl: null};
|
||||
}
|
||||
const knownInstance = findKnownInstance(instanceKey, knownInstances);
|
||||
return {label: resolveInstanceName(account, knownInstance, domain), title: domain, isOfficial};
|
||||
return {
|
||||
label: resolveInstanceName(account, knownInstance, domain),
|
||||
title: domain,
|
||||
isOfficial,
|
||||
iconUrl: resolveInstanceBrandIconUrl(account.instance),
|
||||
};
|
||||
}
|
||||
|
||||
export function useAccountInstanceBadge(
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {PRODUCT_NAME} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import {Endpoints} from '@app/features/app/constants/Endpoints';
|
||||
import {getActiveInstanceProductName} from '@app/features/app/state/ActiveInstanceProductName';
|
||||
import {useAuthorizeParams} from '@app/features/auth/components/pages/oauth_authorize_page/hooks/useAuthorizeParams';
|
||||
import {
|
||||
type BotInviteDestinationOption,
|
||||
@@ -459,7 +459,8 @@ export function useAuthorizeFlow(options: UseAuthorizeFlowOptions = {}): Authori
|
||||
}
|
||||
}, [params, onCancelOverride, publicAppState.data, i18n]);
|
||||
const sessionExpiredMessage = useMemo(
|
||||
() => i18n._(YOUR_PRODUCT_SESSION_EXPIRED_SIGN_IN_AGAIN_TO_DESCRIPTOR, {productName: PRODUCT_NAME}),
|
||||
() =>
|
||||
i18n._(YOUR_PRODUCT_SESSION_EXPIRED_SIGN_IN_AGAIN_TO_DESCRIPTOR, {productName: getActiveInstanceProductName()}),
|
||||
[i18n.locale],
|
||||
);
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user