diff --git a/example/app/testing-grounds/widget-scheduling.tsx b/example/app/testing-grounds/widget-scheduling.tsx new file mode 100644 index 00000000..17ba502c --- /dev/null +++ b/example/app/testing-grounds/widget-scheduling.tsx @@ -0,0 +1,3 @@ +import WidgetSchedulingScreen from '~/screens/testing-grounds/WidgetSchedulingScreen' + +export default WidgetSchedulingScreen diff --git a/example/screens/testing-grounds/TestingGroundsScreen.tsx b/example/screens/testing-grounds/TestingGroundsScreen.tsx index c5460978..3c38ff7a 100644 --- a/example/screens/testing-grounds/TestingGroundsScreen.tsx +++ b/example/screens/testing-grounds/TestingGroundsScreen.tsx @@ -55,6 +55,13 @@ const TESTING_GROUNDS_SECTIONS = [ 'Test image preloading functionality for Live Activities. Download images to App Group storage and verify they appear in Live Activities.', route: '/testing-grounds/image-preloading', }, + { + id: 'widget-scheduling', + title: 'Widget Scheduling', + description: + 'Test widget timeline scheduling with multiple states. Configure timing for each state and watch widgets automatically transition between them.', + route: '/testing-grounds/widget-scheduling', + }, // Add more sections here as they are implemented ] diff --git a/example/screens/testing-grounds/WidgetSchedulingScreen.tsx b/example/screens/testing-grounds/WidgetSchedulingScreen.tsx new file mode 100644 index 00000000..493fdb96 --- /dev/null +++ b/example/screens/testing-grounds/WidgetSchedulingScreen.tsx @@ -0,0 +1,448 @@ +import { useRouter } from 'expo-router' +import React, { useState } from 'react' +import { Alert, ScrollView, StyleSheet, Text, TextInput, useColorScheme, View } from 'react-native' +import { Voltra } from 'voltra' +import { reloadWidgets, scheduleWidget, VoltraWidgetPreview } from 'voltra/client' + +import { Button } from '~/components/Button' +import { Card } from '~/components/Card' + +export default function WidgetSchedulingScreen() { + const colorScheme = useColorScheme() + const router = useRouter() + const [isScheduling, setIsScheduling] = useState(false) + const [minutesUntilSecond, setMinutesUntilSecond] = useState('2') + const [minutesUntilThird, setMinutesUntilThird] = useState('5') + const [scheduledTimes, setScheduledTimes] = useState<{ past: string; second: string; third: string } | null>(null) + + const widgetPreviewStyle = { + borderRadius: 16, + backgroundColor: colorScheme === 'light' ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.8)', + } + + const handleScheduleTimeline = async () => { + setIsScheduling(true) + try { + const now = new Date() + const secondMinutes = parseInt(minutesUntilSecond) || 2 + const thirdMinutes = parseInt(minutesUntilThird) || 5 + + // Entry 1: Yesterday (past entry - shows as current state) + const yesterday = new Date(now) + yesterday.setDate(yesterday.getDate() - 1) + yesterday.setHours(12, 0, 0, 0) + + // Entry 2: Future entry (configured minutes from now) + const secondEntry = new Date(now.getTime() + secondMinutes * 60 * 1000) + + // Entry 3: Future entry (configured minutes from now) + const thirdEntry = new Date(now.getTime() + thirdMinutes * 60 * 1000) + + const entries = [ + { + date: yesterday, + variants: { + systemSmall: ( + + + STATE 1 + Current + Yesterday + + + ), + systemMedium: ( + + + STATE 1 + Current State + Scheduled: Yesterday + + + ), + systemLarge: ( + + + STATE 1 + Current State + Scheduled: Yesterday at Noon + + + ), + }, + }, + { + date: secondEntry, + variants: { + systemSmall: ( + + + STATE 2 + +{secondMinutes} min + + + ), + systemMedium: ( + + + STATE 2 + Second State + + {secondEntry.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })} + + + + ), + systemLarge: ( + + + STATE 2 + Second State + + {secondEntry.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })} + + + + ), + }, + }, + { + date: thirdEntry, + variants: { + systemSmall: ( + + + STATE 3 + +{thirdMinutes} min + + + ), + systemMedium: ( + + + STATE 3 + Third State + + {thirdEntry.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })} + + + + ), + systemLarge: ( + + + STATE 3 + Third State + + {thirdEntry.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })} + + + + ), + }, + }, + ] + + await scheduleWidget('weather', entries) + await reloadWidgets(['weather']) + + // Format times for display + const formatter = new Intl.DateTimeFormat('en-US', { + hour: 'numeric', + minute: '2-digit', + second: '2-digit', + }) + + setScheduledTimes({ + past: formatter.format(yesterday), + second: formatter.format(secondEntry), + third: formatter.format(thirdEntry), + }) + + Alert.alert( + 'Timeline Scheduled', + `Three states scheduled:\n\n` + + `State 1: ${formatter.format(yesterday)}\n` + + `State 2: ${formatter.format(secondEntry)} (+${secondMinutes}m)\n` + + `State 3: ${formatter.format(thirdEntry)} (+${thirdMinutes}m)\n\n` + + `Watch the widget transition between states!`, + [{ text: 'OK' }] + ) + } catch (error) { + console.error('Failed to schedule timeline:', error) + Alert.alert('Error', 'Failed to schedule timeline. Check console for details.') + } finally { + setIsScheduling(false) + } + } + + const handleClearTimeline = async () => { + try { + // Clear by updating with current content + await scheduleWidget('weather', []) + await reloadWidgets(['weather']) + setScheduledTimes(null) + Alert.alert('Timeline Cleared', 'The widget timeline has been cleared.') + } catch (error) { + console.error('Failed to clear timeline:', error) + Alert.alert('Error', 'Failed to clear timeline. Check console for details.') + } + } + + return ( + + + Widget Scheduling + + Test widget timeline scheduling with multiple states. Configure when each state should appear and watch the + widget transition automatically. + + + {/* Configuration */} + + ⚙️ Configuration + Set when each future state should appear: + + + State 2 (minutes from now): + + + + + State 3 (minutes from now): + + + + + {/* Schedule Timeline */} + + 📅 Schedule Timeline + + Schedules three widget states:{'\n\n'} + • State 1 (Blue): Yesterday - shows as current{'\n'} + • State 2 (Green): {minutesUntilSecond || '2'} minutes from now{'\n'} + • State 3 (Purple): {minutesUntilThird || '5'} minutes from now{'\n\n'} + Add the widget to your home screen to see it transition between states. + + +