fix(connection): Happ TV connect renders through the active block style (all 4)

The TV quick-connect was rendered as standalone cards inserted between two
sliced block lists, which broke the panel's chosen style (e.g. split the
timeline rail) and dropped a step. Now it renders THROUGH the active renderer:

- Blocks gain an optional customNode; every renderer (cards/timeline/accordion/
  minimal) drops it into the block body, so it inherits that exact style.
- InstallationGuide renders the FULL block list in the active style and injects
  the TV connect widget as customNode on the "add subscription" step (no more
  slicing / dropped steps) — only for the Happ Android TV app.
- TvQuickConnect is now content-only (no one-off cards/titles); the block step is
  the wrapper. Buttons already use the shared blockButtonClass.
This commit is contained in:
c0mrade
2026-06-03 16:09:49 +03:00
parent ca54cb4732
commit c91c9e0441
8 changed files with 104 additions and 138 deletions

View File

@@ -10,7 +10,7 @@ import type {
} from '@/types';
import { useTheme } from '@/hooks/useTheme';
import { CardsBlock, TimelineBlock, AccordionBlock, MinimalBlock, BlockButtons } from './blocks';
import type { BlockRendererProps } from './blocks';
import type { BlockRendererProps, RenderBlock } from './blocks';
import TvQuickConnect from './TvQuickConnect';
import { BackIcon, BookOpenIcon, ChevronIcon } from '@/components/icons';
@@ -194,6 +194,21 @@ export default function InstallationGuide({
const blockType = appConfig.uiConfig?.installationGuidesBlockType || 'cards';
const Renderer = RENDERERS[blockType] || CardsBlock;
// For the Happ Android TV app, inject the TV connect widget into a step as
// customNode so it renders THROUGH the active block style (cards/timeline/
// accordion/minimal) instead of as separate clashing cards that break it.
const showTvConnect = Boolean(
selectedApp && isAndroidTvLayout && isHappApp(selectedApp) && appConfig.subscriptionUrl,
);
let renderBlocks: RenderBlock[] = selectedApp?.blocks ?? [];
if (selectedApp && showTvConnect && appConfig.subscriptionUrl) {
// install → add-subscription → connect: attach to the add step (index 1);
// fall back to the last block for shorter configs.
const idx = selectedApp.blocks.length >= 3 ? 1 : Math.max(0, selectedApp.blocks.length - 1);
const widget = <TvQuickConnect subscriptionUrl={appConfig.subscriptionUrl} isLight={isLight} />;
renderBlocks = selectedApp.blocks.map((b, i) => (i === idx ? { ...b, customNode: widget } : b));
}
return (
<div className="space-y-6 pb-6">
{/* Header + platform dropdown */}
@@ -321,42 +336,19 @@ export default function InstallationGuide({
</a>
)}
{/* Blocks — for the Happ TV app: first block, Quick Connect, last block.
Other apps (or non-TV) render their blocks normally. */}
{selectedApp && isAndroidTvLayout && isHappApp(selectedApp) && appConfig.subscriptionUrl ? (
<>
{selectedApp.blocks.length > 0 && (
<Renderer
blocks={selectedApp.blocks.slice(0, 1)}
isMobile={isMobile}
isLight={isLight}
getLocalizedText={getLocalizedText}
getSvgHtml={getSvgHtml}
renderBlockButtons={renderBlockButtons}
/>
)}
<TvQuickConnect subscriptionUrl={appConfig.subscriptionUrl} isLight={isLight} />
{selectedApp.blocks.length > 1 && (
<Renderer
blocks={selectedApp.blocks.slice(-1)}
isMobile={isMobile}
isLight={isLight}
getLocalizedText={getLocalizedText}
getSvgHtml={getSvgHtml}
renderBlockButtons={renderBlockButtons}
/>
)}
</>
) : selectedApp ? (
{/* Blocks rendered in the panel's active style. For the Happ Android TV
app the TV connect widget is injected into a step (customNode), so it
adapts to that style instead of breaking it. */}
{selectedApp && (
<Renderer
blocks={selectedApp.blocks}
blocks={renderBlocks}
isMobile={isMobile}
isLight={isLight}
getLocalizedText={getLocalizedText}
getSvgHtml={getSvgHtml}
renderBlockButtons={renderBlockButtons}
/>
) : null}
)}
</div>
);
}