From 5855a88dc630924ea3e047ccb9cc85e578532b08 Mon Sep 17 00:00:00 2001 From: c0mrade Date: Wed, 3 Jun 2026 15:49:58 +0300 Subject: [PATCH] fix(connection): Happ TV quick-connect is Happ-only + matches config-block styles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TV quick-connect block (Подключить TV / Сканировать QR) is a Happ-only feature (check.happ.su/sendtv), but it rendered for ANY app on a TV platform and used one-off button styles (solid btn-primary + grey btn-secondary) that clashed with the outlined-accent buttons that come from the subscription-page config. - Show TvQuickConnect only for the Happ app (detected by its happ:// deep-link scheme, name as fallback). - Extract the config-block button style into a shared blockButtonClass and use it for the TV buttons too, so they adapt to exactly the same visual language as the panel-config blocks (no divergent one-off styling). - Align the code input with the Happ Android TV API doc (5-digit numeric): placeholder 12345 + inputMode numeric. The send call already matches the spec (POST check.happ.su/sendtv/{code}, body {"data": base64(subscriptionUrl)}). --- src/components/connection/InstallationGuide.tsx | 13 +++++++++++-- src/components/connection/TvQuickConnect.tsx | 15 ++++++++++----- .../connection/blocks/BlockButtons.tsx | 10 ++-------- src/components/connection/blocks/buttonStyles.ts | 16 ++++++++++++++++ 4 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 src/components/connection/blocks/buttonStyles.ts diff --git a/src/components/connection/InstallationGuide.tsx b/src/components/connection/InstallationGuide.tsx index 05aa6cc..c04b574 100644 --- a/src/components/connection/InstallationGuide.tsx +++ b/src/components/connection/InstallationGuide.tsx @@ -34,6 +34,14 @@ const RENDERERS: Record> = { minimal: MinimalBlock, }; +/** TV quick-connect is a Happ-only feature (check.happ.su/sendtv) — show it only + * for the Happ app, detected by its happ:// deep-link scheme (name as fallback). */ +function isHappApp(app: RemnawaveAppClient | null): boolean { + if (!app) return false; + if ((app.deepLink ?? '').toLowerCase().startsWith('happ://')) return true; + return app.name.toLowerCase().includes('happ'); +} + interface Props { appConfig: AppConfig; onOpenDeepLink: (url: string) => void; @@ -312,8 +320,9 @@ export default function InstallationGuide({ )} - {/* Blocks — for TV: first block, Quick Connect, last block */} - {selectedApp && isTvPlatform && appConfig.subscriptionUrl ? ( + {/* Blocks — for the Happ TV app: first block, Quick Connect, last block. + Other apps (or non-TV) render their blocks normally. */} + {selectedApp && isTvPlatform && isHappApp(selectedApp) && appConfig.subscriptionUrl ? ( <> {selectedApp.blocks.length > 0 && ( {/* Code input */} @@ -232,15 +237,15 @@ export default function TvQuickConnect({ subscriptionUrl, isLight }: Props) { maxLength={5} value={code} onChange={(e) => setCode(e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, ''))} - placeholder="A1B2C" + placeholder="12345" autoComplete="one-time-code" - inputMode="text" + inputMode="numeric" className={inputClass} /> )} diff --git a/src/components/connection/blocks/BlockButtons.tsx b/src/components/connection/blocks/BlockButtons.tsx index 6f5341b..d0eae14 100644 --- a/src/components/connection/blocks/BlockButtons.tsx +++ b/src/components/connection/blocks/BlockButtons.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'; import { CheckIcon, CopyIcon } from '@/components/icons'; import type { RemnawaveButtonClient, LocalizedText } from '@/types'; import { copyToClipboard } from '@/utils/clipboard'; +import { blockButtonClass } from './buttonStyles'; // eslint-disable-next-line no-script-url const dangerousSchemes = ['javascript:', 'data:', 'vbscript:', 'file:']; @@ -57,14 +58,7 @@ export function BlockButtons({ if (!buttons || buttons.length === 0) return null; - const baseClass = - variant === 'light' - ? isLight - ? 'rounded-xl border border-accent-500/50 px-4 py-2 text-sm font-medium text-accent-600 shadow-sm transition-all hover:bg-accent-500/10' - : 'rounded-xl border border-accent-500/40 px-4 py-2 text-sm font-medium text-accent-400 transition-all hover:bg-accent-500/10' - : isLight - ? 'rounded-xl px-3 py-1.5 text-sm font-medium text-dark-300 transition-all hover:bg-dark-700/30' - : 'rounded-xl px-3 py-1.5 text-sm font-medium text-dark-300 transition-all hover:bg-dark-700/50'; + const baseClass = blockButtonClass(variant, isLight); return (
diff --git a/src/components/connection/blocks/buttonStyles.ts b/src/components/connection/blocks/buttonStyles.ts new file mode 100644 index 0000000..f20d804 --- /dev/null +++ b/src/components/connection/blocks/buttonStyles.ts @@ -0,0 +1,16 @@ +/** + * Shared button styling for connection blocks. The config-driven blocks + * (BlockButtons) and the Happ TV quick-connect both render through this so the + * latter adapts to exactly the same visual language as the styles coming from + * the subscription-page config — no divergent one-off button styles. + */ +export function blockButtonClass(variant: 'light' | 'subtle', isLight?: boolean): string { + if (variant === 'light') { + return isLight + ? 'rounded-xl border border-accent-500/50 px-4 py-2 text-sm font-medium text-accent-600 shadow-sm transition-all hover:bg-accent-500/10' + : 'rounded-xl border border-accent-500/40 px-4 py-2 text-sm font-medium text-accent-400 transition-all hover:bg-accent-500/10'; + } + return isLight + ? 'rounded-xl px-3 py-1.5 text-sm font-medium text-dark-300 transition-all hover:bg-dark-700/30' + : 'rounded-xl px-3 py-1.5 text-sm font-medium text-dark-300 transition-all hover:bg-dark-700/50'; +}