11import { NavigationPage } from '@/components/navigation/NavigationPage' ;
22import { getParams , setParams as setParamsGlobal } from '@/library/powersync/ConnectionManager' ;
3- import { Box , Button , Grid , IconButton , styled , TextField } from '@mui/material' ;
3+ import {
4+ Box ,
5+ Button ,
6+ Grid ,
7+ IconButton ,
8+ styled ,
9+ TextField ,
10+ Select ,
11+ MenuItem ,
12+ InputLabel ,
13+ FormControl
14+ } from '@mui/material' ;
415import { FormEvent , useState } from 'react' ;
516import DeleteIcon from '@mui/icons-material/Delete' ;
617import AddIcon from '@mui/icons-material/Add' ;
718
8- const jsonToObjectArray = ( json : Object ) => {
19+ const typeForValue = ( value : unknown ) => {
20+ //when using typeof arrays are "object" so we have to have this specific case
21+ if ( Array . isArray ( value ) ) return 'array' ;
22+ return typeof value ;
23+ } ;
24+
25+ const jsonToObjectArray = ( json : Object ) : ParameterEntry [ ] => {
926 const entrySet = Object . entries ( json ) ;
10- return entrySet . map ( ( [ key , value ] ) => ( {
11- key,
12- value
13- } ) ) ;
27+ return entrySet . map ( ( [ key , value ] ) => {
28+ const type = typeForValue ( value ) as ParameterType ;
29+ return {
30+ key,
31+ // Only arrays and objects need special cases here since JS will take care of the rest.
32+ value : type === 'array' || type === 'object' ? JSON . stringify ( value ) : String ( value ) ,
33+ type
34+ } ;
35+ } ) ;
36+ } ;
37+
38+ type ParameterType = 'string' | 'number' | 'boolean' | 'array' | 'object' ;
39+
40+ interface ParameterEntry {
41+ key : string ;
42+ type : ParameterType ;
43+ value : string ;
44+ error ?: string ;
45+ }
46+
47+ // A simple set of mappers for converting a string to the correct value for saving
48+ const CONVERTERS = {
49+ string : ( v : string ) => v ,
50+ number : ( v : string ) => Number ( v ) ,
51+ boolean : ( v : string ) => v === 'true' ,
52+ array : ( v : string ) => JSON . parse ( v ) ,
53+ object : ( v : string ) => JSON . parse ( v )
1454} ;
1555
1656function ClientParamsPage ( ) {
1757 const [ params , setParams ] = useState ( jsonToObjectArray ( getParams ( ) ) ) ;
1858
59+ const convertValueForSave = ( t : ParameterType , stringValue : string ) => CONVERTERS [ t ] ( stringValue ) ;
60+
1961 const onSubmit = ( e : FormEvent < HTMLFormElement > ) => {
2062 e . preventDefault ( ) ;
2163 e . stopPropagation ( ) ;
2264
23- const newParams = params . reduce ( ( curr , item ) => ( { ...curr , [ `${ item . key } ` ] : item . value } ) , { } ) ;
24- setParamsGlobal ( newParams ) ;
65+ try {
66+ const newParams = params . reduce < Record < string , any > > (
67+ ( curr : any , item : { key : string ; type : string ; value : string } ) => ( {
68+ ...curr ,
69+ [ `${ item . key } ` ] : convertValueForSave ( item . type as ParameterType , item . value )
70+ } ) ,
71+ { }
72+ ) ;
73+ setParamsGlobal ( newParams ) ;
74+ } catch ( e ) { }
75+ } ;
76+
77+ const validate = ( val : ParameterEntry ) => {
78+ if ( val . type == 'object' || val . type == 'array' ) {
79+ try {
80+ JSON . parse ( val . value ) ;
81+ return val ;
82+ } catch ( e : any ) {
83+ return {
84+ ...val ,
85+ error : e . message
86+ } ;
87+ }
88+ } else {
89+ return val ;
90+ }
2591 } ;
2692
27- const replace = ( idx : number , val : any ) => setParams ( ( a ) => a . map ( ( entity , i ) => ( i === idx ? val : entity ) ) ) ;
93+ const replace = ( idx : number , val : ParameterEntry ) => {
94+ setParams ( ( a : any [ ] ) => a . map ( ( entity , i ) => ( i === idx ? validate ( val ) : entity ) ) ) ;
95+ } ;
2896
2997 const removeIdx = ( idx : number ) =>
30- setParams ( ( a ) => a . map ( ( entity , i ) => i !== idx && entity ) . filter ( ( entity ) => entity !== false ) ) ;
98+ setParams ( ( a : any [ ] ) => a . map ( ( entity , i ) => i !== idx && entity ) . filter ( ( entity ) => entity !== false ) ) ;
3199
32100 const addRow = ( ) => {
33- setParams ( ( a ) => [ ...a , { key : '' , value : '' } ] ) ;
101+ setParams ( ( a : any [ ] ) => [ ...a , { key : '' , value : '' , type : 'string ' } ] ) ;
34102 } ;
35103
36- const changeValue = ( idx : number , value : string , currKey : string ) => {
37- replace ( idx , { key : currKey , value } ) ;
104+ const changeValue = ( idx : number , value : string , currKey : string , type : ParameterType ) => {
105+ replace ( idx , { key : currKey , value, type } ) ;
38106 } ;
39107
40- const changeKey = ( idx : number , key : string , currValue : unknown ) => {
41- replace ( idx , { key, value : currValue } ) ;
108+ const changeKey = ( idx : number , key : string , currValue : string , type : ParameterType ) => {
109+ replace ( idx , { key, value : currValue , type } ) ;
110+ } ;
111+
112+ const changeType = ( idx : number , key : string , value : string , newType : ParameterType ) => {
113+ replace ( idx , { key, value, type : newType } ) ;
42114 } ;
43115
44116 return (
45117 < NavigationPage title = "Client Parameters" >
46118 < S . MainContainer >
47119 < form onSubmit = { onSubmit } >
48- { params . map ( ( { key, value } , idx ) => (
49- < S . CenteredGrid container >
120+ { params . map ( ( { key, value, type , error } , idx : number ) => (
121+ < S . CenteredGrid container key = { idx } >
50122 < S . CenteredGrid item xs = { 12 } md = { 10 } >
51123 < TextField
52124 label = "Key"
53125 value = { key }
54126 sx = { { margin : '10px' } }
55- onChange = { ( v ) => changeKey ( idx , v . target . value , value ) }
127+ onChange = { ( v : { target : { value : string } } ) => changeKey ( idx , v . target . value , value , type ) }
56128 />
129+ { /* TODO: Potentially add an explanation here about how users should write values for a given piece of text? */ }
57130 < TextField
58131 label = "Value"
59132 value = { value }
60133 sx = { { margin : '10px' } }
61- onChange = { ( v ) => changeValue ( idx , v . target . value , key ) }
134+ error = { ! ! error }
135+ title = { error }
136+ onChange = { ( v : { target : { value : string } } ) => changeValue ( idx , v . target . value , key , type ) }
62137 />
63-
138+ < FormControl sx = { { margin : '10px' , width : '125px' , minWidth : '95px' } } >
139+ < InputLabel id = "demo-simple-select-label" > Type</ InputLabel >
140+ < Select
141+ labelId = "demo-simple-select-label"
142+ value = { type }
143+ label = "Type"
144+ onChange = { ( v : { target : { value : string } } ) =>
145+ changeType ( idx , key , value , v . target . value as ParameterType )
146+ } >
147+ < MenuItem value = { 'string' } > String</ MenuItem >
148+ < MenuItem value = { 'number' } > Number</ MenuItem >
149+ < MenuItem value = { 'array' } > Array</ MenuItem >
150+ < MenuItem value = { 'object' } > Object</ MenuItem >
151+ < MenuItem value = { 'boolean' } > Boolean</ MenuItem >
152+ </ Select >
153+ </ FormControl >
64154 < IconButton sx = { { margin : '10px' } } color = "error" onClick = { ( ) => removeIdx ( idx ) } >
65155 < DeleteIcon />
66156 </ IconButton >
@@ -73,7 +163,7 @@ function ClientParamsPage() {
73163 </ IconButton >
74164 </ S . CenteredGrid >
75165 < Button type = "submit" sx = { { margin : '10px' } } variant = "contained" >
76- Submit
166+ Save
77167 </ Button >
78168 </ form >
79169 </ S . MainContainer >
0 commit comments