fix(app): read instance config safely in startup skeletons (#3259)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-07 23:27:59 +02:00
1 parent 317fedeef9
commit c402c52a8a
4 files changed
+38 -10

No files matched your search

@@ -17,12 +17,12 @@ import {
SKELETON_UNMEASURED_WIDTH_PX, SKELETON_UNMEASURED_WIDTH_PX,
} from '@app/features/app/components/skeleton/SkeletonLayoutMemory'; } from '@app/features/app/components/skeleton/SkeletonLayoutMemory';
import {SkeletonLine} from '@app/features/app/components/skeleton/SkeletonLine'; import {SkeletonLine} from '@app/features/app/components/skeleton/SkeletonLine';
import {skeletonGifEnabled} from '@app/features/app/components/skeleton/SkeletonRuntimeConfig';
import {SkeletonEmphasis, SkeletonRadius} from '@app/features/app/components/skeleton/SkeletonStyle'; import {SkeletonEmphasis, SkeletonRadius} from '@app/features/app/components/skeleton/SkeletonStyle';
import { import {
type SkeletonInjectedToken, type SkeletonInjectedToken,
skeletonSurfaceVar, skeletonSurfaceVar,
} from '@app/features/app/components/skeleton/SkeletonSurfaceContract'; } from '@app/features/app/components/skeleton/SkeletonSurfaceContract';
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import {CHANNEL_HEADER_DM_AVATAR_SIZE_PX} from '@app/features/channel/components/ChannelHeaderMetrics'; import {CHANNEL_HEADER_DM_AVATAR_SIZE_PX} from '@app/features/channel/components/ChannelHeaderMetrics';
import {MemberListSkeleton, MemberListSkeletonVariant} from '@app/features/channel/components/MemberListSkeleton'; import {MemberListSkeleton, MemberListSkeletonVariant} from '@app/features/channel/components/MemberListSkeleton';
import composerWrapperStyles from '@app/features/channel/components/textarea/InputWrapper.module.css'; import composerWrapperStyles from '@app/features/channel/components/textarea/InputWrapper.module.css';
@@ -163,7 +163,7 @@ export const ChatSkeleton = observer(function ChatSkeleton({presentation}: ChatS
const [rememberedChatState] = useState(() => const [rememberedChatState] = useState(() =>
Object.freeze({ Object.freeze({
composerLayout: composerLayout:
getRememberedSkeletonComposerLayout() ?? resolveDefaultSkeletonComposerLayout(RuntimeConfig.gifEnabled), getRememberedSkeletonComposerLayout() ?? resolveDefaultSkeletonComposerLayout(skeletonGifEnabled()),
messagePresentation: getRememberedSkeletonMessagePresentation() ?? resolveDefaultSkeletonMessagePresentation(), messagePresentation: getRememberedSkeletonMessagePresentation() ?? resolveDefaultSkeletonMessagePresentation(),
}), }),
); );
@@ -13,9 +13,12 @@ import {
SkeletonGuildRailItemIndicator, SkeletonGuildRailItemIndicator,
SkeletonGuildRailItemKind, SkeletonGuildRailItemKind,
} from '@app/features/app/components/skeleton/SkeletonLayoutMemory'; } from '@app/features/app/components/skeleton/SkeletonLayoutMemory';
import {
skeletonDirectMessagesDisabled,
skeletonSingleCommunityEnabled,
} from '@app/features/app/components/skeleton/SkeletonRuntimeConfig';
import {SkeletonEmphasis, SkeletonRadius} from '@app/features/app/components/skeleton/SkeletonStyle'; import {SkeletonEmphasis, SkeletonRadius} from '@app/features/app/components/skeleton/SkeletonStyle';
import {skeletonSurfaceVar} from '@app/features/app/components/skeleton/SkeletonSurfaceContract'; import {skeletonSurfaceVar} from '@app/features/app/components/skeleton/SkeletonSurfaceContract';
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import {Platform} from '@app/features/platform/types/Platform'; import {Platform} from '@app/features/platform/types/Platform';
import {getRemScaleForDocument} from '@app/features/theme/layout/RemFromPx'; import {getRemScaleForDocument} from '@app/features/theme/layout/RemFromPx';
import Dimension from '@app/features/ui/state/Dimension'; import Dimension from '@app/features/ui/state/Dimension';
@@ -454,7 +457,7 @@ export const GuildRailSkeleton: React.FC<GuildRailSkeletonProps> = ({
}); });
}); });
const {rememberedLayout} = mountState; const {rememberedLayout} = mountState;
const communityActionsAvailable = !RuntimeConfig.singleCommunityEnabled; const communityActionsAvailable = !skeletonSingleCommunityEnabled();
const inlineDmRowCount = rememberedLayout?.inlineDmRowCount ?? 0; const inlineDmRowCount = rememberedLayout?.inlineDmRowCount ?? 0;
const inlineDmUnreadFlags = rememberedLayout?.inlineDmUnreadFlags ?? EMPTY_INLINE_DM_UNREAD_FLAGS; const inlineDmUnreadFlags = rememberedLayout?.inlineDmUnreadFlags ?? EMPTY_INLINE_DM_UNREAD_FLAGS;
const selectedInlineDmRowIndex = rememberedLayout?.selectedInlineDmRowIndex ?? SKELETON_NO_SELECTED_RAIL_ITEM_INDEX; const selectedInlineDmRowIndex = rememberedLayout?.selectedInlineDmRowIndex ?? SKELETON_NO_SELECTED_RAIL_ITEM_INDEX;
@@ -462,7 +465,7 @@ export const GuildRailSkeleton: React.FC<GuildRailSkeletonProps> = ({
const outageVisible = rememberedLayout?.outageVisible ?? false; const outageVisible = rememberedLayout?.outageVisible ?? false;
const organizedItems = rememberedLayout?.organizedItems ?? SKELETON_GUILD_RAIL_FALLBACK_ITEMS; const organizedItems = rememberedLayout?.organizedItems ?? SKELETON_GUILD_RAIL_FALLBACK_ITEMS;
const selectedItemIndex = rememberedLayout?.selectedItemIndex ?? SKELETON_NO_SELECTED_RAIL_ITEM_INDEX; const selectedItemIndex = rememberedLayout?.selectedItemIndex ?? SKELETON_NO_SELECTED_RAIL_ITEM_INDEX;
const fluxerVisible = rememberedLayout?.fluxerVisible ?? !RuntimeConfig.directMessagesDisabled; const fluxerVisible = rememberedLayout?.fluxerVisible ?? !skeletonDirectMessagesDisabled();
const favoritesVisible = rememberedLayout?.favoritesVisible ?? true; const favoritesVisible = rememberedLayout?.favoritesVisible ?? true;
const discoveryVisible = rememberedLayout?.discoveryVisible ?? communityActionsAvailable; const discoveryVisible = rememberedLayout?.discoveryVisible ?? communityActionsAvailable;
const addGuildVisible = rememberedLayout?.addGuildVisible ?? communityActionsAvailable; const addGuildVisible = rememberedLayout?.addGuildVisible ?? communityActionsAvailable;
@@ -39,7 +39,12 @@ import {
SkeletonGuildRailItemIndicator, SkeletonGuildRailItemIndicator,
SkeletonGuildRailItemKind, SkeletonGuildRailItemKind,
} from '@app/features/app/components/skeleton/SkeletonLayoutMemory'; } from '@app/features/app/components/skeleton/SkeletonLayoutMemory';
import RuntimeConfig from '@app/features/app/state/RuntimeConfig'; import {
skeletonDirectMessagesDisabled,
skeletonGifEnabled,
skeletonSelfHosted,
skeletonSingleCommunityEnabled,
} from '@app/features/app/components/skeleton/SkeletonRuntimeConfig';
import {AppStorageKey} from '@app/features/platform/state/AppStorageKeys'; import {AppStorageKey} from '@app/features/platform/state/AppStorageKeys';
import { import {
GATEWAY_PREBOOT_SESSION_STORAGE_KEY, GATEWAY_PREBOOT_SESSION_STORAGE_KEY,
@@ -162,7 +167,7 @@ interface RailProjection {
} }
function resolveRailProjection(layout: RememberedSkeletonGuildRailLayout | null): RailProjection { function resolveRailProjection(layout: RememberedSkeletonGuildRailLayout | null): RailProjection {
const communityActionsAvailable = !RuntimeConfig.singleCommunityEnabled; const communityActionsAvailable = !skeletonSingleCommunityEnabled();
const inlineDmRowCount = layout?.inlineDmRowCount ?? 0; const inlineDmRowCount = layout?.inlineDmRowCount ?? 0;
const rememberedUnread = layout?.inlineDmUnreadFlags ?? []; const rememberedUnread = layout?.inlineDmUnreadFlags ?? [];
const inlineDmUnreadFlags = Array.from( const inlineDmUnreadFlags = Array.from(
@@ -170,7 +175,7 @@ function resolveRailProjection(layout: RememberedSkeletonGuildRailLayout | null)
(_unused, index) => rememberedUnread[index] === true, (_unused, index) => rememberedUnread[index] === true,
); );
return { return {
fluxerVisible: layout?.fluxerVisible ?? !RuntimeConfig.directMessagesDisabled, fluxerVisible: layout?.fluxerVisible ?? !skeletonDirectMessagesDisabled(),
favoritesVisible: layout?.favoritesVisible ?? true, favoritesVisible: layout?.favoritesVisible ?? true,
inlineDmUnreadFlags, inlineDmUnreadFlags,
selectedInlineDmRowIndex: layout?.selectedInlineDmRowIndex ?? NO_SELECTED_ROW_INDEX, selectedInlineDmRowIndex: layout?.selectedInlineDmRowIndex ?? NO_SELECTED_ROW_INDEX,
@@ -245,7 +250,7 @@ function resolveDMActionMask(layout: RememberedSkeletonDMSidebarLayout | null, m
if (layout?.personalNotesVisible ?? true) { if (layout?.personalNotesVisible ?? true) {
mask |= DM_ACTION_PERSONAL_NOTES_BIT; mask |= DM_ACTION_PERSONAL_NOTES_BIT;
} }
if (layout?.premiumVisible ?? (!RuntimeConfig.isSelfHosted() && !mobile)) { if (layout?.premiumVisible ?? (!skeletonSelfHosted() && !mobile)) {
mask |= DM_ACTION_PREMIUM_BIT; mask |= DM_ACTION_PREMIUM_BIT;
} }
return mask; return mask;
@@ -297,7 +302,7 @@ export function writeShellHint(): void {
const isChat = shell.content.kind === 'chat'; const isChat = shell.content.kind === 'chat';
const chat = resolveChatSkeletonPresentation(pathname, !mobile && viewportWidth >= MEMBER_LIST_FIT_MIN_WIDTH_PX); const chat = resolveChatSkeletonPresentation(pathname, !mobile && viewportWidth >= MEMBER_LIST_FIT_MIN_WIDTH_PX);
const composer = const composer =
getRememberedSkeletonComposerLayout() ?? resolveDefaultSkeletonComposerLayout(RuntimeConfig.gifEnabled); getRememberedSkeletonComposerLayout() ?? resolveDefaultSkeletonComposerLayout(skeletonGifEnabled());
const message = getRememberedSkeletonMessagePresentation() ?? resolveDefaultSkeletonMessagePresentation(); const message = getRememberedSkeletonMessagePresentation() ?? resolveDefaultSkeletonMessagePresentation();
const voice = getRememberedSkeletonVoicePresence() ?? SKELETON_DEFAULT_VOICE_PRESENCE; const voice = getRememberedSkeletonVoicePresence() ?? SKELETON_DEFAULT_VOICE_PRESENCE;
const showMemberList = isChat && chat.showMemberList; const showMemberList = isChat && chat.showMemberList;
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import DeveloperOptions from '@app/features/devtools/state/DeveloperOptions';
export function skeletonSingleCommunityEnabled(): boolean {
return RuntimeConfig.getSnapshotOrNull()?.community.single_community ?? false;
}
export function skeletonDirectMessagesDisabled(): boolean {
return RuntimeConfig.getSnapshotOrNull()?.community.direct_messages_disabled ?? false;
}
export function skeletonGifEnabled(): boolean {
return RuntimeConfig.getSnapshotOrNull()?.services.gif_enabled ?? true;
}
export function skeletonSelfHosted(): boolean {
return DeveloperOptions.selfHostedModeOverride || (RuntimeConfig.getSnapshotOrNull()?.features.self_hosted ?? false);
}