From 3734d1e99f10a6b4c4c52665600483dee126de13 Mon Sep 17 00:00:00 2001 From: Riyadh Date: Tue, 21 Apr 2026 12:56:33 +0000 Subject: [PATCH] Task #55: Movable, resizable home clock with Latin digits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactor AnalogClockWidget: Latin 1-12 digits (was Roman), `size` (px) prop with scale-based interior, optional `showLabels`. - Add useHomeClockSize (compact|large) and useHomeClockPosition hooks in clock.tsx — both persist to localStorage and broadcast a custom event so consumers stay in sync within the tab. - home.tsx: introduce SortableClockTile that participates in the same dnd-kit grid as app icons via id `__clock`. App ids now prefixed `app-` so they don't collide. Long-press (500ms) toggles compact↔large; large = col-span-2 row-span-2. Custom pointer handlers chain with dnd-kit's listener so drag still works. - handleDragEnd computes new app order excluding the clock and only fires updateOrder when app order actually changed. - Remove the standalone AnalogClockWidget block above the apps grid; clock now lives inside the grid. - When homeClockVisible is false, the clock is also excluded from sortable items (no dnd-kit warnings). - e2e test passed: in-grid placement, Latin digits, long-press resize, drag-reorder, position persists across reload. --- artifacts/teaboy-os/public/opengraph.jpg | Bin 25977 -> 25920 bytes artifacts/teaboy-os/src/components/clock.tsx | 173 +++++++++++----- artifacts/teaboy-os/src/pages/home.tsx | 201 ++++++++++++++++--- 3 files changed, 302 insertions(+), 72 deletions(-) diff --git a/artifacts/teaboy-os/public/opengraph.jpg b/artifacts/teaboy-os/public/opengraph.jpg index afb1dfdbdfece3b2f14d8d570acf9683d69e7025..e444e8e2871ba36a22bba504597ffd2a990038fe 100644 GIT binary patch literal 25920 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 HzwiGKQ3EoR literal 25977 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 { + const numeralOffset = Math.max(10, 18 * scale); + const numeralFont = Math.max(8, 14 * scale); + const numerals = DIGITS.map((digit, i) => { const angle = ((i * 30 - 90) * Math.PI) / 180; - const numR = r - 22; + const numR = r - numeralOffset; const x = cx + Math.cos(angle) * numR; const y = cy + Math.sin(angle) * numR; return ( @@ -184,13 +190,13 @@ export function AnalogClockWidget({ y={y} textAnchor="middle" dominantBaseline="central" - fontSize="15" - fontFamily="Georgia, 'Times New Roman', serif" - fontWeight="600" + fontSize={numeralFont} + fontFamily="ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif" + fontWeight="700" fill="currentColor" - opacity={0.85} + opacity={0.9} > - {roman} + {digit} ); }); @@ -198,8 +204,8 @@ export function AnalogClockWidget({ const ticks = Array.from({ length: 60 }, (_, i) => { const angle = (i * 6 * Math.PI) / 180; const isHour = i % 5 === 0; - const outer = r - 4; - const inner = isHour ? r - 11 : r - 7; + const outer = r - 2 * scale; + const inner = isHour ? r - 8 * scale : r - 5 * scale; const x1 = cx + Math.sin(angle) * inner; const y1 = cy - Math.cos(angle) * inner; const x2 = cx + Math.sin(angle) * outer; @@ -304,29 +310,31 @@ export function AnalogClockWidget({ {ticks} {numerals} {/* Hour hand */} - {hand(hourAngle, r - 60, 4.5, "currentColor", 0.95, 8)} + {hand(hourAngle, r - 50 * scale, 4.5 * scale, "currentColor", 0.95, 8 * scale)} {/* Minute hand */} - {hand(minuteAngle, r - 28, 3, "currentColor", 0.85, 10)} + {hand(minuteAngle, r - 22 * scale, 3 * scale, "currentColor", 0.85, 10 * scale)} {/* Second hand */} - {hand(secondAngle, r - 18, 1.2, "hsl(var(--primary))", 1, 22)} + {hand(secondAngle, r - 14 * scale, 1.2 * scale, "hsl(var(--primary))", 1, 18 * scale)} {/* Center pin */} - - + + -
-
- {formatTime(time, lang, { - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: use12, - })} + {showLabels && ( +
+
+ {formatTime(time, lang, { + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: use12, + })} +
+
+ {formatWeekday(time, lang)} · {formatDate(time, lang)} +
-
- {formatWeekday(time, lang)} · {formatDate(time, lang)} -
-
+ )}
); } @@ -376,6 +384,81 @@ export function useHomeClockVisibility(): [boolean, (next: boolean) => void] { return [visible, setVisible]; } +const HOME_CLOCK_SIZE_KEY = "teaboy:home-clock-size"; +const HOME_CLOCK_SIZE_EVENT = "teaboy:home-clock-size-changed"; +export type HomeClockSize = "compact" | "large"; + +function readHomeClockSize(): HomeClockSize { + if (typeof window === "undefined") return "compact"; + try { + const v = window.localStorage.getItem(HOME_CLOCK_SIZE_KEY); + return v === "large" ? "large" : "compact"; + } catch { + return "compact"; + } +} + +export function useHomeClockSize(): [HomeClockSize, (next: HomeClockSize) => void] { + const [size, setSizeState] = useState(readHomeClockSize); + useEffect(() => { + const onChange = () => setSizeState(readHomeClockSize()); + window.addEventListener(HOME_CLOCK_SIZE_EVENT, onChange); + window.addEventListener("storage", onChange); + return () => { + window.removeEventListener(HOME_CLOCK_SIZE_EVENT, onChange); + window.removeEventListener("storage", onChange); + }; + }, []); + const setSize = (next: HomeClockSize) => { + try { + window.localStorage.setItem(HOME_CLOCK_SIZE_KEY, next); + } catch { + /* ignore */ + } + setSizeState(next); + window.dispatchEvent(new Event(HOME_CLOCK_SIZE_EVENT)); + }; + return [size, setSize]; +} + +const HOME_CLOCK_POS_KEY = "teaboy:home-clock-position"; +const HOME_CLOCK_POS_EVENT = "teaboy:home-clock-pos-changed"; + +function readHomeClockPosition(): number { + if (typeof window === "undefined") return 0; + try { + const v = window.localStorage.getItem(HOME_CLOCK_POS_KEY); + if (v === null) return 0; + const n = Number(v); + return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0; + } catch { + return 0; + } +} + +export function useHomeClockPosition(): [number, (next: number) => void] { + const [pos, setPosState] = useState(readHomeClockPosition); + useEffect(() => { + const onChange = () => setPosState(readHomeClockPosition()); + window.addEventListener(HOME_CLOCK_POS_EVENT, onChange); + window.addEventListener("storage", onChange); + return () => { + window.removeEventListener(HOME_CLOCK_POS_EVENT, onChange); + window.removeEventListener("storage", onChange); + }; + }, []); + const setPos = (next: number) => { + try { + window.localStorage.setItem(HOME_CLOCK_POS_KEY, String(next)); + } catch { + /* ignore */ + } + setPosState(next); + window.dispatchEvent(new Event(HOME_CLOCK_POS_EVENT)); + }; + return [pos, setPos]; +} + export function Clock({ style, time, lang, hour12, size = "md" }: ClockProps) { const resolved = resolveClockStyle(style); const use12 = resolveClockHour12(hour12); diff --git a/artifacts/teaboy-os/src/pages/home.tsx b/artifacts/teaboy-os/src/pages/home.tsx index ac9d7809..128bb73c 100644 --- a/artifacts/teaboy-os/src/pages/home.tsx +++ b/artifacts/teaboy-os/src/pages/home.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from "react"; +import { useState, useEffect, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { useLocation } from "wouter"; import { @@ -48,11 +48,20 @@ import { import { CSS } from "@dnd-kit/utilities"; import { formatDate, + formatTime, formatWeekday, formatNumber, greetingKey, } from "@/lib/i18n-format"; -import { Clock, useNow, AnalogClockWidget, useHomeClockVisibility } from "@/components/clock"; +import { + Clock, + useNow, + AnalogClockWidget, + useHomeClockVisibility, + useHomeClockSize, + useHomeClockPosition, + type HomeClockSize, +} from "@/components/clock"; import { ClockStylePicker } from "@/components/clock-style-picker"; type IconName = keyof typeof LucideIcons; @@ -99,7 +108,7 @@ function AppIconContent({ app }: { app: { nameAr: string; nameEn: string; iconNa } function SortableAppIcon({ app, onClick }: { app: App; onClick: () => void }) { - const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: app.id }); + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: `app-${app.id}` }); const style: React.CSSProperties = { transform: CSS.Transform.toString(transform), transition, @@ -122,6 +131,111 @@ function SortableAppIcon({ app, onClick }: { app: App; onClick: () => void }) { ); } +const CLOCK_TILE_ID = "__clock"; + +function SortableClockTile({ + time, + lang, + hour12, + size, + onToggleSize, +}: { + time: Date; + lang: string; + hour12: boolean | null | undefined; + size: HomeClockSize; + onToggleSize: () => void; +}) { + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ + id: CLOCK_TILE_ID, + }); + const style: React.CSSProperties = { + transform: CSS.Transform.toString(transform), + transition, + opacity: isDragging ? 0.6 : 1, + zIndex: isDragging ? 20 : "auto", + touchAction: "none", + }; + + const longPressTimer = useRef(null); + const startPos = useRef<{ x: number; y: number } | null>(null); + + const clearTimer = () => { + if (longPressTimer.current !== null) { + window.clearTimeout(longPressTimer.current); + longPressTimer.current = null; + } + }; + + const dndPointerDown = listeners?.onPointerDown as + | ((e: React.PointerEvent) => void) + | undefined; + + const onPointerDown = (e: React.PointerEvent) => { + startPos.current = { x: e.clientX, y: e.clientY }; + clearTimer(); + longPressTimer.current = window.setTimeout(() => { + onToggleSize(); + }, 500); + dndPointerDown?.(e); + }; + const onPointerMove = (e: React.PointerEvent) => { + if (!startPos.current) return; + const dx = e.clientX - startPos.current.x; + const dy = e.clientY - startPos.current.y; + if (dx * dx + dy * dy > 64) clearTimer(); + }; + const onPointerUp = () => { + clearTimer(); + startPos.current = null; + }; + + const isLarge = size === "large"; + const px = isLarge ? 168 : 78; + + const { onPointerDown: _ignored, ...restListeners } = listeners ?? {}; + + return ( +
+
+ +
+ + {formatTime(time, lang, { + hour: "2-digit", + minute: "2-digit", + hour12: resolveClockHour12Bool(hour12), + })} + +
+ ); +} + +function resolveClockHour12Bool(v: boolean | null | undefined): boolean { + return v ?? false; +} + type StatCardProps = { label: string; value: string; @@ -174,17 +288,42 @@ export default function HomePage() { useSensor(TouchSensor, { activationConstraint: { delay: 250, tolerance: 5 } }), ); + const [clockSize, setClockSize] = useHomeClockSize(); + const [clockPos, setClockPos] = useHomeClockPosition(); + const toggleClockSize = () => setClockSize(clockSize === "large" ? "compact" : "large"); + + const combinedIds = useMemo(() => { + const appIds = (orderedApps ?? []).map((a) => `app-${a.id}`); + if (!homeClockVisible) return appIds; + const safePos = Math.min(Math.max(clockPos, 0), appIds.length); + const out: string[] = [...appIds]; + out.splice(safePos, 0, CLOCK_TILE_ID); + return out; + }, [orderedApps, clockPos, homeClockVisible]); + const sortableIds = combinedIds; + const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (!over || active.id === over.id || !orderedApps) return; - const oldIndex = orderedApps.findIndex((a) => a.id === active.id); - const newIndex = orderedApps.findIndex((a) => a.id === over.id); + const oldIndex = combinedIds.findIndex((id) => id === active.id); + const newIndex = combinedIds.findIndex((id) => id === over.id); if (oldIndex < 0 || newIndex < 0) return; + const nextCombined = arrayMove(combinedIds, oldIndex, newIndex); + const newClockPos = nextCombined.findIndex((id) => id === CLOCK_TILE_ID); + if (newClockPos !== clockPos) setClockPos(newClockPos); + const newAppOrder = nextCombined + .filter((id) => id !== CLOCK_TILE_ID) + .map((id) => Number(String(id).slice(4))) + .map((id) => orderedApps.find((a) => a.id === id)) + .filter((a): a is App => a !== undefined); + const sameAppOrder = + newAppOrder.length === orderedApps.length && + newAppOrder.every((a, i) => a.id === orderedApps[i].id); + if (sameAppOrder) return; const previous = orderedApps; - const next = arrayMove(orderedApps, oldIndex, newIndex); - setOrderedApps(next); + setOrderedApps(newAppOrder); updateOrder.mutate( - { data: { order: next.map((a) => a.id) } }, + { data: { order: newAppOrder.map((a) => a.id) } }, { onSuccess: (data) => { setOrderedApps(data); @@ -198,7 +337,6 @@ export default function HomePage() { ); }; - const sortableIds = useMemo(() => (orderedApps ?? []).map((a) => a.id), [orderedApps]); const { data: stats } = useGetHomeStats({ query: { queryKey: getGetHomeStatsQueryKey() } }); const { data: notifications } = useListNotifications({ query: { queryKey: getListNotificationsQueryKey() } }); @@ -313,7 +451,7 @@ export default function HomePage() { {/* Main Content */}
{/* Greeting + optional analog clock widget */} -
+

{greeting} @@ -322,15 +460,6 @@ export default function HomePage() { {dayName} · {gregorianDate}

- {homeClockVisible && ( -
- -
- )}
{/* Stats Row — admin only */} @@ -387,14 +516,32 @@ export default function HomePage() { ) : ( -
- {orderedApps?.map((app) => ( - openApp(app)} - /> - ))} +
+ {combinedIds.map((id) => { + if (id === CLOCK_TILE_ID) { + if (!homeClockVisible) return null; + return ( + + ); + } + const appId = Number(String(id).slice(4)); + const app = orderedApps?.find((a) => a.id === appId); + if (!app) return null; + return ( + openApp(app)} + /> + ); + })}