From b70473ed60f64d97c0758f864043b850a5728770 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 20 Jun 2022 00:33:46 +0900 Subject: [PATCH] feat: Add Badge Image to Push Notification (#8012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix * nanka iroiro * wip * wip * fix lint * fix loginId * fix * refactor * refactor * remove follow action * clean up * Revert "remove follow action" This reverts commit defbb416480905af2150d1c92f10d8e1d1288c0a. * Revert "clean up" This reverts commit f94919cb9cff41e274044fc69c56ad36a33974f2. * remove fetch specification * renoteの条件追加 * apiFetch => cli * bypass fetch? * fix * refactor: use path alias * temp: add submodule * remove submodule * enhane: unison-reloadに指定したパスに移動できるように * null * null * feat: ログインするアカウントのIDをクエリ文字列で指定する機能 * null * await? * rename * rename * Update read.ts * merge * get-note-summary * fix * swパッケージに * add missing packages * fix getNoteSummary * add webpack-cli * :v: * remove plugins * sw-inject分離したがテストしてない * fix notification.vue * remove a blank line * disconnect intersection observer * disconnect2 * fix notification.vue * remove a blank line * disconnect intersection observer * disconnect2 * fix * :v: * clean up config * typesを戻した * backend/src/web/index.ts * notification-badges * add scripts * change create-notification.ts * Update packages/client/src/components/notification.vue Co-authored-by: Acid Chicken (硫酸鶏) * disconnect * oops * Failed to load the script unexpectedly回避 sw.jsとlib.tsを分離してみた * truncate notification * Update packages/client/src/ui/_common_/common.vue Co-authored-by: syuilo * clean up * clean up * refactor * キャッシュ対策 * Truncate push notification message * fix * クライアントがあったらストリームに接続しているということなので通知しない判定の位置を修正 * components/drive-file-thumbnail.vue * components/drive-select-dialog.vue * components/drive-window.vue * merge * fix * Service Workerのビルドにesbuildを使うようにする * return createEmptyNotification() * fix * fix * i18n.ts * update * :v: * remove ts-loader * fix * fix * enhance: Service Workerを常に登録するように * pollEnded * pollEnded * URLをsw.jsに戻す * clean up * fix lint * changelog * alpha-test * also with twemoji * add isMimeImage function * catch * Colour => Color * char2file => char2filePath * Update autocomplete.vue * remove clone? Co-authored-by: Acid Chicken (硫酸鶏) Co-authored-by: syuilo --- CHANGELOG.md | 1 + .../assets/notification-badges/LICENSE | 5 ++ .../backend/assets/notification-badges/at.png | Bin 0 -> 1752 bytes .../assets/notification-badges/check.png | Bin 0 -> 577 bytes .../clipboard-check-solid.png | Bin 0 -> 1402 bytes .../assets/notification-badges/clock.png | Bin 0 -> 1131 bytes .../assets/notification-badges/comments.png | Bin 0 -> 1134 bytes .../notification-badges/id-card-alt.png | Bin 0 -> 844 bytes .../assets/notification-badges/null.png | Bin 0 -> 174 bytes .../assets/notification-badges/plus.png | Bin 0 -> 507 bytes .../assets/notification-badges/poll-h.png | Bin 0 -> 689 bytes .../notification-badges/quote-right.png | Bin 0 -> 772 bytes .../assets/notification-badges/reply.png | Bin 0 -> 930 bytes .../assets/notification-badges/retweet.png | Bin 0 -> 798 bytes .../assets/notification-badges/user-plus.png | Bin 0 -> 991 bytes packages/backend/src/misc/is-mime-image.ts | 8 +++ .../backend/src/server/proxy/proxy-media.ts | 49 ++++++++++++++-- packages/backend/src/server/web/index.ts | 44 ++++++++++++++ .../client/src/components/autocomplete.vue | 12 +--- .../client/src/components/global/emoji.vue | 9 +-- packages/client/src/scripts/twemoji-base.ts | 11 ++++ .../sw/src/scripts/create-notification.ts | 54 +++++++++++++++++- packages/sw/src/scripts/twemoji-base.ts | 12 ++++ packages/sw/src/scripts/url.ts | 13 +++++ 24 files changed, 197 insertions(+), 21 deletions(-) create mode 100644 packages/backend/assets/notification-badges/LICENSE create mode 100644 packages/backend/assets/notification-badges/at.png create mode 100644 packages/backend/assets/notification-badges/check.png create mode 100644 packages/backend/assets/notification-badges/clipboard-check-solid.png create mode 100644 packages/backend/assets/notification-badges/clock.png create mode 100644 packages/backend/assets/notification-badges/comments.png create mode 100644 packages/backend/assets/notification-badges/id-card-alt.png create mode 100644 packages/backend/assets/notification-badges/null.png create mode 100644 packages/backend/assets/notification-badges/plus.png create mode 100644 packages/backend/assets/notification-badges/poll-h.png create mode 100644 packages/backend/assets/notification-badges/quote-right.png create mode 100644 packages/backend/assets/notification-badges/reply.png create mode 100644 packages/backend/assets/notification-badges/retweet.png create mode 100644 packages/backend/assets/notification-badges/user-plus.png create mode 100644 packages/backend/src/misc/is-mime-image.ts create mode 100644 packages/sw/src/scripts/twemoji-base.ts create mode 100644 packages/sw/src/scripts/url.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5853b982a7..01b179f13f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ You should also include the user name that made the change. - Server: Add rate limit to i/notifications @tamaina - Client: Improve files page of control panel @syuilo - Improve player detection in URL preview @mei23 +- Add Badge Image to Push Notification #8012 @tamaina ### Bugfixes - Server: Fix GenerateVideoThumbnail failed @mei23 diff --git a/packages/backend/assets/notification-badges/LICENSE b/packages/backend/assets/notification-badges/LICENSE new file mode 100644 index 0000000000..841c4c682b --- /dev/null +++ b/packages/backend/assets/notification-badges/LICENSE @@ -0,0 +1,5 @@ +Font Awesome Icons +------------------------- + +Ⓒ Font Awesome +CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/) diff --git a/packages/backend/assets/notification-badges/at.png b/packages/backend/assets/notification-badges/at.png new file mode 100644 index 0000000000000000000000000000000000000000..d1492856de2b2f837a24a439eaa13631283aa87a GIT binary patch literal 1752 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^Ts=6kw0hE&XX8=JfFwT?iY zii?v<2$N7Bi^>&8l@KSD7*3B478RF6JMB3xoA`44b`+}mb>__;7 zqpMmJgE@n}a3sTb$(t|lpLK~hxTqkpf%yaf0fq#Y52sb^Kfe4>dPXg(MWbwjr1kVg z&Ux%F$|6lTGLs!Do1a@8nHKWRYX@V+gBc#rm#khl=hgE=4|cW1FJ^bjS|i8XWRxyF z#oJb^)TMdDhHF9}E&Eomna3m-URCSo(o%0e-}y`T)s~*6z5ZU7)g}*|vYneJD4#bw zC7JT{+2n{2{+ zUCHGI^V4o7?h75Jjn7VnhJY`N${+uL;tG5y?E&r4spP<%z~l+(T!^TO5{ zE-;k-m&v7U_^%_N_6kR)dia{{B^PA_9GSD(h15HOC#uNvU5L++w#oOH@pAv#PZHS^ zEEYcIyVW$m%I3+je(B|RZVPU-4a_=LIM<*_Qe|&~!#cr7iF=!K?iN-1uu`(ea?~na?0hV?>pNbO0-%X?Xz*8a4G7E*#p+h*~t=okMsIJ@~EB{ z>?>jrxTaHTUHGc5|IZ8k!oGl6+q$^?HoVI_BjbGF&e5fgu@mK2`o6f5{psW1jZ2&xpN{*t6cjxh*g1H+NCXvoO0k8auh3PPt|~arx~em#-;# z`*%Of?lN<>&uUn^%=EB;-twmbO21^CQjX;xwt6u)SYLDb@~Z|v_NDS={X75mL#EsQ z+lC&Ue_it$9iL`T<5}_3Uj1)|YrV~p^R3e^7mGdM$of@xTJFk;lZGAFc$u@xuWI!C z=Dual7!vz-664k8>Yag?&iQf8;_S6$Pt9?E@>l49uJevEcZJ^7vQM0QB&}LMY8v-f zMO{1l|As-!?Pe3Elk5`Q8Efoy6Kz%dePc@}b|$8P_Wk4}BKhDp)a9fshpJU0<&V9yj9Up{Zl7p9rN71!0s}Gcgdl``ib@P=l{NY zGL(0 ztD-YSd6R_O3@+F=`7B)^os#V{{lVI*gB_b!EI;x1=anb2iV<6Q?U&ar>~8$CclX_I zR>w2`YAtWL^{zh8GS0hy&82ICGnXyPu4iP9Yu#jCCfmI8?t`VJYBAQ80)F2tGEb=g zxLTv^*X(9+OKYCtgu4$yKhF)|eY|LHGO~&kbbg;kep&>N1aRa7Re>q9uWDd(IzF4xaDa_*~FpbAnuJ zwnkyK<(_gv~8!@cHDO{Ghf=dHJr`^3UuF7@2zn&9pkS0o=E zsLns<_xSDEuEIr$m-Zk3{M;|(lQrw4WwX?Kk_CEhJ^uRjc1Fie!7UdQ&V2G%E@8ng zn_~Hin>FC_kvSo+9_{DLy5<0@_F}eO^BZqDFRgDqxKB?`ytL`r%=ZnCEs--D*O0J)q!scwpe_7bN%sF_sb8z zw%4#t;pSSMw9)AYw|oEe`^Pi3)vfn%I~&CMaizK9oMjWe7H=ze{QhE+^d^~%yEpB- zi~R5E^-90mt!}lbPhMK--~v8(i!1*pYkS5Nx*a+Et>5gA5U*36?PsR-huNQ0@JdXn zttygx$@h!>k9@nljqIa?<~xpmzP#-8y{Z+gx=qPC^ZK{Cb@A_zvZ=rJD*KK8w~z8k Y5|z!K({?yAFfcH9y85}Sb4q9e0JT~}umAu6 literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/check.png b/packages/backend/assets/notification-badges/check.png new file mode 100644 index 0000000000000000000000000000000000000000..baeb76babfa3f9f8dff025f7a007a0be3eeb69b8 GIT binary patch literal 577 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^S>I6Pe(Ln`LHy%X=t>?qRu zQF)=zgoYMvMotSyC)E%pp$QEvDj`mlH}0{Y__W=)xa?cq{*=SgbAR$NI(euvKtR3d zq`pb>*2}+NyIk{$!b_Exn?A|(KG%A<;Hh?u0 z$h*dEM_~7lo)7-IkDq_G`NAgrTF`!ed*k;RLFa$eX#aA2|3lpKdE)k&XF7g}d*(0J zGnpLt@u%aSq?A9Ke{{cOU;NmfZ|7B&j(nFIE>u2fmTU52Id~9E0^;!68sJARZY;^kbs&(rdQS$4}F*>k7A jT6*m&mg9iLV8&b7=XV$y?gdTR2@>#h^>bP0l+XkKlL!*` literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/clipboard-check-solid.png b/packages/backend/assets/notification-badges/clipboard-check-solid.png new file mode 100644 index 0000000000000000000000000000000000000000..d8cdfa9da4562d7a247ab7c734933be48bc68fc6 GIT binary patch literal 1402 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^SB9X(wfLn`9l&TTBb>>%Lk z$$p{nvf~PdV8(?^llYuiCvb6yJ_uouP0&)1eZaSX`Nn+9vl8x?-p0=@sh)LrrZoGf z)Vcp|@3lTTt?1jDwH11I_fGldCslWE$vgQcqHo)`%-v$YR_0oyvTW;3`6aQckBjf_ z6F&HyMRobwU%$l|a*i%s@AcUKY>oE+k84*Qo;kOgyF#~H_%aOiKuj&J3k>LmJNTHP6Ly{Y$*?0vJ(LpS~1 z&h1(sI2eTAFBeo#{IS+V$F%>k*T0Xd&JSwOmsw_P{&!EALC3Oilc`9uT9S2+y-38Q zq|8@pHcUS(jpiiWIGAE3He-)DGgHWUZu58E?lw#k#s%uN=X{q)>@(>3dNhkM=V?Z~ z=V5_{H?xZ#nM_+SOEHD#z?CQL2b#C+-BO*o;)JK+R;FFqCeD)PD~oNVvyCjw+o!w{ z7Gs(*`HUee!?Ze!h?GB*49jZO%{k9s^jBg1)v{{AVJ?Q5|6`>TbYv|OQtrM>IevSJ zKrwH9^6`}`&)j>vmf@L9`c?-+d6`KP&#rs4J>Hk~Y`OaJG{%6%XSN1py?$}}z8FJs z%<`4FF)RNjN1V}}{QvfJ)&n;>gH|$Ueyx46^8i!A8<&-#kG;ND3o_Jue=S@3-dWXD z)vt}U;p3TwHcO8%I)rYXvV4jK0}I1c=BchBLBWq!Fo;H-3igs=U}`wkcuGlYrDkDB z1DEboO;tVyMu(>kse)6lOtH{%Xr1xI*yktZdIC)okbuv3J1Xu<{ zx~Q6}_HhL)Tne&9fnkNr3Y|rhW(lp|!1O_}>Sg|=zU`CZogD^=|l0!{9hn} zsSFJduV4BQdg!qRYvZRWLhBh0O!z6=Ata#TuUYt*q3GH4CWlI|PHDCUru$h~P6d12 z(O|gthmR?*a<7q2rl!AUVKc*;XXX2%_pyVVBF$`YwMJ&$(>i02Qh^4cXOCIVemWok z;&C(tJxk_0aMvLxU|!H8Nrox=L;`NtM7UIXb#gP`IQjeG&OH80c}se27dTIDl{=Hq zc3|$8FJIW6ul*yRSG%=E{PS#v3+Z*~yI7mlKi_4RFuu=paQ4r2j2UOr6)H^b>oOdx z;bmF>Y&Q$T=5tFQPVL=id2sVjJ;n#k{hIxp)@=-*&-$x*ekp9rwmo@CMPTce5 zw)-FMFw6FSG0o(@ZNpN<12t!4XE;ye%8FZhIZTf>_3)#6%>As48`G2 zx}RrDH1(}c?DJZYSy#;974DS#c{WP`R}=f^*{lYlP2!(tvn8}{@LVmsr-~yh(Ycd* zeN)?+B(=2sXXf#YvLd`J{m<63IpkS<%KEp8fyePf&c9a-B?1NRryXyFN!j^)GS3-+Wqr|f@pT;X5aMfVv`1$)i;CSHFe-Qp|XC+!;EU|BH+Rk@6X?-G)oes!M^ zW7r|rtkqHR>VQ)H3EtgX?>O!&OR%yeJ75h%f@<3$ zsrqx*q>p))zwu$bAemUZrrTUu_wUWqZW7;hx5wMQThGF<=ls$w=9ji<%TG%%k+PH7 hHN7qL{E{E+>)pi*ZdKl$%fP_E;OXk;vd$@?2>{Mba6AA2 literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/clock.png b/packages/backend/assets/notification-badges/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..9323f8f3070dd4ef538f7df33d54975c449d7cf6 GIT binary patch literal 1131 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 JWt~$(698yKb8i3u literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/null.png b/packages/backend/assets/notification-badges/null.png new file mode 100644 index 0000000000000000000000000000000000000000..be1384df1318b7714fe9625efb9520f6274f7a65 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^RWI14-?iy0XB4ude`@%$Aj z3=9mCC9V-A!TD(=<%vb94CUqJdYO6I#mR{Use1WE>9gP2NHH)lSbMrShE&XXd(e=P ofq~)Bg0KF$9PtYn2!n>}3=Qll-Pf^zADDvbS>G{_wd`efxbGzxk$ieS4g5Ja~=z#wK5NNF8}p7 z^7y;|%6-4Gq!NDW7>CBzJzf5PM`o3Z5&wpYX_-|jPWp3mS6$>zpPlvP(9LC1dkeO$ zbkm;$75dEJoj!9}zVmx#oq4HVPv0sh{p8fxTQO~C=)Hgbk_kWk=L^Z&3+`5QV;k|f9<&z9jkoMUZV6$;FS}kdjsbAXD{sc{ht`!Uj2n#0~APt{VsXS z^XK|>?(_eD{J&^n)NAF5LhQDE7aq-ZS6V6O?lDQ_=~r&+Z+0)AHz>% literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/poll-h.png b/packages/backend/assets/notification-badges/poll-h.png new file mode 100644 index 0000000000000000000000000000000000000000..3b7ded66597679a8d19ef9eccc09834fa7a4f222 GIT binary patch literal 689 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^S>Dm+~rLn`LHo$FZmI6%Nf zx=DefNl}NzIf5fGfuk{jqj7@((?J0iMN5zNHK(>l+tyE-XMc75t61Z|%Y~13%@IoI zF+3)b{O`OzgE_-{1~UeEG5OcV03YEcQM+2hZx8GquM*G8Gi4UE&ZT(@8n(P583t07T#y= zXF9lBLd!vR*KCtl2ktHjWPhjDYgJ&Ld8Ki;=VHD)zEY(R@-D4lE}!fsyJNOVWV1Pg z{*PtWuIks4r&Zlt%C*C_jX!83%d9mAvr~R3cpGG&W(rv${*I|j)4GA{>!DZ&!<w zg>gH$HI}_$04b@H{&jTvi4Tr7ie;acdqpt(aXZV;cNuO0h8rfE{!oA4k-Aaoe^irN z_6J>~JsdMtFQo9sDIe~h@Kc>v>gI!_9Snwg4n5ogkC~3_U@*KazQL_ceBsgC0Y_yw z>O{qKoQ*hCekXGANu2|10a(44!Yj8^dE26A%kM1HdLSlQx5$~(B(nMXi4Ve}_BqzR zjFBtEUofeKmp#zA!d=2RS=+im>=o;-hLcleD|&hCA8udBoOY+@W9y9t7lpL`pT8c$ zdV>MOADCe&yUtGA^Ktu+CqEU+`48|Ho;LYnu-&`yK`f8$3EmsCgWKodwch(xU+2T; zBW`#4WceN*zjHMjDFDSYA_}eiC YY&yB1Z|}lc3=9kmp00i_>zopr0K=U#M*si- literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/quote-right.png b/packages/backend/assets/notification-badges/quote-right.png new file mode 100644 index 0000000000000000000000000000000000000000..0fa483765481dd43d88a470c45614c6a782c748b GIT binary patch literal 772 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^S>PJ6mIhE&XXJ2$cLwu6A1 zJx5alN1Fmin*c|m0+p5ZTH@z_s*8~uVHk#9(ido^GW;uc^~g8?`C$n zzTj)LYSg=_Zv+iuZELFk<>pz-zX?~^r6+r0&+P7}fy+yMWjprFzWMD2_tmS9i=TX+ z@<-=DiLI5u#|VaH+EGdGBRap`y(_9afxDq+YR3H-hLl6BMT`?R3ca1p-QcssL65;G zY*B47;{;Ce6RZx$ytd@)F&I5$+R1PtqVwBXRtL2h1#t#R?Th<%GMs4Pe!`@Xta{5_ zoMC1`V+=#e8kgVi-erAsYwci7*u^pZ!&DuH%U3_E-fA>Rc(W_#kgcqx>5rAM|5=o?ST45;nl`;W)FE}S zB+;ceVVA)oH-lJ-K;;>FJu3u##NDoNEaS$mOdxi-*ChV@KxK8uUB|o@mFEX4FII>R zR=s#L`U=Og*MXi}zV*iRH@#R@dHvAI;42)9R)6CS_Rfob$+#?goyN|bhqetbey!2? zxiq=_+*;4?vCTDle>>`aIhOQ(+9_3e@t3#1$YQHEQrqfnUj05Skj3q0^S9*o*M(*C zEk4>TcD=%}Nc+>Pr1OjAFNXeJzOQtToj2QM(aI?m|GN5Kc2!ND{ast$C;Up_qrbc4 zFN-SPe6&V=+3KxNVw55omaSg;B=)V}j>KJ2Uh}+Exet_Vov>%+-<4@T)3-k<3E3dv zGkyAlMPF@vL}uvmAB^0%e7b5t_}R-F_doe@2e^R_g4g&)NgQu&X%Q~lo FCIBl+W;6f* literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/reply.png b/packages/backend/assets/notification-badges/reply.png new file mode 100644 index 0000000000000000000000000000000000000000..77021f71a7af666a33db652aaaf997508200c769 GIT binary patch literal 930 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^S>Gd*1#Ln`LHo$EVwje$Vx zdKMLhg{m%0LK7O8RkpBzQHzJep)*P!%+{;Vo$sY};P1JeyCXwhwRx#F$~6c$aVWOD zxWqVvVGm;jgFeIJG>7s<&E7xGv+osoyo;58hva|FpY3T|U+DEQZa6sq&-}mn9R|XO zCe?Rv7%AF(G;x@~ccAIVlgkW@HFNoRHGDjlJ2P`BPOOodaH3&{UppIz(CxHK42o6^ z{wGxDvAImR{qjNsj}?RVDV6zbHIAz;FJ$DDIH9-AM1kq0Upt#iz@p0w85y6*tazHy zw0n`vL$ivs>&fB!k6#m&ELEP8W4tu=dxZE7>8*n2HpH2!eqC|#<&|sP>!$E!{5fg3 zV*8D(tY+iXd&lpvm|D*jTl%ZY=hW4Qp<545U2UhnaA8KNReIpX<&S*k_HEONnJVzO zYpx&f-iKBzb!Pmj(Y+XXf8rjiJ-hD}Otzdc@1*{s2a}GlZl7~ZfH{Y`F)lLRcLC$m zr@HRm%#3ld(l*vF_BArRU+`6*nW6mCa@hy6Pwtx9OL2U7^=aCo!z{NQ7KC4WWXw>| zUihAkY456icC74o7EHY?lf)44|LWx38n0IS-|LzhDF2Z0_7$dC$vg}(?MH%xEmdo4 z*4i zH^~=tR^Jr=ReUtT+sNU0NX0~sdm&ug*tXtpk6OyK{-Wa5CyiFwN285i$fa7V@IE^s zk(|1eS?}l4C1S^fgV%Na6}TFt5aG*qqJQ}c#)dzSSNDFYSk>cQ^lVGR(UUw3Pu5vQ zA8;`Hxh?EL_^*R%Czm^H;r;9O&M{8AXM%CUTIpTNUsr7wwK^gBVU5gh`yjqgQBGNh zXZ{Uz=i3-QS68uA=uUXwZ+o6YOk2G^{yp0B=Sg^MLv4%9le1YDYu%FS7l-|~fB1Sr zT*mu!78M?Yt<8LiP1@$tG2Lbh_da3%$z~EPTl~!_BW8!omN=mrCgSX8esmxGTP8!msmB#Ggd}P5;FG*!Tgb+I|bR`O&@?@AIkF qx$OulzH@8idG!_nCyvf%;s@sYvC5uyjAvkAVDNPHb6Mw<&;$T4ou>@| literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/retweet.png b/packages/backend/assets/notification-badges/retweet.png new file mode 100644 index 0000000000000000000000000000000000000000..dc6106048107a8459bd6efc921835d9bf2e25d9b GIT binary patch literal 798 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^S>o_e}ChE&XXJ3DvdCj)`j zeijvlg+f{jg(fsGtHf|lZ2Nn|bG?r~N;e^UY>T|0ISM0VfVc zWI~d+;aaueeGldbdWDOlj62R%UP0D9tJlF&ibIKXl(d!xkm=$ z7lycrwz}CrqN5Fi4)ssG@My+C{Z0dQ_a9N5JAX~pxV-(woQK(BZ)8_3t7G}>k|?;( z<s`NYvM7yc)1O#i_^vjCQT5Ptj|!2f!wXra@ca)}NfCY^xX;Du{g!2mm~0MD z_jti@=Uf_rLsh;mGO~ zVVt{ZdY2UImWca>e`6~B)->#mJ1LYUTyX4P*>_b=LnbN5_3p16wsKb3pVZ`a({>R0 z7czZHW0vrS@8K_IvGg+d{!l)|UTm_Jv%*2{OiWYA0m)xybHOSuIEkO*_}Nqb;;{UL zAC57z85CkZMRAI4-E!KcH{^i-!(EBh(cRlybRIGnF~(iZUvt;nY1@a!$J`Pd|Hkg# z(z50ub18>KSIe7+%wIVqHvTO=AJydf!y%R_&U$l%rvbP83hx6Qf9IC(RGrbr|AN1) z;+n!7&FL(kKd76054Z6ac3ahw^pN@C@{;*G5?0STxACaVukNV>4$Q=b3; literal 0 HcmV?d00001 diff --git a/packages/backend/assets/notification-badges/user-plus.png b/packages/backend/assets/notification-badges/user-plus.png new file mode 100644 index 0000000000000000000000000000000000000000..9d376d04d666f856fc927a82cf78ddadc4629e8b GIT binary patch literal 991 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_vCmwCE4hE&XXJ2!jdY6pRq zXeOZv4J{rPLK6hoJvvxaTtIvll_&oqwmCgzio7xRE$5l=tD!#&Cm9=`-B+R0kSNgx zLvD-@_#QAWV7|aCq5hR&9>cAd`Cfh<&zCb?co6&NI$!DK@?Mq-WImu$&4Ze%$t@7&VSs!(b3QN%oP1Q6Yuk^uWtKO*JXdA zU-Etm^IsRn?fVjDeQdH;;MsAL?>5hRH;eGEA`i+#=dWEd#V5jgR#f{|p&gp)w;Ps* z6?uAn=-$69NGJQK!FAdDB_~Tgx$7QY{~~fGTG#&r_x@#m2KievB6!xloW%T_q3+?? z36itKTD&JMIabAQ*kH2q!ktv!rpoX}!#ga=5lgsvdKShRyua|WPFdoxQvZPi5?QRD z^O%xPi9WAJt@bdY+rTIX!WE zno~#Bj^?E{lU}fC@n}An(`80b5^O< zb}CnX8fU$Kll}Twhal&?1(&v^b2_Ky2nH|uxa{Sfti`KmbO>Fu+H-1!m)au6{Q^-} z+^!jMGXH43p!6&C$_33kQ$vo4n24RV{$Q=PPt?Yj0fo>aESrc#f66vLZ7Lm$ym3IB)lR)_$!$egWo(4R~P4 c@3?)8-#RseRp+u97#J8lUHx3vIVCg!0PtSKlmGw# literal 0 HcmV?d00001 diff --git a/packages/backend/src/misc/is-mime-image.ts b/packages/backend/src/misc/is-mime-image.ts new file mode 100644 index 0000000000..8993ede33a --- /dev/null +++ b/packages/backend/src/misc/is-mime-image.ts @@ -0,0 +1,8 @@ +import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; + +const dictionary = { + 'safe-file': FILE_TYPE_BROWSERSAFE, + 'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml'], +}; + +export const isMimeImage = (mime: string, type: keyof typeof dictionary): boolean => dictionary[type].includes(mime); diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts index 48887bf12f..ca036e8fdf 100644 --- a/packages/backend/src/server/proxy/proxy-media.ts +++ b/packages/backend/src/server/proxy/proxy-media.ts @@ -1,13 +1,16 @@ import * as fs from 'node:fs'; import Koa from 'koa'; -import { serverLogger } from '../index.js'; +import sharp from 'sharp'; import { IImage, convertToWebp } from '@/services/drive/image-processor.js'; import { createTemp } from '@/misc/create-temp.js'; import { downloadUrl } from '@/misc/download-url.js'; import { detectType } from '@/misc/get-file-info.js'; import { StatusError } from '@/misc/fetch.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; +import { serverLogger } from '../index.js'; +import { isMimeImage } from '@/misc/is-mime-image.js'; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export async function proxyMedia(ctx: Koa.Context) { const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url; @@ -23,14 +26,50 @@ export async function proxyMedia(ctx: Koa.Context) { await downloadUrl(url, path); const { mime, ext } = await detectType(path); + const isConvertibleImage = isMimeImage(mime, 'sharp-convertible-image'); let image: IImage; - if ('static' in ctx.query && ['image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml'].includes(mime)) { + if ('static' in ctx.query && isConvertibleImage) { image = await convertToWebp(path, 498, 280); - } else if ('preview' in ctx.query && ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/svg+xml'].includes(mime)) { + } else if ('preview' in ctx.query && isConvertibleImage) { image = await convertToWebp(path, 200, 200); - } else if (['image/svg+xml'].includes(mime)) { + } else if ('badge' in ctx.query) { + if (!isConvertibleImage) { + // 画像でないなら404でお茶を濁す + throw new StatusError('Unexpected mime', 404); + } + + const mask = sharp(path) + .resize(96, 96, { + fit: 'inside', + withoutEnlargement: false, + }) + .greyscale() + .normalise() + .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast + .flatten({ background: '#000' }) + .toColorspace('b-w'); + + const stats = await mask.clone().stats(); + + if (stats.entropy < 0.1) { + // エントロピーがあまりない場合は404にする + throw new StatusError('Skip to provide badge', 404); + } + + const data = sharp({ + create: { width: 96, height: 96, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } }, + }) + .pipelineColorspace('b-w') + .boolean(await mask.png().toBuffer(), 'eor'); + + image = { + data: await data.png().toBuffer(), + ext: 'png', + type: 'image/png', + }; + } else if (mime === 'image/svg+xml') { image = await convertToWebp(path, 2048, 2048, 1); } else if (!mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(mime)) { throw new StatusError('Rejected type', 403, 'Rejected type'); @@ -48,7 +87,7 @@ export async function proxyMedia(ctx: Koa.Context) { } catch (e) { serverLogger.error(`${e}`); - if (e instanceof StatusError && e.isClientError) { + if (e instanceof StatusError && (e.statusCode === 302 || e.isClientError)) { ctx.status = e.statusCode; } else { ctx.status = 500; diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index 2feee72be7..be95becb68 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -11,6 +11,7 @@ import Router from '@koa/router'; import send from 'koa-send'; import favicon from 'koa-favicon'; import views from 'koa-views'; +import sharp from 'sharp'; import { createBullBoard } from '@bull-board/api'; import { BullAdapter } from '@bull-board/api/bullAdapter.js'; import { KoaAdapter } from '@bull-board/koa'; @@ -140,6 +141,49 @@ router.get('/twemoji/(.*)', async ctx => { }); }); +router.get('/twemoji-badge/(.*)', async ctx => { + const path = ctx.path.replace('/twemoji-badge/', ''); + + if (!path.match(/^[0-9a-f-]+\.png$/)) { + ctx.status = 404; + return; + } + + const mask = await sharp( + `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/${path.replace('.png', '')}.svg`, + { density: 1000 }, + ) + .resize(488, 488) + .greyscale() + .normalise() + .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast + .flatten({ background: '#000' }) + .extend({ + top: 12, + bottom: 12, + left: 12, + right: 12, + background: '#000', + }) + .toColorspace('b-w') + .png() + .toBuffer(); + + const buffer = await sharp({ + create: { width: 512, height: 512, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } }, + }) + .pipelineColorspace('b-w') + .boolean(mask, 'eor') + .resize(96, 96) + .png() + .toBuffer(); + + ctx.set('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); + ctx.set('Cache-Control', 'max-age=2592000'); + ctx.set('Content-Type', 'image/png'); + ctx.body = buffer; +}); + // ServiceWorker router.get(`/sw.js`, async ctx => { await send(ctx as any, `/sw.js`, { diff --git a/packages/client/src/components/autocomplete.vue b/packages/client/src/components/autocomplete.vue index 1e4a4506f7..ae708026e0 100644 --- a/packages/client/src/components/autocomplete.vue +++ b/packages/client/src/components/autocomplete.vue @@ -35,6 +35,7 @@