11import { revalidateLogic , useForm , useStore } from "@tanstack/react-form" ;
22import { router , useLocalSearchParams } from "expo-router" ;
33import { StatusBar } from "expo-status-bar" ;
4- import { useEffect , useState } from "react" ;
4+ import { useState } from "react" ;
55import { ScrollView , View } from "react-native" ;
66import { SafeAreaView } from "react-native-safe-area-context" ;
77import { Button } from "@/components/ui/button" ;
@@ -17,24 +17,82 @@ import {
1717} from "../hooks/use-submissions" ;
1818import { collectInvalidLabels } from "../lib/collect-invalid-labels" ;
1919import {
20- EMPTY_SUBMISSION ,
2120 type MosqueSubmissionInput ,
2221 mosqueSubmissionSchema ,
2322} from "../lib/schemas" ;
2423import { SubmissionFormFields } from "./submission-form-fields" ;
2524
25+ type Submission = NonNullable < ReturnType < typeof useMySubmission > [ "data" ] > ;
26+
2627export function SubmissionEditScreen ( ) {
2728 const { id } = useLocalSearchParams < { id : string } > ( ) ;
2829 const { data : submission , isLoading } = useMySubmission ( id ) ;
30+ const scheme = useThemeScheme ( ) ;
31+
32+ return (
33+ < View className = "flex-1 bg-cream" >
34+ < StatusBar style = { scheme === "dark" ? "light" : "dark" } />
35+ < SafeAreaView edges = { [ "top" ] } className = "bg-cream" >
36+ < View className = "flex-row items-center justify-between px-s-5 py-s-2" >
37+ < IconButton
38+ icon = "back"
39+ size = "sm"
40+ variant = "ghost"
41+ onPress = { ( ) => router . back ( ) }
42+ accessibilityLabel = "Back"
43+ />
44+ < Text variant = "display-sm" > Edit submission</ Text >
45+ < View className = "h-10 w-10" />
46+ </ View >
47+ </ SafeAreaView >
48+
49+ { isLoading || ! submission || ! id ? (
50+ < View className = "flex-1 items-center justify-center" >
51+ < Text variant = "body" tone = "muted" >
52+ Loading…
53+ </ Text >
54+ </ View >
55+ ) : (
56+ < EditSubmissionForm id = { id } submission = { submission } />
57+ ) }
58+ </ View >
59+ ) ;
60+ }
61+
62+ function submissionToDefaults ( submission : Submission ) : MosqueSubmissionInput {
63+ return {
64+ name : submission . name ,
65+ subtitle : submission . subtitle ?? null ,
66+ about : submission . about ?? null ,
67+ address : submission . address ?? null ,
68+ street : submission . street ?? null ,
69+ area : submission . area ?? null ,
70+ city : submission . city ,
71+ lat : submission . lat ,
72+ lng : submission . lng ,
73+ open : submission . open ,
74+ tags : submission . tags ?? [ ] ,
75+ facilities : submission . facilities ?? [ ] ,
76+ photos : submission . photos ?? [ ] ,
77+ } ;
78+ }
79+
80+ function EditSubmissionForm ( {
81+ id,
82+ submission,
83+ } : {
84+ id : string ;
85+ submission : Submission ;
86+ } ) {
2987 const update = useUpdateMySubmission ( ) ;
3088 const del = useDeleteMySubmission ( ) ;
3189 const [ errorMessage , setErrorMessage ] = useState < string | null > ( null ) ;
3290 const dialog = useAppDialog ( ) ;
3391 const toast = useAppToast ( ) ;
34- const scheme = useThemeScheme ( ) ;
92+ const readOnly = submission . status !== "pending" ;
3593
3694 const form = useForm ( {
37- defaultValues : EMPTY_SUBMISSION ,
95+ defaultValues : submissionToDefaults ( submission ) ,
3896 validationLogic : revalidateLogic ( {
3997 mode : "submit" ,
4098 modeAfterSubmission : "change" ,
@@ -52,7 +110,6 @@ export function SubmissionEditScreen() {
52110 } ) ;
53111 } ,
54112 onSubmit : async ( { value } ) => {
55- if ( ! id ) return ;
56113 try {
57114 await update . mutateAsync ( {
58115 id,
@@ -71,33 +128,9 @@ export function SubmissionEditScreen() {
71128 } ,
72129 } ) ;
73130
74- // biome-ignore lint/correctness/useExhaustiveDependencies: form from useForm is stable; re-running on form identity would loop
75- useEffect ( ( ) => {
76- if ( ! submission ) return ;
77- form . reset ( {
78- name : submission . name ,
79- subtitle : submission . subtitle ?? null ,
80- about : submission . about ?? null ,
81- address : submission . address ?? null ,
82- street : submission . street ?? null ,
83- area : submission . area ?? null ,
84- city : submission . city ,
85- lat : submission . lat ,
86- lng : submission . lng ,
87- open : submission . open ,
88- tags : submission . tags ?? [ ] ,
89- facilities : submission . facilities ?? [ ] ,
90- photos : submission . photos ?? [ ] ,
91- } ) ;
92- setErrorMessage ( null ) ;
93- } , [ submission ] ) ;
94-
95131 const isSubmitting = useStore ( form . store , ( s ) => s . isSubmitting ) ;
96132
97- const readOnly = submission && submission . status !== "pending" ;
98-
99133 const handleDelete = ( ) => {
100- if ( ! id ) return ;
101134 dialog . show ( {
102135 title : "Withdraw submission?" ,
103136 body : "This removes the pending row. You can submit again later." ,
@@ -124,83 +157,60 @@ export function SubmissionEditScreen() {
124157 } ;
125158
126159 return (
127- < View className = "flex-1 bg-cream" >
128- < StatusBar style = { scheme === "dark" ? "light" : "dark" } />
129- < SafeAreaView edges = { [ "top" ] } className = "bg-cream" >
130- < View className = "flex-row items-center justify-between px-s-5 py-s-2" >
131- < IconButton
132- icon = "back"
133- size = "sm"
134- variant = "ghost"
135- onPress = { ( ) => router . back ( ) }
136- accessibilityLabel = "Back"
137- />
138- < Text variant = "display-sm" > Edit submission</ Text >
139- < View className = "h-10 w-10" />
140- </ View >
141- </ SafeAreaView >
160+ < >
161+ < ScrollView
162+ contentContainerStyle = { {
163+ paddingHorizontal : 24 ,
164+ paddingTop : 12 ,
165+ paddingBottom : 48 ,
166+ } }
167+ keyboardShouldPersistTaps = "handled"
168+ showsVerticalScrollIndicator = { false }
169+ >
170+ { readOnly ? (
171+ < View className = "mb-s-4 rounded-md border border-line bg-surface p-s-4" >
172+ < Text variant = "label" >
173+ This mosque is already { submission . status }
174+ </ Text >
175+ < Text variant = "caption" tone = "muted" className = "mt-s-1" >
176+ Approved and hidden mosques can only be changed by an admin. You
177+ can still view the details below.
178+ </ Text >
179+ </ View >
180+ ) : null }
142181
143- { isLoading || ! submission ? (
144- < View className = "flex-1 items-center justify-center" >
145- < Text variant = "body" tone = "muted" >
146- Loading…
147- </ Text >
148- </ View >
149- ) : (
150- < ScrollView
151- contentContainerStyle = { {
152- paddingHorizontal : 24 ,
153- paddingTop : 12 ,
154- paddingBottom : 48 ,
155- } }
156- keyboardShouldPersistTaps = "handled"
157- showsVerticalScrollIndicator = { false }
158- >
159- { readOnly ? (
160- < View className = "mb-s-4 rounded-md border border-line bg-surface p-s-4" >
161- < Text variant = "label" >
162- This mosque is already { submission . status }
163- </ Text >
164- < Text variant = "caption" tone = "muted" className = "mt-s-1" >
165- Approved and hidden mosques can only be changed by an admin. You
166- can still view the details below.
167- </ Text >
168- </ View >
169- ) : null }
182+ { errorMessage ? (
183+ < View className = "mb-s-4 rounded-md border border-danger/40 bg-danger-tint p-s-3" >
184+ < Text variant = "body-sm" className = "text-danger" >
185+ { errorMessage }
186+ </ Text >
187+ </ View >
188+ ) : null }
170189
171- { errorMessage ? (
172- < View className = "mb-s-4 rounded-md border border-danger/40 bg-danger-tint p-s-3" >
173- < Text variant = "body-sm" className = "text-danger" >
174- { errorMessage }
175- </ Text >
176- </ View >
177- ) : null }
190+ < SubmissionFormFields
191+ form = { form }
192+ isSubmitting = { isSubmitting || readOnly }
193+ />
178194
179- < SubmissionFormFields
180- form = { form }
181- isSubmitting = { isSubmitting || Boolean ( readOnly ) }
182- />
183-
184- { ! readOnly ? (
185- < View className = "mt-s-6 gap-s-3" >
186- < Button
187- label = { isSubmitting ? "Saving…" : "Save changes" }
188- onPress = { ( ) => form . handleSubmit ( ) }
189- disabled = { isSubmitting }
190- />
191- < Button
192- label = "Withdraw submission"
193- variant = "outline"
194- onPress = { handleDelete }
195- disabled = { del . isPending }
196- />
197- </ View >
198- ) : null }
199- </ ScrollView >
200- ) }
195+ { ! readOnly ? (
196+ < View className = "mt-s-6 gap-s-3" >
197+ < Button
198+ label = { isSubmitting ? "Saving…" : "Save changes" }
199+ onPress = { ( ) => form . handleSubmit ( ) }
200+ disabled = { isSubmitting }
201+ />
202+ < Button
203+ label = "Withdraw submission"
204+ variant = "outline"
205+ onPress = { handleDelete }
206+ disabled = { del . isPending }
207+ />
208+ </ View >
209+ ) : null }
210+ </ ScrollView >
201211
202212 { dialog . element }
203213 { toast . element }
204- </ View >
214+ </ >
205215 ) ;
206216}
0 commit comments