From c402c52a8a6a6a76779be4695b1904b8b344c3ed Mon Sep 17 00:00:00 2001 From: Hampus Date: Wed, 7 Oct 2026 23:27:59 +0200 Subject: [PATCH] fix(app): read instance config safely in startup skeletons (#3259) --- .../app/components/skeleton/ChatSkeleton.tsx | 4 ++-- .../components/skeleton/GuildRailSkeleton.tsx | 9 ++++++--- .../app/components/skeleton/ShellHint.ts | 15 +++++++++----- .../skeleton/SkeletonRuntimeConfig.ts | 20 +++++++++++++++++++ 4 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 fluxer_app/src/features/app/components/skeleton/SkeletonRuntimeConfig.ts diff --git a/fluxer_app/src/features/app/components/skeleton/ChatSkeleton.tsx b/fluxer_app/src/features/app/components/skeleton/ChatSkeleton.tsx index a8c7c7f60..1ee67bde3 100644 --- a/fluxer_app/src/features/app/components/skeleton/ChatSkeleton.tsx +++ b/fluxer_app/src/features/app/components/skeleton/ChatSkeleton.tsx @@ -17,12 +17,12 @@ import { SKELETON_UNMEASURED_WIDTH_PX, } from '@app/features/app/components/skeleton/SkeletonLayoutMemory'; 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 { type SkeletonInjectedToken, skeletonSurfaceVar, } 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 {MemberListSkeleton, MemberListSkeletonVariant} from '@app/features/channel/components/MemberListSkeleton'; 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(() => Object.freeze({ composerLayout: - getRememberedSkeletonComposerLayout() ?? resolveDefaultSkeletonComposerLayout(RuntimeConfig.gifEnabled), + getRememberedSkeletonComposerLayout() ?? resolveDefaultSkeletonComposerLayout(skeletonGifEnabled()), messagePresentation: getRememberedSkeletonMessagePresentation() ?? resolveDefaultSkeletonMessagePresentation(), }), ); diff --git a/fluxer_app/src/features/app/components/skeleton/GuildRailSkeleton.tsx b/fluxer_app/src/features/app/components/skeleton/GuildRailSkeleton.tsx index 9c3ebc8fd..673940c5e 100644 --- a/fluxer_app/src/features/app/components/skeleton/GuildRailSkeleton.tsx +++ b/fluxer_app/src/features/app/components/skeleton/GuildRailSkeleton.tsx @@ -13,9 +13,12 @@ import { SkeletonGuildRailItemIndicator, SkeletonGuildRailItemKind, } 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 {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 {getRemScaleForDocument} from '@app/features/theme/layout/RemFromPx'; import Dimension from '@app/features/ui/state/Dimension'; @@ -454,7 +457,7 @@ export const GuildRailSkeleton: React.FC = ({ }); }); const {rememberedLayout} = mountState; - const communityActionsAvailable = !RuntimeConfig.singleCommunityEnabled; + const communityActionsAvailable = !skeletonSingleCommunityEnabled(); const inlineDmRowCount = rememberedLayout?.inlineDmRowCount ?? 0; const inlineDmUnreadFlags = rememberedLayout?.inlineDmUnreadFlags ?? EMPTY_INLINE_DM_UNREAD_FLAGS; const selectedInlineDmRowIndex = rememberedLayout?.selectedInlineDmRowIndex ?? SKELETON_NO_SELECTED_RAIL_ITEM_INDEX; @@ -462,7 +465,7 @@ export const GuildRailSkeleton: React.FC = ({ const outageVisible = rememberedLayout?.outageVisible ?? false; const organizedItems = rememberedLayout?.organizedItems ?? SKELETON_GUILD_RAIL_FALLBACK_ITEMS; 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 discoveryVisible = rememberedLayout?.discoveryVisible ?? communityActionsAvailable; const addGuildVisible = rememberedLayout?.addGuildVisible ?? communityActionsAvailable; diff --git a/fluxer_app/src/features/app/components/skeleton/ShellHint.ts b/fluxer_app/src/features/app/components/skeleton/ShellHint.ts index d480cf96f..08e2cf6b3 100644 --- a/fluxer_app/src/features/app/components/skeleton/ShellHint.ts +++ b/fluxer_app/src/features/app/components/skeleton/ShellHint.ts @@ -39,7 +39,12 @@ import { SkeletonGuildRailItemIndicator, SkeletonGuildRailItemKind, } 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 { GATEWAY_PREBOOT_SESSION_STORAGE_KEY, @@ -162,7 +167,7 @@ interface RailProjection { } function resolveRailProjection(layout: RememberedSkeletonGuildRailLayout | null): RailProjection { - const communityActionsAvailable = !RuntimeConfig.singleCommunityEnabled; + const communityActionsAvailable = !skeletonSingleCommunityEnabled(); const inlineDmRowCount = layout?.inlineDmRowCount ?? 0; const rememberedUnread = layout?.inlineDmUnreadFlags ?? []; const inlineDmUnreadFlags = Array.from( @@ -170,7 +175,7 @@ function resolveRailProjection(layout: RememberedSkeletonGuildRailLayout | null) (_unused, index) => rememberedUnread[index] === true, ); return { - fluxerVisible: layout?.fluxerVisible ?? !RuntimeConfig.directMessagesDisabled, + fluxerVisible: layout?.fluxerVisible ?? !skeletonDirectMessagesDisabled(), favoritesVisible: layout?.favoritesVisible ?? true, inlineDmUnreadFlags, selectedInlineDmRowIndex: layout?.selectedInlineDmRowIndex ?? NO_SELECTED_ROW_INDEX, @@ -245,7 +250,7 @@ function resolveDMActionMask(layout: RememberedSkeletonDMSidebarLayout | null, m if (layout?.personalNotesVisible ?? true) { mask |= DM_ACTION_PERSONAL_NOTES_BIT; } - if (layout?.premiumVisible ?? (!RuntimeConfig.isSelfHosted() && !mobile)) { + if (layout?.premiumVisible ?? (!skeletonSelfHosted() && !mobile)) { mask |= DM_ACTION_PREMIUM_BIT; } return mask; @@ -297,7 +302,7 @@ export function writeShellHint(): void { const isChat = shell.content.kind === 'chat'; const chat = resolveChatSkeletonPresentation(pathname, !mobile && viewportWidth >= MEMBER_LIST_FIT_MIN_WIDTH_PX); const composer = - getRememberedSkeletonComposerLayout() ?? resolveDefaultSkeletonComposerLayout(RuntimeConfig.gifEnabled); + getRememberedSkeletonComposerLayout() ?? resolveDefaultSkeletonComposerLayout(skeletonGifEnabled()); const message = getRememberedSkeletonMessagePresentation() ?? resolveDefaultSkeletonMessagePresentation(); const voice = getRememberedSkeletonVoicePresence() ?? SKELETON_DEFAULT_VOICE_PRESENCE; const showMemberList = isChat && chat.showMemberList; diff --git a/fluxer_app/src/features/app/components/skeleton/SkeletonRuntimeConfig.ts b/fluxer_app/src/features/app/components/skeleton/SkeletonRuntimeConfig.ts new file mode 100644 index 000000000..d2588c77c --- /dev/null +++ b/fluxer_app/src/features/app/components/skeleton/SkeletonRuntimeConfig.ts @@ -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); +}