Make scheduling and dates timezone-safe
This commit is contained in:
@@ -3,11 +3,22 @@ import DateTimePicker, {
|
||||
type DateTimePickerEvent,
|
||||
} from "@react-native-community/datetimepicker";
|
||||
import { useState } from "react";
|
||||
import { Modal, Platform, Pressable, StyleSheet, Text, View } from "react-native";
|
||||
import {
|
||||
Modal,
|
||||
Platform,
|
||||
Pressable,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
|
||||
import { fonts, radii, spacing } from "@/constants/theme";
|
||||
import { useAppTheme } from "@/contexts/ThemeContext";
|
||||
import { formatDate, formatDateTime } from "@/lib/format";
|
||||
import {
|
||||
calendarDateFromLocalDate,
|
||||
calendarDateToLocalDate,
|
||||
} from "@beenvoice/domain/time-zone";
|
||||
|
||||
type DateTimeFieldProps = {
|
||||
label: string;
|
||||
@@ -31,17 +42,18 @@ export function DateTimeField({
|
||||
const [draft, setDraft] = useState(value);
|
||||
|
||||
function openPicker() {
|
||||
setDraft(value);
|
||||
setDraft(mode === "date" ? calendarDateToLocalDate(value) : value);
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
function applyDate(next: Date) {
|
||||
const normalized = mode === "date" ? calendarDateFromLocalDate(next) : next;
|
||||
const clamped =
|
||||
next.getTime() > maximumDate.getTime()
|
||||
normalized.getTime() > maximumDate.getTime()
|
||||
? maximumDate
|
||||
: minimumDate && next.getTime() < minimumDate.getTime()
|
||||
: minimumDate && normalized.getTime() < minimumDate.getTime()
|
||||
? minimumDate
|
||||
: next;
|
||||
: normalized;
|
||||
onChange(clamped);
|
||||
}
|
||||
|
||||
@@ -60,7 +72,9 @@ export function DateTimeField({
|
||||
|
||||
return (
|
||||
<View style={styles.wrapper}>
|
||||
<Text style={[styles.label, { color: colors.mutedForeground }]}>{label}</Text>
|
||||
<Text style={[styles.label, { color: colors.mutedForeground }]}>
|
||||
{label}
|
||||
</Text>
|
||||
<Pressable
|
||||
accessible
|
||||
accessibilityLabel={`${label}, ${
|
||||
@@ -81,28 +95,53 @@ export function DateTimeField({
|
||||
<Text style={[styles.value, { color: colors.foreground }]}>
|
||||
{mode === "date" ? formatDate(value) : formatDateTime(value)}
|
||||
</Text>
|
||||
<Ionicons name="calendar-outline" size={18} color={colors.mutedForeground} />
|
||||
<Ionicons
|
||||
name="calendar-outline"
|
||||
size={18}
|
||||
color={colors.mutedForeground}
|
||||
/>
|
||||
</Pressable>
|
||||
|
||||
{Platform.OS === "ios" ? (
|
||||
<Modal visible={open} transparent animationType="slide" onRequestClose={() => setOpen(false)}>
|
||||
<Modal
|
||||
visible={open}
|
||||
transparent
|
||||
animationType="slide"
|
||||
onRequestClose={() => setOpen(false)}
|
||||
>
|
||||
<Pressable style={styles.backdrop} onPress={() => setOpen(false)}>
|
||||
<Pressable
|
||||
style={[styles.sheet, { backgroundColor: colors.card }]}
|
||||
onPress={(event) => event.stopPropagation()}
|
||||
>
|
||||
<View style={[styles.sheetHeader, { borderBottomColor: colors.border }]}>
|
||||
<View
|
||||
style={[
|
||||
styles.sheetHeader,
|
||||
{ borderBottomColor: colors.border },
|
||||
]}
|
||||
>
|
||||
<Pressable onPress={() => setOpen(false)}>
|
||||
<Text style={[styles.sheetAction, { color: colors.mutedForeground }]}>Cancel</Text>
|
||||
<Text
|
||||
style={[
|
||||
styles.sheetAction,
|
||||
{ color: colors.mutedForeground },
|
||||
]}
|
||||
>
|
||||
Cancel
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Text style={[styles.sheetTitle, { color: colors.foreground }]}>{label}</Text>
|
||||
<Text style={[styles.sheetTitle, { color: colors.foreground }]}>
|
||||
{label}
|
||||
</Text>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
applyDate(draft);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.sheetAction, { color: colors.primary }]}>Done</Text>
|
||||
<Text style={[styles.sheetAction, { color: colors.primary }]}>
|
||||
Done
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
<DateTimePicker
|
||||
|
||||
Reference in New Issue
Block a user