From 107b593002d67b51e12c707e12cbd6c27e28be79 Mon Sep 17 00:00:00 2001 From: Sean O'Connor Date: Wed, 1 Jul 2026 02:37:58 -0400 Subject: [PATCH] Center live activity timer layout --- widgets/TimeClockActivity.tsx | 158 +++++++++++++++------------------- 1 file changed, 70 insertions(+), 88 deletions(-) diff --git a/widgets/TimeClockActivity.tsx b/widgets/TimeClockActivity.tsx index 27b587a..cd4f494 100644 --- a/widgets/TimeClockActivity.tsx +++ b/widgets/TimeClockActivity.tsx @@ -1,7 +1,9 @@ -import { HStack, Image, Spacer, Text } from "@expo/ui/swift-ui"; +import { HStack, Image, Text, VStack } from "@expo/ui/swift-ui"; import { + frame, font, foregroundStyle, + layoutPriority, lineLimit, minimumScaleFactor, monospacedDigit, @@ -12,120 +14,100 @@ import { createLiveActivity, type LiveActivityEnvironment } from "expo-widgets"; import type { TimeClockActivityProps } from "@/lib/time-clock-live-activity.types"; -const TIMER_HORIZON_MS = 24 * 60 * 60 * 1000; - -function liveTimer( - startedAtMs: number, - modifiers: ReturnType[], -) { - const lower = new Date(startedAtMs); - const upper = new Date(startedAtMs + TIMER_HORIZON_MS); - - return ( - - ); -} - function TimeClockActivity(props: TimeClockActivityProps, _environment: LiveActivityEnvironment) { "widget"; - const green = "green"; - const title = props.description.trim() || "Clock In"; - const clientLabel = props.clientName.trim() || title; - const subtitle = props.invoiceLabel.trim(); - const startedAtMs = props.startedAtMs > 0 ? props.startedAtMs : Date.now(); + const island = "#FFFFFF"; + const businessLabel = props.businessName.trim(); + const clientLabel = props.clientName.trim(); + const title = clientLabel || businessLabel || "Clock In"; const timerMods = [ - font({ design: "monospaced", weight: "bold", size: 20 }), - monospacedDigit(), - foregroundStyle(green), + font({ weight: "bold", size: 17 }), + foregroundStyle({ type: "hierarchical", style: "primary" }), lineLimit(1), - minimumScaleFactor(0.85), + minimumScaleFactor(0.75), + layoutPriority(2), + frame({ width: 100, alignment: "center" }), ]; const compactTimerMods = [ font({ design: "monospaced", weight: "semibold", size: 11 }), monospacedDigit(), - foregroundStyle(green), + foregroundStyle(island), lineLimit(1), - minimumScaleFactor(0.8), + minimumScaleFactor(0.75), + frame({ minWidth: 38, alignment: "center" }), + layoutPriority(2), ]; const clientMods = [ - font({ weight: "semibold", size: 13 }), + font({ weight: "bold", size: 17 }), foregroundStyle({ type: "hierarchical", style: "primary" }), lineLimit(1), - minimumScaleFactor(0.85), + minimumScaleFactor(0.75), ]; - const subtitleMods = [ - font({ size: 11 }), - foregroundStyle({ type: "hierarchical", style: "secondary" }), - lineLimit(1), - minimumScaleFactor(0.85), + // Avoid greedy layout primitives here: the live timerInterval Text can + // collapse when paired with Spacer/maxWidth. Fixed centered boxes keep the + // content stable without forcing left/right edge alignment. + const titleRowMods = [ + layoutPriority(1), + frame({ width: 140, alignment: "center" }), + ]; + const sideZoneMods = [ + frame({ width: 100, alignment: "center" }), + ]; + const bannerRowMods = [ + padding({ horizontal: 10, vertical: 10 }), + frame({ maxWidth: Infinity, alignment: "center" }), ]; - const bannerTimer = liveTimer(startedAtMs, timerMods); - const compactTimer = liveTimer(startedAtMs, compactTimerMods); - + const bannerTimer = {props.elapsedShort}; + const compactTimer = {props.elapsedShort}; + const logoLarge = ( + + ); + const logoSmall = ( + + ); return { banner: ( - - - {clientLabel} - + + + {logoLarge} + + + {title} + {bannerTimer} ), bannerSmall: ( - - - {clientLabel} - - {compactTimer} + + + {logoLarge} + + + {title} + + {bannerTimer} ), - compactLeading: ( - - ), + compactLeading: logoSmall, compactTrailing: compactTimer, - minimal: ( - - ), - expandedLeading: ( - - ), - expandedCenter: {clientLabel}, - expandedTrailing: bannerTimer, - expandedBottom: ( - {subtitle || "beenvoice"} - ), + minimal: logoSmall, }; }