From 9e61bfa75ecade876a0f2024c51c57a02496ca8b Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 30 Jan 2026 09:41:02 +0000 Subject: [PATCH] Add NotificationDecoration component Add the NotificationDecoration component to shared-components. This is a leaf component that renders notification badges and indicators for rooms/items including mentions, unread counts, call indicators, etc. --- .../activity-indicator-auto.png | Bin 0 -> 3766 bytes .../invited-auto.png | Bin 0 -> 3968 bytes .../mention-auto.png | Bin 0 -> 4635 bytes .../mention-with-count-auto.png | Bin 0 -> 4791 bytes .../muted-auto.png | Bin 0 -> 4018 bytes .../muted-without-activity-auto.png | Bin 0 -> 4018 bytes .../no-notification-auto.png | Bin 0 -> 3522 bytes .../notification-with-count-auto.png | Bin 0 -> 4144 bytes .../unsent-message-auto.png | Bin 0 -> 4007 bytes .../video-call-auto.png | Bin 0 -> 3860 bytes .../video-call-without-activity-auto.png | Bin 0 -> 3860 bytes .../voice-call-auto.png | Bin 0 -> 3936 bytes .../NotificationDecoration.stories.tsx | 120 +++++++++ .../NotificationDecoration.test.tsx | 80 ++++++ .../NotificationDecoration.tsx | 90 +++++++ .../NotificationDecoration.test.tsx.snap | 242 ++++++++++++++++++ .../NotificationDecoration/index.tsx | 9 + 17 files changed, 541 insertions(+) create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/activity-indicator-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/invited-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/mention-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/mention-with-count-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/muted-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/muted-without-activity-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/no-notification-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/notification-with-count-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/unsent-message-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/video-call-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/video-call-without-activity-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/voice-call-auto.png create mode 100644 packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx create mode 100644 packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.test.tsx create mode 100644 packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.tsx create mode 100644 packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/__snapshots__/NotificationDecoration.test.tsx.snap create mode 100644 packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/index.tsx diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/activity-indicator-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/activity-indicator-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..6ac546adbccfdea05bc0f93180ab6545553ec292 GIT binary patch literal 3766 zcmeAS@N?(olHy`uVBq!ia0y~yU_QXWz;uCw2`F+wwC^zk17EhMi(^Q|oHuuDvqTd` z8XoSv9%9(}QlwmgLr*P&B_w9nqNIiITm@p9X6XHoHTtRGduq?!a2x;9bCCy68yH)^ z-lfODaNzM99U!@wGlPMFgT;h_frUv5Xb&SV14Cm28$*MG12cnz!U9GH1%V3;3<4ZT z$`Y(z@A~(<-l%Q5{=9s%*{lsSIWih{{`vR$`|0oT;?u*E?QYz!uCMv>?CWaQhRhss zhGm7t)xUq>#{J##$3J|$d%G}(VaBnv*y}*+fAq>8I0N>Boe~RP zrxWSJQDtaRJ{lmS0fN6!7)>FgDP%N-kW`P3W|`3}Gn!>avkcZo_GsZSS~v`R;jn|5 of#LuEmn-KN0ehcppl&BKL(&F!P5GIxBY+|dp00i_>zopr0MGn5HUIzs literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/invited-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/invited-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..cfd3f0f556f61e591ef2007ff96671d63487bedf GIT binary patch literal 3968 zcmeAS@N?(olHy`uVBq!ia0y~yU_QXWz;uCw2`F+wwC^zk1HY-Ki(^Q|oHw^^yJb@) z8Xj7vD|-stH+OU>u=TbIu4&0wD$o-V5phJRW38CdFXk&~7}p{t zj}qy?4YxeDO(@;$XlD54WMiucr^@p@qv#m%;4^zZUpajC%%0t8n{NVb)JeMzB7Gc~ z85|TAFfu3zTwq`j;K*QL;9xOfU|?aA0y>V7mw}G=55<-}y5^7dIza}pE zyZZ5c#)gf)>{caY|WS|nxjE0Z7XSYoY z4>!}xsr!3Ldj9?opK7aM$3?qUWOc01$JZq{}*prei_JM19hO88ML{pt2W9{Sq~Ip N@O1TaS?83{1OO5ws`~%{ literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/mention-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/mention-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..bacce8176c82bd8357dfe9435300ace9cf0702a6 GIT binary patch literal 4635 zcmeHLeNYl+7=M+9rZd`AYo$dSSFV+4Sy_r=PII0m)ppCT1pD$6Sd;=0VA-in({t7o z5$XCoXF}OhBoj5Wbt+h}+Y%*6i=v z$SEqT#S^?eb(_1=4#huA{l#NG-;pnc!_ehpMyh#UGfERv%hSH*#qO|b?-Bb#0Kio( zOa#Dj!4?1(*srvjX6FLHnG7h%2n>XPz%`fcz?#LKRx2I<+_nS?>TjW@TeOp^x=BTL zu?wKA?>z&hP$Oqsv=FgzqMIy74Pg_G(BDSU5C+*HtgagjTZX;{sboft(z zz^GFhRhpe|`ANYq8Oo)Cml4*B#J4jsbt>)EfT=rI6&B4{1oG6YLWHOP*fctp z-o(I~$Md2ju2BsyDx%QakD03-q^HMRq#TOw<3Y;64K@sXlwiLS7kq4xWKtw>Nl(n# z42q%My*%!CzkCCndFoMidani_v&S{ge^#p#ofuhL3paFlxD)FQ&(Da=eFX%;A-u|> z!jlEs3`7=91_7-_FeBLY^X=ROb#2%EtQnM?#4s%kbt5&gM1qM@4#r?8X3$!vluzNW zThiGLO2GzXH2p|qFS?vI60Km0&-(F2dY&D4x!dYsvxl?#!PxN8N`_K@FZ>u0m6R!h z)1P@{yV0cag8`cUHgR2=YHFT9lutCvh;ejf{#s|Q2X~2irpW;c^1SU_yqv<}%v4I* zj1r&qxYC^d`1ZA%x6nzi_37-?**h!licLC^y`d_GV60dBO4oeY2@uw@*%dDsL7@T! z^G}D5E1|9(G1cRB;kBMrT=(3Cbvz$bfZ=(Og%!=r+__H*nfioM=+yY>`{ZJuNb-vg zXLTQSQo6c+(g9}ZkibNgS)Po*G-C;+wI?;rp>P(xZ6;yh^uTll8xuz~b>D=B9E3II zzR`N5U=`V{C=1uq+x+xQ8Lbt1*u8yjKyv`Q`9y9NKVT?_+x{R~eHYV5(q8OJx4cp! z_~odhxfkN*#WioC1$PwZy-6(be#N)cC% zB2HXcej1@aejt|8a(!%3PqTu^c-&`n+YWGL3llS*?oijD8QQ!h+4se0IbNjS&2HF+ zknm#X6km$ACNyzU;%ZFm*d6|_OZKe8|8b~Lh}W37<#go**v66{#X>k|)8qlJ@3qouwN;3%LTnZCpMIw{$k-rbgNzL_f7`mg zyW}vHaLojOUCWzUz*~3X@2)%lUl)eB*4c{1k{qz?iFE;elTQd(>_Z|i_3?sPB_J#$ KBA6G1JNqjgKs%rS literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/mention-with-count-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/mention-with-count-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..773d314ec3ad49f8b7f193ee46bf1c22915581cd GIT binary patch literal 4791 zcmeHLSx}Q#6uvA&P*#`PC<-{%Wrj*{KtP~?l}afJt!ODCn-r1F2oVTNNVG!iK*@*| z5dr~GlwpZ1k`T7Av=NbnWgzTf-$@Dqviu=`L#NXRA3AlWk9oRx&YZdT%sJop%{k}i zZ{3`Ab{^OX0MK#y+UXJiYD@rByEIi5mab3IrvT6gE>34JW68^-xMiQ90Ol&RG}MzB z6A_W`$j#iBUD;ihpKBSqmo{Ii8Q`WpZ`r!9MW43tc0}VdCt8rV<>Ux5eb=0E$KJYK zev?`mNcFQjpFOy|_nYs=ps^oLl%2eUF1>q6AG#>>IkzT}MMh!Rad|>srgyzNGm_6*_ZWr1-gSNr6lWWz$ z`JiyYScBr6pI8~TPY_x4hhwoTnK~>ZdjS`}Fz=CW8?gAi9c4D%Pvj?bINOVv!ZI69 zmfcJ%T3*OOk*4CIb-%3f-|PsH$MaRr2M;tq8I2lErrJtc9=XLy zr&CxozuPM?UR+|8BwZ22PxZ_ov{}Hs%r$y*yoNdm5%8qu#@CUaFhHqi7bNF1N1eq)8s&&1T#>FjC(DYfpP%eMFziQJc znti-n#-U!rn+cmjn=7oyIQi{}1i-}Z?gB?D$0_p3=u}eOT6I;5E8D~r$2&JnJDFpP z?o1)VKhB_iquvR0(8zKNO7%P)|Ai#NU2p%s%`{QNRPk2ht6)=`zzr|7HQ$-S#jj-S zDR0EJCXwh8vX&>?;fCUmN-YHQ#7M2Y0MSH?=DBal*Rrov6}fo}%bRk}1TDtaL|jc= z^=pXf?wNSuftR$AMk?ZALSwK{`ZBNK{i&eI>gF0-PW@ztiIsz7P4k`;n0#649y6G?V(BGh#r%ZP0&ClPh<%Hpog#mTc^k&z1&+#vz@7RRt^k-Y`vYR zO(Nm&T=T-B3%34wJ~{o@BA22CTLTcUA!bNWzxMobFJb8f=Spvh1>9LJgazUz{uZt>gl#DG)dP=*w(>-X}zZl6be`C8aL};NYHv^4z}J{q59IN{Zs7T#0tVp zVPcLUU6hRWwu4>|k11+ZOw3Q%=w#Y9uZMPnOlYQQS=S%a{GIb%~4>AO6ruI5HR*I9N;= z7+9F3fR13~WngG*U}I=-aA0O|P*}jopdf&$tVx=ULG8iR>t%cYUp#pAcWZb3oS*-W z$JhO?Wk{&XS-`0F@L%uuefwVDJ$?GLe}46!+3(N2sY-uu5fc+rlk$04p%H_DB$HHv zkld{;AD_*>w(rlYbFHndZ=>aBpA9Q}f75*SSw0!NeI@@MdF$I*GZ^p!)oO|5MM#|g z+unEf`RBsdHfCR{<>e&Q^G8$G|{QUaz>ggS&a_6;q_Zr>) z{qf<)e{W}Q2AZJ6VsaqlPvy%?_1`xxJZ}4?PknyG%(ZW*Z;MJ*uJZ8-jAZfkH?;G%eO53^@ibq3D7Q2>xwU%Z|0=lapq_6 zY35(a;B)lS&+h-$^4~Ayzx$WKkRS!LQRPid_4)J9>-OHdtbBaG#D4<@1IDGhCnAz&4|0xA`5*e_`*vpX=}Wcx`I!XYTVk zyZ>&B4sVyfc7J2Z!$%AUzHT!BMW@=c8_(5`7k_5{&dcD_1B$u>+A`!uERpd)s*JpR zGa7EA;fB9t9ZeylDP%N-kkl9%%`&4|W;Dx;W*MwKmC?dsv~a*(IQ$3pxc~nTy0La0 gu;T)wPq|~pTUA~liCs2gJ)78&qol`;+0PSkiQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/muted-without-activity-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/muted-without-activity-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..d4dbca6e3921e1a547802f6979ccb4a9497365d4 GIT binary patch literal 4018 zcmeAS@N?(olHy`uVBq!ia0y~yU_QXWz;uCw2`F+wwC^zk1An@wi(^Q|oHuv9SBIra zG(6mz&41*LiN?lRrW*{r7J0Ga{GIb%~4>AO6ruI5HR*I9N;= z7+9F3fR13~WngG*U}I=-aA0O|P*}jopdf&$tVx=ULG8iR>t%cYUp#pAcWZb3oS*-W z$JhO?Wk{&XS-`0F@L%uuefwVDJ$?GLe}46!+3(N2sY-uu5fc+rlk$04p%H_DB$HHv zkld{;AD_*>w(rlYbFHndZ=>aBpA9Q}f75*SSw0!NeI@@MdF$I*GZ^p!)oO|5MM#|g z+unEf`RBsdHfCR{<>e&Q^G8$G|{QUaz>ggS&a_6;q_Zr>) z{qf<)e{W}Q2AZJ6VsaqlPvy%?_1`xxJZ}4?PknyG%(ZW*Z;MJ*uJZ8-jAZfkH?;G%eO53^@ibq3D7Q2>xwU%Z|0=lapq_6 zY35(a;B)lS&+h-$^4~Ayzx$WKkRS!LQRPid_4)J9>-OHdtbBaG#D4<@1IDGhCnAz&4|0xA`5*e_`*vpX=}Wcx`I!XYTVk zyZ>&B4sVyfc7J2Z!$%AUzHT!BMW@=c8_(5`7k_5{&dcD_1B$u>+A`!uERpd)s*JpR zGa7EA;fB9t9ZeylDP%N-kkl9%%`&4|W;Dx;W*MwKmC?dsv~a*(IQ$3pxc~nTy0La0 gu;T)wPq|~pTUA~liCs2gJ)78&qol`;+0PSkiQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/no-notification-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/no-notification-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..9f58a62407b42321e1382e699e8f73b601b1bbd6 GIT binary patch literal 3522 zcmeAS@N?(olHy`uVBq!ia0y~yU_QXWz;uCw2`F+wwC^!cyxh~pF{EP7n`?%Q3)d0RH^SDLW!}C&`|q3X^UF&&{>%QmZvH-7+tsFq#>U1#Tc4#T zu`n>4nccv~(BR;}%;2D~fRRB#-~t1K07nJ`0|$!<0|N__6wr~3ybKJD$jZd#&ac0_ z@n?7LWAp6t*sZ7i^sX>;cfU3|pj32Q`~00xmoCfKeZ9Hf*!b~dKksQW8;Y(jJuX-O z(eQXbLxjwKrtGZH~9{Xkf+;T}q0~1DGgFWB( z-zhuxcYEHgZ!cwLuMDlWD|)BOzAna;DIws$6r;hG>bUy4cU5xI{Kqy6GB^ugU|_C( z`ItYi;$i1co#UI$x7Qf~RWeB>Bz)id@lpQf`O9WU?0nebeQ)Qhbu+W`YJOh%`uohU zjo&NEj;}u7#~?AKflcDZG5h-=|Gw9k-!0$rbs775`}E7-O0)T+x0kFlWZ?F9VCH#q z_x$#AcK=NN-aC7K-?sOi;kE@&x_Z~!em_-S+{+kI0kW@JC;sF0{<4~)r*p$)zCSts z`r7=WTThPI_b^68faKlR+^hapc(g8kPt8x$a+y6jzmCVd30v#WosrFR;1I|o)=6LE zHuuNp*M3}f_O}16g15(bf7gFG7TjOEF}V-ulJoK_8F+F|n_mvz9`pAq|NZ}uKmGTf zzPI#d>FVkJK)>`^0}YA(y#M5FKihn})3?p%te?loroMo&t^C|W_xZM4>)xHMFqqfN zATi6`f{}OeCfYE|Hht|Z_jV* ziGTc9arb)5itVQA@+PJ`OKx&B9CZXG9|J>BqVgpzB@vn6MwOxEz|jC14G{cg^k@nh zO(CNxgrsK8XqFkxGNV~$G|OP^^Nbb_qlE+Ig~JDHV0Zujf17eOb|8Zd)Zu4l`2Fml UxO9Z65Kx4{)78&qol`;+0OThY#sB~S literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/unsent-message-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/unsent-message-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..c2bcf320c59f347878ce3ceb08a80703715042fb GIT binary patch literal 4007 zcmeAS@N?(olHy`uVBq!ia0y~yU_QXWz;uCw2`F+wwC^zk1AmOCi(^Q|oHuv(ubx`U zaQx%*IMe*=0n7)TqE%LGk-ulU=Fuigq zAR*;x0Eb?dsY}+9>ni@+I_79UseW16cD>tp&gaT~^#^7juAH~I%xpF<1H*x_%V_xZ;ee*FK* zCY4b0I`7uDmlN+UxBKzp(x=zi`RC@|-1Gcl>Fqojpkekwj5E5D{omUD{kS}Q&z8dP z%(w5?B}07C^5@^Z-~0FcdG+Sasimnv|G(DOkNN*&9%Dl$FhVXDJ-qUAGP~WM-%nO{ zzrUVyZ_Ce%?PBJ+G7K|#z;QF1gS?m{GJ;2yjRr6NB49LyjHZy$6hcz1Ihtigv&?9g z8O<_So7AI)!)W0!S~yU@aQMf_!0`Y7wUg(89d8CUP(PcQ!R1fa_2Yt)Y(NnPPgg&e IbxsLQ0I3DV?*IS* literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/video-call-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/video-call-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6f8b5b35282dfcd688b33e9c1305c8c5589cbb GIT binary patch literal 3860 zcmeAS@N?(olHy`uVBq!ia0y~yU_QXWz;uCw2`F+wwC^zk1K&wc7srr_IdAUTul7%5 zIPmfDu8lv!*%k&!+&JO1@x(Hf`~wQjM^7c0Zi>)2nQ-KhsKiq~E=k?#Gycd<3A z&YgFE{hpV<{w(RY{CQ>Ef$h(2mZz5)&E#QVIB@C=ACT7}U}2I1TF%JJ zz|h#h#?av4z|7#Fuz-<4LEr)dg8&C^Wj?LnT}%Uo5Wf?>}1x11&~g zgMF#>?mwf?Gx#urU2c4VVfBHzo1e2YENgCHleoWY>**T)Jq1sWZ1%psE%(Nj`q$eS z8v-p4F&gZ<8@)|$_p=k{Y~QcHC&M5yTNY?>tXUfg%i^ Lu6{1-oD!M<+je(= literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/video-call-without-activity-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/video-call-without-activity-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6f8b5b35282dfcd688b33e9c1305c8c5589cbb GIT binary patch literal 3860 zcmeAS@N?(olHy`uVBq!ia0y~yU_QXWz;uCw2`F+wwC^zk1K&wc7srr_IdAUTul7%5 zIPmfDu8lv!*%k&!+&JO1@x(Hf`~wQjM^7c0Zi>)2nQ-KhsKiq~E=k?#Gycd<3A z&YgFE{hpV<{w(RY{CQ>Ef$h(2mZz5)&E#QVIB@C=ACT7}U}2I1TF%JJ zz|h#h#?av4z|7#Fuz-<4LEr)dg8&C^Wj?LnT}%Uo5Wf?>}1x11&~g zgMF#>?mwf?Gx#urU2c4VVfBHzo1e2YENgCHleoWY>**T)Jq1sWZ1%psE%(Nj`q$eS z8v-p4F&gZ<8@)|$_p=k{Y~QcHC&M5yTNY?>tXUfg%i^ Lu6{1-oD!M<+je(= literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/voice-call-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx/voice-call-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..86f5cde837ccf25a264e080cd733d413a0899bd8 GIT binary patch literal 3936 zcmeAS@N?(olHy`uVBq!ia0y~yU_QXWz;uCw2`F+wwC^zk1HY)Ji(^Q|oHut4zP?|~ zaQq|x-(%-9xqnOxYr{WJ(Yp-yIo?f=j0o*Y4Q4qaz?D zW{aXoooP~b+B5O}C-?ony1ne?)9*I5_uG#@{_<;&{r>}-{pa7){`|8BXydl@R1oRw zz|7#Fuz-<4LEr)dg8)Yc0|N((2?GNQlN8WxjJymCjSXxJ4Gvh99T2FqD$e?Q=H=Rr z_3v-pi@om4(DSP6XN?`>hMe7Tc3VE)|GBpC{Jv=W+FwUG8vVNa&XD~=iZD5l)bny1Oh@Sp= zRS%Ed-3&B_GowM#rs%=Co3BixAK!VlUzmYe7^ri`+IKbo-mz~xcG&%%P2snGh|0h{ zCGVTR->vz5^KAV)`#-l<_7-;^=HF*u_GY{AbUDTdtM5QZq&)uf>|lA^|0mDi=k2UJ zfAKhQttLn=B>=3gm2mU8_F6f6t$qf3F^LHl%*$W-u?dw{5F`FH~1&mgt@X?ir=M#Bw%sW+NJMpMXW3L&YXFq&mXv&?9g8O<_Sdl93B!)W1v tuW--<_EZ1=XZ{!S6xdH@19gy@8B(SfRi0Z_z8EON;OXk;vd$@?2>^Z(u0H?( literal 0 HcmV?d00001 diff --git a/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx new file mode 100644 index 0000000000..db7ddf1f0a --- /dev/null +++ b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.stories.tsx @@ -0,0 +1,120 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +import React from "react"; + +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { NotificationDecoration, type NotificationDecorationProps } from "./NotificationDecoration"; + +const defaultProps: NotificationDecorationProps = { + hasAnyNotificationOrActivity: false, + isUnsentMessage: false, + invited: false, + isMention: false, + isActivityNotification: false, + isNotification: false, + hasUnreadCount: false, + count: 0, + muted: false, +}; + +const meta = { + title: "Room List/NotificationDecoration", + component: NotificationDecoration, + tags: ["autodocs"], + decorators: [ + (Story) => ( +
+ +
+ ), + ], + args: defaultProps, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const NoNotification: Story = {}; + +export const UnsentMessage: Story = { + args: { + hasAnyNotificationOrActivity: true, + isUnsentMessage: true, + }, +}; + +export const VideoCall: Story = { + args: { + hasAnyNotificationOrActivity: true, + callType: "video", + }, +}; + +export const VoiceCall: Story = { + args: { + hasAnyNotificationOrActivity: true, + callType: "voice", + }, +}; + +export const Invited: Story = { + args: { + hasAnyNotificationOrActivity: true, + invited: true, + }, +}; + +export const Mention: Story = { + args: { + hasAnyNotificationOrActivity: true, + isMention: true, + }, +}; + +export const MentionWithCount: Story = { + args: { + hasAnyNotificationOrActivity: true, + isMention: true, + count: 5, + }, +}; + +export const NotificationWithCount: Story = { + args: { + hasAnyNotificationOrActivity: true, + isNotification: true, + count: 3, + }, +}; + +export const ActivityIndicator: Story = { + args: { + hasAnyNotificationOrActivity: true, + isActivityNotification: true, + }, +}; + +export const Muted: Story = { + args: { + muted: true, + }, +}; + +export const MutedWithoutActivity: Story = { + args: { + hasAnyNotificationOrActivity: false, + muted: true, + }, +}; + +export const VideoCallWithoutActivity: Story = { + args: { + hasAnyNotificationOrActivity: false, + callType: "video", + }, +}; diff --git a/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.test.tsx b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.test.tsx new file mode 100644 index 0000000000..f79e092f4d --- /dev/null +++ b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.test.tsx @@ -0,0 +1,80 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +import React from "react"; +import { render } from "@test-utils"; +import { composeStories } from "@storybook/react-vite"; +import { describe, it, expect } from "vitest"; + +import * as stories from "./NotificationDecoration.stories"; + +const { + NoNotification, + UnsentMessage, + VideoCall, + VoiceCall, + Invited, + Mention, + MentionWithCount, + NotificationWithCount, + ActivityIndicator, + Muted, +} = composeStories(stories); + +describe("", () => { + describe("snapshots", () => { + it("renders NoNotification story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders UnsentMessage story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders VideoCall story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders VoiceCall story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders Invited story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders Mention story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders MentionWithCount story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders NotificationWithCount story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders ActivityIndicator story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders Muted story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.tsx b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.tsx new file mode 100644 index 0000000000..03be962fbf --- /dev/null +++ b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/NotificationDecoration.tsx @@ -0,0 +1,90 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +import React from "react"; +import { + MentionIcon, + ErrorSolidIcon, + NotificationsOffSolidIcon, + VideoCallSolidIcon, + EmailSolidIcon, + VoiceCallSolidIcon, +} from "@vector-im/compound-design-tokens/assets/web/icons"; +import { UnreadCounter, Unread } from "@vector-im/compound-web"; + +import { Flex } from "../../../utils/Flex"; + +/** + * Data representing the notification state for a room or item. + * Used in snapshots and passed to the NotificationDecoration component. + */ +export interface NotificationDecorationData { + /** Whether there is any notification or activity to display */ + hasAnyNotificationOrActivity: boolean; + /** Whether there's an unsent message */ + isUnsentMessage: boolean; + /** Whether the user is invited to the room */ + invited: boolean; + /** Whether the notification is a mention */ + isMention: boolean; + /** Whether there's activity (not a full notification) */ + isActivityNotification: boolean; + /** Whether there's a notification (not just activity) */ + isNotification: boolean; + /** Whether there are unread messages with a count */ + hasUnreadCount: boolean; + /** Notification count */ + count: number; + /** Whether notifications are muted */ + muted: boolean; + /** Optional call type indicator */ + callType?: "video" | "voice"; +} + +/** + * Props for the NotificationDecoration component. + */ +export interface NotificationDecorationProps extends NotificationDecorationData {} + +/** + * Renders notification badges and indicators for rooms/items + */ +export const NotificationDecoration: React.FC = ({ + hasAnyNotificationOrActivity, + muted, + callType, + isUnsentMessage, + invited, + isMention, + isNotification, + isActivityNotification, + count, +}) => { + // Don't render anything if there's nothing to show + if (!hasAnyNotificationOrActivity && !muted && !callType) { + return null; + } + + return ( + + {isUnsentMessage && ( + + )} + {callType === "video" && ( + + )} + {callType === "voice" && ( + + )} + {invited && } + {isMention && } + {(isMention || isNotification) && } + {isActivityNotification && } + {muted && } + + ); +}; diff --git a/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/__snapshots__/NotificationDecoration.test.tsx.snap b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/__snapshots__/NotificationDecoration.test.tsx.snap new file mode 100644 index 0000000000..a7c7da94f4 --- /dev/null +++ b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/__snapshots__/NotificationDecoration.test.tsx.snap @@ -0,0 +1,242 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[` > snapshots > renders ActivityIndicator story 1`] = ` +
+
+
+
+
+
+
+
+
+`; + +exports[` > snapshots > renders Invited story 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[` > snapshots > renders Mention story 1`] = ` +
+
+
+ + + +
+
+
+
+`; + +exports[` > snapshots > renders MentionWithCount story 1`] = ` +
+
+
+ + + + + 5 + +
+
+
+`; + +exports[` > snapshots > renders Muted story 1`] = ` +
+
+
+ + + + +
+
+
+`; + +exports[` > snapshots > renders NoNotification story 1`] = ` +
+
+
+`; + +exports[` > snapshots > renders NotificationWithCount story 1`] = ` +
+
+
+ + 3 + +
+
+
+`; + +exports[` > snapshots > renders UnsentMessage story 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[` > snapshots > renders VideoCall story 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[` > snapshots > renders VoiceCall story 1`] = ` +
+
+
+ + + +
+
+
+`; diff --git a/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/index.tsx b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/index.tsx new file mode 100644 index 0000000000..42c7a3451f --- /dev/null +++ b/packages/shared-components/src/room-list/RoomListItem/NotificationDecoration/index.tsx @@ -0,0 +1,9 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +export { NotificationDecoration } from "./NotificationDecoration"; +export type { NotificationDecorationProps, NotificationDecorationData } from "./NotificationDecoration";