1- import React , { ChangeEvent , useRef , useState } from "react" ;
1+ import { ChangeEvent , useEffect , useMemo , useRef , useState } from "react" ;
22import { Tooltip } from "react-tooltip" ;
33import { validateProjectName } from "../../features/Projects/application/validateProjectName" ;
44import { Project } from "../../features/Projects/models/Project" ;
@@ -20,21 +20,30 @@ const buildOptions = {
2020} ;
2121
2222const ProjectPreferences = ( ) => {
23- const project = useProject ( ( s ) => s . project ) ;
24- const projectDataKeys = Object . keys ( project ) ;
23+ const currentProject = useProject ( ( s ) => s . project ) ;
2524 const projectKey = useProject ( ( s ) => s . projectKey ) ;
2625 const setProject = useProject ( ( s ) => s . setProject ) ;
26+ const saveProject = useProject ( ( s ) => s . saveProject ) ;
2727
28+ const [ editedKey , setEditedKey ] = useState < string | null > ( null ) ;
29+
30+ const editedProject : Project = useMemo ( ( ) => {
31+ if ( ! editedKey ) return currentProject ;
32+ const p = useProject . getState ( ) . unserializeProject ( editedKey ) ;
33+ return ( p ?? currentProject ) as Project ;
34+ } , [ editedKey , currentProject ] ) ;
35+
36+ const projectDataKeys = Object . keys ( editedProject ) ;
2837 const formRef = useRef < HTMLFormElement > ( null ) ;
2938 const [ prevBuildMode , setPrevBuildMode ] = useState < ProjectBuildMode | null > (
3039 null ,
3140 ) ;
3241 const [ errors , setErrors ] = useState < Record < string , string > > ( { } ) ;
3342
34- const handleNameChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
43+ const handleNameChange = ( e : ChangeEvent < HTMLInputElement > ) => {
3544 const key = e . target . name ;
3645 const value = e . target . value ;
37- const [ _ , error ] = validateProjectName ( value ) ;
46+ const [ _ , error ] = validateProjectName ( value , editedKey ?? projectKey ) ;
3847
3948 if ( error ) {
4049 setErrors ( {
@@ -59,41 +68,61 @@ const ProjectPreferences = () => {
5968 confirmText : `Yes, change` ,
6069 dismissText : `No, keep the ${
6170 buildOptions [
62- project . buildMode as keyof typeof buildOptions
71+ editedProject . buildMode as keyof typeof buildOptions
6372 ]
6473 } `,
6574 cancelImmediate : true ,
6675 } ,
6776 )
68- ) e . target . value = prevBuildMode || project . buildMode ;
77+ ) e . target . value = prevBuildMode || editedProject . buildMode ;
6978
7079 setPrevBuildMode ( e . target . value as ProjectBuildMode ) ;
7180 } ;
7281
82+ useEffect ( ( ) => {
83+ const handler = ( e : Event ) => {
84+ const d = ( e as CustomEvent ) . detail ;
85+ if ( ! d || d . id !== "project-preferences" ) return ;
86+ setEditedKey ( d . params ?. projectKey ?? null ) ;
87+ } ;
88+ window . addEventListener ( "dialog-open" , handler ) ;
89+ return ( ) => window . removeEventListener ( "dialog-open" , handler ) ;
90+ } , [ ] ) ;
91+
7392 const handleSave = ( ) => {
7493 let projectData : Partial < Project > = { } ;
7594
7695 for ( const [ key , value ] of new FormData ( formRef . current ! ) . entries ( ) ) {
7796 if (
7897 ! projectDataKeys . includes ( key )
7998 || typeof value != "string"
80- || typeof project [ key as keyof Project ] != "string"
99+ || typeof editedProject [ key as keyof Project ] != "string"
81100 ) continue ;
82101
83102 projectData [ key as keyof Project ] = value as any ;
84103 }
85104
86- if (
87- Object . entries ( projectData ) . some (
88- ( [ key , value ] ) => project [ key as keyof Project ] != value ,
89- )
90- ) setProject ( projectData ) ;
105+ if ( ! Object . keys ( projectData ) . length ) return ;
106+
107+ if ( editedKey && editedKey !== projectKey ) {
108+ saveProject ( editedKey , { ...editedProject , ...projectData } ) ;
109+ } else {
110+ if (
111+ Object . entries ( projectData ) . some (
112+ ( [ key , value ] ) =>
113+ editedProject [ key as keyof Project ] != value ,
114+ )
115+ ) setProject ( projectData ) ;
116+ }
117+
118+ setEditedKey ( null ) ;
91119 } ;
92120
93121 const handleDismiss = ( ) => {
94122 formRef . current ?. reset ( ) ;
95123 setPrevBuildMode ( null ) ;
96124 setErrors ( { } ) ;
125+ setEditedKey ( null ) ;
97126 setTimeout ( ( ) => {
98127 ( formRef . current ! ) . querySelectorAll ( "[name]" ) . forEach ( el => {
99128 el . dispatchEvent ( new Event ( "reset" , { bubbles : false } ) ) ;
@@ -124,11 +153,13 @@ const ProjectPreferences = () => {
124153 < form
125154 ref = { formRef }
126155 className = "-mx-1 [&>*]:px-1"
127- key = { projectKey }
156+ key = { editedKey ?? projectKey }
128157 onKeyDown = { e => e . key == "Enter" && e . preventDefault ( ) }
129158 >
130159 < div className = "mt-2 !px-0 space-y-1" >
131- < ProjectFavicon defaultValue = { project . favicon } />
160+ < ProjectFavicon
161+ defaultValue = { editedProject . favicon }
162+ />
132163
133164 < label className = "label gap-2 pl-3 pr-2 bg-base-200 rounded-xl border border-base-content/10" >
134165 < span className = "flex flex-col gap-1" >
@@ -140,19 +171,20 @@ const ProjectPreferences = () => {
140171 < input
141172 name = "name"
142173 className = "input input-bordered input-sm w-full max-w-60 placeholder:text-base-content/45 data-[tooltip-content]:border-error data-[tooltip-content]:focus-visible:outline-error"
143- defaultValue = { project . name }
144- placeholder = { project . name }
174+ defaultValue = { editedProject . name }
175+ placeholder = { editedProject . name }
145176 onInput = { handleNameChange }
146177 onBlur = { e =>
147178 ( ! e . target . value )
148- && ( e . target . value = project . name ) }
179+ && ( e . target . value =
180+ editedProject . name ) }
149181 onKeyDownCapture = { e => {
150182 if ( e . key != "Escape" ) return ;
151183 e . preventDefault ( ) ;
152184 e . stopPropagation ( ) ;
153185 const target = e
154186 . target as HTMLInputElement ;
155- target . value = project . name ;
187+ target . value = editedProject . name ;
156188 resetError ( "name" ) ;
157189 target . blur ( ) ;
158190 } }
@@ -165,7 +197,7 @@ const ProjectPreferences = () => {
165197 </ label >
166198 </ div >
167199
168- { project . mode == "pj" && (
200+ { editedProject . mode == "pj" && (
169201 < >
170202 < div className = "divider mt-1.5 mb-0 first:hidden" >
171203 </ div >
@@ -196,7 +228,7 @@ const ProjectPreferences = () => {
196228 id = "build-mode"
197229 name = "buildMode"
198230 className = "select select-bordered select-sm"
199- defaultValue = { project . buildMode }
231+ defaultValue = { editedProject . buildMode }
200232 onChange = { handleBuildModeChange }
201233 >
202234 { Object . entries ( buildOptions ) . map ( (
0 commit comments