From 4228c2e094d16c5fe3d208a517854e55783b2c67 Mon Sep 17 00:00:00 2001 From: Timur Khazamov Date: Mon, 13 Apr 2020 16:08:39 +0200 Subject: [PATCH] [POC] use serviceWorker from create-react-app (#1286) * Service worker with toast notifications * Update CSP to allow fetches from now.sh * Fixed clearing timers * rounded icon for pwa (#1301) * rounded icon for pwa * cirle pwa app icon * fix fonts caching * fix app * fix css import * Updated csp tp inlcude worker-src: self * add worker CSP rule * use square icon Co-authored-by: Timur Khazamov Co-authored-by: Faustino Kialungila Co-authored-by: kbariotis --- now.json | 2 +- public/apple-touch-icon.png | Bin 26297 -> 9221 bytes public/index.html | 19 +--- src/components/App.tsx | 1 - src/components/Dialog.scss | 2 +- src/components/ExportDialog.scss | 2 +- src/components/HintViewer.scss | 2 +- src/components/Modal.scss | 2 +- src/components/RoomDialog.scss | 2 +- src/components/TextInput.scss | 2 +- src/components/Toast.css | 33 ++++++ src/components/Toast.tsx | 97 ++++++++++++++++ src/{ => css}/_variables.scss | 0 public/fonts.css => src/css/fonts.scss | 4 +- src/{ => css}/styles.scss | 2 + src/css/theme.scss | 7 ++ {public => src/fonts}/Cascadia.ttf | Bin {public => src/fonts}/Cascadia.woff2 | Bin {public => src/fonts}/FG_Virgil.otf | Bin {public => src/fonts}/FG_Virgil.ttf | Bin {public => src/fonts}/FG_Virgil.woff2 | Bin src/index-node.ts | 5 +- src/index.tsx | 7 +- src/scene/export.ts | 4 +- src/serviceWorker.tsx | 146 +++++++++++++++++++++++++ 25 files changed, 308 insertions(+), 31 deletions(-) create mode 100644 src/components/Toast.css create mode 100644 src/components/Toast.tsx rename src/{ => css}/_variables.scss (100%) rename public/fonts.css => src/css/fonts.scss (74%) rename src/{ => css}/styles.scss (99%) create mode 100644 src/css/theme.scss rename {public => src/fonts}/Cascadia.ttf (100%) rename {public => src/fonts}/Cascadia.woff2 (100%) rename {public => src/fonts}/FG_Virgil.otf (100%) rename {public => src/fonts}/FG_Virgil.ttf (100%) rename {public => src/fonts}/FG_Virgil.woff2 (100%) create mode 100644 src/serviceWorker.tsx diff --git a/now.json b/now.json index fae86942..fa3a3204 100644 --- a/now.json +++ b/now.json @@ -21,7 +21,7 @@ }, { "key": "Content-Security-Policy", - "value": "default-src https: data: 'unsafe-inline'; connect-src https://*.excalidraw.com wss://excalidraw-socket.herokuapp.com https://excalidraw-socket.herokuapp.com https://sentry.io" + "value": "default-src https: data: 'unsafe-inline'; connect-src https://*.excalidraw.com https://*.excalidraw.now.sh wss://excalidraw-socket.herokuapp.com https://excalidraw-socket.herokuapp.com https://sentry.io;" } ] } diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index 518c8962efec26bd6a567fb6562bf49a32b4d56b..e228a73235ec6ac9beb694458008bb2d48b5efca 100644 GIT binary patch literal 9221 zcmW++cRbYpAAjF)_DZrhNk+=bKC6t3Z%SsAon&TnCxsA^R5oSrd6{>LjBK*UDYI}y z&OE>K`|CcBdwm|S&+GkszTU6*YrJ2Y7~Q0&;iLfo(Cg`H-v$6eeuMxj1o_9>yU+;$ zexRqVaVOyZhD~sx^S!5}lvgfg8^ZNd>bGuvVzx9$g~v@4Z@+pLsbp4s!!cL$1tNn( z?7g-Ci-;Psp+QGGrG)|dfk|X8^L3vWpB!j)@ii3IOsZhBqK{5v4sST<~v7>f3VvP%LNB0 z(LP;O15)xl*k?tbmY{a#`xsB>TmGRiCnOGZQyL%h0@iuRA;$Kyc6?5 z(3z9gIDZt&kUfEbG94#c> zB02vS`v~qjeFzusCa@L~JXMWPo}1PI$h&t3ybz?5UA2Kr9}qDk;2kwRlXaF=<6~fQ z_s7>q#>NS!pJ!sEN%`S)oclD+a5$X#;6DVZ3rg1B!imFz>yn)}xr|4U4>)T2|L=jez5q)Ye4CkL5 z=BEauZK@2%?_Qb2w@m#92rLwZJ;t`v){~+sGi+Hx$_N5S*c&vEg zcbsi6I!Z-u7^4nNt-U%}KmRT+84Ifk#|byKtHQ%MsdC-B|C@ zwmO%Es?A`#i$_EPdcij9D z`0rhbYy&qKq@SCetJ16)VHDk^^-W)qlWfW%HQMwW%|n967}BaT!E|2$rf{AGUme~A z8wlO>QJ{4&UL3a{hM5~B_}$=dmzfPN%QrgU5?Kk>HTU9 zmkY{S6IbZpK9D|hTo%JC>^&26f65_dv+YtJcZ z_h_7%{T4`ZeHolvD~FViG|Um-1VkDg(8C-5ggrC9FD%%3*fYy@2Av|B_|MT{fH;Co zyT_}%#0Q0@$W09Rab$(XM~5^rp579<@22L zQ+$09&XoOZiCO+Rqm&J9_`cMsA07;ZS%{m!#$&rE(iW$vz*CM2V|PIZ z6kk3h$ef5|Y*8&ZE*d+s`E;KWnj$`^6ET_DV6Hva5FgA2>_n(b7789~Ar7wZH;g^8 z?EXXchb4yBk=(Jbyu~V;^y9t;OM=lK!I+V*MsBH`i0ci{LPKkfJO-BZ3oSlWsiodJ zTyN4@eCy6_1<5X-MwPtX9FsG1NkGjWgxYNWDk_6PHtnSS62)B{z4m-`|n=u-7v(K-gW!~Fo8$E3UA_yOv z_k%Sa^Q}L^1u(MjzuhZ~rB3GlKDfU}J-jc8uF+mJvfbkc=8LxD?Ky(HDvoT$OA0AI zR()vpp&(dz)tLSviA&pIV5nan>@%Th-^3`GI_1<(nU92_kn%s!Tt7IedTZZ2Ju3D1 zQG*Vi?IZ$Z#y=uG@H6mG=%`m??fJ9fs5>q#D0I(&Uf~_iHmLG;rC&S*bFCq7 zjgSAQ!jw0EWFF0IOruh&tdDBIjNTZ`AAn9q2ewnI&-dM2G8?Ighld(M~fb( zLO8S{>tWgmsE|@3sYD^VtH#n9{ClY$bSm>N@JiIfX*}`IuG^z_p2hite*ZoA2^BDW zkjQQT2{V|!>I8!r8YJX*v}dPrU2bwB2b>cjbV9sSl^xW*?&#r4U(?z4;idiT?S=6E z+5Ym9*)^u7`@-7N}sb!rj{xZ8PlXVC#3`4HwxbLoS>ef#})m ze*~EJ+uFBvBCyy(E!5iZvIJ&4Q%FT(`MrzKUJpS;*D!o?{dGd z+?LSMoZ8-Dx&6ES(9Hy<>;?Q0p7fy3fbv~o0BSbvQSrvW_0MDe1}}Z)vpz&k9i7^^ zulL9G9{K;U99{AI38a9u#ACLOgu$b|y}j~SrR#Y1*WZV(|8ufNGoM<+vgS7sq6hQ6 z8kJ3yKD*JXDtwm&iRB=>xVRI7U*K~SlEtlIgA48(0rqW<;)=!t%6!JVKjKC9e3=id zVc;g1;4u-pt6%Yb?a4^W+(IaUPl!J7Q+LYoA*-PGCkPNfEE;8Hf9X-*lEMIpWxiOGv>1B^QIVV9$~q*X0zB53*^9-J3w7UN4NZR z#m&<6R?(WoXjl`l4ywry6VTkJLW0inV9O1NvP!HwX!~HG@20l z%8et63jBpX_kD9eg$692hz|ZoVA;L{1&oNt&6+OPvXU)KP~y_Q*RIq=|2J8%MVhg( z%YJ(RpQ>Z{c)393XM!kbrou&if8(Bd?f{1Fd}4ndh}~7LYQZ+uxrBENwcs_OI1wsA^G{k*sL^U3_;TF;A)@IchQwjuwYl=idq#TAV4viHKm^cS)ph z!5z|Nt#x-e8UvdsV#?L@ThJy;^L$6&&&{jfemrOEj&6wpTMU4$BvDqgM36nbxO!V5 zVWGdJW0w?`muK#Oxp($PIN^vLf|dj3w7AqH#EfaU!F3Yn$kB-KX42)mFGTXnO_V-x zQ2{>qR?@IYMpDS8nO$!%&>zyfMIcn1yaR>Q8;7h=Tqi_DU0`Ig##{C0%yF zS+)|#8T#|299wZxN3>9!A?UZociud4@c-S@1ZMR${^Jl8-V8?dr6q)TUM28#R>IL% zAe}Ah*t_U&^({o@#PWMuyEznZXd2jhdY?-rVL)s}e8w8B6~*wt&8>uc;k>X{i~4|4 z8Q3xe3BUK7Rmc8=ZSMV;sfXG#ny7C(mtReew-Nho4@Wma(l5OI z4t?3w0DPut;&{@1V1+)gf#FCf1nvJ^wN|z*UuwWQfueJd6!voh|cG|azwVWo!7Uy$v!9fWnyWaHX9X|$TNKwlvx9^1_x1vN5aOcrz+@KWw=gwy< zo+5tw1a-`3y&|ozfIrufOl^^9^?PE%#DCY4{2+@3NR5dm5G@3_5$Q6c;?#VKE zfuD%-KJHR~lmA}DQL+_52LyjlWXT)<_Cb=5{*yi|T4Qr9t#35C7kU>i7Cj`N={`=}q^UX29pQIPcI92)K zi;h513wCc^FiFo@@9b}jlO7y>n%F0J8Zlv1j7HcEFb!QI$-XPqC;An&s6WlkFh4)^ zsYZyrz@blJt|ek{sA=%2SZ}nTU*h^FdLWpJOYl`Ft;LMgs~sYK9;kYRIRKK`&MB!bw532xmjAgNO%UvN*|cM84!8zlvIG8|<3R zW&eu+)VG)|5y|Hd7y?mFOU#9`mQ--b?VDOPzg*ct-1x!`noYR}eP2cB0ePLaf7d*s zhL7B!y*FSJure|Zx`T?xJxB+Oo*_F_Nm7LAPSU_@BBl%r&nOq7g`wkPICt1G7va{aP>nM{zWwocKtiU-01=Y4=dD5CF`5= z2*ATCw|5mIg{1ZZL7`^i-p(Ro%_zO9gev)ruSW>*vXotk`;#jEFeJuyIgRjbk0c@y ze7NdDJwR4kTaBgbddrBvC!SAJ+jgmTacDVl(6`3X5xgF(&5`qu_kk!jtN%#XP>r6cf7=28o%tbQ8|-bd{n~Bbd<7Zg1@4(i2bhug8Ugj zmY4xS-qdkbTjpi>IW{r$@n)L^vhc^V)S9wZ`{V8KIN0ev`?i^F`?pL=$9$6hMcmF!Or&kn{;OQ zXST9H_wjK(+FJjrEaPHrAgce-@nqBz^i!?3!hHmo-TkyT+Nj&W`9|*f?Eut`V7WpU|nebi1e{a&cA|_#d8pAl_zGX#27_gh?@t>*cA?;YL zhfvbnNWF}NAO+o;ECP0)hwhymgp3%epX~lnKRCUe&!90&L}vHhak1TALuC5Q9ejp&DkN5cd8@pTdjl-`{##}YeddAYb^*`70(utN#nhB4#G7D4HS9*STAdPgjxxY;*D74#5C*(&Pcv@CgrfTJ_wM4aXgqam# zqS1a-z|VNX5KK^`X9wE9sYyt^M2hj{6S*sgR63;TpPXV&LW#W?_G$!3yhW~pO-n4*c6A)sO73{Q7MRDh`;+6P#jHB-_miL}r>=a*uI-Fvm;;4A$xZ?A3u_OU zioVj5!;Q3wm(Beh;!k@Dh}H`=u`DN?tL{{lPyp)COpqE``gyuYp58N)TH zzeP&!uuV00%vNg=CDo8Ef4ctU#n2HmJT-(ADc9ovtBugGlKBH0=ymC|@7w8ewnvyh zvtm7wkZOK@il!E(VI%Q=nFEII3VNb^p5;whK9O;Wb@#)!s8)4iS=ZI4>T*+LrII%) z`g8-VT+4IT6{)5#pSlF0dNwiTbJ)Ovd79BAkHXFMmU%ZE;MeeQ(MMOU-;;4kn~%ds z;lkaPA}ATl`}dD-qQtzXdvk>e^MzycG!8d}vEMDj$V4n++A@(Glq!_njp^Zoqv=_> zOE%LWzk*nBi)3b3x-kg7rKreE2WX@}U=y%nZk6tykCrDBY>t{9{lh?`(7F(()I4%q zi{kwczQMHqUJUEc(aZnJ)=rA_ay* zCi<69GCEH)Bl}gXT{xEME68S&X$$&55xnyU6V;m^j(KHW zGx_N2DmmrZOdDJ5i?F10edu&jS2Q}i7ELYt;=S*=|HGP3E+**4SODU*1suA za8<^|I|GwQ9y1RAoPVw2nPZKU>gg=vWYWh@Z-_IbVs|*s+)w|R6v$pWvF8(eVp?Q- zp!+A7=IVlQhcvj<{`gP*(xu!KqG;J*4j&0s7NhBY`JX@U!yWPVTAkQ4oCD<{=oE}~ zr)URy-SnW4X{6Wk%nN#(p4dGKIyJHM`uOZT7|h$$5V&aZ{Qs~vY((0|6fB)6%-&9h z0&6flH7wEo{LT3@NNei)fNTqD+6F4-44z1#MPN&W5Ck}IuT07&`#n$E?BaF%(3xW*estrPwK{%l73~J?V2XDNo z@$<}fd2N6FkT7)fp-0Q2X^pR+OQ42v$k|DtAD#yXd~vnV^bdX4E%p%LgW-7n-ndc{ z*I$yM-PL;!Zn>+7(~Q`h0rug3okz&~-u*<8`+c1i81lqU;+ zPvm{p0>9W;x))x}UcQ+orP=3_sq=r)@_RO~=k*t~WMa7iei>?h&Ey+{3tPmZY1sNR zZUF*cjQAn|{>%2=$b&g2K*5u`(V^*`+VaLyj-FP;A}I=5t1%r&d@C|tU=hsQMI2-sJV!@lhvJ3)4YpdpN3?`U?-g%4F#?medAZ6#8 z8FI^^f3ouIOzq*G!@8MVC7UG2B*&;OBE3>-&JKj7ou_lM%;&JiCX#GmKilPn#yQ`1 zKu!hWL5w0Km$G0jHx$%hJcSlq%1VnYrHHh_PG>e5lk=0rU`?y33bQC;TV4Wjpn-mM zXY6tq=VW?~5IJ?7{3H^5Is@=?CNpfG1=HNGE}rGXN_9lOr0-ci)MubVCOG^WT|_=o9w(!;WosNp_puu0cV6QTV(nyc$9 z>kq!PeN%j)!`3oxJ5v#Y^M(jMZRK^KC#MJ{e?Gd;p}II#+ea8B82*t+X6Y#*HvbmQ zMC!lr+tlSBr9;r=YWGGN_|)hSbu$;fANd;|xF7X$i?mr=%*1Hhqa~hkuxXIp;MoT) zD-@^moMMfgT_Sd}k$2yM__6_l`|)-Os=XR_0j66rIyjbPR^F6#yR^zblM6_&@w=by zHcZkXElvdp6<1BP$l&&P6d3^j28RaFlTigzgrOPqe;?`=3L1CMsrxg%K9*$5b|b{> za6|Cpz$iv5%;L`UlLb{zJnoXteiMyl_H8KmEb>K?&F+o@$L=)<-Wl+!thxug-h}0p zsh5TShQ%)X*=(|-OB5tSEimil5GXDIsMH>b?>Xqga`0JWQ(0aw@?Ys4Qg>(k-$X)y zAef=Y*ET=AmDR*|Jo49xTn*2O572|!<|XLjpHn#60~&BgjX8fn@%(YxGvXBi1d?Os z57`F(jR1ZS!h^GLo$QZIBjgqQJegfnkj&+?k%+JI#UA-xbCqQ!+v;T zQ?amVFkhf7zogJUzmiBx#wgo^Fc_Q(JXz2?|8%8dhbK})+^4*qSvke9Hh_P8clyUd zj#gA6lV?2wDQ*blLF$HX z3GHiNcF_odnm5TJ}6KQDHFK~oMeeLX5LPk1K_^Gu+yvOzoWoT~o6A(Ilf z6vr+X<@@cz@>XmpxoVNs(gSu4zyk4YnhLxF0X`S@Se*5ZJ051yjlFVxQdD8Cr*4x1 z6g^g-8ayw6;;zc4bvL~`2c+OzIUhm>7uAGxdOXr@>o?|3tG;6jCfa*v2xsE1Tm&i* zT;Fy8`_fa+Ez_`0Dbd46)2a;VJ_uw(nthw4#*fxUbNr66Fi?TwY-o@7*R$4x?OxeE zoKLt+#>f4~jNRzH1eQ>eZw~>8(m#cQ7iS z=FG)XV|Vxj|F%0Ja5i8QVQ%mVqCiw#@x^@bd&d;$55Xmc=lZuZ#&*SW0Xtr>11S|5 zL{AU-5CX|Xfp@N(T0o!|32GkpiMeZ<}fW@7KOb zZ%3`=1cX&=Ap|?e=L}If z?Z0$IR$v-zIdea~_Dx^Rd2K!XaD zDw#mJwpPV8vbRI%zvrQmE?ZWES2Q}C5<2ebEPBynGDMK7umA`;L72s{Elv|dedWPL z5MPU}2c&;8zBc4mH4T;Ry_!7Ks6{9t(Ah!%qT_{Rb*LLu^-Cr9q8 z#Q-&2Gx)@SCg|dC{j>>d@-%4-id3bo{Hl?5r|6id7=c#bUx7dFndQIO-~qXApuM>P z1-(=bPT6(OIqlGKZi~M<69_+*mLNXs6IZ4CFs`Pgl;<`Ah_hDylG=i*0eU%{JR764 zb8g~~)V?888vZqkkw6{ih4;(~c}(#$S;=4gq~Bfd&Fuu5<}j<^Q{^LoD}g)~msU6j zll5HeO1vB2JpEp{{prqmB(C}x!LB+delNDmKx=Wl5AY)JW}u+s&G$@dq`2Awxy;+>+ZjXjRBNRL z6Y)q$OP((io&jOJ*C4}cu-!N3C3PXuzx>TZAuwtXPTWDPtY_7DLZ<4eeFoqXSZr5( zU`w=hrCxP8z^&u1KqfFRvrkl85t_`QAO0%iM3B$z}g=>41h$;UH&1X#d6 zyT-arDE)J9io;Cd-H#{r%j1|4WQ?fxq5y1uHqej)JYw31qfhK9s)QW|qw89gJLVzPST0!$)s5fEz{{5jw^q)SK3@TnWm8xcaH z$fH+O{-Iz}6;!dUd*zp_$Av^A%HYV#cpC7EJV+<|BE*cX+AJC|RZKUDA9#}eM{!@r z;a4E3D(NBwP*RZ^5YQ4?l`-!af{#4Tfv+D?UteDunU(t_IO!q1I>T|DV{_23^-;iW za)hLC9{v6dHGItUQSLqrj!y2-{$`*ePD3a!I4Yx1u@7#s(R%#t?bdaG2W>EJ z{s1R^5%w!1NJ07;Jl(MkX7}PxY?R?ufY&!8-!}Q)|8Txu1$SFN@P9-Ggwm zC8n{i7A0Le2UMi-JTHt3QFKgq`aIeNUC?sZ)ELniH`HBik^+=bE~x{CKFuTFBPl{6 z@e&Knz{o$$==Mk)^ELX?aojAJ;bYiO(^+g!KChq%%nN3Ui_PEQD4wgAU6EOMaWN-L xJvFK7)|d?D%+sXI=11xO(i~=?>e5j!W~wAo+R^(9k+ser3qo=3m`yJ=_0{DWlQ&Urn+1Xh} zOG^vm%Z{^`uw9bJw43`4Gm>He*BnGSXlT?mWhdpKRW!Iu5xm67}u^{`^G;mE{;K^QvcNW zii!#bn4`J5nURo?@XZ!~bb{*YYR2c!pBcawb#-;$booc^4|o8786F<~#txK$4S`MQ zbo#%90Q?Q~1%3qV4s_Yp*2d`S>iUM~UrhkU0nEVX8yXrIy}iAR($dmz^Zl!_%h~pMwp-e)SFBuf5ZQKgV^K2|F$}alUVq{!w-m`-w2Me?DTs&{MEnZ=jSt` zqN2X}^zJKCd;LcN0CF>R&8h=-e(d!h#UJn{5D4F<13Z{m z*!i*7e-wW(kG;M9x10x*nL1$S$FvjZ^P>s)K68F%nUz1#35bb5lkj-_Z_@*Pu<|b` zDEQ4TzjgY-1)z6U9Wd@# zxVyXm6u!(!fy^L>XA-*(ezyJ1&CQvte|xj)9O&R@5m;-iyqJ7|O~3<)wfwE&2mfL9 zKh~*P_0C#m?ejk^1NoV2o_#J>xqns+273#u@3CV5zR0AXp@V-V7tjH~2fe1$dY^em1|KH9z}(z(4hWH1LlG{`Y7A+!Mk-xj+u^^V>Zk{M!pV z{jB)&&%A%X2EZN$a{K?py$s}JAol@z!GD?zAa`NS>Hd>s06J&I&h+B{1R1`EomB>K zr}3+;2c~Z4(mNA7^9`^A8Nirdg#hk9b4CI({LDLrKdTq8UccuP-#h=XpBw)y&mY+z zVE>-)u%0DZc>?_3lbN;O&ng4ntmj%_190|br@8KiK%ei)#XkOL)|vOCOdjlY(EdIA z-?JBUuD{|9)*!nLSnI!s|7YI`{}K6t53%0UgE8zj_@4fM1^)qf&CJYx*9LRp2eJQG z@E?F5tXbCka?l2Bz>F<(;Rm@LyPtt^e-{BCVvRi@7J*y>&|LVz_wVfs-v<4~e*hkK zzXvt}Hkb?l-yQqsQqNpA0D1ph_`%x!mGU1T4_J@vSbrWMkR@$n`7X2iTZ#2Vi49LPA1w+5ofz8Gj`L_}TLr zfRQzi19*WAz}ooL2>8le`2fJn%;T7tL0{J2dztNj*Bk6n?715&mbq-ezP4HW|J7v> zPkt{R0voWeS7vVdS7V)vABgwtzR%u|-9B^4#6IS)sssK1t9UXOUUr%OD!lA`KAyVBLa!`S*N^wg0bJ2D)cmzds8je4hJ<{?Wic8u&*8|7hSJ4g8~l ze>CvFN(1N3O^x{HNz8+V<~K3c-v<7*GOQlt`^23OVCl!BvvSh*N}*EuL&pJ zoR7djDF_+&8u){9>9Dnz3?A9F3Xf72Ltv^o)wOd>QO3}LS-#n+zLelhvlOYXSL z!Xjvk4t=uw{j)^%kp(*TSY#OVJoN?qh1#KA={yVnhd9Ah3Rzk^VEXvvM2A~sG3`qU z*>x0q*x{Zn41B%Yj6H!i4!Pap7q1iOCK*P`hF}Rs#VZXR4EWavpLt{-1l@;LR1fsG zyd?+AxAs>tXq5L~I!d_DFf^B>NXZt|>-Ke1NA-!q)dC%Bs8pqHMt^3Ct5${cY}WGU zvY6^pys35aSG}IW4aq7<9Pw)LQGt%}1=4(rn*GFjbuN80#y4uog|?oIfLai$UpRt# zhTUR@KX59au3GSboKB|?Y)TKRS=4iB1gqmEZPmEJGAe_G|m69I(7Rhmk6@Uh5Q9)7x0AbU-B|7&`*Fj_VPqH zl!;hWVlUw+Ehc95A*1GQU53QX6j{VA5wX3+C2lyPq0+f^fw>68mzRi#Nha@+5VV@P zl-rkYeRdrNe{#Rdc-DDq(mi8H#OARYT{>{`+Fl-i%V*J$jCc%D@cEi_cR2@-M{Bkv zYsd2X#|%s7#`|83;~qp8+<>-w?cBMuB!{~q6Jzo904e#3!CB>8iLsVL%ex!YP=^bi zW(-#Eq3z>4T_C+@wT$CofmJ;H*VJ#$nzU!40k%#oVs?@K1Mxsi@!t59Avuw-f z-xe!if-NmPsgR5kYCCwD)LIvrU6pl_f6pzsrfvaY7rr5`C3YmqAXQAr;iJ-JI7nL3 zXq(W51G4o-VjEvU;xgg6-1nSMz36kXa4wKpoE`fP@kWN`zZ8MG^XTHRkwnmHkIj+( zg~s|jAyqxM2)&}=K3UlyBKI?0oH|E_{Hs;giN07J@pntagEDWLSjS0~CAv2xo|ZpL zvL$xv>+jdfvyKwCD7WN6V2H9!s)1tnOxs=uFRR*Z!VtiYEv5KdMjp~zx7eq=YY2gZ z>{{;grq0*dsa&#teO7u11|@kNePL1>nY}n7;LGef@rOqbO7h1@au7}ZjUHU);Oh7A zaep{2!E+na(&Zr2ATf~bgg{Di?1Emtv~k$=vT4X#uwLm>@r1V6y_TZ^`4xTD5~9^H zjyJT097M6!=XXwjy-zW`E~V|Sp(%Py#;1MbZrjx>B3L@;Vo9MAvx_X-Cr=edCk>r@pKy6! z#fEnD>|^a`w{3))1n<%-4L-JSP*t6EtmgMa2#Fum+qL3$PavQ1b;Wf{<~a_)tCr|w zb4PVD`$L+d)C4O2Na}c^9c0v$w-PcMzCWLnqLPVwL#5>Nm_QLnk&&TM ziT(DdmwffY^Sl{xcF|6{la54)#{n{$62(+kiCrg0p2YN=ImUB?E`&vBl$`&}RbQiW zV9mSG*cCFu;;(Ia-e_qo7KuW2y%qCa-ef(i{O}M(rNKc|q%>7821;6FV+cLJep1#{ zVnYRi{H*Q}zHg$;kNVZ(gWL(kVQ<1yMTd>THx}rsZs~wSdBNiKt1=@X<)eKzcc{U` z-Q1RiIYYILZu`J)vfk5ZVJu=FPBOA+(a_ZB0iy=mg$rZ!D5dxX%`-Dv+&D-_ zF*2sdvMb{_BHd_Wt+}GYlVoYE=hm8GixqS{QsJDtBK`!TK$h}$dpec-gonw>g5H~Y zRq_{5UBSH;F*xGp5J;v%C(PcL@L4`;QcAZkcq@9S1fgEDHibv<$XBi|$4e?bNu;u> zoT6I_T6`_5^a=1T_r;H2O|3cx7482$2Syq&@s`szS( z@HtdhXkXrHA)>!I=@QCZrL3FOJj!9LKVNM)0EyvGA^q2?+_wN*(e|lsaY|Ds+0)%L zmbZfN^t2u_q{eG#ASd6xitErJd8H_P;4Jz?+bmo>8VenyHWqR4|q+mtCyzO92`efW2B zE$3#GH^^m`om>%z5_)x96R8o;soOr|C-}T(()r;-vM|~g&ybJf4MGlAc=kQFm@ZRv z2&!omdUt=%F8n1PD2F~2YPjVXS``^md!k82J2-x4wQ9z&kspB+^r(7&XYoM{-eI)g z6`y)@`Sr9J`r8qk3~}yF#t?^we^n0+ib|;6ZO=cR{(6F*aAe^1SkdZe2-#ty92Tt; zEs~|S<<44TN;JWjQE($sWDD~0+04(^aYQX62%Db}K!}))*E;Fq;knR(fAePkWIK=;wFdp7W3OL=xr z1nZP-pMV7vNoK(MR;R+C^p(%{5A^K4LyhMM%_^eu8fXR;4(^3r>2yNhyZa}DLB2wC zGHnwi>a0_a${#=Rs#9G5V?RyzT#V50dP4GuU+wMI5mhAmu(%@atZ@HU{Kz%IZC_-p z{0J2Ssg~#CBJF98H{l%E7m0C~P5wLL!cMZEW311WOc!r%ZTMDX~fQ7fe&5ddDxVqaHj9^Eu9;8VOK)ekq5XOgbFeP5OGw(>}260 zHQ!F@Q|re2xZI+#7`(85qneEi(%07{>+4MIsEw0om& zjgxAPYHHj9g>5&KRW)^ayVqr0iQ;BpVAH$GUL4NK)Mgj<)*Vls$nuD7l34<;`5|F? zV~>_veud8QV*_R~ofE5gQjv7kz|SXQdFonyb%pMBZ=ytw_(=3!r1rf^@yE0WE=5>A z0ATOC=b&l{;{&hp71Wy>8fW}YsBoR_i$98ftGYuMyLIMW`a+3WIj5}a9ou5Krn#JT z-@Lq&mj0?T;K?shb;p#j4^wc4m%az^X9Mv;}CBv_V==bOt-ayyW z5KpSi*LFyiix}wfW#6+JBPz|ph;-JVc;)(?0XX6XC6}WDMbiz@1`o!ZcwtHL(c|f# zozd>ghSZYYeto&wb%~fxP+l5``q@oU8RXGm4WSpGGDr$YE?shEK+dMx{5&|0o0oCs zz9H_mfW|%@s*(sG;Gbq!JrQiz^TH9&;(gxjR|?8oDFD^Fy{1whM0m`?@p;lobA|A= zN?Q>$DMT5+?}0;_NL-p?aPL(V#<#tKc5 ziT%~(y)DhWeK8N3R9^>YRBlIgXNxYn5Ete|4B2pBvaE80s}Zr8*iqP#G@t7x|BW8* zjYS8}7S`5}P_vN_&|2g=r>#o6@2y1ln{0>^NVW-vWQkthB4mD9gI$47j6+49p$qnN z>~~G6NGC77=vIf%jL^=#Wn7u^crEOkSz+tL?EM%C23hZED+|tlAx%^1tKZoV?5$J5@EOs;m5`)XY1o zu3s~wP`QWosWivgCfQg6$oH^$Cy6u;dkM<%7Qb*EHXh>6*4mZaIjTOXND-`k+?8UQ zAmna`xn7w<9)trJ`hhKJyj3-Dt7eu&Skv~BH6xLq9;ltzVltqQYznzUGlhFF23zwv z8rJC9uj`DuWF~%~vhrhT-OR>Y2J>9ThC99CWJ6cXg~Hu^9v9S_Z=d+SSR}zMZ$yHU z;uVEb!vsi~K)PQ}oz?az6b!RJ$y;ve6kf6C1n(QwKoyQN!-GSMi_C<>mg?neZjTT| zg8g~xvO|SWyT0hOjO<#~eIXVrvSKV4t)*0=_qoeTOj}0mYTR*^0>vxY;Jg!bltb`P z#k_J$IX=u19X@G-RqKQ18K>RHLa9btUZ}m7iWdleBu7A0Ek4~uVPcPx*rly7am7vCp=Z6aNNII4KQ zSW$h==)v>3ePungchy@Q=~=lAS)z?41CeBteFGeb6NpnL>XepIt2C{giH;NyZEtT_LbH-7;jnf_1oOgX}3nK38elxd*GwQ{9p1JFZCF90> zI;EXoL~oLMJEkD-d=cK26lzIwC2J?P8*V60&C0B-LK(Iio#5^Xn6;HgVDJG?jU6WV*zPqREpg)p9!_4nypvy?5-bJZ)_tue{q0qdZJE&m7z@ z+;)ENu!j)5Xt55PADa3Ydmm`@-c){DvCs0j#d2vB_CCUD@xFDFWm0e#-(94`FDFOAEEw|)SHBf(J}gN z=}Mz=NluR|EwGw*1@CNo_Yy0I(Dx=->iO-nTWLlSd{Y!S>1rUUL9!Hu=dddmJ;Z_7 z$1%L;Qao=ezgo&;;nwnLy7jG=_`&%6(KarGE4Sl#J$Dk^|L3n zo`tZI_72-A(gp?&ySuHuSko}ZX>-;wJ>$@(z>8EPA4+G1pW(sUiKVxXT zAo=x~)??NUE}hbyEz?Sf%?EnmO9t zQj4Y<*%yX3xYCof(m5$PfbfO({(N2uRJPNU=VD{fI!o0QG#Ok!>y{Ae5JC5H9fZsc#-wvjSlWB%?+*(ZnY6+c>e z_v1lfzAzoX_f{zExChpKYv?q2Gjizh@j`u<0hO1ql10lDZ>1kgOM2rVS`B4=ScbtH z6lC1YN_qc8Q;|YGJ)Gvh4q0;m1DPL;H-^FsN>20`_!07to!wU7vm3(TmnAq)nLy5y zN1wcVFjM6-TBn%vIWkclmDIDF2$`#2z#)#HZsFk5X{*j~_@S!oHJnOl{4LIEES4L& zIr&CWIYc{VTdv%x3bGkC=0<vHSA`^|+V|<&-s+96d9$@$NvqFoeXxD0 z)@|rYg)iZiMavabLGkOx(P-$&N?Tmb2)+K3bNW3>r0dz$MFwYcz2~Zdwki#%$+$R(HuADAi54f(fj&)&K(k3*a{tsvou4!K_5$*&E38M$fHG#o;i>u~dK zxb^LLm*gwEdsI?Xqg;)&PT*5m&Jg&hBJ{bvlW@4!%t0hnzj3Q>7<3`^Z4xYk_`PQF zyqYQnieTCHMS-UAcb+ZwSW0XR>bQe|&o?Le4);5knTx;~X3Qc~@Un@4fdTJi;cDRv zH>KgN-Q9+laNUaLV<4hyzU6{5N|k48G4Dy&Z8vXx(OXb617AcFEIZ)q-}`6!qm(kA z!I!{yd|s2Uu0I*kJGPu`(RE&MXxc{-8bnt5PkG32tN6)QqpKIaz)|2!3EAu+`s9kO zz7E3Is*+4sT(V!K6Q-?#Mv5D6%dw8w++u)4AEce6bcUz<&eqHG>6@;203RAC>ztAJ z#To+URdOcK^UE%R{KKlR7l(WLc=kan{mCI0a|sR7BgENj_}hB zd+kW&2HMLc*c3hiFNZ+TP<=i4-#(T zr$>kASdnF-YP!f5*&>LZ#X(upJlK1ef=5iCjT@{2YHOFvLMjxM0}IOc-MYD}H_;hY zT?~2E$RieXDTPb#-B7N>wIIV*@CxUS+?XAP>J?#|EwjDBO#^2*wuI*EbGV1j-yu>4- z%Bm#bbbmml?=kO%vd~;f^pWN*IEeeLY&oM$X-bRpHg3JR_0=bj2c6NgnTj*cea~qv zF9S98_Pn76Q%OZCoQ_i$+)p3|is3v1aw+d4bsMCNI7_R}T6I>*_1B|`r-__`y-(m1 z&?RC+M~V(Xxyp5#&rSZmP{`8V5+81C(%2W{_1tNNz}{saP{>(3;(W-!-mo-fC#?oO z6v0R^p)2DDQ$nio*m44|-MqV=)&(Kbrc4UhDx96F8f5+H{m zwdH$JQ8o$_^=Rxu`|ZAgO^(Msx*E&N9yGMnMK0gFMc+4Ios>D^ROk*}WY*g;@A}&( zYb^{pE``1f*Zh1sYH*3F@)B;7`^!FwFFXUEXDu?~PY=L%?))3o| z8@cl6o?Dxj!Qt3r^I(*;MK$~rRD^{t++_#1n*a1ze<9ze3*`hxWvJ_mLh}RkAb6@E zUC5Vv3E%sd#??1h-PpKB*UB|Hbh{DqU{jBZeWhghJtywB8BzGPfs&pNj?~A>n~m(0 zS^?)+S*Safr*)O1h^F!IZ!sg-&#iSTj}fbtXQe{$?Z_vu&_N4EI@50~rpcWuHSrt` z-z1W=R>@!Dm3oDxoYj&Hl1w!ffr=T#yx6`+I^ezMBB9#jDPv<|yt<38v@fYvhLb$E zHEHYNEM7}u7QO)p58a)`82$kst|zFO}W(pl7<;j~Isx|9_*uJ|q8 z6J#G7#W-aUTA0d{nCf{t;uB`kcJj~Mk3H%V!v}Are|QvHo3k^YORjOx{A;6oFm>uu z8@$~WmBSfm19dnWzgzvN06Ua`zUusBMehV28Di@&>qF*v;u`K%KRxUjfNJB+iky-O z$3hiWP#r2QX3>F#JURZ`zuqiCnX}Ha@pFPy&lD1{r7s5uGiuK?~%P>aD3oJ4_W%>>U6pYSqzI3Rj$M zwW1*_4Jc{(IAp7Z!a6V?AxFrs47*kLfiUxHw<{rJ@@s3xz`iz246 z(DhSejSq9q47>=OnA%y>N_&PQeof5EB_7&5oVp&u5RG&$HC;*_PSHEWXf=Um+-hUj z6tv8H8cket*_cGYe6;I?Zv>rh=c(LTv-My!(O}_f`nWemj+@6pMXA@^Gz#x4%1GUe zHGx#bYaFg6>81{^hscHK%K55VU*h9pUI=!oU!I;W$^X=%BC~bHtf7j0#p>MW6_q%m zr`!AU(;07w9G%)p_Y&8yJdLY&`zpwysDT+Oo!^pTHHdod{|(MP-D#;N_SZTC`t z<8EZ|oxDjR$=gd^yLpuK;DnW^I3j6>XWS01cZevcxfj-9Y0};OThCXc;Zv_+)DTLz zR1!{vHL5BL;fYie`*hQ7lY_>vICV + + @@ -64,24 +66,9 @@ - - - ; + onCloseRequest: (id: number) => void; +}) { + return ( + + {[...props.toasts.entries()].map(([id, toast]) => ( + +
+
{toast}
+ +
+
+ ))} +
+ ); +} + +let toastsRootNode: HTMLDivElement; +function getToastsRootNode() { + return toastsRootNode || (toastsRootNode = initToastsRootNode()); +} + +function initToastsRootNode() { + const div = window.document.createElement("div"); + document.body.appendChild(div); + div.className = "Toast__container"; + return div; +} + +function renderToasts( + toasts: Map, + onCloseRequest: (id: number) => void, +) { + render( + , + getToastsRootNode(), + ); +} + +let incrementalId = 0; +function getToastId() { + return incrementalId++; +} + +class ToastManager { + private toasts = new Map(); + private timers = new Map(); + + public push(message: React.ReactNode, shiftAfterMs: number) { + const id = getToastId(); + this.toasts.set(id, message); + if (isFinite(shiftAfterMs)) { + const handle = window.setTimeout(() => this.pop(id), shiftAfterMs); + this.timers.set(id, handle); + } + this.render(); + } + + private pop = (id: number) => { + const handle = this.timers.get(id); + if (handle) { + window.clearTimeout(handle); + this.timers.delete(id); + } + this.toasts.delete(id); + this.render(); + }; + + private render() { + renderToasts(this.toasts, this.pop); + } +} + +let toastManagerInstance: ToastManager; +function getToastManager(): ToastManager { + return toastManagerInstance ?? (toastManagerInstance = new ToastManager()); +} + +export function push(message: React.ReactNode, manualClose = false) { + const toastManager = getToastManager(); + toastManager.push(message, manualClose ? Infinity : TOAST_TIMEOUT); +} diff --git a/src/_variables.scss b/src/css/_variables.scss similarity index 100% rename from src/_variables.scss rename to src/css/_variables.scss diff --git a/public/fonts.css b/src/css/fonts.scss similarity index 74% rename from public/fonts.css rename to src/css/fonts.scss index 165bf6a5..87b5339e 100644 --- a/public/fonts.css +++ b/src/css/fonts.scss @@ -1,13 +1,13 @@ /* http://www.eaglefonts.com/fg-virgil-ttf-131249.htm */ @font-face { font-family: "Virgil"; - src: url("FG_Virgil.woff2"); + src: url("../fonts/FG_Virgil.woff2"); font-display: swap; } /* https://github.com/microsoft/cascadia-code */ @font-face { font-family: "Cascadia"; - src: url("Cascadia.woff2"); + src: url("../fonts/Cascadia.woff2"); font-display: swap; } diff --git a/src/styles.scss b/src/css/styles.scss similarity index 99% rename from src/styles.scss rename to src/css/styles.scss index b5bb79b8..dfa40772 100644 --- a/src/styles.scss +++ b/src/css/styles.scss @@ -1,4 +1,6 @@ @import "./_variables"; +@import "./theme"; +@import "./fonts"; :root { --sat: env(safe-area-inset-top); diff --git a/src/css/theme.scss b/src/css/theme.scss new file mode 100644 index 00000000..8e8107ee --- /dev/null +++ b/src/css/theme.scss @@ -0,0 +1,7 @@ +:root { + --text-color-primary: #343a40; + --bg-color-main: #fff; + --shadow-island: 0 1px 5px rgba(0, 0, 0, 0.15); + --border-radius-m: 4px; + --space-factor: 0.25rem; +} diff --git a/public/Cascadia.ttf b/src/fonts/Cascadia.ttf similarity index 100% rename from public/Cascadia.ttf rename to src/fonts/Cascadia.ttf diff --git a/public/Cascadia.woff2 b/src/fonts/Cascadia.woff2 similarity index 100% rename from public/Cascadia.woff2 rename to src/fonts/Cascadia.woff2 diff --git a/public/FG_Virgil.otf b/src/fonts/FG_Virgil.otf similarity index 100% rename from public/FG_Virgil.otf rename to src/fonts/FG_Virgil.otf diff --git a/public/FG_Virgil.ttf b/src/fonts/FG_Virgil.ttf similarity index 100% rename from public/FG_Virgil.ttf rename to src/fonts/FG_Virgil.ttf diff --git a/public/FG_Virgil.woff2 b/src/fonts/FG_Virgil.woff2 similarity index 100% rename from public/FG_Virgil.woff2 rename to src/fonts/FG_Virgil.woff2 diff --git a/src/index-node.ts b/src/index-node.ts index 29c83b4f..d830d8a1 100644 --- a/src/index-node.ts +++ b/src/index-node.ts @@ -54,8 +54,9 @@ const elements = [ }, ]; -registerFont("./public/FG_Virgil.ttf", { family: "Virgil" }); -registerFont("./public/Cascadia.ttf", { family: "Cascadia" }); +registerFont("./static/fonts/FG_Virgil.ttf", { family: "Virgil" }); +registerFont("./static/fonts/Cascadia.ttf", { family: "Cascadia" }); + const canvas = exportToCanvas( elements as any, getDefaultAppState(), diff --git a/src/index.tsx b/src/index.tsx index 9cbe023d..22692fc1 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,10 +2,13 @@ import React from "react"; import ReactDOM from "react-dom"; import * as Sentry from "@sentry/browser"; import * as SentryIntegrations from "@sentry/integrations"; + import { TopErrorBoundary } from "./components/TopErrorBoundary"; import { IsMobileProvider } from "./is-mobile"; import App from "./components/App"; -import "./styles.scss"; +import { register as registerServiceWorker } from "./serviceWorker"; + +import "./css/styles.scss"; const SentryEnvHostnameMap: { [key: string]: string } = { "excalidraw.com": "production", @@ -52,3 +55,5 @@ ReactDOM.render( , rootElement, ); + +registerServiceWorker(); diff --git a/src/scene/export.ts b/src/scene/export.ts index 311a2518..37178508 100644 --- a/src/scene/export.ts +++ b/src/scene/export.ts @@ -94,11 +94,11 @@ export function exportToSvg( diff --git a/src/serviceWorker.tsx b/src/serviceWorker.tsx new file mode 100644 index 00000000..8e1a6231 --- /dev/null +++ b/src/serviceWorker.tsx @@ -0,0 +1,146 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +import { push } from "./components/Toast"; + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === "localhost" || + // [::1] is the IPv6 localhost address. + window.location.hostname === "[::1]" || + // 127.0.0.0/8 are considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/, + ), +); + +type Config = { + onSuccess?: (registration: ServiceWorkerRegistration) => void; + onUpdate?: (registration: ServiceWorkerRegistration) => void; +}; + +export function register(config?: Config) { + if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener("load", () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.info( + "This web app is being served cache-first by a service " + + "worker. To learn more, visit https://bit.ly/CRA-PWA", + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl: string, config?: Config) { + navigator.serviceWorker + .register(swUrl) + .then((registration) => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === "installed") { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + push( + "New content is available and will be used when all " + + "tabs for this page are closed.", + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + push("Content is cached for offline use."); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch((error) => { + console.error("Error during service worker registration:", error); + }); +} + +function checkValidServiceWorker(swUrl: string, config?: Config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl, { + headers: { "Service-Worker": "script" }, + }) + .then((response) => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get("content-type"); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf("javascript") === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then((registration) => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + push("No internet connection found. App is running in offline mode."); + }); +} + +export function unregister() { + if ("serviceWorker" in navigator) { + navigator.serviceWorker.ready + .then((registration) => { + registration.unregister(); + }) + .catch((error) => { + console.error(error.message); + }); + } +}