From 50e8016b147106f2549a00d4a097d1190465049e Mon Sep 17 00:00:00 2001 From: Vomitblood Date: Fri, 2 Aug 2024 00:03:57 +0800 Subject: [PATCH] settings json functions --- bun.lockb | Bin 168530 -> 169198 bytes package.json | 1 + .../HeaderBar/{ => Settings}/Settings.tsx | 0 src/contexts/SettingsContext.tsx | 186 +++--------------- src/lib/defaultSettings.ts | 10 - src/lib/lowDB.ts | 53 +++++ src/pages/_app.tsx | 4 +- 7 files changed, 88 insertions(+), 166 deletions(-) rename src/components/HeaderBar/{ => Settings}/Settings.tsx (100%) create mode 100644 src/lib/lowDB.ts diff --git a/bun.lockb b/bun.lockb index a89fb6bd356ffe4acccaa4f04544ee028ca0b0b5..d4f8a20f90e9bf6c33bf4be97d3bbfd87cf3c313 100755 GIT binary patch delta 30733 zcmeIbdz{VH7yp0u;V=i|J~0=@(70w8j57=~Mn*WqBq1e(!MHPSBg{;tyP}`=|5pnzh&3x3$(@d%fT9^L~4u z&t5Hc>h)4H(P$)EqUXI!(Ttu^2=|wlq&a6on%T65FA3^rG%#ov@dbFyMd$P30Q_bVai$G8Yx|BZ^paZhA$K{O9 z^z5WQ5`Go*2IwQuQqZxP83V>O%*t+3*5e6<|A9YL<;~>}qP##kkEarJiNkBC)YmBQ z@l=N9!Z40EZ@jMHtrA}raYe+pp@wv4^5I$8G(i{!{|7pW{FFn#(!IUWxsNka6{J<5 zH$i<+8D%|P#E9ikQSJbm$S7y9K$JfT^+WeUMVZY|>Guv)lpdRzJ!<%bOwY!u9#1rU z0aW@ehKd$LGO|Za9O=pRTn@J<=u3rImy%cu~6}L=Gd{L z#x~5H^gxu|FT<&?Kqa2Y8vv~-bV$bVVT|W0TDSUD1sV<-9?TeA4S`JD1~tXfC!sRo z3rsA_S+j;s!=O^X6e@~6O1&ub3>Kq9-W*+6EIMOmjNRH>%l5}{bdbdlfwG=?m#CLf zPC`Y^mv8iV@IYQntlh2@RMz7$IW}ut_Hg84V_U`%t;P)*J8JykAx$O`-vgN~ zwSnE=9p4#O?=4$m-fXuaRV>@ zNNQ?(yCGCG-$Hxw$1|*lt2bzP#<+2r13lB6 z#)C4l$BjY#v6y2GkW~UoB^W-X5Qn- z+d{>}=1_5ZeTV8~+Xat839Q__XewmYDmyeQyW!yBqxyR`r`Qesj(p;ftN}w%Br7{} z5_)<%wy_n6O0`#WbjG+L4Tojs{E5#cqR%=svaQXZbof>9;)daw6GnPGEcsHW!HW)E z2$hf@n3*}6gylKvbljnPmyFKskY=mf+M%Nx4j(ljV;oI~XN|}j=V{&EuFuRKGd^={ zj>p}Rf8B}yyb=2_8@nZMSVP;-b2{37tl7!(V{TqJ1yOHk_Nb8rh$lO9#DvVT9?z3B zl&HQpX!~U+)M^Kno$E&Tn9d##*(Wc$i@j&3LZ#mxXldwnsPwy=cCyfeM~#-v;mNSZ z%k|_{>}tnXJ`MQAb|M3E}+vWq+pJ%wEAcS9v~*FeSc zh0rkQvrti#h#Q+ZICIiN@D(iH&KqOkMd3&9wB^U3x#E(6REW#&fXc+3p%QY@PKR<( z(YzQ`V*h(Amx&HRrQhhgZTV;L(r*n^CRzX$1D=6O`xAZb@gIN}Lq>Ju5F(o2i9l#4 zXnANjRPv63iiM+C8tE{vgS}J(Gy9Jp%o?^KxTM{{Q3J+}%^I26i*_>Mk^XkOcJN|g z9jF|3!l7c|8paVrCJw~^GU0LreyE?FC>C^u3g3Th#(+$+!H15#%piM!zEG(jkvVp7 zW)mi9WG~@N#*y*HWsJ_~KRnY@2L&Y|9&2dRO+!2$jtzO!p&Y65M&wcu3)8Y}jsBit z-Q6H5q<;=x7-mm2Fl(%pRC{XT=2SX&D( z2IP*&8ksdZBRkuj_+uJK#j25Z$2Xy&@N-7l^7c@1U2wGR>NZdr(5?S_nsVEp7-M_R zZSNeobMt1Sv8-7xRDxy=?iQE54V8&!jk7I!+2NmrRzj}F+c9w(D*gIQu=#UPS%O{@ z?WMfcq3nw6OvZwa$Uy0%vT{hWSxK&4+V466h^FvV_H2o(dZldQ^|Wxa+#CYlD7wHu^WXqDa* z2~C-XMYN_Vo$v8*T9S7XNe$>Whc1CiD9v!_D5xCE(;eCjDzRS`8U?+g$Atb~zRr?S z-#__m=u_tS_rEW$ef-q&A!VPg9z##dn#S zmU?PTk~dIVud12sjVPlp)=X9tbciVpZ%!QH9X)=W~*=oUAo zsIxlf#uRT#S-tAUWPc8pbAV!^!svinty{#Vs0%tLHpSbioL&{1te(>sVpDv(@pMZW z!`F<(?QE6uC?#6suGAN5r}%z>Z=u&lB>C&Hj*S`AD}#D_SJ12KB>U$hY>lwAF1#_| zk0zfqv**R!?r>RZQS})))Pf*+nS4;)qV?n^$^PdMiV~O;9q{jg zYhyJjtRL{l)v!5R-H~t-FSKOc3gE;P%QgPr;BJPqg4WlZ(~dhWeHQDSW+}cZH7zq5 zBze2m)EAp2`|}apM02lBjSKkRgd?t-CHX6}&BXg&%eVb%>5B=;zLf~u=&7+uzEBc) zE34Fz61K-C5w{nbr}$gNdgNG*ZiVpy{}XVX;4qXev`-fVQv8A19#4C#1;*o=7Ktgo zmB@m6ZT%!~u{wHHVzL^jFCc!NvwC7>L{G^wzQ zo>~o?l2iP5vqi_+8lm3~xE@!z8{=(P7MBgFX6c-i6m?h^q@?&eH}QDVY3J4XNdfl#h*{?IozpSJ|0yz2 z&^jXcE0c$1?BY7VW5C}5&Yo8c&x31kIqO48vU!~f0{OM9c&cd=Co`kb)mB85nCj(fA2!H7|cFU47m2WIu z6Mea1lK)jHS*;{hegP*&*omQDs=nAI**`4R)}DC5p|8NnupTRV4m+G3S+V3TTW6Nr zo6%Oc?3(P~ico@&{dIG|SCRD9SWoSkhiNY6 zdh55-larHu_u#XZ*3s)ZN_NuSMM-S8H%-YjoAX%*fbMYOB|E>)ayV4R;vI0ZG}1CL z1lwg0JK1)ClWtfV6YxDKb#hkVUqeY;#?Hm*r{I`djz5HWlUwZJ?5T#r*%M2eUj&CQ z#JRNm0Zz>EPDZD}25mQWHk ztR3h-2`2$pLW0>F9n`C8B>Nu?x|;@5ef#0axAl|!l}P@yr>i*o5jfG3q|W+&1lQ~8 zu<>2o)1_MoGRSlnBA3Bwuy*8b_6+B!4eT-K?In4Hv;lB0#+w0pC8m6#*C9 z)!v@W9Sr!g;o3=WwLur$nd0qto4$BwvVYZWR~5&_p6<3&$=9_4-o);DRi9+v1cbNi zsf_q7rMv8zSkN}NU+bC}0>e;ZXy?R`9%yPaY=KPSq`&VaN*%1yaY~(Z>$oI;{2fKw z`iH^U9+G7DI$USn`sO6g^9uT=_*?e$co2!DC_D2S$0~mZhePc;Zkp^_`TQc%@a{40pW3djkG(a0H$t1x|=Jz{!STKlKTy zGy1~d6#s4gZHJR2V*>t1;p~+pu{o7Nu7!vU&1B8 zaczR5s|~YtwcXwaPNJSeC#gciacts5dNU<C>9Go0l>_ctKvG$mLNu2&k4p%}_xO!h-7@y+zX4^Xt85dP&;qIij zeJt-fPOqAftX|g_CeU!aZ6G@#5b(AeuP0AT_ANk2T8m5am7E}XL3Z{CN_P5^lwM-u z)!5EX41tkNZyj)bbA_{#&;d%a>`dDwF=Ud<5ZT_glk}>bWdCf0_AZlrc@nOZtEMj@ zhdn2$(SJWBF&-TWpIvayY&bf8G7i<32PXN4Q4)vQ$F9Y25`gSj`h?tjbsnxD``)Wt zPD%C~gpN<|3HT4f$*}AaE~BeV(HChFKD9_cHA?49O;KBQ!PFFg$!T_HHe6P~AB5{= z%_h!ya#MVpaz%MgBz$rCBoV7LlhR#Q=|`*7 zI=@x%`>cyQn!S9r^arJ0RSpbAi+ zszbw}@)0WX2oMGu0r~vDK(XH$$P10Pxa(7?Z|c++rNwl$`=fJ3y?|3IRQN=P|8KMc zZ8|#bi7=1MLNHsO>(LU_aP+aC%7BAH(bi=7TdsZ6*82z}G37b+9KbnP z6rs6R#TT~RBUJP~0A%1pKt4sO40PC%{*}svM}YY47?6)p8Rs~V`V&AD{TayTXNgRy z_{C!XH!2gH1v2pOK>D8t@)0Tq{2>gV|09*ikWebgD7B#tWwgIhnW&K?7b+fT;_yPH z*pxrgF2RxiH!4k<^GC+XZ9zcXk?BFx{YbbK4RR4sMdy$2P4 zu7*ne2hcd^k52t*DE~aaI`lVT_z0Ea8HYck*qCD3c?6=sWv9UvhyLkMak4C!mp_O+ z#e{LFDB^eIrJ%x>hst>}bf^A7sCRNGpdNmo>lVaZ&Z4(MlMxroqC}%%SNd9^+Sh# z?9>;fQokL!jPr?8FI40^9Udx&roRZT@^V&ql!nsw8~(^Z-#Q(I%0iximW7^!$^u=2 z%AwcGEOL@p7Aoy3LaFjpmf8P8?fI<=nIMb?LT_+r6{mwxkynLEPO1-;0dI0>98_{v z04nVgq4Ft8Mcx8=C1_`;^uINif@pdtlz*PP9oi2n7G*-k<-?#dz;K6-fXYW`Ddl4`;|6Z5aDdQNvkdIK= zIR9RkBuIGh5h}%huS@Q>Jn`>!i4_+AUYFS0;@|5M`yA}w>ym%3Oa8qs`TzI2q{4q( zmn;vTGrUypi(j{SvT$jYTh>&ZGpAqP=0!Ij`e=94BbhO;jHuP?_Ivxs^t~s3e7_A} zZmM=MrT$%AYHcreDmm_allX;IAH4JI>F372U>*0al#_2*qV42w{Vl$=(w5XoC!hIp z%(@k0TZc4H>ASmq=E0SpW{u0L_wf3OY2SbU=K1_*eq9;9_o+kg{r>LcEdzh=pk_eZ-nZza1C_o8$ET0#i4q^8$s1jpN2aR*JE)| zHPZ7J_tcA)glgZCpo-V&OM2>FZ-(mk;F@anW=|dRR;bQ=GpG{uGPsp+VQ&RhKxe$w zQxADNRBwiBp)0)IQ&(LYs>i<_R7tuJZVO!O(x7Us$1LrsCoK!rd*D)Z%(9-k?mMA+ z`m&%()w|&K!6m#CR5$DVcd&0c_Q9p;_~qF5F7_=Cs$29?xD#+`?*>&zJ?mZUdk_2I zf;#m*?0X;k-V3TO`ZU~mxE}8ZRaZU#ee7F-eJg@0U8k?WzLnSq*IlcX*jIpkD}$|2F>aJ_YfRoJ&0`&I>2A6*Ey1uk}VP~EM^tj4}I*avryj#-0! zYq4)lP-W;{aQomA)&|u8oxc|QKEOV>OdbCL_N~Ld4}xm2J_>gNE^S>#?s8`_>252z?svJY0{$Aa^R~7h>NA?As7jV|4lk?AwTaaM@aI#J)|~w=t;3 z>t%2&;lefr)kK}K3Hvr_8tyz?kL^MAq@KSW`#!TB}d7ZwK~$ z8dS6OGPsp+VLO7#=!_lMw-fu|p3@a}V&5+8+Zj|Z=t8(HaIw3B>P0cid;~ z!M-oB4{ote{Q~>;V&4}*^`<@zcOI_C-k^G0&)=0cP%YEx`>=06_Q5UJYCrZJ zz`p%K^`2e^w-PSwKv1pF83(ZMAojr(=n4n1?-2GK4D#4+A>0CD#eQ*h11^M4|`Cnn*VeEt3tm6-3-x2IP98@3aqi`qS(vAdq z7<<+c>^q8maNBh1QSAE~`;G?HC;BwpdAJ^52YEtv{@2*|4fcH#^qKq-v!k{T?n@YF7|j(eW}MB z$G#KT2X|PCvF}7s9o4(w_Q555A5`Dy{O_^v2ke78rsIFWzLVJZLr@*pN8wJu zrJW3_@Aa&c*!Ls$!JX8pKVsic*!N>l{iILBorml3Q&9b^=l_I#r?Br-P@UH4r?Bs5 z?1TGFtDmv&7wr2v$a7=M;8wzg{Ss8a>x^Hp?=<$oUClj=p#p%{V%UIY!zX%Ui6G6ymqw6S2Epvo2yQV)MQ}m{X=M;}G_%Secs3NlSrG(HYAAvZWf3e0MbO2Z z7QuND^eBsVHFVc zG#M56Go&Jd%_8V+DpW*JwGx8y6%q6?g(BD@g4jw3?lxm8A(&Jd!5$IZV`3^Js2hf0 zdSwI|W|s)|i69{i!2pvVhT!2F5F8Uhris4+L1GmIvu{8!*c=tX2@#}KL6Bu;RYCA< zRRm{6FwCS@MbIG}!Gfv?MwrtgI4^=8;Rr^V`QZo_MIi7+AQ)rPBM|h8MDU&nvW<#F z5E6wTGZMjgvrGglMGzK+V4}&0LNKHng3ThxF%_yIs9If>H500-PU>D$D1t53X%SnU z7E{fb>If!9BiJK?ToV(Gpl%HW)1wjOn_VKVz>36xdO)e|W^JN+ z%=ZY75&BoqvZqxU+N+KIKi~R8%deQ#VF*CY>S}J|~SDTpDX-p-r7qEt;C2gu&6I$l@va0gDg(*x_xhwAKq;BaZ zWAra|WR86I$UV51%$v`v*11>yv_B$y(7Gyzxq$ljYxt4OOC7xGWRCwJV!R=l(8F!H*lWW~g$Yk8t zZCM{r4H$WNO9p-ep%@hdRD|t-IxvFNx@r*tJ$PG;Pm?!8lBj>88N;II)P0EybNmb94s4foVp4Mc* zL!X{0U@CYNJO*ZfCxArzQSdeR20Q=~K}$dqkf%kh=ud{W0jZ!ZxETb1ct^Y;#)}v6 znt9vx|fgls~1zo{yK%Q+%lSfkIE=4?$d&Mn55@-dIK^u??ZU*f@8n^{?03AUm z@GyNPFUVG&31qv;u93U$pM%|C5BLJ?1qZ=b;4oM!Pc+P?@C-0Oa)3O(C;R?B&=ael z2XnwnU>jdj0ayptgAHIK*aSWXTfug) z8|(pJfW2TpI0z1bFU^}TtLWSxD4qoJ1=t1jiU*SQqCj;}6Vw59fn>flpa84@@{q|J zUv;8q5$ z0M1gDG}{C;1aV?LZznl7rJ6AP1y}!9wK6 zK?~Z~hmWDZJY=&#qViKJeg{8*pFjeY>yVd%N-qBhYzOi*jpT93+mfb5CXjkb(I0?y zpb)GF8^F%jn32yC2^~JAT8(eoB)LYj8rfNsGe-@&$*g}>m2dqFl~03MT4?A~&{<$6 zkQ~|uNbXGm$)Faf31UDE<6od&%8jNdd$XaS0jFYXe)y~O)u3O9qcKsvMmGKkZ_ z@^{gC88FppBL+*`G$50<12Tpvmjz@Dw@$_gf=-|#=m2h!cz3nC4MAtn72FCW3wHzQ zUfLCo*T1cSjyFciqZ z*PT`NN+cc=e zkSO#Zm<}EQ_k;UDKFD**PeP^s3Gf(r*rAU=9|h8X26!AuJIM;Cpz=JeoFUu^x`57d zG?!!1PgIH$vd`9lz2G_904o9R`4$P3VaEk1BbvK@HzMlYzH5MEnqX) z1U7wks91#li*1ed@$kPa?`KY&aq zbt3;0NE?@zcET$Lma*KvB9pcwuD=Hi!k0;6@M%DuG&{CJ;kopw)ptia!w`+;myOsa{2jr9g3D zDF`hAWKJJcoFQ}gff!Z6k(Gv)2jxInAaj-h(q8%rq`gq-BV~77u|xD@%-riv>^8Vw zCy|NAWdJF=OMAVtcwZdr_7Mk3*;Ve6go9{Y&8d*VWbkNscWG--F1n=BUOXxT30y^F z#iXxEp_+aF7bov<47Y#{qGrXephA%AjsXS?+%-ORPzqJEl94 zgo=cQ)JfTGV_jj2i)6=*aH#CG&fqX`8;XK*X6h>9P9U4iOL{I+0Dk?D9cr1Z*Xf7c@N6B1L-4Ikm2*fDAei3;@Hy zATSiju_+6h2_!*^Tx5bFxiEvlC?K7r!!U=AgpL4@0ZEXO7#{_XfcwFHK-x?IvMl4F z*}zSZvdhN;@tV6_BIlqLGz+#`8Mu2dwA$e^;OvLhzJ z%dYwRSW+)zYJpkOO zf5@qS5GqkFlgQXN0vUTcD4GoG$@&+a(AC}@+?`M?ltEpEB#LFghkq(x7V-^s z-)3>~fw+LV`&|_oF?^K$`d;_dWnQn%P;>EBzbCHQ}u5*0rjN z8MaadBFC4uzkZ%DWoqHPNlCkD6o_jQ7oXtCHEUO@aK7CyEa?sB`}ots%{6MR3XgQ( z;u}7sc=I)rF4d(Med6O1J*!Mg0S(;O1kWod(PGle3s2G@AuhgoT!QDM$u3Y0ydh=G zyaK+NgPtl-RfUGEQY|Ch_YJRo{7n1GKj(btRrMXStf?!PNMH=l4`$*j)zsE4%&cFf zZu2%NYbvi+O>7M-n?9>mv^P-BOj)fu|IbF+)0HUvF%6 z2n*xm2o3_=^jWXMy`?IeIOpGc9B|dF5xL<`@_U-P!W;t z%aQx;|0V9$c@dAGZa{)M;3=xtUxtaiuJQPW>msv_=^{7ZU`Nl%6VE^2z0tYNoP#cjqCz@@{)Kw!PB>@xo@Lt66wB~`jw#*#^+q= zQ;kk+P;q*J*|8D3Hk!+RZ@9U!q&GrcHq|yUk^2tl1Md%evr5ji*XSep0!>~p=`>Ia z%`hR$%=AsFsW&6itlC6~j5R+=Z}*kflMhCh-#(yWj-zIC619ohO#2Ge%pIH6An$-` z=Ez5?YJ&T!>RTIacsYFQMv0rIl6gq5)$JERrfnQB;FCJPjin|b&I*{lru2sl)h61s z`A}7D?!Mi6SFbmAc0BXaF|Q)s0FCh; zS@UJ{Hkvkf-^$&u2Q;%S=b-G^u(K|~lX zFuQ0#b~q_yqw#H1T`IXR58w8auUBgAb)tW$R8H?d!!~-=6>92H(`ed0{nPgFZKWkvT-RBxqrm^VK~i+$$sZpcw{RJfDol902e@FL_- z(`W}#xz!wjV>{u9ip~d+wLvM?urdeEbscGBDY^QIO`(pC&EwlVzPG7p1781Q| z`gO3_CC6h283N)GtML#ygcq$chd_8l6q9_M2&=GBBz0-i=^7Q zG^j*_<8S56>iPAU->tEm$=G%-CiT4_?cDdeAAR`XxMjC)4WXTANz5EIJ800{eQ|vD znjVYa@|NRiAsSd&-+i@w%lP2!5&4J3ZG^3KO>Atc?;>#Kn!qmAAY=j}*^6fMepMwJ z#(g7kwD;@2>8DP~SqV8&LZWdKGmoCl_u{5AtXjM7`?$-*~z|TTAqzt7o#A@ww{aO-eL}J}2niH`PB>vfkV-ue>8GB6_vpMAVeq ztzO`X4LQD;O}kZu8+yach23nQcTK}Rs%fPA*7|3kZnJ6k%3f6&L5>L6JJ;O1M>XI9 zn}vH+O!I#T4s>$gYrp^D+ExDFsL(KZJ$6vo? z`9O&Ri8ma()VxRowaV<;%am60@JRPX^@-0kd^%=h#gEBEq9Tj*qKV$8nntN!+*a4{QDO!(%=_BrQ4pmX_73n_p<*ty|rc z+E2RTP@24-^?%az+>b^x&13t~Xr8HXP*pAwkX^IftV0y(z9T%WY{xqX4B6X~A)Ccn z=P8TLZ~Ijjc?#+PXO7_=&5=tgESjiuUo&2+RqOn>)y&r&`-#PN9nH)GXwj>qdE)>l z=>L$Qh!9JYrfRWCJBWIAUW#Y2Px)HXIz}>OzIpQ?`afwtlIC;FrGo^= z8>Y@7l@#T^S%2lMDTmw6n>dqR60kU>W+yZ45YEU5nl?vp#!wUg3!~g?{`?-Y)5IKR zPrEP7-xIa|Q1^E?y=qxwEw}s1{g=x=TQGjc>NZXd19LFA^-JuG?qaHc#lTZd{+Cxr zbk4hYYAE%%x}i9sxW~*t#y&WIR8{fO+wEm5V7h%pELtN}Ho0FZ-_=p=dUrT`4sXS- z<`h$U@9u1dpJeLmH8LX)GvpYv_$UkaH{(~Bb-y81-OQDr8791&DT8ag&AOSn-$I?% z3GOTEw=R09+&v##X2ubQ{;PSew;wPsUcp7rm~to; zxuBc%97JB|XDe9%HHAMABukbo(e&zVr4wT8*tMC;Cp3E|+emd`+Z8r<IXZGM zJ7v_jxYu)=iDIRq+@DRze|2NU1I1^wavG95<8CwEq(z(C?Bm8SWvUDv^4Y~M*IIPF z&CHb8Oz`||-ObK#RWo+wnD3}%DBH`|cd2!7c7GLO^`sxt_qD&A;>Bc}#FM8zjp5sR*3&L&jUa*5h8=b!ca=I$QCC)7TooNwrRPdeo$!By|6lvyPdUkQT{Pjf;7& zCTV+6IqkMGtrJZ?7RgzIznO38XDMW*Tqn1F@gs?2v^o7FvpfCVjA<($`OiDrHo-jp zlkEdbImaHmZ&A+-HS>SP7R#SuX6LV5%Q@zqH+P?+=7d>R%zHz1VyI>h`}ASnbETR` z7d+0BsqSuY;?i-ey$j9RQ>vQGRAgPE{yCBT)yV(e@P8Z8JyI}o(e-gG?e`0oT3)GK z!nz7{mVlE1I~_YIHOyH#YY3;t)zyg=`^d!cqU&kCwOMRdoaS89ZeuO(bzFSJjQLI7 zV6uL-UBp5Dzqgd5@AYSO7S=g6u*SU>F@IOkI(=|rz&TPROytgTd2(0vrtLFMOrBlS zSyd)34)qP3$i~u5$_1!dB@s^Amiga4*P2lm_}25E&3Au7X6_qLoxCCJv*}EdXs`1svsSvg zzY%kJxLP{8W;^+UM$X;|gsf3^@SMMR?~o0>o?hTpw;`!QIGr`6xj2t}0#+{c2KL^# zVQ#s3KCj9_A}11aObQK}7r=(X#+N#MvVQx@d;zrpNo6G0`999^b^B4~iHj_`^##BU z#j)A`D!|)jv^hjYl>6g1OGd@NJN(puxt80kGixq4%3oq3uWOPO47TFl$hM|04Vgpi zkddeS0eiwMzQh6F{q2|z3r4=RFStopk92| zFWXf9gRey0->-=}pI7?bbxZOYL)MmfoMJNmC^F}h=5z_Z8hz^z6_N0Aw(YLl4^FPz zAS!W?WtsJ5n)`b^k2Tm8@oGX>`TkJORR~_^%M;FW<;ID9KBv~0;APdozlZVo;xW(4 z6qS=K6WkxzdTh(I(tAJo>XJ1Q_jwXL?r;6X7kj7h#61tn$+diy*QBvyc9@S7Vf$Jt zOs*fSb4t1TiR5JX>T#@SR;p60smDoS&WX1D<*$2XVxLDlhuXiMLc3?r+Kl*F4!cz07glV;^{n9&DUL@ki#8*Bc(Cx>?T+=e67XM~jqgV;*&af*pegyHE~)QIicjw`!#OyJ91rUlTJctLBf#w(Z1!Un3yn<=Y15+{nJ<2#<7s_pDZK`+C3qSi`zu!<9U1XP*GMXW8yA zM}7X<{M)-VHGCgoML6wn;?CklykiH!f4Zfp#4^vmaCNT9ytbX4(|oy8x}>*h z!TkxU-xu}o{J{R`Jja(@T-m!i()~5Q231DuySnF`a#UfVt($KR z_~PoPlHM3qXv&2!?*UUU#M{7snpx^z-Tg8Dhuwd@P2+wW;a)A z<~;W}^Tv(;AaPUuX*U+}s^n4J+7`rNe^qfB%eZ+OBC8bs5e`-t9o*}u+w`dWh}PR-X3c}6VTb9>oqHQUSx zx+b5bK@GZowQhCP(uWE=Tx(D|-+DAT?@U3{VI2-#I&!V1O*yYeK6&NLrZV0(CM49` zxX#9#?5`FcNKDwSCY8Oi)M<2wynT@m&q^mI*N8Xk-%;T!CWU(2l?vrHf1`$ttlRw! d1ExYdZ>>=4);>aunl^FX*cGd)c%Pm8e*tfRl(7H+ delta 30618 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 Promise; - fetchUserSettingsCloud: () => Promise; - updateSettingsLocal: (settings: SettingsType) => void; - updateSettingsCloud: (settings: SettingsType) => Promise; - deleteSettingsLocal: () => void; - deleteSettingsCloud: () => Promise; settingsLoading: boolean; }; @@ -33,163 +28,46 @@ export const SettingsProvider: FC<{ children: ReactNode }> = ({ children }) => { const [settings, setSettings] = useState(defaultSettings); const [settingsLoading, setSettingsLoading] = useState(true); - // fetch user settings from localStorage - const fetchUserSettingsLocal = async () => { + // initialize a new lowdb instance for settings outside of state + const settingsDB = useMemo(() => { + return new LowDB("settings.json", defaultSettings); + }, []); // empty dependency array ensures this is only created once + + // function to update settings + const updateSettings = async (updates: Partial) => { try { - logcat.log("Fetching user settings from localStorage...", "INFO"); - const userSettings = localStorage.getItem("userSettings"); - - if (userSettings) { - // deep merge user settings with default settings - const mergedSettings = merge( - {}, - defaultSettings, - JSON.parse(userSettings), - ); - - setSettings(mergedSettings); - - logcat.log("User settings fetched from localStorage", "INFO"); - logcat.log("User settings: " + userSettings, "DEBUG"); - } else { - logcat.log( - "User settings not found in localStorage, using default settings", - "WARN", - ); - } - setSettingsLoading(false); + await settingsDB.updateData((data) => { + Object.assign(data, updates); + }); + const newSettings = await settingsDB.readData(); + setSettings(newSettings); + logcat.log("Settings updated successfully", "INFO"); } catch (error) { - logcat.log("Error fetching user settings from localStorage", "ERROR"); - logcat.log(String(error), "ERROR"); - setSettingsLoading(false); + logcat.log(`Failed to update settings: ${error}`, "ERROR"); } }; - // fetch user settings from Firestore - const fetchUserSettingsCloud = async () => { - try { - if (user) { - logcat.log("Fetching user settings from firestore...", "INFO"); - const userSettings = await firestoreRetrieveDocumentField( - `users/${user.uid}`, - "settings", - ); - - if (userSettings) { - // deep merge user settings with default settings - const mergedSettings = merge({}, defaultSettings, userSettings); - - setSettings(mergedSettings); - - logcat.log("User settings fetched from firestore", "INFO"); - logcat.log("User settings: " + JSON.stringify(userSettings), "DEBUG"); - - // and then save to localStorage - updateSettingsLocal(mergedSettings); - } else { - logcat.log( - "User settings not found in firestore, using default settings", - "WARN", - ); - } - setSettingsLoading(false); - } else { - logcat.log("User not logged in, using default settings", "WARN"); - setSettingsLoading(false); - } - } catch (error) { - logcat.log("No such field", "ERROR"); - logcat.log(String(error), "ERROR"); - setSettingsLoading(false); - } - }; - - // push new settings to localStorage - const updateSettingsLocal = (settings: SettingsType) => { - try { - // apply settings into state - logcat.log("Applying settings...", "INFO"); - setSettings(settings); - logcat.log("Settings applied", "INFO"); - - // save settings to localStorage - logcat.log("Saving user settings to localStorage...", "INFO"); - localStorage.setItem("userSettings", JSON.stringify(settings)); - logcat.log("User settings saved to localStorage", "INFO"); - } catch (error) { - logcat.log("Error saving user settings to localStorage", "ERROR"); - logcat.log(String(error), "ERROR"); - } - }; - - // push new settings to firestore - const updateSettingsCloud = async (settings: SettingsType) => { - try { - // apply settings into state - logcat.log("Applying settings...", "INFO"); - setSettings(settings); - logcat.log("Settings applied", "INFO"); - - // save settings to firestore - logcat.log("Saving user settings to Firestore...", "INFO"); - - // push entire settings object to firestore - if (user) { - firestoreCommitUpdate(`users/${user.uid}`, "settings", settings); - await firestorePushUpdates(); - - logcat.log("User settings saved to firestore", "INFO"); - } - } catch (error) { - logcat.log("Error saving user settings to firestore", "ERROR"); - logcat.log(String(error), "ERROR"); - } - }; - - // delete settings from localStorage - const deleteSettingsLocal = () => { - try { - logcat.log("Deleting user settings from localStorage...", "INFO"); - localStorage.removeItem("userSettings"); - logcat.log("User settings deleted from localStorage", "INFO"); - } catch (error) { - logcat.log("Error deleting user settings from localStorage", "ERROR"); - logcat.log(String(error), "ERROR"); - } - }; - - // delete settings from cloud - const deleteSettingsCloud = async () => { - try { - logcat.log("Deleting user settings from Firestore...", "INFO"); - - if (user) { - firestoreCommitDelete(`users/${user.uid}`, "settings"); - await firestorePushDeletes(); - - logcat.log("User settings deleted from Firestore", "INFO"); - } - } catch (error) { - logcat.log(String(error), "ERROR"); - throw new Error("Error deleting user settings from Firestore"); - } - }; - - // fetch user settings from local on first load everytime + // fetch user settings from local on first load every time useEffect(() => { - fetchUserSettingsLocal(); - }, []); + const fetchSettings = async () => { + try { + await settingsDB.init(); + const storedSettings = await settingsDB.readData(); + setSettings(storedSettings); + } catch (error) { + logcat.log(`Failed to load settings: ${error}`, "ERROR"); + } finally { + setSettingsLoading(false); + } + }; + + fetchSettings(); + }, [settingsDB]); return ( diff --git a/src/lib/defaultSettings.ts b/src/lib/defaultSettings.ts index a8094e6..0e6d4f5 100644 --- a/src/lib/defaultSettings.ts +++ b/src/lib/defaultSettings.ts @@ -9,16 +9,6 @@ export const defaultSettings = { font_family: "monospace" as string, font_scaling: 100, }, - timings: { - auto_refresh: false as boolean, - auto_refresh_interval: 30 as number, - eta_as_duration: true as boolean, - military_time_format: true as boolean, - timings_bus_expanded_items: 3 as number, - timings_train_expanded_items: 3 as number, - favourites_bus_expanded_items: 0 as number, - favourites_train_expanded_items: 0 as number, - }, } as const; export type SettingsType = typeof defaultSettings; diff --git a/src/lib/lowDB.ts b/src/lib/lowDB.ts new file mode 100644 index 0000000..ad2dcd6 --- /dev/null +++ b/src/lib/lowDB.ts @@ -0,0 +1,53 @@ +import { merge } from "lodash"; +import { Low } from "lowdb"; +import { JSONFile } from "lowdb/node"; + +// define a generic interface for json structure +interface Database { + data: T; +} + +type UpdateCallback = (data: T) => void; + +export class LowDB { + private db: Low>; + private defaultData: T; + + constructor(filePath: string, defaultData: T) { + const adapter = new JSONFile>(filePath); + this.db = new Low(adapter, { data: defaultData }); + this.defaultData = defaultData; + } + + // initialize the json file and merge with default data if needed + async init() { + await this.db.read(); + if (!this.db.data || !this.db.data.data) { + // initialize with default data + this.db.data = { data: { ...this.defaultData } }; + } else { + // merge existing data with default data to fill in missing properties + this.db.data.data = { ...this.defaultData, ...this.db.data.data }; + } + await this.db.write(); + } + + async readData(): Promise { + await this.db.read(); + // ensure that the data is merged with default values every time it is read + this.db.data!.data = merge({}, this.defaultData, this.db.data!.data); + return this.db.data!.data; + } + + async writeData(data: T): Promise { + this.db.data!.data = data; + await this.db.write(); + } + + // update a specific part of the json file + async updateData(callback: UpdateCallback): Promise { + await this.db.read(); + callback(this.db.data!.data); + await this.db.write(); + } +} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 6fb4c69..b91cc5f 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -18,8 +18,8 @@ export default function MyApp(props: MyAppProps) { return ( - WhensApp - + Stort + {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}