@@ -13,12 +13,16 @@ import { PiGlobe } from "@react-icons/all-files/pi/PiGlobe"
1313import { PiLock } from "@react-icons/all-files/pi/PiLock"
1414import { PiTrash } from "@react-icons/all-files/pi/PiTrash"
1515import { PiX } from "@react-icons/all-files/pi/PiX"
16- import { useEffect , useRef , useState } from "react"
17- import Select from "react-select"
16+ import type React from "react"
17+ import { useEffect , useMemo , useRef , useState } from "react"
18+ import Select , { type StylesConfig } from "react-select"
1819import { useTemplateStorage } from "../../context/TemplateStorageContext"
20+ import { useTemplateSync } from "../../hooks/useTemplateSync"
1921import { applyTemplateStorageAccessFailure } from "../../storage/templateAccessError"
2022import { useEditorStore } from "../../store"
2123
24+ const FlexAny = Flex as unknown as React . FC < Record < string , unknown > >
25+
2226function visibilityFromKnownPrivate (
2327 isPrivate : boolean | null ,
2428) : "everyone" | "onlyMe" {
@@ -38,17 +42,15 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
3842 const provider = useTemplateStorage ( )
3943 const templateId = useEditorStore ( ( s ) => s . templateId )
4044 const templateName = useEditorStore ( ( s ) => s . templateName )
41- const setTemplateName = useEditorStore ( ( s ) => s . setTemplateName )
42- const setTemplateId = useEditorStore ( ( s ) => s . setTemplateId )
43- const transformations = useEditorStore ( ( s ) => s . transformations )
44- const setSyncStatus = useEditorStore ( ( s ) => s . setSyncStatus )
45+ const setTemplateIsPrivate = useEditorStore ( ( s ) => s . setTemplateIsPrivate )
4546 const resetToNewTemplate = useEditorStore ( ( s ) => s . resetToNewTemplate )
4647 const denyTemplateStorageAccess = useEditorStore (
4748 ( s ) => s . denyTemplateStorageAccess ,
4849 )
4950 const templateStorageWriteBlocked = useEditorStore (
5051 ( s ) => s . templateStorageWriteBlocked ,
5152 )
53+ const { saveNow } = useTemplateSync ( )
5254
5355 // Stable ref so the getTemplate effect doesn't re-run when onClose identity changes.
5456 const onCloseRef = useRef ( onClose )
@@ -63,7 +65,6 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
6365 const [ canChangeVisibility , setCanChangeVisibility ] = useState ( true )
6466 const [ isDeleting , setIsDeleting ] = useState ( false )
6567 const [ isSaving , setIsSaving ] = useState ( false )
66- const prevVisibilityRef = useRef ( localVisibility )
6768
6869 useEffect ( ( ) => {
6970 setLocalName ( templateName )
@@ -92,13 +93,10 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
9293 return
9394 }
9495 setLocalVisibility ( record . isPrivate ? "onlyMe" : "everyone" )
95- console . log (
96- provider ?. getCurrentUserSession ( ) ,
97- record . createdBy . userId === provider ?. getCurrentUserSession ( ) ?. id ,
98- )
99- setCanChangeVisibility (
100- record . createdBy . userId === provider ?. getCurrentUserSession ( ) ?. id ,
101- )
96+ const session = provider . getCurrentUserSession ( ) as {
97+ id ?: string
98+ } | null
99+ setCanChangeVisibility ( record . createdBy . userId === session ?. id )
102100 } )
103101 . catch ( ( err ) => {
104102 if (
@@ -120,20 +118,21 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
120118 const saveTemplate = async ( opts ?: { closeAfter ?: boolean } ) => {
121119 if ( ! provider || ! localName . trim ( ) || templateStorageWriteBlocked ) return
122120
123- setIsSaving ( true )
124- setSyncStatus ( "saving" )
125-
126121 try {
127- const saved = await provider . saveTemplate ( {
128- id : templateId ?? undefined ,
129- name : localName . trim ( ) ,
130- transformations : transformations . map ( ( { id : _id , ...rest } ) => rest ) ,
131- isPrivate : localVisibility === "onlyMe" ,
122+ setIsSaving ( true )
123+ const saved = await saveNow ( {
124+ reason : "settings" ,
125+ overrides : {
126+ name : localName . trim ( ) ,
127+ isPrivate : localVisibility === "onlyMe" ,
128+ } ,
129+ } )
130+ if ( ! saved ) return
131+ useEditorStore . getState ( ) . hydrateTemplateMetadata ( {
132+ templateId : saved . id ,
133+ templateName : localName . trim ( ) ,
134+ templateIsPrivate : saved . isPrivate ,
132135 } )
133-
134- setTemplateId ( saved . id )
135- setTemplateName ( localName . trim ( ) )
136- setSyncStatus ( "saved" )
137136 if ( opts ?. closeAfter !== false ) {
138137 onClose ( )
139138 }
@@ -148,29 +147,14 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
148147 }
149148 return
150149 }
151- setSyncStatus (
152- "error" ,
153- err instanceof Error ? err . message : "Failed to save" ,
154- )
155150 } finally {
156151 setIsSaving ( false )
157152 }
158153 }
159154
160- // Auto-save visibility changes (instant, like template name).
161- useEffect ( ( ) => {
162- if ( ! provider || ! templateId ) return
163- if ( ! canChangeVisibility ) return
164- if ( isSaving || isDeleting || templateStorageWriteBlocked ) return
165- const prev = prevVisibilityRef . current
166- if ( prev === localVisibility ) return
167- prevVisibilityRef . current = localVisibility
168- void saveTemplate ( { closeAfter : false } )
169- // eslint-disable-next-line react-hooks/exhaustive-deps
170- } , [ localVisibility ] )
171-
172155 const handleDelete = async ( ) => {
173156 if ( ! provider || ! templateId ) return
157+ if ( ! provider . deleteTemplate ) return
174158
175159 setIsDeleting ( true )
176160
@@ -207,6 +191,30 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
207191 }
208192 } , [ onClose ] )
209193
194+ const selectStyles = useMemo <
195+ StylesConfig < { value : string ; label : string } , false >
196+ > (
197+ ( ) => ( {
198+ control : ( base ) => ( {
199+ ...base ,
200+ fontSize : "12px" ,
201+ minHeight : "32px" ,
202+ borderColor : "#E2E8F0" ,
203+ backgroundColor : canChangeVisibility ? base . backgroundColor : "#F7FAFC" ,
204+ opacity : canChangeVisibility ? 1 : 0.6 ,
205+ } ) ,
206+ menu : ( base ) => ( {
207+ ...base ,
208+ zIndex : 10 ,
209+ } ) ,
210+ option : ( base ) => ( {
211+ ...base ,
212+ fontSize : "12px" ,
213+ } ) ,
214+ } ) ,
215+ [ canChangeVisibility ] ,
216+ )
217+
210218 return (
211219 < Box
212220 position = "fixed"
@@ -232,7 +240,7 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
232240 onClick = { ( e ) => e . stopPropagation ( ) }
233241 >
234242 { /* Header */ }
235- < Flex
243+ < FlexAny
236244 px = "6"
237245 py = "4"
238246 alignItems = "center"
@@ -250,7 +258,7 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
250258 icon = { < Icon as = { PiX } boxSize = { 5 } /> }
251259 aria-label = "Close settings"
252260 />
253- </ Flex >
261+ </ FlexAny >
254262
255263 { /* Content */ }
256264 < Box px = "6" py = "6" flex = "1" overflowY = "auto" >
@@ -296,6 +304,7 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
296304 if ( ! canChangeVisibility ) return
297305 if ( option ) {
298306 setLocalVisibility ( option . value as "everyone" | "onlyMe" )
307+ setTemplateIsPrivate ( option . value === "onlyMe" )
299308 }
300309 } }
301310 options = { [
@@ -312,26 +321,7 @@ export function SettingsModal({ onClose, knownIsPrivate }: SettingsModalProps) {
312321 < span > { data . label } </ span >
313322 </ Box >
314323 ) }
315- styles = { {
316- control : ( base ) => ( {
317- ...base ,
318- fontSize : "12px" ,
319- minHeight : "32px" ,
320- borderColor : "#E2E8F0" ,
321- backgroundColor : canChangeVisibility
322- ? base . backgroundColor
323- : "#F7FAFC" ,
324- opacity : canChangeVisibility ? 1 : 0.6 ,
325- } ) ,
326- menu : ( base ) => ( {
327- ...base ,
328- zIndex : 10 ,
329- } ) ,
330- option : ( base ) => ( {
331- ...base ,
332- fontSize : "12px" ,
333- } ) ,
334- } }
324+ styles = { selectStyles }
335325 isSearchable = { false }
336326 isDisabled = { ! canChangeVisibility }
337327 />
0 commit comments