From beee8587d1c75b8ccee2e21eba00f4b0acb43b5b Mon Sep 17 00:00:00 2001 From: Polle Pas Date: Wed, 26 Oct 2022 13:41:55 +0200 Subject: [PATCH] #30 Make installable and better error handling --- .gitignore | 1 + CHANGELOG.md | 1 + data-browser/index.html | 27 +- data-browser/package.json | 4 +- .../{safari-pinned-tab.svg => mask-icon.svg} | 0 data-browser/public/maskable_icon.png | Bin 0 -> 48171 bytes data-browser/public/maskable_icon_x128.png | Bin 0 -> 2333 bytes data-browser/public/maskable_icon_x192.png | Bin 0 -> 3883 bytes data-browser/public/maskable_icon_x384.png | Bin 0 -> 9993 bytes data-browser/public/maskable_icon_x512.png | Bin 0 -> 16013 bytes data-browser/public/robots.txt | 2 + data-browser/public/site.webmanifest | 20 -- data-browser/public/sw.js | 3 - data-browser/src/App.tsx | 21 +- data-browser/src/components/ErrorLook.ts | 6 - data-browser/src/components/ErrorLook.tsx | 62 ++++ data-browser/src/components/Navigation.tsx | 29 +- .../src/components/NetworkIndicator.tsx | 63 ++++ data-browser/src/components/Searchbar.tsx | 7 +- .../src/components/SideBar/AppMenu.tsx | 85 +++++ data-browser/src/components/SideBar/index.tsx | 21 +- .../src/components/SideBar/menuItems.tsx | 8 +- .../forms/FileDropzone/useUpload.ts | 5 + data-browser/src/hooks/useMediaQuery.ts | 25 ++ data-browser/src/hooks/useOnline.ts | 21 ++ data-browser/src/styling.tsx | 3 +- data-browser/src/views/CrashPage.tsx | 39 +-- data-browser/src/views/ErrorPage.tsx | 83 ++--- data-browser/vite.config.js | 89 ++++++ data-browser/workbox-config.js | 8 - pnpm-lock.yaml | 294 +++++++++++++++++- react/src/useServerURL.ts | 11 +- 32 files changed, 761 insertions(+), 177 deletions(-) rename data-browser/public/{safari-pinned-tab.svg => mask-icon.svg} (100%) create mode 100644 data-browser/public/maskable_icon.png create mode 100644 data-browser/public/maskable_icon_x128.png create mode 100644 data-browser/public/maskable_icon_x192.png create mode 100644 data-browser/public/maskable_icon_x384.png create mode 100644 data-browser/public/maskable_icon_x512.png create mode 100644 data-browser/public/robots.txt delete mode 100644 data-browser/public/site.webmanifest delete mode 100644 data-browser/public/sw.js delete mode 100644 data-browser/src/components/ErrorLook.ts create mode 100644 data-browser/src/components/ErrorLook.tsx create mode 100644 data-browser/src/components/NetworkIndicator.tsx create mode 100644 data-browser/src/components/SideBar/AppMenu.tsx create mode 100644 data-browser/src/hooks/useMediaQuery.ts create mode 100644 data-browser/src/hooks/useOnline.ts delete mode 100644 data-browser/workbox-config.js diff --git a/.gitignore b/.gitignore index 86193736c..97d5d5323 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ publish .nohup */lib */dist +*/dev-dist */yarn-error.log test-results */nohup.out diff --git a/CHANGELOG.md b/CHANGELOG.md index 43027ef57..58339d431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This changelog covers all three packages, as they are (for now) updated as a who - Add folders with list & grid views, allow drag & drop uploads #228 - Show icons in sidebar - Add scoped search, funded by NGI NLnet Discovery #245 +- Make web app installable #30 - Add cookie based authentication #241 ## v0.32.1 diff --git a/data-browser/index.html b/data-browser/index.html index 0247496ce..bc9ef6893 100644 --- a/data-browser/index.html +++ b/data-browser/index.html @@ -9,13 +9,15 @@ - - + - - + + + + Atomic Data Browser @@ -89,23 +91,6 @@ .setProperty('--background-color', 'black'); } - - - diff --git a/data-browser/package.json b/data-browser/package.json index 2b726f1ae..5bcc853cf 100644 --- a/data-browser/package.json +++ b/data-browser/package.json @@ -25,8 +25,8 @@ "react-intersection-observer": "^8.31.1", "react-is": "^18", "react-markdown": "^8.0.3", - "react-router-dom": "^6.0.0", "react-router": "^6.0.0", + "react-router-dom": "^6.0.0", "remark-gfm": "^3.0.1", "styled-components": "^5.3.3", "yamde": "^1.7.1" @@ -42,6 +42,8 @@ "gh-pages": "^3.1.0", "lint-staged": "^10.5.4", "types-wm": "^1.1.0", + "vite": "^3.0.5", + "vite-plugin-pwa": "^0.13.1", "workbox-cli": "^6.4.1" }, "homepage": "https://atomicdata.dev/", diff --git a/data-browser/public/safari-pinned-tab.svg b/data-browser/public/mask-icon.svg similarity index 100% rename from data-browser/public/safari-pinned-tab.svg rename to data-browser/public/mask-icon.svg diff --git a/data-browser/public/maskable_icon.png b/data-browser/public/maskable_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b5f4c26429d7167785d13921692558b7488372fb GIT binary patch literal 48171 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 DM~{z1 literal 0 HcmV?d00001 diff --git a/data-browser/public/maskable_icon_x128.png b/data-browser/public/maskable_icon_x128.png new file mode 100644 index 0000000000000000000000000000000000000000..647954c2a55e5c75f9ec027daad630f3218dcf80 GIT binary patch literal 2333 zcmV+&3F7vNP)Px-*GWV{RCr$Pon43+=_g~x&}x8 zXu$E`7a_dXg!^x~9$@Jj6qc4?@0K-AA>oVy)c+2lb~b?e#YKQ-0MKl-zX!|Tn*kJ> z0fa#VD%);@>b>JmfZzlG)t^GBoo+y(*?=%;2YLm7$e&|CRPMV2Di3V18U*VBD4$${ z`lSXyAOhVT_|}eZ3NX5rAgT-kfXd`XYay^2fbz+(6?~OIi&W4)Z&@Kasi6V;%NwEc z@Pw5RSPMY)TnM!@iwQfwH|Mug!tz%n@NFe%|NY!^k3wnvTI(RN5`g|WKf4I*tq}q! zuDccHUVPF6V1fs#XPZzvb#z2wd6e@e#57 zxw}SaE6VJ_Jy4vuN6EP+oMasUBL7c*@nhFrzIgj5Cw%cfzf<_7D1kV?dF≫;L+; zbpVK!kG=PGO9Ars`Tc;HDo()6ftQt?Yr=_E0 zYH~Y#@Y*XT#8ufwqXDQ^t02CTjxhRZ0`IFM;$L9wJpM3&ecxozfBNqyV#J?9QLD*zMYA^zTtl-NC%+JrK zI(*l=pTbAq{jyvXt~?r+)aT2GM8{X(2r~!vKymx##2~BKWP%#720+EhGCE?n2?(-| zU57q|&ySygf391#GAD>AfdFFs<{MyUdN&leZA$%BdF|q?25bSy*Z_dx=wU#>w1*P}EC9fEc64{k-dyZLd5B4#JQfxf zT9ZABgF|7c9RT6%_%w~e`CP7{`|TOD0^oY^Iiv!u0LU9DTy~=_J9HL>+5n);`?>;~ z=eoM;0WAP{>F-5DmkMYBz)OF!Ht2wW8~`r6Kj~I2Rr{Bf8mPMhasYJge%be@Jwg@@ ziU2`8ZKMMTY84;_K*#PM)}cCzRZ0~g1%Pt*4;y%$iZDvs`*k9u0Hi%a)z8R0|E}_2 zNS@ypJ(o%itOS5U{wW+sRUc5L0Wt-^mVb)iN7VpZ&8XHx0s!{z$CUlFbb-Alaa{@( zAOQe7cj_9WVz0{@05IiWSMa4WU_wBV21pbDD_`o6{&-5jOa)c|faxIJyB}KxC;&%S z04x9~oTEA~z*-|kA&3qDR*vKZfCv;;n!{E(bO5jlKy1gT?tbhvfW1y*0ibA+@<4!< z2Cxf3BLUzh{}L38G+;ac7*6p3(Gz^wrQs}m3a=vD!& z-A_>fk|_Wt091}jst2&wMIg>JDFE|{85tM~XGI~v4nBqalPQ3>kSz`qYr)L>5;E-m zzw- zhA!1-j9oo@8oG0rKJ=g0?4y=~Pq|Cha`(E+B3Q=H0YmokQ_@ox#_d0;0~v zd|rSeh=X23SKjaPJ=MK{EGM~`3OEFxjRFWmT!TPI%a^nXwbuYH&Ih;zpOpf*(i!M3 z`r<0UN&#$!fZ|z&L5fk90EmT9JasdO!v=W`w_VS!3&?V+>2m_k!Dm?mB*9>6hrm7f z#sDBwg09c;$t(LN1YWL*Z4Hp-bgB<*$QyKVz6iW1_L4r4Y>?ka@fza*;MghT`9v~` zpys)Tu>eFtp!~@9sB^_mFBXffDW<81@c>B2%V+tpVK8~OvmXVIY8T08{6p2pNw{yZG6pVs21BS(5?=b%5)y^8h$bfCs<>5QdKX!0F|C1#mk2 zL~j!1xQ|&c-zxy?@ZcNg0l@b$>nnHwSceDSI1d26k6B;A1Hd{w_{Mnv@O{ks3LXH~ z;lVe~1Ay;i)>rTVunrHtaUKACAG5xK2Y_{W@QwQ)(R;-Ca3gS#00000NkvXXu0mjf DOx8Ii literal 0 HcmV?d00001 diff --git a/data-browser/public/maskable_icon_x192.png b/data-browser/public/maskable_icon_x192.png new file mode 100644 index 0000000000000000000000000000000000000000..0d4673142160ae72d8c9159a7a10f346771ae595 GIT binary patch literal 3883 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μcnLYue1hQ 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 JY8}Uz{{o?+POSg{ literal 0 HcmV?d00001 diff --git a/data-browser/public/maskable_icon_x512.png b/data-browser/public/maskable_icon_x512.png new file mode 100644 index 0000000000000000000000000000000000000000..3348755760a714d2ef934acc4b7cf3b154c6d72f GIT binary patch literal 16013 zcmeHui96Ka|LA)b3|Yn!lgPdm*=38dOUj;PCs`s&mTWVG$XXGF2-zbll4T|(LYA_W zC1g$ZeHk6|evG+*TAeNR%y>N4=r>PR%d!#>MZs5B50LoU|dX%$9uN?AO%oM1E1aziB{Q}1uu zW#?7r2wOyslrYgX4isSVcxRO4-tN4|LWApO$NZy)>B`0p5;yIU;4A$TTlT^IxX|Cy zDr-9-<=eL*BrgO*VR=u$RWSIn^@1ui{$MDa3Pukxz~F!WOXh>oW21A;Tu@l(V1yd4 zaIgk^ZcT=$d9`|qW&T2e5ZI%Cmcvk&^&v#^(^>N)e?j13IShXnBV`2fuvX)nCG>wm z5TS1BKPW)(BP>MdvZ%5}^k1YLAUDLnu%V}QfMnRRj%)qdA1MXB`nwp9jRyZ5xqCj9 z?=MCeXz;%%(t=~~^g4xYe?b6py#6lc{SP_+k1rlXZ!7f!AHL-K4tzqQaw~||9e{*>Qr$E`8I~Gdtr*COFABhVnKXgzs)>H??bT7gJB{<- z_BbO_9g8p#O&IZn4>k*Ga~gAxl7fE6Mzy+Yv8xa`(k#zYi^lrBcZ$R zLSrAl9_+uqTWfLW+FdsB1fBN|W&Bhx_0%tZKDNd^*K5Hs%?E+i@B#v1cdM^EjB=0kbLXL0c(oEG%#5zDet?3b{7SOe>w2Ro z`Lsv^$; zY2u7Ro`(?VH&`}Fdw5~zuTBDHWoRiU_ldj%Ayt9@3yd`CM;EbttFsVmXsDonCwXDz_wB%v zp{3!4H6E3jQGZ5_(C1B6i6Xd3nb+!dnYzI51p@XgBcFKk8UH4EKRVGc=IuO5nBsl% zgZoufDEV`9^gcgZ=?L}f2dLj>M?_rHO3mslyD`A&G69hu%XuJ#q=U!pkcWN?P&O0@ zyPI!~H+PsGtqpv9T3z%aNbTKRX2kx;iC*%L2kvZW5M1CGsD=265#HB{TVj778)|lb z`>J@`%Z4t3JX~7Fsge*7HK=}FC;#qA2>an5Zc3awMCCctfbHW4qKAVN?z`KLEO*40KfFAL5yL)_=r zyc0@xxy7RH%C4M(Ge@h~*C;p$(`^Zd@(RHHQ7bIdC% zUo;BO3~To7Tb$VGkRlvI=>r+*e-ZLRnWHk_hTdwjKeU`p zFeCGU4&}GrKZ*3n!nM%i6sAdtA2Xw*%h$c0W2Z9UUe-K*$gvJ}QpPI*6c!r;QDaf! zpEPrLbGCMuLPJikc6ntJ7=L+ZtX1gI&1K8?Gvj5|*Hk%Kk-WS>&v>C!K|bS*ErJg2 zTj6I&?!vpRU!rlh{VrYDyHnpq@JiFHRD98R!qNBWB-yvZbPqT2OYS%dOO%1p<6UwD zX~K_(H@Ad#&UF&@=_%99byFd_8;n5>2`?5hBb*}DN2|IBJ3Hfp zgrfzToiBus1oi>3bA0DU&!|kV?RQE0p;O+9%s%a{gj;B% z);$u(hGY^=Ifc@XNWL(Hay1H!S6*x5=q3W?iv!BX?s%UK>5)hAZJ8UJZ;)6UZBMND zG5%b0w2yh6bn&yL2;v%7&etm_Wu~9MUM?&L`Xw9HFn{mY;1HlRtBag>x%vEFH?@QR zudOew0ODBy@gep;J#tWmqTAZYImq)y;+iA;ICG?zFWE&u%tfx>cUc);Y2Z3>B2?2} zz|&C7)4sG@3Vqlvh;FQ-)J|UuC@a=I-m8(G8I{fdq4dmn?@jlYWKZ#9 z;;Th|vszu76?&o%N2xKVX6y}xQsVe6r!s8)pDM4-arBG<{0|*)g!euYsX$Nk8idT> zQxcRYb4PaK7~eLwgxTIL{cCxY+eRY|I{LU4vXp^w6S6?Q>{ zzmsDKHDq*tIP^>N-1?`qujqeYJeNnQOUuKJ^lS~+*!w4+Ug~np#?5TDJd|gtvs68w z^@9>Dr;Wlo2*T)z`aY`E;g4b4lOuBs*`#S@I8lwVtCy^h$bBv3SZ$Dlsqfj#EiVVr z?g**i)d}}_G@gh8sVH-IAQFBK9*iT&`)$vp?o+1tup@kn8)t6yp#uh718UBRz51LL z5YpT7@l)jU>_b1uwG*F}C{OR*-}J_0hP>HQEVkekUapaw}4%6@_J_J@HqX2_XlWVSUZG^AzmJNY$=`0eF>Fqh4%jZ8{H z%)9Ap5l%Il_s(0^F6|So-IM>FIr50*Sns0Sl(p9nb-X34Cy#TF2C4j5`53k%BS@gT zm6rAE=T4vHmI8arYfMz-)18NmVTCdg-eL9nPT9CEg{IxdcY@Ue1sGW1Xp9EXCpc#k zHpgJ>LrY^WBjKVF>qSqi%iks@PCp;w4+fFK zx-_SYs$2JR!^fW&vl_{+z4L7%UqpengacJWgkM(ysXfCc7|P2uNsx}HsV!YRft z-fVtG{-;dcs;S>@nw`%3NC-&{Topa?;{y6y3{=1M^|vep^mR%5gVWu7zw3@k@=U2- zk#Q)ZHJSGRKqffA^CVn+W`3b-r-FlZy0U9@{npg?CaC^#G%XB8FNlX>Q1L^2FPLG0 zUA5N`##Z#LYLx3BP_r8;uwQ%&;%GR0#vco@CkolxS5LkVDV003;x@T{YkiI)~Z`r zM$VKc?rSElGNEewV)w6_M%~X(HUH#Nc+e-5vHr|EP%G%CT;dC7dh^jtHX?V+U?YfM^mYz>_D7W`@-LNU&hK#H$ zdbg^%Y|OL0y*)qszUd;cGrNuZk!NlHY36TX+iwT=S{F86k}U7xfBM$DxW1iLw4|Bp zRXaM|vvLT;Q;P7rRc968zfLBpEMC7{*&Iq9;y?bf(6;>i=(aMaAmDfeP-Udtv2Z|4 z=#Le3>qh^9yu7k_VyVFPXPwAq%jJi9bT$6nSs$I+-zk5rAo=MrKAe&_yoKu6kt7^{ zqiR3Cp}rsTn(`$(_kX&>{k@lSv%a|EyUe)n;WZ(R{jZ)sAABd#NcV-nQUKJ^!#yg;) z)*NZAJ668AQ)3j{(f28hoUgo7MbOa#mZu9WFCN+AjQ*BRz1=O+`#F23jlZ{HxU*)l z`!nBSo8Il9t{uy+{!@Bf!`B4lw9zHe`OXGUF*_Xd9#iU1N*_q>iM^(*a(wwm3{@zA zsqI<#{bZs9xNu`FX+|0qJH2Q|t4Vkf1E2M}qujLmlhv|#w2?gHh6jNH38Eh9U*Cxu@k8Gr-fBPWS1lV1~>$3nC2(O>IJ-11?5&mUw^EN0IoW!fcIxlW}Qp%Mm zPRwIY>$vw<-F!fGLj|gvL+CSbz=Z)jTwYKMqTJ@BQ)x*3m2cn4x0co_a6baw^E)Xu z>N%Af5d0xX7R18^kDxrYVLwP}ev4|OCb1Aktxdq36H}Wq>z`t29&Ta zksoZhMx3gCy)nC84fKVF+mkFDK3+!dPeN^MSHJrJ9H|n{%k9{n*QjYt=(+%ljKmjJ zRaJ8u8X6BA&VFHW@$QsX6B-o@nu&t)qH&7hx$g%ggZB&Vx9EOO!ayZEq^_W22|YYAnaiuhW3fsUms!fSo^HZ5L|+fi`osrRD@ox@pE@Yra15g1P`Da5 zPTm^8{UJ$;RyIw_T&^7p+1#l}Xtl|rd}X42mFRchHV+q%7xiahiUb@*^NB`t!?jex zEKQ)SuGmwgx5}BgtzF-DC0?C`tBdukwPUeyv|2xA?Dtnk<-5pyeqSZqqR4ykWNEm6 zSXw@dFd!-Yt#L^>a$Udt;-}NgJj#PbJ;D*)5xv8FWYa6mBf&G*_Ti=~~9|cf8@>XkkACuYjK5HE`=jImrh6k<%0%A1F1bGY< z&%JT##Lu}X@{8X;3Kn-e^=fzy+mzFYCNeG#N_wYLmMYVI3~3~4yKp@_JY9YBRV_td zo3be1dT=j4zfj|SGXSIFJg6Zc_L^uN;)NFb-nLYjqUs)%-_M%!k_-^2eXXx4wU41k@tsF6^w(qB$L9_B^bY|&BC!zb>tEJI?$$J~HYW@3( zY(O*EtiHU~CA!Smvt81I**u}eeJaK2jD(Sg%qv^VRf7UHc1CV^*RNo6yoOLTo+`s{ zG1njIt$k?pVvZ}%yKVb;_5%FTpb_%)6Zbj2?q}_hLEc`8MKfof|4KMrX zlN$uGPw3+CTq3@!lTh?5>E6(5!fx~iA5ds2?+y`S+P{|z18GYE-TF+_hA$|+;u?Ryn=ANjFR*)Nv^;uVcAc=$q9ezf zZfR6a^$xmxB2@7(Q{_a(mzDiowUv@nC3zLY6=7G1M8>x^=HGfWl6Gx;x|Aqa!)9aW z3Tg<=*0+6M&*2QILd)cjn4N!e<^izHbGCWK7PDD6*q0V_ti^|#h8d>bq0zNFbDMW; zzT5~j4xKD-^QQCMG4$SPfMpD!lKS}JFO;)E$0M)r5pM0O-H;RgLYx?^;+){`4r$1$ zeW_+ux;`_sg2wH5{`>Ee4J-DgLxi>KSv07_t9sOz%rPHw_Vm)8q{XR`?P)2_ffBZ-CwG4z68oUgUo$_< z!2xWG2@S6%H`5gAOqs`v*sJY%9z@R<#5|^Wn6eY^^OoU`uD}w#k{`^|@v-03y zaS@(zl>y|Z=ai_#WTL8cL4$@th}KIW*8rhYO}0vnC|M$4mI?i9 znX-xtSQxgq^;sI=7-HbL@s4T=N7&}uvQ$?H{`hmWqHc2uIZ8B}BRPAzzB`4cdjl#= zY#v%D`RCb3X)oO#9LWl79opBmG!Sckix*oD^gjwtoEEg=v29uE!q~F!@dk>0o$3$W z3r%uYk^?{tUt-C(!uM(d6LpH5u3;$rvEvLURad0=wm|u1<%MsjdUw`tZ1%QZ7&Yw9 z!kx!)ai%;NT=i>)T4g(r-{DfBl+bqfK>PQnjUHk<7YrY4gvvZCdz-&_7xd*0|Ds?0 zwExUTzoXr%y7Fu1!w`4Zss-^oz0#0U(R@x~eai1-D-PbTvu>`hWFI|DbuGc*RsCu% zUA1dlP?UykKDO#;(HqLcsyw*z$a)upA7-nu8MV3KkktG!_j%=Hf0v`wXL{+3E4ss# zARon+V$R_^3F^vr-TPJ+OZjU{T?q>P*@pv80-JTC%|k6a9ZO7b!{V;Isvwt@1USJz zUT@zJqsNxF85Nz9oS|J#x#Y=uxz0gFYq0-S^|S1cNJH23vve4|O-i-7kzMyg>}u{0 z=*&>nK=n6%wLHvDO2OC zD^sB?0)hJeedZkQovV7vGdm%RHPGZS(EGtAS4EnLhVEXvW^aAyc1Q!;Agbfa%jU)K zbTzpTZt9u+`9leb$Ys!9JrMp=00@`PkS7K4T@&c_(xoJ!ap_)n7Q!9pe!euvK-uev z&I~g$oW0QWRS91=<-1EHo>4tf!Bj#>m{;wy;MsUsa{f{G!JFiJbe7UkbBe}mVdoAF z>D+-|Y!u4uY!QjFa=)!+G94fC+Pj|%fweATcBs6t%aH4id}Y<06|`cxu>Q61o-4!m zBgut}80hAbAa=%R0*_Tgemjz!)Nd;<1jin|h<51V0Y|;Jm>pcPc0o0L?BEnPXRVL{ z%WDQNqiHqYwsd!d2X3Ez>YqsN9lPLu!f{rjgko95hbP&hjr=Ot`{V=*J&>3!M0Ob;!h zHpJ?{pVIF~kG&MMS;DYJZjidpimVbOnq$?foT1g8AQQ9AJy&7Hz9=FVt_`Dx$8G!5 zV?X>%5L31$e>9#rf|}HUSd;Jk0D64WuJ?X7PS?rI0%a&j+=2_k9x#6dXFG^4cFY3^TfuLh52X_HPv8Fdp9={FKH~OlR zaB(}W=!N+q_%T`sxM~weD<|mp_XXS+C#^k)cC>iU!RzYk40zBV>^VVjbDzCfN_B|B z!j^hmgBJ~W17~j1ls@PqN2eh3D9OD!;%NN`&>{HOZeSSEi7pbDeGy^NzHlrakbo3z zi{WtmPr8^62^Yb#@%n@Zu`waK5vKGt zQoBhSLh3HPCu%lEJr%A>V6WR&@D!7|xw%s*gZ0ye!jqhwuw3Y=m`s@}2JcuO(U8%D zd+|k4EPe4qDR(`2E7!Tk3g3j9#j#Vvd>tP6*tj!O#hk6U;w}wc|6L<`I*)?X=DHY_ z`#x=M_6}R26aTs5htejHYteJpNZ5LYj zX?0x+D@-{oGwmt&nBlGO$Y6F^#$y{IdMQKIl~0T-AyH!sd+s^*`EoJc%JBs zZyqvYwdFU&2ELVIKtB=IhMM{==Uwi&9xcS4g}WFeclI_|%99H@{?b75+MK?0R(D&S z{+;5uI6e1n3Y@k3&i#yFDd?JF(x)gfymT*@n%4FxpQqM^c+&fxT%$8muu#$3+B@#h zsmwdm5ipEF$I5;rg&VQdn%JpM&mH{`&MK9X-m$L@yZq+-xx)yl)SR)kcrw*EqyFPg z98?B*HzT(>s1RfJx2stkpr&3IljES&yB>IbXMm_nD+N;!J%9GLJKmin_~av`Z~Qt% zcpP_@S5H`LMxDhbCut>k^#naZgg}agB3lSAwd2YAcbnVWByq*V-%SK``ZG@w#AF2q z0?;s@bM>xa82pnQ8Nmo)8{7eXC$#Sa$TG05z}pX({N*`dITMZGDSpEPOk>OBqrhegsF|+3UYAMOm^F zofaOEW#ZE_#0MXac~$G$$%a+zND$(abJxDSL9I)Sw3LP)kkB?xT zxAx_IYR&uo_jFTvO`xqCD;d6BnPPaUbZ-eNNr4)EY0m5ID!s$${=TL3)IUICeq-T#XklVjJO&CV ztC{+sK$N<9F(qOL7vLnd8_=R$kSR5`*z3eefGDd9*SVdchwGW6FU!jysEPqqc~iS`Nqwx9o(s$ zBl~sZT)`)`VUveH#J9^TOaWBD95;fO=`ly5>V)}?v8`F%GwkrUsYX8VLkF0Xy#M){1!&+rOLfbCGCjxYj zHb^Bj#x7IbZd$3gE}32kqsNQeo6TuMhLK!0u8k(gWb$ygK8j6a*{~NZ;%g7)Fc2#r z&44ttnr!7M*rnLaB?YaPtPZ8P4yPihi9CBUu68n2{8DYnQyD>XhKSbVAm^E?lklU* zt8Zc;y?I)S3OvhA0dv#JaVNBGwHex;7&_rv4Q)2b?F#E+@_JTsOz9B3L-bNmRfsf1 z-rj6Gg6KZO{8&vRlgsfpUH@BHje-bkjEY^(e%=28Hg?EMI}R?0t%KVYd3U@6U`9h_3N5*SD} z%Mi`tvNmfwAaVS6hh!UUx|zR4p(RCnPNrl{C3SpI#DX5nV-T_B08NguTuO1ya0Xvi z>Wl6DR5|ichMLHcLOd(kvv#uRd%9KnHrlY0cM8mYMr1UM@~OhY6mujV{^Pj zZ;NvGW$RRDK*sxO%kuc+e`fRUntTgHA;#(%9H2}A`^&9tc%DU6n z+QPfAWOvxX#{zR{NLaH8$WjJ>>uIac=hGHp6*3a|(NcO#vy#6|s5ib0$rg&fm)|nq zX=r-|(c}L)=L}tbGyUW^!SWG19+o`r0djQ*e!-9|C7mP0zuUE0K{68Hg2Vg1l;c1i z`rCjZ)Kb*g=&OsxAcMLDE=oj~idoR(W0RMGlF&PV-+^H>P!;`iHvo;u1s6QP1RyMd z_o|bzIR_vZMn+ZuETDg3LX9n;asWs@Hf@H|QMC3gP%8u^en1>O-tZ6+e3XB77YLuF zF%gyw15zJ<91Mv4C;mV$5Ey{-zc%}SpsUI%T99e%9>kWEPX zy4abV2YSaf#ASAc2A??xETLK(BE48hJ#pY@drqx59Z5)}If2~HYLln&pFgl}L*yUJ z*JZzvY(>M{qEMLfh>~V@6v5llp90MEz$*ZVmw`L@HVp=_=e@%64m^hQAICQ{#ZU7n zEskp_)AVD0d-1uH^NG*JTn;q!_HW>KqB@iztH$EOP7xA-t#U_UV$!`k}C80gkV3`B?zo=SuPbibU#;LmJV1*P{8j@HJu)gHte zW{5y)AHFsIhB<;G@n~n|Oaxz=Wil0hHU1okH6Uo%xxTxn>X{3z#+$Wxc|dJXkl~G&`DbBmMfR%$lhP*m8x26`Y;BmiabE(!pc|b3h)~A!x)0 zgsGX~Qe?bpkYm*iRukw1=E2#>(HxaC2az~nN*hTeP`l7x1TBC1Trkp-!fK*;%0rs6 z6#Y_+ z(_o+}TE!uJ^%OJJi)fj)NX&j+Dg#)0iu%uFlN?sk@Pao#ox@>Qx!4tJU&+JcxUG(Z z%&E3gus$U|rfd|;MpXVX#wxhJq{rV;&9mrLTLl(NwB28JS{0xZCNc%s*Rt#e7(H1g z1WyBen(HmyFVb`$da#j!GP7W4XWt)ci_xE?wln6xc-tTa zm7%UA^^N>ZmI}?j_x#yAXqz>z7{bllP8#0)Of|Ksnoikj0i_Ciu(Nxh$r0w*5j-tg z>$kk)^#1<1$lK|p(`L|k<*A1#goCzV5MtcGq`s3?q-2`wmV}n!YkM+C$0xevuw#$W zRrEGFPnz@AmS_!@73g2{4&OWTlEFt%kt3<{;wP%9gcLSUTPZpCn3~MM1emB%#S1kt)*e^h9m^$zv_ho19^FCSAFGtJ=l~oul&CeZwLt zpW{PD!ePrgVGfOk2zsP&h>9>4vm7%i-6oJjtKPJvjamLMDGe@Qy$#At%Q*36f2~LI zRKrOu<$*c@xg84f@>G>^%Dt?FoG`_AWdD4^A9ekO6_TV_mrg3!#O5D4Yu!(cCjh8$XRhPYvMm zn+*ZFOhYKB(4e;5P_v{A;QBMj07y465G>9B9T}oqXJ4ob{osVj_1qTO* zgiD}=M3Km!Lc}IQ;On6afFY0HvQX*USeDShERm@FI{pPU9(sK6bf@oSjU3;}%c$_n zDDV)Q@8cXkbe(N=J`D@;6kSqFPk5Q zTcf0wvMtw$(B!iKE9-v5<>RV-(O4ST^#0maP7IzT=Xx6Z@v)g7>L#3qv6fdWGID?lsWi;Rg53ek|1N317_57rv zSGPG~oisTbSY9tMSonM?dC(Dx?rxj3GsFsOnth%}b?>-}?DW%t)lAQkiahveSokY2 zSoo|kVbB5cG&YoD_@f8U#c5ysR6qMY7UJCtgs0nqj`| zE(OzQ(|pR?9S6GVl7?14B4Mqjo8ze;k@T(~S<%bj2x7Db?l_Zl&=|@wxI>FFV5CEY zKSzH1ZG?~pcY4!N*Bf*lyuFQnc}0-G&4_W9oHZuPjAVmo80t)dep>`>jJD|AM?x@V zint4p1#s-5o~o~2zC<9w^C&6!O>#IbOW<`WDEttc$BLqal(~zjWjS(|LwGD8l7?UVax)Rq7QPtM>~anpQ(|0T1BPdEU+o7uLG!YoWpoU&539`F zy8wvOhtRh+G5A(0`r_?${6WvQm^Ek3$kuUZ*AbdL;+l%ML**iLDs@8}5Xrjzp{f}Y z6m~=Py+cOkz+LoHp_aXZlCr%Z&>oTP;@308vdfsAX#s$_5k{&i^w^*9y|QD%M3KNxZZaCGDFK-k?2Jyr&BQXvbNbJnsyCKc8loZ4{zL`uPa#(d&6fs1dR zHUEY!0S!0pnBKUj=ZNp#!R%QKZQbTv6TnG{e2QT2!CBZ>9|OD}?ELiyR{3CKfx{=O zoSY$kR`IBxPHd&>G~herE|@I6x|B-Xa@u0A%7W$bSRIEZ$9(N4TIN?N_cij z8{uSf_l5zGO>-4dmJ!!((bF^y-#ImTDac0B%P$ zSR4#^^{qsEk?{Ck5#odiGbmq}%LF~*pdQFt@JtH_Y-Hv4Jp!2r_rI^FoE3rfQ+F#m zfqm6=`&Pwdsd;&x=T(S(h4-9IKSwpY{qmmj++8Oq_n~B06Dvcj*%rp*`FoO1t9taR z>BoA&-PL~exxuA{g%!VdMTZSbm*aQ8ev!QcOyY*I(zvpWMDmL__skO#)mfg3PE&)E zqy&CixZxo|K4uedi*Yw7iO9NN$)-=k?)^$ z59Y%&FGrk?GyJvvGFd6ZzzM3lr%br(gk1e0TFO>z-V`JArh|uk2>OW zpYM^N7Cuq^h*+`yA+YUd{u6L<_6~!^p44X(JfLV209Swxp5b^TLAu0a?dGOm!2MWf z3*7O$rcA5=RxEnpfiO`yJb!AzLP?oym;Odf12mbDI?oK=(>frdW#jf~mC4@@=ab9; z7YbM=ck)qehp3Yh6BVy0in*kvKK*KBY+fSw7+esPQ$A*x1(}4)u}yzyl)IVFz6qAh z%L+9kX&tD4vcBFvo~-~Km~jR;1bIbwaOv|12GudX5V_6WBwRWRzMY$vOt;N%eA-5z zf+LgAIbXNlXW2}M02=JIJkU^h6U4>EC2nw=FPZgO`qu%a7q0e+vSNh?w;vim_I4w} zOZbTNhOBr-9H;t|Xe>C3hP)7PMwA08T0^)@J)R>#%K=>CAYwdJ5eFCPkyr8Y%OVQG z$LEqkETG^kk9J;VO`fIX5?x;)@{@{&8Ow3VU^vUomkvTg{I)YCxApUL2s zF*YLCROl}xaIgT1&6wYR=AdvOGw9C=6~F*|+YoFctZ4&YJRNK#c<@p*r^uZ4FC+(` zWKzsOmH#5h`(Fz57t()7{f|`tqreAN1Z;M1& literal 0 HcmV?d00001 diff --git a/data-browser/public/robots.txt b/data-browser/public/robots.txt new file mode 100644 index 000000000..c2a49f4fb --- /dev/null +++ b/data-browser/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / diff --git a/data-browser/public/site.webmanifest b/data-browser/public/site.webmanifest deleted file mode 100644 index 9728ac76f..000000000 --- a/data-browser/public/site.webmanifest +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "Atomic Data Browser", - "short_name": "Atomic", - "start_url": "/", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/data-browser/public/sw.js b/data-browser/public/sw.js deleted file mode 100644 index 49284b905..000000000 --- a/data-browser/public/sw.js +++ /dev/null @@ -1,3 +0,0 @@ -self.addEventListener('install', () => { - // TODO: Do something. -}); diff --git a/data-browser/src/App.tsx b/data-browser/src/App.tsx index 61e012581..98acf996e 100644 --- a/data-browser/src/App.tsx +++ b/data-browser/src/App.tsx @@ -22,6 +22,15 @@ import toast from 'react-hot-toast'; import { DialogContainer } from './components/Dialog/DialogContainer'; import { registerHandlers } from './handlers'; import { ErrorBoundary } from './views/ErrorPage'; +import { NetworkIndicator } from './components/NetworkIndicator'; + +function fixDevUrl(url: string) { + if (isDev()) { + return url.replace('5173', '9883'); + } + + return url; +} /** Initialize the store */ const store = new Store(); @@ -30,11 +39,8 @@ const store = new Store(); * In dev envs, we want to default to port 9883 */ const currentOrigin = window.location.origin; -store.setServerUrl( - currentOrigin === 'http://localhost:5173' - ? 'http://localhost:9883' - : currentOrigin, -); + +store.setServerUrl(fixDevUrl(currentOrigin)); /** Show an error when things go wrong */ store.errorHandler = e => { @@ -86,16 +92,17 @@ function App(): JSX.Element { + {/* @ts-ignore TODO: Check if types are fixed or upgrade styled-components to 6.0.0 */} + {/* @ts-ignore fallback component type too strict */} - {/* @ts-ignore TODO: Check if types are fixed or upgrade styled-components to 6.0.0 */} - + diff --git a/data-browser/src/components/ErrorLook.ts b/data-browser/src/components/ErrorLook.ts deleted file mode 100644 index aac007685..000000000 --- a/data-browser/src/components/ErrorLook.ts +++ /dev/null @@ -1,6 +0,0 @@ -import styled from 'styled-components'; - -export const ErrorLook = styled.span` - color: ${props => props.theme.colors.alert}; - font-family: monospace; -`; diff --git a/data-browser/src/components/ErrorLook.tsx b/data-browser/src/components/ErrorLook.tsx new file mode 100644 index 000000000..efacc8694 --- /dev/null +++ b/data-browser/src/components/ErrorLook.tsx @@ -0,0 +1,62 @@ +import { lighten } from 'polished'; +import styled from 'styled-components'; +import React from 'react'; +import { Details } from './Details'; +import { FaExclamationTriangle } from 'react-icons/fa'; + +export const ErrorLook = styled.span` + color: ${props => props.theme.colors.alert}; + font-family: monospace; +`; + +export interface ErrorBlockProps { + error: Error; + showTrace?: boolean; +} + +export function ErrorBlock({ error, showTrace }: ErrorBlockProps): JSX.Element { + return ( + + + + Something went wrong + +
Show Details}> +
+          {error.message}
+        
+ {showTrace && ( + <> + Stack trace: +
+              {error.stack}
+            
+ + )} +
+
+ ); +} + +const ErrorLookBig = styled.div` + background-color: ${p => lighten(0.4, p.theme.colors.alert)}; + color: ${p => p.theme.colors.alert}; + font-size: 1rem; + padding: ${p => p.theme.margin}rem; + border-radius: ${p => p.theme.radius}; + border: 1px solid ${p => lighten(0.2, p.theme.colors.alert)}; +`; + +const CodeBlock = styled.code` + white-space: pre-wrap; + border-radius: ${p => p.theme.radius}; + padding: ${p => p.theme.margin}rem; + background-color: ${p => p.theme.colors.bg}; +`; + +const BiggerText = styled.p` + font-size: 1.3rem; + display: flex; + align-items: center; + gap: 1ch; +`; diff --git a/data-browser/src/components/Navigation.tsx b/data-browser/src/components/Navigation.tsx index 80dc0e35b..35986a936 100644 --- a/data-browser/src/components/Navigation.tsx +++ b/data-browser/src/components/Navigation.tsx @@ -14,6 +14,7 @@ import { shortcuts } from './HotKeyWrapper'; import { MenuBarDropdownTrigger } from './ResourceContextMenu/MenuBarDropdownTrigger'; import { NavBarSpacer } from './NavBarSpacer'; import { Searchbar } from './Searchbar'; +import { useMediaQuery } from '../hooks/useMediaQuery'; interface NavWrapperProps { children: React.ReactNode; @@ -60,20 +61,26 @@ const Content = styled.div` `; /** Persistently shown navigation bar */ -function NavBar() { +function NavBar(): JSX.Element { const [subject] = useCurrentSubject(); const navigate = useNavigate(); const { navbarTop, navbarFloating, sideBarLocked, setSideBarLocked } = useSettings(); const [showButtons, setShowButtons] = React.useState(true); - /** Checks if the app is running in PWA / stand alone mode or in a browser */ - const isInStandaloneMode = () => - window.matchMedia('(display-mode: standalone)').matches || - // @ts-ignore standalone doesn't exist, but it does - window.navigator.standalone || - document.referrer.includes('android-app://') || - isRunningInTauri(); + const machesStandalone = useMediaQuery( + '(display-mode: standalone) or (display-mode: fullscreen)', + ); + + const isInStandaloneMode = React.useMemo( + () => + machesStandalone || + //@ts-ignore + window.navigator.standalone || + document.referrer.includes('android-app://') || + isRunningInTauri(), + [machesStandalone], + ); /** Hide buttons if the input element is quite small */ function maybeHideButtons(event: React.FocusEvent) { @@ -101,19 +108,19 @@ function NavBar() { > - {isInStandaloneMode() && ( + {isInStandaloneMode && ( <> navigate(1)} + onClick={() => navigate(-1)} > {' '} navigate(-1)} + onClick={() => navigate(1)} > diff --git a/data-browser/src/components/NetworkIndicator.tsx b/data-browser/src/components/NetworkIndicator.tsx new file mode 100644 index 000000000..f9e032bff --- /dev/null +++ b/data-browser/src/components/NetworkIndicator.tsx @@ -0,0 +1,63 @@ +import React, { useEffect } from 'react'; +import styled, { keyframes } from 'styled-components'; +import { MdSignalWifiOff } from 'react-icons/md'; +import { useOnline } from '../hooks/useOnline'; +import { lighten } from 'polished'; +import toast from 'react-hot-toast'; + +export function NetworkIndicator() { + const isOnline = useOnline(); + + useEffect(() => { + if (!isOnline) { + toast.error('You are offline, changes might not be persisted.'); + } + }, [isOnline]); + + return ( + + + + ); +} + +interface WrapperProps { + shown: boolean; +} + +const pulse = keyframes` + 0% { + opacity: 1; + filter: drop-shadow(0 0 5px var(--shadow-color)); + } + 100% { + opacity: 0.8; + filter: drop-shadow(0 0 0 var(--shadow-color)); + } +`; + +const Wrapper = styled.div` + --shadow-color: ${p => lighten(0.15, p.theme.colors.alert)}; + position: fixed; + bottom: 1.2rem; + right: 2rem; + z-index: ${({ theme }) => theme.zIndex.networkIndicator}; + font-size: 1.5rem; + color: ${p => p.theme.colors.alert}; + pointer-events: ${p => (p.shown ? 'auto' : 'none')}; + transition: opacity 0.1s ease-in-out; + opacity: ${p => (p.shown ? 1 : 0)}; + + background-color: ${p => p.theme.colors.bg}; + border: 1px solid ${p => p.theme.colors.alert}; + border-radius: 50%; + display: grid; + place-items: center; + box-shadow: ${p => p.theme.boxShadowSoft}; + padding: 0.5rem; + + svg { + animation: ${pulse} 1.5s alternate ease-in-out infinite; + animation-play-state: ${p => (p.shown ? 'running' : 'paused')}; + } +`; diff --git a/data-browser/src/components/Searchbar.tsx b/data-browser/src/components/Searchbar.tsx index 8fd0618fa..a792be217 100644 --- a/data-browser/src/components/Searchbar.tsx +++ b/data-browser/src/components/Searchbar.tsx @@ -97,8 +97,11 @@ export function Searchbar({ ); useEffect(() => { - setInput(query?.toString()); - setInputFocus(); + setInput(query ?? ''); + + if (query || scope) { + setInputFocus(); + } }, [query, scope]); return ( diff --git a/data-browser/src/components/SideBar/AppMenu.tsx b/data-browser/src/components/SideBar/AppMenu.tsx new file mode 100644 index 000000000..ac74bcddd --- /dev/null +++ b/data-browser/src/components/SideBar/AppMenu.tsx @@ -0,0 +1,85 @@ +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { FaPlusCircle } from 'react-icons/fa'; +import { constructOpenURL } from '../../helpers/navigation'; +import { useCurrentSubject } from '../../helpers/useCurrentSubject'; +import { appMenuItems } from './menuItems'; +import { SideBarHeader } from './SideBarHeader'; +import { SideBarMenuItem } from './SideBarMenuItem'; + +// Non standard event type so we have to type it ourselfs for now. +type BeforeInstallPromptEvent = { + preventDefault: () => void; + prompt: () => Promise<{ outcome: 'accepted' | 'dismissed' }>; +}; + +export interface AppMenuProps { + onItemClick: () => void; +} + +export function AppMenu({ onItemClick }: AppMenuProps): JSX.Element { + const event = useRef(null); + const [subject] = useCurrentSubject(); + const [showInstallButton, setShowInstallButton] = useState(false); + + const install = useCallback(() => { + if (!event.current) { + return; + } + + event.current.prompt().then(result => { + if (result.outcome === 'accepted') { + setShowInstallButton(false); + } + }); + }, [event.current]); + + useEffect(() => { + const listener = (e: BeforeInstallPromptEvent) => { + e.preventDefault(); + setShowInstallButton(true); + event.current = e; + }; + + //@ts-ignore + window.addEventListener('beforeinstallprompt', listener); + + //@ts-ignore + return () => window.removeEventListener('beforeinstallprompt', listener); + }, []); + + const items = useMemo(() => { + if (!showInstallButton) { + return appMenuItems; + } + + return [ + { + icon: , + label: 'Install App', + helper: 'Install app to desktop', + handleClickItem: install, + path: constructOpenURL(subject ?? window.location.href), + }, + ...appMenuItems, + ]; + }, [appMenuItems, showInstallButton, subject]); + + return ( + <> + App + {items.map(p => ( + + ))} + + ); +} diff --git a/data-browser/src/components/SideBar/index.tsx b/data-browser/src/components/SideBar/index.tsx index d2172129c..42ab31cf9 100644 --- a/data-browser/src/components/SideBar/index.tsx +++ b/data-browser/src/components/SideBar/index.tsx @@ -3,14 +3,12 @@ import * as React from 'react'; import { useHover } from '../../helpers/useHover'; import { useSettings } from '../../helpers/AppSettings'; import { useWindowSize } from '../../helpers/useWindowSize'; -import { appMenuItems } from './menuItems'; -import { SideBarMenuItem } from './SideBarMenuItem'; import { SideBarDrive } from './SideBarDrive'; -import { SideBarHeader } from './SideBarHeader'; import { DragAreaBase, useResizable } from '../../hooks/useResizable'; import { useCombineRefs } from '../../hooks/useCombineRefs'; -import { About } from './About'; import { NavBarSpacer } from '../NavBarSpacer'; +import { AppMenu } from './AppMenu'; +import { About } from './About'; /** Amount of pixels where the sidebar automatically shows */ export const SIDEBAR_TOGGLE_WIDTH = 600; @@ -57,17 +55,10 @@ export function SideBar(): JSX.Element { {/* The key is set to make sure the component is re-loaded when the baseURL changes */} - - App - {appMenuItems.map(p => ( - - ))}{' '} + + - + @@ -112,7 +103,7 @@ const SideBarStyled = styled('nav').attrs(p => ({ overflow-x: hidden; `; -const SideBarBottom = styled('div')` +const MenuWrapper = styled.div` margin-top: auto; flex-direction: column; justify-items: flex-end; diff --git a/data-browser/src/components/SideBar/menuItems.tsx b/data-browser/src/components/SideBar/menuItems.tsx index e4fb6c552..83073f54b 100644 --- a/data-browser/src/components/SideBar/menuItems.tsx +++ b/data-browser/src/components/SideBar/menuItems.tsx @@ -6,25 +6,25 @@ import { SideBarMenuItemProps } from './SideBarMenuItem'; export const appMenuItems: SideBarMenuItemProps[] = [ { icon: , - label: 'user settings', + label: 'User Settings', helper: 'See and edit the current Agent / User (u)', path: paths.agentSettings, }, { icon: , - label: 'theme settings', + label: 'Theme Settings', helper: 'Edit the theme, current Agent, and more. (t)', path: paths.themeSettings, }, { icon: , - label: 'keyboard shortcuts', + label: 'Keyboard Shortcuts', helper: 'View the keyboard shortcuts (?)', path: paths.shortcuts, }, { icon: , - label: 'about', + label: 'About', helper: 'Welcome page, tells about this app', path: paths.about, }, diff --git a/data-browser/src/components/forms/FileDropzone/useUpload.ts b/data-browser/src/components/forms/FileDropzone/useUpload.ts index b57e7ae2e..7acac3d7d 100644 --- a/data-browser/src/components/forms/FileDropzone/useUpload.ts +++ b/data-browser/src/components/forms/FileDropzone/useUpload.ts @@ -14,6 +14,10 @@ export interface UseUploadResult { error: Error | undefined; } +const opts = { + commit: true, +}; + export function useUpload(parentResource: Resource): UseUploadResult { const store = useStore(); const [isUploading, setIsUploading] = useState(false); @@ -21,6 +25,7 @@ export function useUpload(parentResource: Resource): UseUploadResult { const [subResources, setSubResources] = useArray( parentResource, properties.subResources, + opts, ); const upload = useCallback( diff --git a/data-browser/src/hooks/useMediaQuery.ts b/data-browser/src/hooks/useMediaQuery.ts new file mode 100644 index 000000000..d9f07f90c --- /dev/null +++ b/data-browser/src/hooks/useMediaQuery.ts @@ -0,0 +1,25 @@ +import { useEffect, useState } from 'react'; + +/** Watches a media query and returns a statefull result. */ +export function useMediaQuery(query: string): boolean { + const [matches, setMatches] = useState(false); + + useEffect(() => { + if (!window.matchMedia) { + return; + } + + const listener = (e: MediaQueryListEvent) => { + setMatches(e.matches); + }; + + const queryList = window.matchMedia(query); + setMatches(queryList.matches); + + queryList.addEventListener('change', listener); + + return () => queryList.removeEventListener('change', listener); + }, []); + + return matches; +} diff --git a/data-browser/src/hooks/useOnline.ts b/data-browser/src/hooks/useOnline.ts new file mode 100644 index 000000000..e806fcba5 --- /dev/null +++ b/data-browser/src/hooks/useOnline.ts @@ -0,0 +1,21 @@ +import { useEffect, useState } from 'react'; + +/** Returns true when the users device is connected to a network. */ +export function useOnline(): boolean { + const [online, setOnline] = useState(navigator.onLine); + + useEffect(() => { + const handleOnline = () => setOnline(true); + const handleOffline = () => setOnline(false); + + window.addEventListener('online', handleOnline); + window.addEventListener('offline', handleOffline); + + return () => { + window.removeEventListener('online', handleOnline); + window.removeEventListener('offline', handleOffline); + }; + }, []); + + return online; +} diff --git a/data-browser/src/styling.tsx b/data-browser/src/styling.tsx index f36f7dc03..f63e57667 100644 --- a/data-browser/src/styling.tsx +++ b/data-browser/src/styling.tsx @@ -39,7 +39,8 @@ export const zIndex = { sidebar: 10, dialog: 100, dropdown: 200, - toast: 300, + networkIndicator: 300, + toast: 400, }; /** Default animation duration in ms */ diff --git a/data-browser/src/views/CrashPage.tsx b/data-browser/src/views/CrashPage.tsx index 0b4cdeb6f..520f82586 100644 --- a/data-browser/src/views/CrashPage.tsx +++ b/data-browser/src/views/CrashPage.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { Resource } from '@tomic/react'; -import { ContainerNarrow } from '../components/Containers'; -import { ErrorLook } from '../components/ErrorLook'; +import { ContainerWide } from '../components/Containers'; +import { ErrorBlock } from '../components/ErrorLook'; import { Button } from '../components/Button'; +import { Column, Row } from '../components/Row'; type ErrorPageProps = { resource?: Resource; @@ -21,22 +22,24 @@ function CrashPage({ clearError, }: ErrorPageProps): JSX.Element { return ( - - - {children ? children :

{JSON.stringify(error?.message)}

} - {error?.stack} -
-
- {clearError && } - -
-
+ + + {children ? children : } + + {clearError && } + + + + ); } diff --git a/data-browser/src/views/ErrorPage.tsx b/data-browser/src/views/ErrorPage.tsx index ec24136ee..c1f91e4e3 100644 --- a/data-browser/src/views/ErrorPage.tsx +++ b/data-browser/src/views/ErrorPage.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; import { isUnauthorized, useStore } from '@tomic/react'; -import { ContainerNarrow } from '../components/Containers'; -import { ErrorLook } from '../components/ErrorLook'; +import { ContainerWide } from '../components/Containers'; +import { ErrorBlock } from '../components/ErrorLook'; import { Button } from '../components/Button'; import { SignInButton } from '../components/SignInButton'; import { useSettings } from '../helpers/AppSettings'; import { ResourcePageProps } from './ResourcePage'; -import { Row } from '../components/Row'; +import { Column, Row } from '../components/Row'; import CrashPage from './CrashPage'; /** @@ -20,43 +20,54 @@ function ErrorPage({ resource }: ResourcePageProps): JSX.Element { if (isUnauthorized(resource.error)) { return ( - -

Unauthorized

- {agent ? ( - <> -

{resource.error?.message}

- - - ) : ( - <> -

{"You don't have access to this, try signing in:"}

- - - )} -
+ + +

Unauthorized

+ {agent ? ( + <> + + + + + + ) : ( + <> +

{"You don't have access to this, try signing in:"}

+ + + )} +
+
); } return ( - -

⚠️ Error opening {resource.getSubject()}

- {resource.error?.message} - - - - -
+ + +

Could not open {resource.getSubject()}

+ + + + + +
+
); } diff --git a/data-browser/vite.config.js b/data-browser/vite.config.js index 3c65048e7..c4f7ed8cc 100644 --- a/data-browser/vite.config.js +++ b/data-browser/vite.config.js @@ -1,5 +1,6 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; +import { VitePWA } from 'vite-plugin-pwa'; export default defineConfig({ plugins: [ @@ -15,6 +16,94 @@ export default defineConfig({ ], }, }), + VitePWA({ + registerType: 'autoUpdate', + devOptions: { + enabled: true + }, + manifest: { + name: 'Atomic Data Browser', + short_name: 'Atomic', + description: 'The easiest way to create, share and model Linked Atomic Data.', + theme_color: '#ffffff', + icons: [ + { + src: 'android-chrome-192x192.png', + sizes: '192x192', + type: 'image/png', + purpose: 'any', + }, + { + src: 'android-chrome-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'any', + }, + { + src: 'maskable_icon.png', + sizes: '1024x1024', + type: 'image/png', + purpose: 'maskable', + }, + { + src: 'maskable_icon_x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'maskable', + }, + { + src: 'maskable_icon_x384.png', + sizes: '384x384', + type: 'image/png', + purpose: 'maskable', + }, + { + src: 'maskable_icon_x192.png', + sizes: '192x192', + type: 'image/png', + purpose: 'maskable', + }, + { + src: 'maskable_icon_x128.png', + sizes: '128x128', + type: 'image/png', + purpose: 'maskable', + }, + ] + }, + workbox: { + runtimeCaching: [ + { + urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i, + handler: 'CacheFirst', + options: { + cacheName: 'google-fonts-cache', + expiration: { + maxEntries: 10, + maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days + }, + cacheableResponse: { + statuses: [0, 200] + } + } + }, + { + urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i, + handler: 'CacheFirst', + options: { + cacheName: 'gstatic-fonts-cache', + expiration: { + maxEntries: 10, + maxAgeSeconds: 60 * 60 * 24 * 365 // <== 365 days + }, + cacheableResponse: { + statuses: [0, 200] + }, + } + } + ] + } + }), ], references: [{ path: 'lib' }, { path: 'react' }], optimizeDeps: { diff --git a/data-browser/workbox-config.js b/data-browser/workbox-config.js deleted file mode 100644 index 7320ef078..000000000 --- a/data-browser/workbox-config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - globDirectory: './', - globPatterns: [ - '**/*.{ts,png,xml,ico,html,css,svg,webmanifest,js,jsx,tsx,log}', - ], - ignoreURLParametersMatching: [/^utm_/, /^fbclid$/], - swDest: 'publish/sw.js', -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ccebc2e8..a4dfd1cdb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,6 +106,8 @@ importers: remark-gfm: ^3.0.1 styled-components: ^5.3.3 types-wm: ^1.1.0 + vite: ^3.0.5 + vite-plugin-pwa: ^0.13.1 workbox-cli: ^6.4.1 yamde: ^1.7.1 dependencies: @@ -146,6 +148,8 @@ importers: gh-pages: 3.2.3 lint-staged: 10.5.4 types-wm: 1.1.0 + vite: 3.0.5 + vite-plugin-pwa: 0.13.1_vite@3.0.5 workbox-cli: 6.4.1 lib: @@ -227,6 +231,18 @@ packages: leven: 3.1.0 dev: true + /@apideck/better-ajv-errors/0.3.6_ajv@8.8.0: + resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + dependencies: + ajv: 8.8.0 + json-schema: 0.4.0 + jsonpointer: 5.0.0 + leven: 3.1.0 + dev: true + /@babel/code-frame/7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} @@ -1919,6 +1935,23 @@ packages: playwright-core: 1.26.0 dev: true + /@rollup/plugin-babel/5.3.0_loko3dyoqyfrad2b2tv6icqcai: + resolution: {integrity: sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + dependencies: + '@babel/core': 7.19.0 + '@babel/helper-module-imports': 7.18.6 + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + rollup: 2.79.1 + dev: true + /@rollup/plugin-babel/5.3.0_yzwiwxlebv3rv47aimfjn4byyu: resolution: {integrity: sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==} engines: {node: '>= 10.0.0'} @@ -1944,13 +1977,28 @@ packages: dependencies: '@rollup/pluginutils': 3.1.0_rollup@2.77.2 '@types/resolve': 1.17.1 - builtin-modules: 3.2.0 + builtin-modules: 3.3.0 deepmerge: 4.2.2 is-module: 1.0.0 resolve: 1.22.1 rollup: 2.77.2 dev: true + /@rollup/plugin-node-resolve/11.2.1_rollup@2.79.1: + resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + '@types/resolve': 1.17.1 + builtin-modules: 3.3.0 + deepmerge: 4.2.2 + is-module: 1.0.0 + resolve: 1.22.1 + rollup: 2.79.1 + dev: true + /@rollup/plugin-node-resolve/13.3.0_rollup@2.77.2: resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==} engines: {node: '>= 10.0.0'} @@ -1976,6 +2024,16 @@ packages: rollup: 2.77.2 dev: true + /@rollup/plugin-replace/2.4.2_rollup@2.79.1: + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.79.1 + magic-string: 0.25.7 + rollup: 2.79.1 + dev: true + /@rollup/pluginutils/3.1.0_rollup@2.77.2: resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} @@ -1988,6 +2046,18 @@ packages: rollup: 2.77.2 dev: true + /@rollup/pluginutils/3.1.0_rollup@2.79.1: + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 2.79.1 + dev: true + /@rollup/pluginutils/4.2.1: resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} @@ -2393,8 +2463,8 @@ packages: '@types/yargs-parser': 20.2.1 dev: true - /@types/yauzl/2.9.2: - resolution: {integrity: sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==} + /@types/yauzl/2.10.0: + resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: '@types/node': 16.11.4 @@ -3247,7 +3317,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001335 + caniuse-lite: 1.0.30001425 electron-to-chromium: 1.4.131 escalade: 3.1.1 node-releases: 2.0.4 @@ -3282,11 +3352,6 @@ packages: ieee754: 1.2.1 dev: true - /builtin-modules/3.2.0: - resolution: {integrity: sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==} - engines: {node: '>=6'} - dev: true - /builtin-modules/3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -3354,8 +3419,8 @@ packages: /camelize/1.0.0: resolution: {integrity: sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==} - /caniuse-lite/1.0.30001335: - resolution: {integrity: sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w==} + /caniuse-lite/1.0.30001425: + resolution: {integrity: sha512-/pzFv0OmNG6W0ym80P3NtapU0QEiDS3VuYAZMGoLLqiC7f6FJFe1MjpQDREGApeenD9wloeytmVDj+JLXPC6qw==} dev: true /ccount/2.0.1: @@ -4671,7 +4736,7 @@ packages: get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: - '@types/yauzl': 2.9.2 + '@types/yauzl': 2.10.0 transitivePeerDependencies: - supports-color dev: true @@ -5218,6 +5283,10 @@ packages: resolution: {integrity: sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==} dev: true + /idb/7.1.0: + resolution: {integrity: sha512-Wsk07aAxDsntgYJY4h0knZJuTxM73eQ4reRAO+Z1liOh8eMCJ/MoDS8fCui1vGT9mnjtl1sOu3I2i/W1swPYZg==} + dev: true + /ieee754/1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: true @@ -6110,6 +6179,10 @@ packages: resolution: {integrity: sha512-TYfxx36xfl52Rf1LU9HyWSLGPdYLL+SQ8/E/0yVyKG8wCCDaSrhPap0vEdlsZWRaS6tnKKLPGiEJGiREVC8kxQ==} dev: true + /json-schema/0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + dev: true + /json-stable-stringify-without-jsonify/1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -7589,6 +7662,11 @@ packages: engines: {node: '>=6'} dev: true + /pretty-bytes/6.0.0: + resolution: {integrity: sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==} + engines: {node: ^14.13.1 || >=16.0.0} + dev: true + /pretty-format/27.3.1: resolution: {integrity: sha512-DR/c+pvFc52nLimLROYjnXPtolawm+uWDxr4FjuLDLUn+ktWnSN851KoHwHzzqq6rfCOjkzN8FLgDrSub6UDuA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -8174,6 +8252,18 @@ packages: terser: 5.10.0 dev: true + /rollup-plugin-terser/7.0.2_rollup@2.79.1: + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + peerDependencies: + rollup: ^2.0.0 + dependencies: + '@babel/code-frame': 7.18.6 + jest-worker: 26.6.2 + rollup: 2.79.1 + serialize-javascript: 4.0.0 + terser: 5.10.0 + dev: true + /rollup/2.77.2: resolution: {integrity: sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==} engines: {node: '>=10.0.0'} @@ -8182,6 +8272,14 @@ packages: fsevents: 2.3.2 dev: true + /rollup/2.79.1: + resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /run-async/2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -8381,6 +8479,11 @@ packages: engines: {node: '>= 8'} dev: true + /source-map/0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + /source-map/0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -8715,7 +8818,7 @@ packages: dependencies: acorn: 8.8.0 commander: 2.20.3 - source-map: 0.7.3 + source-map: 0.7.4 source-map-support: 0.5.20 dev: true @@ -9194,6 +9297,23 @@ packages: vfile-message: 3.1.2 dev: false + /vite-plugin-pwa/0.13.1_vite@3.0.5: + resolution: {integrity: sha512-NR3dIa+o2hzlzo4lF4Gu0cYvoMjSw2DdRc6Epw1yjmCqWaGuN86WK9JqZie4arNlE1ZuWT3CLiMdiX5wcmmUmg==} + peerDependencies: + vite: ^3.1.0 + dependencies: + debug: 4.3.4 + fast-glob: 3.2.12 + pretty-bytes: 6.0.0 + rollup: 2.79.1 + vite: 3.0.5 + workbox-build: 6.5.4 + workbox-window: 6.5.4 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + dev: true + /vite/3.0.5: resolution: {integrity: sha512-bRvrt9Tw8EGW4jj64aYFTnVg134E8hgDxyl/eEHnxiGqYk7/pTPss6CWlurqPOUzqvEoZkZ58Ws+Iu8MB87iMA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -9216,7 +9336,7 @@ packages: esbuild: 0.14.54 postcss: 8.4.16 resolve: 1.22.1 - rollup: 2.77.2 + rollup: 2.79.1 optionalDependencies: fsevents: 2.3.2 dev: true @@ -9327,12 +9447,25 @@ packages: workbox-core: 6.4.1 dev: true + /workbox-background-sync/6.5.4: + resolution: {integrity: sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==} + dependencies: + idb: 7.1.0 + workbox-core: 6.5.4 + dev: true + /workbox-broadcast-update/6.4.1: resolution: {integrity: sha512-oz1WAEppIatucgIc49zXPsyQG6004eoKsyiJVGDyN94LIFpUDfGa+cykN32X0PaqOC9bdlj+4EjVBi0OuwkIHA==} dependencies: workbox-core: 6.4.1 dev: true + /workbox-broadcast-update/6.5.4: + resolution: {integrity: sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==} + dependencies: + workbox-core: 6.5.4 + dev: true + /workbox-build/6.4.1: resolution: {integrity: sha512-cvH74tEO8SrziFrCntZ/35B0uaMZFKG+gnk3vZmKLSUTab/6MlhL+UwYXf1sMV5SD/W/v7pnFKZbdAOAg5Ne2w==} engines: {node: '>=10.0.0'} @@ -9380,12 +9513,64 @@ packages: - supports-color dev: true + /workbox-build/6.5.4: + resolution: {integrity: sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==} + engines: {node: '>=10.0.0'} + dependencies: + '@apideck/better-ajv-errors': 0.3.6_ajv@8.8.0 + '@babel/core': 7.19.0 + '@babel/preset-env': 7.16.0_@babel+core@7.19.0 + '@babel/runtime': 7.19.0 + '@rollup/plugin-babel': 5.3.0_loko3dyoqyfrad2b2tv6icqcai + '@rollup/plugin-node-resolve': 11.2.1_rollup@2.79.1 + '@rollup/plugin-replace': 2.4.2_rollup@2.79.1 + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.8.0 + common-tags: 1.8.1 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 7.2.0 + lodash: 4.17.21 + pretty-bytes: 5.6.0 + rollup: 2.79.1 + rollup-plugin-terser: 7.0.2_rollup@2.79.1 + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 6.5.4 + workbox-broadcast-update: 6.5.4 + workbox-cacheable-response: 6.5.4 + workbox-core: 6.5.4 + workbox-expiration: 6.5.4 + workbox-google-analytics: 6.5.4 + workbox-navigation-preload: 6.5.4 + workbox-precaching: 6.5.4 + workbox-range-requests: 6.5.4 + workbox-recipes: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + workbox-streams: 6.5.4 + workbox-sw: 6.5.4 + workbox-window: 6.5.4 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + dev: true + /workbox-cacheable-response/6.4.1: resolution: {integrity: sha512-omXplP3miJhQwx+jfFnqO9xWgNc8CLG6EWRvTyc8R81cA/4zhqh87yj9UVH+fGUmuIXOUBPAuulSazXUsvKFWg==} dependencies: workbox-core: 6.4.1 dev: true + /workbox-cacheable-response/6.5.4: + resolution: {integrity: sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==} + dependencies: + workbox-core: 6.5.4 + dev: true + /workbox-cli/6.4.1: resolution: {integrity: sha512-br8uzjbL0I04Dhq17QlJy72RSKlv1o554bSUvuIYuR8EZmxHS6ZzXrYSorw/imZgBKZzmOVb2JL8zOe0f94dng==} engines: {node: '>=10.0.0'} @@ -9413,6 +9598,10 @@ packages: resolution: {integrity: sha512-5hosqpSK+48jHlj+5EHN5dtH1Ade4fqTe4+xX3U9wWK1SDaXEqXpVxdHuBqYfg75UE1PUINA0rhMZWTqeGoLFg==} dev: true + /workbox-core/6.5.4: + resolution: {integrity: sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==} + dev: true + /workbox-expiration/6.4.1: resolution: {integrity: sha512-N912AGhi95vhf2vebE3wPhnGjnR+t5W4yALDY7Pl6bcuhySNbwkkp2RjQcBB+dxrdiX2rOvavvdcf/q1LSnEyg==} dependencies: @@ -9420,6 +9609,13 @@ packages: workbox-core: 6.4.1 dev: true + /workbox-expiration/6.5.4: + resolution: {integrity: sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==} + dependencies: + idb: 7.1.0 + workbox-core: 6.5.4 + dev: true + /workbox-google-analytics/6.4.1: resolution: {integrity: sha512-L1JQISg1CxMAlqw3HXpWB2gRYsmJ9F9OgC2/UNAZLyOJTFk1faZziPS4eXe+UaHevZ+Ma66Z2zfYxPUTr5znjQ==} dependencies: @@ -9429,12 +9625,27 @@ packages: workbox-strategies: 6.4.1 dev: true + /workbox-google-analytics/6.5.4: + resolution: {integrity: sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==} + dependencies: + workbox-background-sync: 6.5.4 + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + /workbox-navigation-preload/6.4.1: resolution: {integrity: sha512-npgZYoeaE+teQvpWqZLgJDJ6I3qxwqAfnSIa8yrNQ2sLR1A88uWGGsiRzfUsIdKjVCLPQVZ+clwb6XU1vyW9Lw==} dependencies: workbox-core: 6.4.1 dev: true + /workbox-navigation-preload/6.5.4: + resolution: {integrity: sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==} + dependencies: + workbox-core: 6.5.4 + dev: true + /workbox-precaching/6.4.1: resolution: {integrity: sha512-Sq8d+/wfcXFjwuVwKe2VxD4QddRBgkO6pJVgpHbk5WFynR8dc8Zj3BlJ38e4nMlRuBZ8996TIgAmk/U6Rr5YHQ==} dependencies: @@ -9443,12 +9654,26 @@ packages: workbox-strategies: 6.4.1 dev: true + /workbox-precaching/6.5.4: + resolution: {integrity: sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==} + dependencies: + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + /workbox-range-requests/6.4.1: resolution: {integrity: sha512-X/asYHeuWIKg5Tk+dfmiEOo9hlkQ1K737dnENj8zL97kZDdcfokPT5CuXgM2xqX7NMoahONq1Eo2UoFfJNjZzg==} dependencies: workbox-core: 6.4.1 dev: true + /workbox-range-requests/6.5.4: + resolution: {integrity: sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==} + dependencies: + workbox-core: 6.5.4 + dev: true + /workbox-recipes/6.4.1: resolution: {integrity: sha512-Yu9tLmgD25NorZPO3FHJUii/Y2ghrx2jD2QKMaWBBplshw1MFokqlmr3Dz3O6NI8jBBUnK5Dtbl0+SCwVGSCqg==} dependencies: @@ -9460,18 +9685,41 @@ packages: workbox-strategies: 6.4.1 dev: true + /workbox-recipes/6.5.4: + resolution: {integrity: sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==} + dependencies: + workbox-cacheable-response: 6.5.4 + workbox-core: 6.5.4 + workbox-expiration: 6.5.4 + workbox-precaching: 6.5.4 + workbox-routing: 6.5.4 + workbox-strategies: 6.5.4 + dev: true + /workbox-routing/6.4.1: resolution: {integrity: sha512-FIy27mwM3WdDASOTMX10OZ8q3Un47ULeDtDrDAKfWYIP/oTF2xoA1/HtXpOjBlyg5VP/poPX5GDojXHXAXpfzQ==} dependencies: workbox-core: 6.4.1 dev: true + /workbox-routing/6.5.4: + resolution: {integrity: sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==} + dependencies: + workbox-core: 6.5.4 + dev: true + /workbox-strategies/6.4.1: resolution: {integrity: sha512-2UQ+7Siy4Z5QG2LebbVhDLmPG3M7bVo/tZqN4LNUGXS6fDlpbTTK6A3Hu0W8gCVwIX0tSg7U3mVhDntH4qt3Dg==} dependencies: workbox-core: 6.4.1 dev: true + /workbox-strategies/6.5.4: + resolution: {integrity: sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==} + dependencies: + workbox-core: 6.5.4 + dev: true + /workbox-streams/6.4.1: resolution: {integrity: sha512-0t3QKBml3Qi37JniDfEn0FfN4JRgMK6sEcjGxvmMGwlHAyKukZr0Gj58ax1o1KYGGJr72RDBK+YXI9Sk9cKifw==} dependencies: @@ -9479,10 +9727,21 @@ packages: workbox-routing: 6.4.1 dev: true + /workbox-streams/6.5.4: + resolution: {integrity: sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==} + dependencies: + workbox-core: 6.5.4 + workbox-routing: 6.5.4 + dev: true + /workbox-sw/6.4.1: resolution: {integrity: sha512-IJNYcNbjugMB9v+Yx7uswohjOaYoimw5dI0Gcaj2zrJHKjV0bom+BPRCdijmttN/3uVbX57jhNe8SMzWMj7fHw==} dev: true + /workbox-sw/6.5.4: + resolution: {integrity: sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==} + dev: true + /workbox-window/6.4.1: resolution: {integrity: sha512-v5G1U+NN0sHErvE9fzHRA75FrfRFj/0dihFnvno5yqHZZIb9G4U2AarodSDRBC3t6CsnLO68l1Bj1gsHqsM9Qw==} dependencies: @@ -9490,6 +9749,13 @@ packages: workbox-core: 6.4.1 dev: true + /workbox-window/6.5.4: + resolution: {integrity: sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==} + dependencies: + '@types/trusted-types': 2.0.2 + workbox-core: 6.5.4 + dev: true + /wrap-ansi/5.1.0: resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} engines: {node: '>=6'} diff --git a/react/src/useServerURL.ts b/react/src/useServerURL.ts index 0a1ee41b0..e361f6722 100644 --- a/react/src/useServerURL.ts +++ b/react/src/useServerURL.ts @@ -1,16 +1,7 @@ import { isValidURL } from '@tomic/lib'; import { useCallback } from 'react'; -import { isDev } from './helpers/isDev'; import { useLocalStorage, useStore } from './index'; -function fixDevUrl(url: string) { - if (isDev()) { - return url.replace('5173', '9883'); - } - - return url; -} - /** * A hook for using and adjusting the Server URL. Also saves to localStorage. If * the URL is wrong, an error is thrown using the store's handler @@ -20,7 +11,7 @@ export const useServerURL = (): [string, (serverUrl: string) => void] => { const store = useStore(); const [serverUrl, setServerUrl] = useLocalStorage( 'serverUrl', - store.getServerUrl() ?? fixDevUrl(window?.location.origin), + store.getServerUrl(), ); const set = useCallback(