Files
duty-teller/webapp-next/src/hooks/use-telegram-theme.ts
Nikolay Tatarinov 16bf1a1043 feat: migrate to Next.js for Mini App and enhance project structure
- Replaced the previous webapp with a new Mini App built using Next.js, improving performance and maintainability.
- Updated the `.gitignore` to exclude Next.js build artifacts and node modules.
- Revised documentation in `AGENTS.md`, `README.md`, and `architecture.md` to reflect the new Mini App structure and technology stack.
- Enhanced Dockerfile to support the new build process for the Next.js application.
- Updated CI workflow to build and test the Next.js application.
- Added new configuration options for the Mini App, including `MINI_APP_SHORT_NAME` for improved deep linking.
- Refactored frontend testing setup to accommodate the new structure and testing framework.
- Removed legacy webapp files and dependencies to streamline the project.
2026-03-03 16:04:08 +03:00

90 lines
3.1 KiB
TypeScript

"use client";
import { useEffect } from "react";
import {
useSignal,
isThemeParamsDark,
setMiniAppBackgroundColor,
setMiniAppHeaderColor,
} from "@telegram-apps/sdk-react";
/**
* Resolves color scheme when Telegram theme is not available (SSR or non-TWA).
* Uses --tg-color-scheme (if set by Telegram) then prefers-color-scheme.
*/
export function getFallbackScheme(): "dark" | "light" {
if (typeof window === "undefined") return "dark";
try {
const cssScheme = getComputedStyle(document.documentElement)
.getPropertyValue("--tg-color-scheme")
.trim();
if (cssScheme === "light" || cssScheme === "dark") return cssScheme;
} catch {
// ignore
}
if (window.matchMedia("(prefers-color-scheme: dark)").matches) return "dark";
return "light";
}
/**
* Ensure --surface differs from --bg so cards/cells are visible.
* iOS OLED sends secondary_bg === bg (#000000) while section_bg differs;
* PC desktop sends section_bg === bg (#17212b) while secondary_bg differs.
* When the CSS-resolved --surface equals --bg, override with whichever
* Telegram color provides contrast, or a synthesized lighter fallback.
*/
export function fixSurfaceContrast(): void {
const root = document.documentElement;
const cs = getComputedStyle(root);
const bg = cs.getPropertyValue("--bg").trim();
const surface = cs.getPropertyValue("--surface").trim();
if (!bg || !surface || bg !== surface) return;
const sectionBg = cs.getPropertyValue("--tg-theme-section-bg-color").trim();
if (sectionBg && sectionBg !== bg) {
root.style.setProperty("--surface", sectionBg);
return;
}
const secondaryBg = cs.getPropertyValue("--tg-theme-secondary-bg-color").trim();
if (secondaryBg && secondaryBg !== bg) {
root.style.setProperty("--surface", secondaryBg);
return;
}
root.style.setProperty("--surface", `color-mix(in srgb, ${bg}, white 8%)`);
}
/**
* Applies theme: sets data-theme, forces reflow, fixes surface contrast,
* then Mini App background/header.
* Shared by TelegramProvider (initial + delayed) and useTelegramTheme.
* @param scheme - If provided, use it; otherwise resolve via getFallbackScheme().
*/
export function applyTheme(scheme?: "dark" | "light"): void {
const resolved = scheme ?? getFallbackScheme();
document.documentElement.setAttribute("data-theme", resolved);
void document.documentElement.offsetHeight; // force reflow so WebView repaints
fixSurfaceContrast();
if (setMiniAppBackgroundColor.isAvailable()) {
setMiniAppBackgroundColor("bg_color");
}
if (setMiniAppHeaderColor.isAvailable()) {
setMiniAppHeaderColor("bg_color");
}
}
/**
* Maps Telegram theme params to data-theme and Mini App background/header.
* Subscribes to theme changes via SDK signals.
* Ported from webapp/js/theme.js applyTheme / initTheme.
*/
export function useTelegramTheme(): "dark" | "light" {
const signalDark = useSignal(isThemeParamsDark);
const isDark =
typeof signalDark === "boolean" ? signalDark : getFallbackScheme() === "dark";
useEffect(() => {
applyTheme(isDark ? "dark" : "light");
}, [isDark]);
return isDark ? "dark" : "light";
}