From badb35bd1f521aebfcac3543caddad3b72094583 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 12 Aug 2026 09:40:26 +0100 Subject: [PATCH] Timeline MVVM 1 - Shared TimelineView and Overlay Buttons (#34464) * Add shared timeline overlay buttons and view contract The TimelineOverlayButtons presentational component (unread bar, jump-to-bottom) plus the shared TimelineView VM contract (types) and its i18n strings. Consumed by the TimelineView virtualizer in a follow-up commit. * Add shared TimelineView component (TanStack Virtual) Headless TanStack-Virtual timeline driven from RoomTimelineViewModel: anchorTo:"end" viewport stability across prepends/trims, cold-load cover, jump-to via scrollToOffset, and visible-state reporting via the virtualizer's onChange. Adds the @tanstack/react-virtual dependency and the virtual-core isValidAnchorItem patch (runtime + types). * Use Compound spacing tokens in timeline overlay and clarify comments - Use Compound spacing tokens for overlay margins/insets instead of raw px - Drop the stale Virtuoso reference from the overlay CSS comment - Note the real-world duration of COLD_CAP_FRAMES - Describe each load phase in plain English * Remove jargon and explain/expand things in simple terms. * Use time rather than a frame count and improve description. --- .../jump-to-bottom-auto.png | Bin 0 -> 18000 bytes .../jump-to-bottom-with-highlight-auto.png | Bin 0 -> 18410 bytes .../jump-to-bottom-with-unread-auto.png | Bin 0 -> 18410 bytes .../unread-marker-above-auto.png | Bin 0 -> 18956 bytes .../unread-marker-below-auto.png | Bin 0 -> 18937 bytes .../TimelineView.stories.tsx/default-auto.png | Bin 0 -> 42153 bytes .../with-jump-to-bottom-auto.png | Bin 0 -> 43531 bytes .../with-unread-marker-auto.png | Bin 0 -> 44715 bytes packages/shared-components/package.json | 1 + .../src/i18n/strings/en_EN.json | 3 + packages/shared-components/src/index.ts | 1 + .../src/room/timeline/TimelineView/.gitkeep | 1 - .../TimelineOverlayButtons.module.css | 201 +++++++++ .../TimelineOverlayButtons.stories.tsx | 104 +++++ .../TimelineOverlayButtons.test.tsx | 179 ++++++++ .../TimelineView/TimelineOverlayButtons.tsx | 117 +++++ .../TimelineView/TimelineView.module.css | 57 +++ .../TimelineView/TimelineView.stories.tsx | 152 +++++++ .../TimelineView/TimelineView.test.tsx | 169 ++++++++ .../timeline/TimelineView/TimelineView.tsx | 402 ++++++++++++++++++ .../src/room/timeline/TimelineView/index.ts | 20 + .../src/room/timeline/TimelineView/types.ts | 201 +++++++++ patches/@tanstack__virtual-core@3.17.6.patch | 47 ++ pnpm-lock.yaml | 21 + pnpm-workspace.yaml | 6 + 25 files changed, 1681 insertions(+), 1 deletion(-) create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/jump-to-bottom-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/jump-to-bottom-with-highlight-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/jump-to-bottom-with-unread-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/unread-marker-above-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/unread-marker-below-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineView.stories.tsx/default-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineView.stories.tsx/with-jump-to-bottom-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineView.stories.tsx/with-unread-marker-auto.png delete mode 100644 packages/shared-components/src/room/timeline/TimelineView/.gitkeep create mode 100644 packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.module.css create mode 100644 packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx create mode 100644 packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.test.tsx create mode 100644 packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.tsx create mode 100644 packages/shared-components/src/room/timeline/TimelineView/TimelineView.module.css create mode 100644 packages/shared-components/src/room/timeline/TimelineView/TimelineView.stories.tsx create mode 100644 packages/shared-components/src/room/timeline/TimelineView/TimelineView.test.tsx create mode 100644 packages/shared-components/src/room/timeline/TimelineView/TimelineView.tsx create mode 100644 packages/shared-components/src/room/timeline/TimelineView/index.ts create mode 100644 packages/shared-components/src/room/timeline/TimelineView/types.ts create mode 100644 patches/@tanstack__virtual-core@3.17.6.patch diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/jump-to-bottom-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/jump-to-bottom-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..759ad990026db88e8143a8f055ffc8600c98da0f GIT binary patch literal 18000 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 l?2PZ)>@H(&&rI@xPd`UJ_g{{s#ro7(^Y literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/jump-to-bottom-with-unread-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx/jump-to-bottom-with-unread-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..48be5c1bd1ab7a559e79ccdbd785e23e0789238b GIT binary patch literal 18410 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 Ee;E|CzW@LL literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineView.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineView.stories.tsx/default-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..10224ea1efbe76a3f97ca0ba7dbcff1118ab9fd3 GIT binary patch literal 42153 zcmb@u1yqyo8wNa3lu*Gy6eJ8pLBIk;(nLW(rE8#+(zOu-1{M~eB3&X#!z9LFV}Mdh zN{ku<5n;r}sL}B~Lr{MIug>|-caCR=`S$ZZ_x)Vgbzk>h+}2Rp$<3|H6=Hlm2~|$! zkSJ7cha&9X3)DrU$<}`H{_mG}bgar%BLD2e*zun5o0FN>Yt6Lu{MxvbAxv90KKxJY z5rPDLth7p>U@5$^dBYH6LpI&2fVF~s{Kduvh5VFT*|u^+Xg$k#-OQyrexm}1dZ$cU zX>Xze!uQ6Mvt4h=mC~h2+o;))IR4Bf%G5L^sDz944AZm6f6u`c$b5;VZ0~Um-4u;R z+p;nYp>Nn)C&vY5Pr{0qcOTb`OiydEO1inaW&6&y7kfpFiH@AUdSbenZtZVfY^KY~ z-=5_j^oKBiVZSY~@hH#l`hQ?HJC|;P3#$q|m?oQ-oTh@2uxS~4s5T=g2dA_;mM#yL z2ID*EB=?2EXh(C$<@gt$XuYMTy<=Sil036tAC2`QR}~!YmW|R%h*{+UoZIX2at_IR zoG^Oz}hi$&U1MrO2=Y_Vpey7J#mW_QGZV@hra(+ z@J@s(wjS8E{%WtHlz%We8K>XaBfyAavQn4>UFQM>i_YZR_ui3&QTuAPU#t|LEynC>haz8W`D&OMHYZp_`PuGJ5 zM)#*78pCx;dzPaXCvJ&HtmeQy>~XKRig1kEbR&WX>tnNY;>8J=-m&KCgXc`rvWzfk zJ~-(p1=}dE((WrE$+uv`&YF&#BPp2%c{Z&o<**iY8Ej@^j|3$%Qp<8`Zk}w~tubzj zT>ccMiEff}GL-L%mRR0+KF!RL$HIR)Ipq)YF*cV{c2ipRS0LZ~Gohf=89j3D^#bLiN` zJs57xN9#knW97S#qTME#edH0?n)fHEoZ0pE-*8uAap=_rTc7D9nb~u^hndy_&i@U! znc3P1H%V~0LFDbbc3@hFAMTjr7kd_Yf6&ye^;2(OvYfPR=5w40p{jz=QV#HyOIn(r z8eXa+6r4)%KHo z^bFi%$Me1F4Rx}$n}ip=6?HQ8p&5E8*sQdM9OXFDe@UXFU^I_E$7kMKaiBK3ov`TJ zD6l+Hz1y+fy~Hjvnw$!5-sD{v<$PsQNZ9)p&ortMGdEI z^v0UiBnf<|(j$x49usor3wO{gri}4kf$p#o(;^qk?x&5jvlnFS=9*c4@_1arTk59W z?Nn^IkZC+Dr#AfYm4u6DsF;pZJlqiD``GrVG|h^}vt`D^GYNmFZm#eYrB7vGC}UyS5|die8MQ#`is7|K8{kH+U*BasPu zJWXV=@Sy_Y%ZC*zi8`p2EfOT#W8=nnxP4s;yYK~sqDs&qy2wgj{Zs`T>g(`(zy9gT zcm0FhQ%VjF?aoB0ln#|9@y1;oE1EIBcOpoxYikwbBSEyRkg@daWxWU~Q1uVeG0oJ? zvkFdqhp~MS%;`vkk5GR^7xOn>a^%zT`6WoOGO6ph5l8Or)Z3dsCczs64uV9S^0`P1fSE@EUp}V)-sC6dz&w%y=Lph zT&wzD0)$=fd7{H)90f4_wsILW&t30G{y_UmPQx9|Ydgr92Cf$7StUKb)Xd7x4 z9<+Kp=PH;V0&-rr)WRh&BMTFyt2zNejWy6DOW1Mss9%*0DUW)hQA$XEL@Q~JQly+_k;_0U zx1Mn}zZ`s_+Q#$Hzh(QdaG4pG^3Qzbbe6I#!-Cs1MHIF=5iw1nuEyCaO5TvC54rNs z-p>Gu1Y0JZGrd3`#nO6pGoV^C5%lflDnsc$oBD*A0hRp1k1BH=%?hV0zUWv7}<# z_B51ykt>cwEre0dI3Z3(;3G10saUUk11*C$F&)NPBq$!6W{v8}COr$}9mU$Bnmoy| zA{nm3mf`8w>ld|C9JWa6lRl=%FBUEf7Z+d&_@9e$yy)0rLrDl`Wp7w*?4s&U-tjQ( ztcSt1>=Gy#LVDvs)W>UhXPLKwm4i*QSxh6H_zc_o4b`qv-W4iiQG3^y%eC1hT=sx# z&PXT~QrO=@wW#hTtd@vEizBLn`9JZ*BQ*qfAoN}-#Y%ESQ5H*DQq=EavL(~?g0rK} zK8!hQ-9RbXe&_@VuC={XK{*8P!rz5)4%Xy3b-N5^;U9y)QPagc(_@C8v2jE@(35Ik zfh5V{k+m|4nsr9zrkVD(n+RD`JIskWmTFUn?X~2o%35@|jDeqBK;x`C^!~7}tWkj-7H`4E zbBgHG-H#p3h6VRjsdKoQ2qAqf8{@=p>!zo4Mr4*&@D_xtbecC`dua^2{77i_dbEKbUH z|LSZ>zopYZB)NuxBb2}PCD}k?K0A+|T|O<(Q75LK?Hu_o;GV6Ee!1A9_OMq>;cvED3&e+#%8;0us)Uv;M=x!{ z$4py4>ia=1yyQ{pN^bDpy$MqbVOd!H5)sRhKGUqHwf-V)Z~zn>FoN6)Q5NFe8gX>f zJ?&vSt;E7u8Im|oEB!k{L>WRkaA6JYUrmQhUEa9-eQ*JD;km18--RZ(M(kccSH*6C z*&lxqUjKUa9mMs|JqwjUn4G*h$Ns|pZ(L^k*B;NBl?xJo=`!=*yHPIWIPJ^8qHn+< zLfp+8hk*IPYRYsy2*UgV93sMe7(^ryVt!r7nF+uq`)~gpr>|;$O~+3Hlu&Hl z$mz8hd)O;;K4fIE4YEAl(sN9X^|)(Y$An>t_qEeHCtm&K2#hx?gA^zXaG2uz0{tk^!2;=B6P(UilR`|hEzc0|>7aCV6uf5IULVYJ1Jps>sNgbyC=YL)Lr_Bj@12%8}zZMDBEByQH~#nHV-)%9aE6%JXTk>)Wj*^N?p2NF#>3BRiG|l zj&78W_?pmjDFw8SVaeu&G!a6!y)Bi5(^CsO)dC>J=%eBN;sWjCeMgHgRxeo=DGNz$ zeXwP765x6+#-$C3Q9>%(cNd;?@CoFXxim$V`~gUdPO>q}&Vy%Li91)+d7y6hidCZX ztm;Qb0RL(98JuRezvY zB8*b-+=KUV!>tC2@r^8HGbTN=0Xv(na4LS-vVGU_p^;W9UB;Lu@HkG+y+kACLW1X% zxpw{#0XyBSrpf*DHJC0jd61o(uHlpn-n>(N)NDFzG;FL?1u#eL#qM8s3x3qQpr1OAZdv?Iz# z*TQD)gNfx=n&!m~E#MKmU)IwCd&7sLgOX_LSq=Nn{7X_c-A5NC5i@pCvgR*K^wGv@ z;iqobEy<3|xyg09KhCm?JnL?F#=S!=S31*Zpi3X?;h&A0;SE>x8M&%JO31a((;@Go*6 zx}j6;|9D&NBg#)c^=Mw?GCkZ2->rfxA;wDc{}BDph5eMHI6%Cjp!n;F*8>TZNG&Pz z1&jJfDfOr9+^y!p=;ty&j;aLPkQR2X) z?|GMqp?$U)>%h%g`>ZYtxbx5EBUgQWaJG8g$K37By9%8{kUQX|4_mDGm3+Ggh{H=$ zjqP28_~9O-k)%OXxM(n_m)jxTstxaP*XMPK=Px$S!QnPYXF z9-2q~=K(&h>nJmiNXd*qFnO#Pui5DCt7}=)eUd1QhUD25y~N_YE%EbKN_tix&x&eg zKe{MSx~Y@i_J$=e%=!PUDH!C&*$^~T@MYr(0nsD1lBF&W{WaS3Dr*RIV=@n2dlYWd zJbi}lW9w&#p?;{*p@mY97%6Z(kSiU3m)^VRDCPP4jDakNyf{jG7zYsF6@|raj)mbA z{DL=uN7P+USfM2?Zw%FbgVVF$FlX|dn5dZT7c||78fR!jO2*5*U zuE5*SqegyH-_4FH{TQfJK#z{U#==L_u+Uykgxt_a{`k z$kF;J+p*&LF|U~zP3^*O=!-7DpW`q&w_>HsO;n~{6s^b2`aHbJW}zy2?p{w&tQ@RX zT&7yBAxi317usGN4B+ednK44g8Nr9G-}-V*>b^YgZTa}rl|DfkX5c2M4|S*5V2RSw zjgTr{C3sS@h5$-)u%U%hX-VaO`F-w-Sl^X-9vH3d+LU7CCpC$Z3chmBZ2UKLz`EKh9isx;&aw^L9YtxFDQ&o?*i(Ygq&$lv8etBKO7WrV3)zVgoJX%H`FH% zUBw0&?U^vonIX61rpNJp)O|8O)6UdC(!lS@cL?mS$y(SK7M{XiZH<5s^<__yYe&2C zq8a)?bs9CthOa!~NjoAID2gS1?_GOtOB66$ClUbUmZ zJM34O=S}?V+eh27^afP9sdo)>Et*K~y#v`>VrxRh`rr8|#@E$7a^ow1$^#t|yi#D- zshwM4-{YZ)=F2lnAU?#DVJi35qaQTw$}#(+szIHj9E-zz7%TV}x!Oxm+! zcw-2BqLRnxZgGE`kz<>lseLP~-6TU!jdVD^F@=z!Nre#mE9;{aK7tjsP$dpyPaH&n z_q88z**endWGxEHXNntrAM=tMrxT5_YvRX*yP1FHv0DC24mcN8Lt_aKH}Oh4e(M5# zX!>KNTKCZ^>wUM5BzU3Ib1@dH^W=C374PQXCvU!2JSP^9arL1Loe?*|c3ZB@^{$Sz zX!2xuOxz#qVJ0QeINMEnB=<-Z{oXTB6R8EH06Og=v4h%VqZh&8h+M+VxAtyK-W%EA zX+QgjU}({(G_`OCQFnLY(Bj0`6uD9btUFsruhg|yBMIC2(r0|=-HFXJM>3CGW5?xg zQ1EF11%#MpoV)}{LnlMGX_?OZU4R~|Xr6rC`+UtiEw@&bX*){?A-!Gu@%2FZm+{@$ zk?8}!q$5sh9pA}!@v2(!&lc!u%U+DQ$wEWnwFa+cZ?Xmh!o3VH)B-AMicOFFz6vBc z);-RPK9U@V_mT@gt}Z}m;qMZ49}_bXD;TwPOqD<`G>zbBqGaE+H$czeSM=6b@S-;r zTyU&_hpv-`T!`zsrtdGbm4Q!4?}Qy)B|UR_R2k@m!G~Aps+CTu&5(K{=3TpClr;Nj z-6$pZf{XA6sQE_K}TnwTdW^R4mc2=P1`AF2i#r85#lM3VyqwX-Q{ zv|2+b<<4hirb_DQ0F*@dd`ThP9@k`Qe}T@N!I5i4_=(y6U^2;hX_@vbx;3%HQ9St1IJBI(WvR?uK5kz|#UAAD^NX z4)v%DZJ#XZ3+_D58p{hM*@kGFIW5Z5dj(e<*apu5!aUMOzc%|Wt2$cA=KEOT!pM=X zJVXk``GFeHrXyW*ZKJ2!O|$Rh7&+z`?fMhOAC>)19$7}CUq@0t-ZY$sS=^M49dabFULDLJ~>DlT$T>27?~MWx)FTdRsvz5 z8(qNvdkX&4ak?s8fBwmEy!Tnn8SBu@5~@vOgsd7+S#Ii6GG_tUT|O(6mJ^5G&AyI` zlr1<;%Q5cD{{u_2Fl3jPwzH`B+?U$}b#ISqsCJojW!r`X2)gih_fZu!C*XmP*)H3* z73*zToP{yrsizv_WC_%lxNWlks8CWAWGCa>$=%=Hzf*l{TwP*H1av^*N2)(1=a(7T zCio=LyjMK#LADr5o=zf}5kJUpf*Lnj-#i5e?-PP(96m(YL=>;E1=%arb* zsLim>PkDj;2T_&{u*Vwsd-v*u{^yX( z$^h7I-G8|9%7_0&LAihqhp1osa(l0t{C{uZG{B-?9^BdQ|6{k=vCW&fk^s4VW42EP z;(wdVY{#ZeTLIlaRD4j1G5oK&Ke+!*^Zxt5v!px^R9)Nops;f3#!07N${<#h%M;^Bo5?^1-f8ryl)pDY8GUb(P+mK z!sLLMHrCFQ|DxLosG`iqRTwYe1Mk|gvK(9aA&lfWGI^I1KVhY$9(l1|$q$sTOlUiO zOaW+4%q~h!jZ&QaoNHM-*hpsHNPOU--m~B41Yd9L^#8Esr>2rg=24ZPRQ1u6^RTYh ze&!wMQMt-f`lw4XyTubdDmuvQ2`JMc1)w#%3CI-tD(Tr>L^LMl@`{1r!DOPG#f02-_ zrCIRGF8HR>LdRZgmd6B^t zXBl#nylU&QAj+f8!o1!k9-LVTuxZPGVM9nyC~+)FX6wH$<~mr%D{J#wwbwIc@^^jW zSxZ$ht>jxTUl>GK7+L&@`?cT)A9sYyEVOilv`%OFEPa>nboU{mX{qw_?6$-LYLvA7 zGve^h)&>SuaqLwbsEaJOB!7Er;)(au>p?EgP}_U(2(%4>XY9e!k1e7jEUnP}tD8rO zvEJUc*$Ze#W0XQQF$=G?w<;K3&A?&RVd0Xd2c3)yGK@Z6b*_()HDa(A3bhHW*@U-= zDms}R;tO=-Dp=ZrM{`u7Z9vg#IvBZh)J*Q!e;~Mnv1eJ0Z`wy_k`c~z1AZn$s9}Qglo)ik9{vFdaVITk+zRUk} zdF2s~9~%iM=_AM@qW7Ekt(`_>tM|}`767iC46*-{fZt#lEDh(5L~)BLaa?#r$Y_61 z9TdwnbvLTed!F4O{$v&sXhT^Bc{8s^K!CVSeJ=7Y8Q}osE5V5xef;n_t17iucv+{P zAEfQO4k4n4$TL*AX~4{mM+w?pcUe@=B!L)y0C?&P%K-a@l`Y%z`)W~c(?dC*W4CNk z`J)nkfV^0H)-(oImB07Pg5Ws*K{v~g-%*GmfOWw}Y(cHB|5N;Ey~M#b9oue$dZK!ie5J&4_3!U3?|~`>=<-u)!}54J^1frg zkMAtWVVSIiBzIe7WMqUgwa;LXGs^<;B@iBU(78wf5wmQ_DZUTGrmEc(XZe@8T^7*$lzzG*Ced2} zM#dEg88&Sc*_S&VPJPa+erii7Zdba>p#h~ds{?&0CAmH-V>mH*^hbi$7kxR&Z~3HO zRq9a>>!V=2GQJ-fWF9subr(0ouQM5RHupm#Z(i;>tGZmZP*I<7s3#q!&{q6ZR1e=y zq$5b5hz?^d{P8-mi+n>XUONei){YFzL2a!Y^muIeu)KS0>q+aNLtaIC?))pu?z0|w zEWx3oz5VHPL=^D{S_v^zEuk>>o%u^P$;+l?P(0f)!+d_`Im`srQ_d;l3ahVau71sr zIy)emTHX??{mEvh&s04o>=5mMOSM}CM}8KQ z>qyU(CE6nwEvlQI?7O^Ov=6DP^kbppC|Sy;hb*~*BRek#Vn&dwi}6bJ5=-VBW+fm) zTmM*^5P`WrP4_CA&CnfqeKgHQdi&xv6}wN&PhW~A+VHOGCVuIweb$1Z_sz^I%B2-9$XDLn2fO>B}RJi{6^zGEjqk z?>#?RBe06*L*!^Jm5A!+%AMjumy?pl-+x{f)e#lOeZtW$N=icCxZAUm+U_krL{Tz(V_ObyZ_gtB6x6fBGpg~@&tRja)8x*l zgkW*=*__e-sd?>EsL84%Px7tEWbABj%9B;$Lq5sIf5Y>jUu`|i?3L2WS; zLBGRZ>76XA2UY8b6TgI`F8Jn4lrH5$gm3&U@G!uq)9xFi&fB;lM2Bep0AtO z`NInJ((>}aFX*u@6xkNnmt%i^()Vyv?!exabj9A@WdQ}R>06p8U*o*OQ7sgdUp@Xz zEwy`WWd$MWCqZ_U8N1fKAo44MxD@0u%5T(`t?5yaGn*5CGjwWxMWB>`R>?LW6<#U^ zq7NbAijn2_cf$Af&u%QpYbaS+o>3*}hypW%KGK6C$05G3l(IbzzF+A8ldIVOTR-+s z;r$o<;BV)dU$eTX& zEyLhRy%%#;%0qvaCe^*-5gSAv3}rz9&?~pZ7IpjXKz89T_Rd{71cO|+NF_f`3vVES z4FNEgb>hEu%Qs2NFPY73o8asKiqn|y;eruBY0wLY!qX^ERbeC2Ka$mrrnPJLJHJZm zf>}DwcJ}Nu0RpT}rm@LDP1pp+{?h$MsL#H|M<U&Ik-b_W?D#f?E-yXL&3|4A&=C>_rEmto~Yynk>Z6MGNb$S>)_|Uy7Ef76arg8 zuA-93X}$jHpjP2<<2{ZP-G-44hb$9}eT(W8xk0yj7xlb#+j*U=yPfa)`ESx~<>DUq z3J5Lk)c}TtJ9WjfqCpOyQ5#PS&Ew06?^w_;nSU<|fdB7C zM+{SNMICCA8Oqnb=3ebsfm3FtQvd9Z&PMx1OGPCv0IaZ1=m0&+*N?$gN<6<9tqO z#B+7Qi_=@ka~7|v%|jaIZUq&wGwA8&H?&O~v$htGmbg z_QO9sJI$GN*>HsY#~S5cFBPOoa?LP(e%sNTWZh@>5c=ou0n&O0N+etjoUG${lynTwZNcK#(q;EA{H5!wmJ6}UAeTd&P)6VujT z98TfcjFZ)lUcq%T>OOi zS@Zw;2DomV13d10;-@|{mJ`AMIaksrhFYEUU5kKPRd(OOFKs+3HpXA?6{R})kWN%DI~B9Avo*EJ+~neaSnnEFQUj-* z%RotEmEC>on1LB`xp>xErtKaLnQEmTomzz_5b#DkS)k;(R*7MmBewQx@@?4_?qv4(-z);ijx9Dtb4Z>K5AQuf_``Pc^hi zmM)Eqxen9}D^jO}jf(&Yy@UI`K9>He`^_f*yz-r3fv41^ZTpJZBAri}@U4GO> zoaZFB?)*y~7^UM3qog7;Bm+fUvu)4T^S^wJek+Ac*X?bG4TS1pZh-RUi#=p7Tbh0cHgqHtnE?|Qa0GS!+8N*5pb1wsY^h;=`ez-r_=RG zTg0d#`{u0~maO_XsrmuqAA7!TXaVXmr>}<_yS%9F|5M9fo5{-i*iTz-<@zSX$ecTP zkCHaWgmMMz>D^n;Y5>KvpE7)K^CbSL?l*6h3gYG2WQ*+PU=`w0U_Pjz#r*R%lxg7) z=U+=EYpI9Ff5$Gr7Rw;FNB;>U;2>f3J2x5NSF({rHbO-mT$+?Rd?EqzKY+}Ok&jZO zeGZRfAbS5s3*?pthU64{NcJO8Ia((AtNV9OD6P&1Gb9Vi!02i2&u9aTZ@DZCYgKOu zsza2RF4p8pU~G3A|CwNxep~wE)`-nMgJH=x6v*5o4v8POs96G-?#qe5>6|3+u*(>< z989G+zg1+`O4ew1@1??Hml@TdNb8HB(iCTieu!FL2Tn2He9x#5APj|>C z188@Cp2_OUBCV^y9#M9`tp|Gn7pD>|5Q0Yjs(5j(hPE&!N9`?LeeI^ksOL9g_ZFE~ zYXGy4qpp030GUsxCow5DW=gb5hAFT9Ri zSXora!K^e^4CaZkZ9!Vnei2P@%w8SFdyQTJTeuElX6Yv(XtlBJE8NSRO7R6m+qzE5 zR~*>8gqm3*1UKfzn>!3<)oy|aTrtUbwlG6QfeLfFV=2=}&$6)040^OJ!@Dwlq+`M$ zRNUwiz`=#n&ix|kXbet&InL0lt2X0q*0WOwTqAGBJVVaCt?ALZV^4H%osuk0vPG3v zh894wHu(#}rL8C)p(ph&@yCrNiR##Xd!$@D2Ra4_z!t8s;-N+|NqE^aT_sLavQ3PV z5#S5wuYB2?4GcC=jU}XX^)t@%Z5$@p;V)I{ge-(4k|`P#^bTO*)HGfxT1&c4h6xn6 zt1%UL0&KdIBHJR z4?ee{R`23XJ=LXmN#3m1JmlWMMh=PaC*1Vtkd!f$?=S{miaiuHA(VL&w*X|+!U9SoP0mh<)EQ45y`#?q5CdW=WgkjhAb#g>=O zjCHW-c-e+6P-x5A0urO?{vZX|mZr5c3VIqG?M)Esca}k0g3Y`(AAV{E0y2lXR?4m0 z;n^e;EWx0(Ppv*qR>!rtI0(87F4y#KuY#S(?0-%ASyuv9R!W965wkXwh~V=!znwr2 zjr!y5r771q$k<$aLriN13g6c?){_`MmIl>fcmmQJ!%kX#x!f6)WNC;8kgjl|t6?VT zq?WiWn5fag=O1QHn5)=~{GM2_7?Wq{YsT?cdic{>rj**|oD-+Nax^|%epXug7?Cym z>XGoP``USf_*_U%6@E5Gp!}{6D2#QEWx>ubmA?fI5~qC5T0g_OkAB=MqDh5{ggL+{ z4@e_7NiMbOUMoc!HXR@0go{2X{qTNw)``16Ci|opS|%>iu;_k{qDlVM+Hlkx*~$i3 z80HzOv`t0R%hsMikt^0SHMx4u`j+!`Sr?1;r3jX=fuQ8z^I?SV@k;Ta8ko{2SWF(< zK10jcY3e&rK7c7F_gR218x3%-hq+eN-&R&>B%nSu91%V>r zd~R&{&5)F#In!#B+g2=Nr(gjR*r6jhhM0}^qNkDWhI9SQ|H2WW)@x8>n&R?_eL@wq z9q6%e>~nfJ+U9t>Ax8GRTgUcnntHU1Ej;&kpZ&Oeq3-o-Oe8U7RK!Hi&m7TeJtuAkP;>{SiLQQ;pL; z;%4W)Q^-gg?*W=b%6`p)=86{~u$DlXROOYWY~%aG66fBw=;(3px)4u@yLNvYTXZfu zC!Zl+xcBO`MU%sOaxO`icF=M2$$XZip}58n5lklkhF#eI!EUp-wN>CCqYcFFXh|1`1*1&{C~5a`p&F`; z_5#(gQ+-O?*82-2ddX8on!x*Z(`S-psV8%wT0rHOckmqOBY52LdTaa)zP}b#*xl<0 zdM)TPA6By850Jb@#aD{0FBa%MWgjq!3;bf@A%ZK&?XfIIJ`N~OVE`{9hmxDI@rdgz z9Q&Q-b*0DVjPD;2(VGGN583#f`HErM$x>k3#xK;=EzKF5g zm16+6U+%*D9Jex?cjg32O{v1jypf6Ao(6(%=RMcY)lq`TkDs!a+DoS&v>Zw07!Cd1c8I*+_Op!mC&E-?3^5w(XF_OgcE=Rq#l5ji!*wRS@2|JZMgFF@O??nHGk$QV# zftDk#K#|;NDulS&6{TNb_wc5|Vx2tIWQ5;}$u12|C)t3lWszqmaIy0H-#U3vHRnDC8?I8BW%Nl^Sp4<;RZ09@@pa#6|BH&GhIpS&%kbgH_h^hp zgMH^5gH##)3e@?&?V)0NRVE_x+@i+c@MbpEGwSIhXhsK?UXqzV^Gnd8WKtd1Rq*iB zJdnA~1K?xVjC3-z;RZAK4j?p)#$H;ctJ30l&)Gc5jD5t`^?Bko-??VO3q&pr6C^07 zDyg^{!?*fNlZ^BGQPVN|!MZ*sIa?Bc0@0_FCg*^$ppPzMLDjY|n`&VmA^$K2io^$J za@Jz}eoQWSnDYXFWkY5esST6RURjzyW$@w?uC68J`p!|?;dd;IitoFqV7lP9pKj+? zfKacgRVfgJ+X)@TOu-tMAO_wK!0-v~)#aeT)JH=m~OB@=I?lzjx)C z+;{Jf1wbiT(d2Y{R@3K%E9c?UDr4j_fyC8?S}9TZs5tcG%0E+u5Z68CTOvtY@9u+$ zNNpj?zV?aK>UNQJ9dwgaoSa`-i5qT+?LBcDV&@p@&|R3uDA&v|XAA~owDDNjFKPEG z*kD~GDY3GMnR2-+XcFlA9@k@kAL|I3*ePQy8Be1*SGUyyD?h4`t~w55&e}h-1LJyl|9FCi(#6Iebg~sc)PP^ z!Ig`alO6^?9}d??%V;$UqzK^9i2uqzfMPu2zA*)vTV0{Cuf~(#zEhGXHra!foibsB zz&U-Tv5Axlt8QjEjCEM(lP6?{%ONI-{~;e|ZvpaAK9qh+C1_(RIXx^BxEo1#3WbCJW1Jrv1D$br}Whf>tED;Ac zKr`?oZ!Js6-Sc|Q3@@~YWz9YY&c*tK(GkHazU|6-Petz5h;_Xr0hEXj8IFE=u+H#> z?rx1(({7c&0n-uEq6|ahXEV*Z-u;KjkLYCk%S`Tw+K0Cdoxe zp5=fP@LvDlbWvzWlOl5Z(cbefwQYl+{gWBMU;b{~AM)Pp2P77}UsC#ADzrY>AXVPe z#dhh*3sKXYm+-Nr<2uJ>eZL;_nVIKFYu^8-1imgnfyV}1J#}_JC>sC9-@}fiXPkFl zdH@?;_;`P?{!UzXk(5oHzD%Q|JN53Y0KRqHziQ#Z?+4*<(0u5wWHYS~N_L@e+6ol8 zG!f9V`tu6fh6D{sxz+MLM8w2kQ}SBZ1wt9Z&c|!7U^E57dtktliL>j>ec038Inp)2 z!tl7&3;0|vU54uuGIVr;!l39vrB{FsRWlzwxIejao z0n{xdAO*C^5+%Fm0$$@x{1==~|CC3S_Ib@7nC>o`g8iystB)2;{CEx3_9-07`&s@4 z>ytD{`9E7Im_J;MKCopE~B=rR1WiPautogYxa>l->}-;uHl zYnv`J8ohppKWF-z@)9J=SivTLCoXWA0G<1<|J_x>sj?1`f^Gq)Sh+>Qf#Vx_2xOdu zlVzz~tb*V&;|5~a|B#opi3>IzFZ1}yD06tsj?w4@J=5ywpE>5$)AQ+)#$A2o0a89y zx7=g9QcQY%$c`@5E?92Kse6kv{0o4PQ}5cgWrhY;laBT8!#JFM8Snab-9JL^*09@R z_2v-G%;Y{%iD+%pNVZ#k`ycH!L72-Skxdj5ciK7E26T=F^2yATi-5gLp*g<@w>m>K z86k{vGcJABQfw}oGEL#=LXT=IFEzZG&A(r%8s3hz-%g#a>UVhO{L)C~V5OF}1tFaP z`oErLEcPQ;Gpyr=DcM?HFNshWyb=1L;Cr=h#XUji9H2IJBKH>8;Wi^3GotLFFmSWj zJuV!K`skIQCKD^~+3e8IFVpRW2hBF}&@SMGv5>2t*n{WGC zuJ9z(yw=SF8JnNLXv}rZ#**d{qj}Qq!)B7 z-^Kt7IKE#bCiN#|R(c(BEH!H1y;Va?)KJ9>SOMFf%b^)P8I0c{KsRjabYv{T_KX1u=9f!` zD&U3*?LDo?m8tlvDl%Sm7aRko*6bAkWoF#{#NL(fnRklS_WwdQ@%k7j{Otg+l!2P+ z2hH!(BosbTi)wnlj|j?pBxAybfztp;_geMw4^9%lAWK|DI%Br~*OSo`Rg$1= zULZ|jeb3{r_@yodwDm`w@yT^Xn=0xrZfXbzlU~`79k@SDdwE?_9ELFIe@vVvDWb=`WzCl0D;g z!XpVO@DmKVLR}7byW$Aw@{#S61@cCDB1Q!>@G;GAj~vzVWXW}rSFQy3{H$^;jnT(j zWm&SolH3%5w;HzmyEa^JrUQdxqwP)HtX%tZG5WlMVjJ^fnW}7g>IMcfJCgBgXew0Ajc8wa{#RmCx*pJa3HW*L z8Tc7inQWm4T|$iMT)ep1vuMDyG8;!L{I4hX23ImdYRTyq?HqFY*ZSK zzzyZ}WBjIpF#`YY%W|G?Km=VRCub@Q{`s341D7 zvTuhRkM}xu;db;%@RKub(Nf42=1zCdtQGKEE9X7FA>hMGd&1^{QU}JeMZrqJw(XF( zVX-y?AI&;Qg6=hX&#oE4+1=S78>*50hQ(8yPR`=7P5( zA=4SPtyH1RC%h9XZ|S-+k`ag)TF8)lZG zYyCkd-46z+{galJ^;wfHKnglTC45p;fj}nFcwwX+m7ykyYsI^zgEm_!&zkcl$a2`l z)uk!A3uQdufD`s=7COS!K6D`~z+vv**w{E~>zSVJh3T=hdcJmC{v#Il6Aas{ zy#EXR7bp%Ak& z%BV{-s)MsLm(!Ict8Ll~&{lug^AJ>@(S%_#UkC%qp4PS2U6yO9hC3_Mpd6%zfU}>VGiS_eOA($?tCdcR%z($lX?65JIr_JkYzg= z#Ij}`Vqmh;L58^C39OV+=WL^ZSN9{M$rbz}!AM6!tXhWQr(8;Dp?Se#p-Ol0BbF7Y zqgY7yd~{U39_?zsM>TD`U_cRt2W(8#S0H>_RrL`9ECz?FQ4rb%_6I25M}1YTmJN zHn6wFYY6@bR|z;-9}fQdKmBseMt&<_4FNXH3=jUYUeR9%GQ%QVV+y;$j}ow-Wi;P2 zs&*SyQwz2jb4X04;)#u++qf9$`vu(*!{M(!HmyjI;OIoE}cKq;~^*@_fMF5s` zG8C`kRb_t7vU$^22I;Qb|GMu87ccYc&HV!7d%-i7N|{%HqFEF3^gl4j6~-8_BD)>D z;6&dpFnRqQp07IKWX5_y&TG$T@dV%nn^(Ff0fQvhh%fbP(0%`i;le|%Uw8k zly26-SN=djFXd}^zpmO}e+!Un>j7gz9r?(30PFg=erj7!8e6{#t{^7tA@d#r2GG0(}PH}cO>WEMQtrkflU3sJr43dO(n7E03apU=o6)l*B_x3l>U9ccap!Qc9jR zyYc+bd2>Ja#l3I*AjAA-X3d&4v+BEsoAQ-k%5}uu=PJz|M9vn-Bv*lS|B?1zTTE&T z>oS#CezMVQ0l!XnIDHD0fRGBKFF_x>t(nX{ic4Yn0vJ&}0ZY=@3#5Qo$L^CxPqVP86aE5hULVK)(G> z-YNFRP^IXW)0k*?*Y&sElGH6J#-=rK)#b({R?qP!2ioFdi~A*Y*D|*Xu*Qc zpI<$yz9T|$U{;S&UI2g*U&-_vgRcwLDs^3h0Dx5O-mDA!hvnrmH#I;e){GZh{Ak7a ze!w1xhC}J3ZakkB(stqb;laKt1LLcXyFs6?`>|$=X;FGH0-BI`A+CKUDd(KRiKdd$ zl_MPx#9(B`t2r3k(2KAOAzq;|YKNrxh-O~M!VZp*@)KVtjLchRDeO$L`M^QYtu8BfYs^L2`J4 z?d5B+*^*bugDE|ND%k`117jtFNeQg`Lv(M-){V|(2A%j75OmPxJooo3u-~(CNIreq zlwuRJUZV1nn7ymb@!r}`QL@V8qca0>W>YWXRdbWxOb-ky^I4Z8ffmWOr5?-`wGdSB z>+gH}dD9QDtnU?Tmmdo;8c_rMD%EhMLkP`< zv)*@j?_^DL-YaLL8x-ZZYqf{qT%6jlN~X^C46_bJ{?T2LTij+3ybfBhne~t@pS{0# zzr86qc|u1NG*ycehwX&kPOxMmN$qw-untwR2(pSwhi zN}`@yys6sKc=>AAsecb5M2oqb(fhJe(yI)L`U^=*EE;YWNAPyP`E|}`vv_^#tTfVQ zd%HL1l(ckh1kHIl&;IWfXDW5i89LzV+ zp6DeXd?MUq!k{IiBC%aQ!^Pj>?W}LxgFl1G6IG8DGE(;01BR&f7N48C5Uil~@_zS3 zaqAz*Dlxq~RqF2110kkrxvvwACap_%-k7Ut&tEUrv)yO?F|ET%RCtEA2me4&+r)sG z{}w-RMxa`7^KAUhlVKnQr=TJWD!k8YSDI&;bNG zGjzG;;O`Ku#>B^gthy|Q=#}BZu0ILR0uffRjK;RSr~$a`>y3KJ>T5z<0wgND=4f>* zBuqdIiOby^Z8FTmX0O$^ zEK3oZ$Qge9HRtzNFKr#yx~Yl{A!dgG1megm0w_!#&s$n!-d!8 zQ|aegb&58<78~ZwulaJM<9p~``{&;aAe|!1864u9jWczfvsgw|_7_&%@!vSAn~j+$ z=tz@pvN2Szicrr@UHDc1tDdF6+nSF#DfB20cHnF}In{p3_Wd29Z=_2XXS(ks&ywyL zfwnngb+^KPdiuL;(}*qv{~Jngw-k77R4Skq_&s>Y{1XJU*M_$5|B*kX*09!Bk~_H> zL3l#+-9z!7jnA#YwI1Ki`nD#zq7q_PRFf@28WTQ9KIZK;OP;KMm7JFlIys+!(%RnM zQdn#GsWheVP?tfw+pP76xow4|cDePFrgwp%a}(?~O=ZPf>GK~vvcze;x!G6qaCU>* z=r1_(gL-C)y3%JGg=x~&{7fBtnH^^{G`Q`xA7CE#WR3 z)ZOahg=Wuyb}l#AFtzeah?p+C@a7yQW5;8>xXr+qtTNMaXs+K{DAlk$BJ$INoTv{) zrX9Oolg7L(UL?M_77i~JvoFocxzW~xxo71=QRYL__NElKoW94|V^6^K@X8mdYvBg*aP{N?_51~i>144t?PJ+UYRH1jdEyrIWu(1>(t$X-j(4Yc{6 zc1P_Yec_Rj@AlQMPVPGu)(f4CxRXWi4{PXVvjm5VLKpJL+S?Ddias2sj z`N&7bC3%DP@{+Gxl1B$qMbz4FjjtS3`)D>ZTNbD~H9xaYtg|Xs-$hV6l?bZHJxO`X z%wr=*XTS;)KKdroZdk*tsrug%h|e+;vT*Uo_tpabz?Yz77r-z7FdM=To4cDiGId7k z+O%7Wb)t=ohL3mYcW}Mtxi6@3R6+Bwxz$*vUPsB(-8M0htzqQfJobL|9{cH;wEbP< z#cyUdh`N>c=PGY8Z%MD7%}%RJIW2~OoFFC9gT9t^j(Bty3{)>JdIR#V=gqqj_U0so%{@0=M7Im2ByUWL@MQ zee#jRU9Qb~rs@8tQibcmlbky_`mReK)%kaa1=v+MN5!gT*NMj@goqU^|DAhQ7#V`gtaneJxo_4v zByf&-fkcmGQnnap6ff<6;-ia{*t>e6foNs$BpdqPZ|SgkEgc%UiqN+6yfl+KA@zbN z#a8$*Q>zS&57lSfVdF0v;M{}aBorr^VQJXvRee~#v2_>uNt zg4CHis#hN{W-GhQ4>~Ds1EqL1BO(<8ZmqybF^74_|IasOmUANjIG2P?L*}p>J-S6we}A%W8OrVswWq`uU{N= zhzEFxz6as&@0iWq5vo|8W>_rZ6Dr)F(7ns=C(fJ6OawFbG(^GVfHGzRS6;{`xH`s1 zn{Q;?ELO4)Z8Ysq?G74ZEx*_O_yMS~!Yh~u^t6JWdTCkn(}U<-B3avZ^ZkTI-tSQi zBw6{^$AWM^T3r1*S)#y92L&c3Qo8YYl1j)2hJj`M)|Y&p)Gf(=ckI_WdA-M7z- zRUnKngq}FvCAoks6v5R108#n?0(wop6; zdIEb@@}zdpjfF16p!gnIq6eHrzMaHRFg^4vkQ7=abF=Glhzfjv`sc)5gq{~F0#q`EyF(r#I zM+gww32!wjeuK>?@E41=_Row?nSfWf+Ib@iMTD%Epq{xXW467LGbhAzIX=GOrR!

4Zd7c8>z{YVLFx0KLcJ= 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 literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineView.stories.tsx/with-jump-to-bottom-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/TimelineView/TimelineView.stories.tsx/with-jump-to-bottom-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..8d9f3484b07f70ebc2cc930df3faafcf1a781610 GIT binary patch literal 43531 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 MRqcJLd(8d*51mtrffj5-AE~-xPa`A_1pSjh` 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 literal 0 HcmV?d00001 diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index 414e1a5802..5a906f28ac 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -68,6 +68,7 @@ "@element-hq/element-web-module-api": "workspace:*", "@matrix-org/emojibase-bindings": "catalog:", "@matrix-org/spec": "^1.7.0", + "@tanstack/react-virtual": "3.14.8", "@vector-im/compound-design-tokens": "catalog:", "classnames": "^2.5.1", "counterpart": "^0.18.6", diff --git a/packages/shared-components/src/i18n/strings/en_EN.json b/packages/shared-components/src/i18n/strings/en_EN.json index d93dfba100..a661272f26 100644 --- a/packages/shared-components/src/i18n/strings/en_EN.json +++ b/packages/shared-components/src/i18n/strings/en_EN.json @@ -83,6 +83,7 @@ "notifications": { "all_messages": "All messages", "default_settings": "Match default settings", + "mark_all_read": "Mark all as read", "mentions_keywords": "Mentions and keywords", "message_didnt_send": "Message didn't send. Click for info.", "mute_room": "Mute room" @@ -101,6 +102,8 @@ "shared": "New members see history", "world_readable": "Anyone can see history" }, + "jump_read_marker": "Jump to first unread message.", + "jump_to_bottom_button": "Scroll to most recent messages", "jump_to_date": "Jump to date", "jump_to_date_beginning": "The beginning of the room", "jump_to_date_prompt": "Pick a date to jump to", diff --git a/packages/shared-components/src/index.ts b/packages/shared-components/src/index.ts index 194f471630..2abd8c1bcc 100644 --- a/packages/shared-components/src/index.ts +++ b/packages/shared-components/src/index.ts @@ -44,6 +44,7 @@ export * from "./room/HistoryVisibilityBadge"; export * from "./room/right-panel/WidgetContextMenuView"; export * from "./room/timeline/DateSeparatorView"; export * from "./room/timeline/TimelineSeparator"; +export * from "./room/timeline/TimelineView"; export * from "./room/timeline/event-tile/actions/ActionBarView"; export * from "./room/timeline/event-tile/EventTileView/DisambiguatedProfile"; export * from "./room/timeline/event-tile/EventTileView/E2eMessageSharedIcon"; diff --git a/packages/shared-components/src/room/timeline/TimelineView/.gitkeep b/packages/shared-components/src/room/timeline/TimelineView/.gitkeep deleted file mode 100644 index 8b13789179..0000000000 --- a/packages/shared-components/src/room/timeline/TimelineView/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.module.css b/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.module.css new file mode 100644 index 0000000000..cf3b19ba26 --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.module.css @@ -0,0 +1,201 @@ +/* + * 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. + */ + +/** + * Overlay layer — absolutely fills the timeline so buttons can be positioned at + * the top or bottom without affecting list layout. + * Children use their own absolute positioning to match the legacy components. + */ +.overlay { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 1000; +} + +/* ── Top unread bar — matches legacy TopUnreadMessagesBar ────────── */ + +.topUnreadBar { + position: absolute; + top: var(--cpd-space-6x); + right: var(--cpd-space-6x); + width: 38px; + pointer-events: all; +} + +.topUnreadBar::after { + content: ""; + position: absolute; + top: -8px; + left: 11px; + width: 4px; + height: 4px; + border-radius: 16px; + background-color: var(--cpd-color-bg-canvas-default); + border: 6px solid var(--cpd-color-icon-accent-tertiary); + pointer-events: none; +} + +.topUnreadBarScrollUp { + display: flex; + align-items: center; + justify-content: center; + height: 38px; + width: 38px; + border-radius: 19px; + box-sizing: border-box; + background: var(--cpd-color-bg-canvas-default); + border: 1.3px solid var(--cpd-color-icon-tertiary); + cursor: pointer; +} + +.topUnreadBarScrollUp svg { + width: 20px; + height: 20px; + flex-shrink: 0; + color: var(--cpd-color-icon-tertiary); +} + +.topUnreadBarMarkAsRead { + display: flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + background: var(--cpd-color-bg-canvas-default); + border: 1.3px solid var(--cpd-color-icon-tertiary); + border-radius: 10px; + margin: 5px auto; + cursor: pointer; +} + +.topUnreadBarMarkAsRead svg { + width: 12px; + height: 12px; + flex-shrink: 0; + color: var(--cpd-color-icon-tertiary); +} + +/* ── Below unread bar — marker is below viewport; appears at top-right ── */ + +.belowUnreadBar { + position: absolute; + top: var(--cpd-space-6x); + right: var(--cpd-space-6x); + width: 38px; + pointer-events: all; +} + +/* Green dot just below the chevron button (bottom edge at 38px), hinting the marker is downward */ +.belowUnreadBar::after { + content: ""; + position: absolute; + top: 30px; + left: 11px; + width: 4px; + height: 4px; + border-radius: 16px; + background-color: var(--cpd-color-bg-canvas-default); + border: 6px solid var(--cpd-color-icon-accent-tertiary); + pointer-events: none; +} + +.belowUnreadBarScrollDown { + display: flex; + align-items: center; + justify-content: center; + height: 38px; + width: 38px; + border-radius: 19px; + box-sizing: border-box; + background: var(--cpd-color-bg-canvas-default); + border: 1.3px solid var(--cpd-color-icon-tertiary); + cursor: pointer; +} + +.belowUnreadBarScrollDown svg { + width: 20px; + height: 20px; + flex-shrink: 0; + color: var(--cpd-color-icon-tertiary); +} + +.belowUnreadBarMarkAsRead { + display: flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + background: var(--cpd-color-bg-canvas-default); + border: 1.3px solid var(--cpd-color-icon-tertiary); + border-radius: 10px; + /* dot centre is at top: 38px, radius 8px → extends to 46px; add 4px visual gap → 50px - 38px = 12px margin */ + margin: var(--cpd-space-3x) auto 0; + cursor: pointer; +} + +.belowUnreadBarMarkAsRead svg { + width: 12px; + height: 12px; + flex-shrink: 0; + color: var(--cpd-color-icon-tertiary); +} + +/* ── Jump-to-bottom button — matches legacy JumpToBottomButton ───── */ + +.jumpToBottom { + position: absolute; + /* 12 because height is 50 but button is only 38 = 12+(50-38) = 24 */ + bottom: var(--cpd-space-3x); + right: var(--cpd-space-6x); + width: 38px; + /* give it a fixed height so the badge doesn't make */ + /* it taller and pop upwards when visible */ + height: 50px; + text-align: center; + pointer-events: none; +} + +.jumpToBottomBadge { + position: relative; + top: -12px; + border-radius: 16px; + font-weight: bold; + font-size: 12px; + line-height: 14px; + text-align: center; + display: inline-block; + padding: 0 var(--cpd-space-1x); + color: var(--cpd-color-text-on-solid-primary); + background-color: var(--cpd-color-icon-secondary); +} + +.highlight .jumpToBottomBadge { + background-color: var(--cpd-color-icon-critical-primary); +} + +.jumpToBottomScrollDown { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 38px; + height: 38px; + border-radius: 50%; + box-sizing: border-box; + background: var(--cpd-color-bg-canvas-default); + border: 1.3px solid var(--cpd-color-icon-tertiary); + cursor: pointer; + pointer-events: all; +} + +.jumpToBottomScrollDown svg { + width: 20px; + height: 20px; + flex-shrink: 0; + color: var(--cpd-color-icon-tertiary); +} diff --git a/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx b/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx new file mode 100644 index 0000000000..daf46a51b4 --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.stories.tsx @@ -0,0 +1,104 @@ +/* + * 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, { type JSX } from "react"; +import { fn } from "storybook/test"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { TimelineOverlayButtons } from "./TimelineOverlayButtons"; +import type { ImmediateScroll, TimelineViewActions, TimelineViewSnapshot } from "./types"; + +// The overlay takes an already-split snapshot + actions (not a ViewModel), so the +// story flattens them into props — this lets interaction tests override a single +// action (e.g. onJumpToLive) without rebuilding the whole object. +type OverlayStoryProps = TimelineViewSnapshot & TimelineViewActions & { scrollNow: ImmediateScroll }; + +const TimelineOverlayButtonsWrapper = ({ + onStartReached, + onEndReached, + onAnchorReached, + onVisibleRangeChanged, + onAtBottomStateChange, + onJumpToReadMarker, + onMarkAllAsRead, + onJumpToLive, + scrollNow, + ...snapshot +}: OverlayStoryProps): JSX.Element => { + const vm: TimelineViewActions = { + onStartReached, + onEndReached, + onAnchorReached, + onVisibleRangeChanged, + onAtBottomStateChange, + onJumpToReadMarker, + onMarkAllAsRead, + onJumpToLive, + }; + return ; +}; + +const meta = { + title: "Timeline/TimelineOverlayButtons", + component: TimelineOverlayButtonsWrapper, + tags: ["autodocs"], + args: { + // Snapshot defaults: scrolled up off the live end so the jump-to-bottom shows. + items: [], + atLiveEnd: false, + pendingAnchor: null, + highlightedEventId: null, + isAtBottom: false, + canJumpToReadMarker: false, + numUnreadMessages: 0, + hasHighlights: false, + // Actions. + onStartReached: fn(), + onEndReached: fn(), + onAnchorReached: fn(), + onVisibleRangeChanged: fn(), + onAtBottomStateChange: fn(), + onJumpToReadMarker: fn(), + onMarkAllAsRead: fn(), + onJumpToLive: fn(), + scrollNow: fn(), + }, + // The overlay absolutely fills its positioned parent; give it a sized box to sit in. + decorators: [ + (Story) => ( +

+ +
+ ), + ], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +/** Scrolled up off the live end: the jump-to-bottom button, no unread. */ +export const JumpToBottom: Story = {}; + +/** Jump-to-bottom carrying an unread-message badge. */ +export const JumpToBottomWithUnread: Story = { + args: { numUnreadMessages: 5 }, +}; + +/** Jump-to-bottom with the highlight (mention/keyword) colouring. */ +export const JumpToBottomWithHighlight: Story = { + args: { numUnreadMessages: 3, hasHighlights: true }, +}; + +/** Read marker sits above the viewport: scroll-up + mark-as-read bar (top-right). */ +export const UnreadMarkerAbove: Story = { + args: { atLiveEnd: true, isAtBottom: true, canJumpToReadMarker: "above" }, +}; + +/** Read marker sits below the viewport: scroll-down + mark-as-read bar. */ +export const UnreadMarkerBelow: Story = { + args: { atLiveEnd: true, isAtBottom: true, canJumpToReadMarker: "below" }, +}; diff --git a/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.test.tsx b/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.test.tsx new file mode 100644 index 0000000000..4496f5fb24 --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.test.tsx @@ -0,0 +1,179 @@ +/* + * 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, screen } from "@test-utils"; +import userEvent from "@testing-library/user-event"; +import { describe, it, expect, vi } from "vitest"; + +import { TimelineOverlayButtons } from "./TimelineOverlayButtons"; +import type { TimelineViewActions, TimelineViewSnapshot } from "./types"; + +const baseSnapshot: TimelineViewSnapshot = { + items: [], + atLiveEnd: true, + pendingAnchor: null, + highlightedEventId: null, + isAtBottom: true, + canJumpToReadMarker: false, + numUnreadMessages: 0, + hasHighlights: false, +}; + +// vi.fn() carries a constructable signature that trips assignability to the +// action method types, so cast the record of spies to the interface. +function makeActions(): TimelineViewActions { + return { + onStartReached: vi.fn(), + onEndReached: vi.fn(), + onAnchorReached: vi.fn(), + onVisibleRangeChanged: vi.fn(), + onAtBottomStateChange: vi.fn(), + onJumpToReadMarker: vi.fn(), + onMarkAllAsRead: vi.fn(), + onJumpToLive: vi.fn(), + } as unknown as TimelineViewActions; +} + +// The jump controls must be exposed to AT/keyboard (the overlay is not aria-hidden), +// so plain role queries (which exclude hidden elements) are expected to find them. +const button = (name: string): HTMLElement => screen.getByRole("button", { name }); +const queryButton = (name: string): HTMLElement | null => screen.queryByRole("button", { name }); + +const JUMP_TO_BOTTOM = "Scroll to most recent messages"; +const JUMP_READ_MARKER = "Jump to first unread message."; +const MARK_ALL_READ = "Mark all as read"; + +describe("", () => { + it("renders no buttons at the live bottom with no read marker", () => { + const actions = makeActions(); + render(); + + expect(queryButton(JUMP_TO_BOTTOM)).toBeNull(); + expect(queryButton(JUMP_READ_MARKER)).toBeNull(); + expect(queryButton(MARK_ALL_READ)).toBeNull(); + }); + + describe("jump-to-bottom button", () => { + it("shows when not at the live end", () => { + render( + , + ); + expect(button(JUMP_TO_BOTTOM)).toBeInTheDocument(); + }); + + it("shows when at the live end but scrolled up off the bottom", () => { + render( + , + ); + expect(button(JUMP_TO_BOTTOM)).toBeInTheDocument(); + }); + + it("calls onJumpToLive with the imperative scroll handle when clicked", async () => { + const user = userEvent.setup(); + const actions = makeActions(); + const scrollNow = vi.fn(); + render( + , + ); + + await user.click(button(JUMP_TO_BOTTOM)); + + expect(actions.onJumpToLive).toHaveBeenCalledWith(scrollNow); + }); + + it("renders the unread badge count", () => { + render( + , + ); + expect(screen.getByText("7")).toBeInTheDocument(); + }); + + it("omits the badge when there are no unread messages", () => { + render( + , + ); + expect(screen.queryByText("0")).toBeNull(); + }); + + it("applies the highlight style when there are highlight messages", () => { + const { container } = render( + , + ); + expect(container.querySelector('[class*="highlight"]')).not.toBeNull(); + }); + }); + + describe("unread bar", () => { + it("shows scroll-up and mark-as-read when the marker is above the viewport", () => { + render( + , + ); + expect(button(JUMP_READ_MARKER)).toBeInTheDocument(); + expect(button(MARK_ALL_READ)).toBeInTheDocument(); + }); + + it("shows the bar when the marker is below the viewport", () => { + render( + , + ); + expect(button(JUMP_READ_MARKER)).toBeInTheDocument(); + expect(button(MARK_ALL_READ)).toBeInTheDocument(); + }); + + it("calls onJumpToReadMarker with the scroll handle and onMarkAllAsRead on click", async () => { + const user = userEvent.setup(); + const actions = makeActions(); + const scrollNow = vi.fn(); + render( + , + ); + + await user.click(button(JUMP_READ_MARKER)); + expect(actions.onJumpToReadMarker).toHaveBeenCalledWith(scrollNow); + + await user.click(button(MARK_ALL_READ)); + expect(actions.onMarkAllAsRead).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.tsx b/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.tsx new file mode 100644 index 0000000000..33ba9fd047 --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/TimelineOverlayButtons.tsx @@ -0,0 +1,117 @@ +/* + * 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, { useCallback, type JSX } from "react"; +import classNames from "classnames"; +import { Tooltip } from "@vector-im/compound-web"; +import { ChevronUpIcon, ChevronDownIcon, CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; + +import { useI18n } from "../../../core/i18n/i18nContext"; +import type { ImmediateScroll, TimelineViewActions, TimelineViewSnapshot } from "./types"; +import styles from "./TimelineOverlayButtons.module.css"; + +interface TimelineOverlayButtonsProps { + snapshot: TimelineViewSnapshot; + vm: TimelineViewActions; + /** Imperative scroll-to-anchor handle from the parent View; the VM invokes it + * when the target is already loaded, else falls back to a pendingAnchor load. */ + scrollNow: ImmediateScroll; +} + +/** + * Absolutely-positioned overlay on top of the timeline list: + * - **Unread bar** — shown when `canJumpToReadMarker` is `"above"`/`"below"`; a + * scroll-to-marker + mark-as-read pair at the top-right. + * - **Jump-to-bottom** (bottom-right) — shown when not at the live bottom, with an + * optional unread badge and highlight colouring. + */ +export function TimelineOverlayButtons({ snapshot, vm, scrollNow }: TimelineOverlayButtonsProps): JSX.Element { + const { translate: _t } = useI18n(); + + const readMarkerDirection = snapshot.canJumpToReadMarker; + const showJumpToBottom = !snapshot.atLiveEnd || !snapshot.isAtBottom; + + const onJumpToReadMarkerClick = useCallback(() => vm.onJumpToReadMarker(scrollNow), [vm, scrollNow]); + const onJumpToLiveClick = useCallback(() => vm.onJumpToLive(scrollNow), [vm, scrollNow]); + + return ( + // Non-interactive layer (pointer-events:none) so clicks fall through to the list; + // only the buttons re-enable them. Must NOT be aria-hidden — these are AT-reachable. +
+ {/* Top unread bar — marker is above the viewport */} + {readMarkerDirection === "above" && ( +
+ + + + + + +
+ )} + + {/* Unread bar — marker is below the viewport; sits at top-right like the above bar */} + {readMarkerDirection === "below" && ( +
+ + + + + + +
+ )} + + {/* Jump-to-bottom button — matches legacy JumpToBottomButton */} + {showJumpToBottom && ( +
+ + + + {snapshot.numUnreadMessages > 0 && ( +
{snapshot.numUnreadMessages}
+ )} +
+ )} +
+ ); +} diff --git a/packages/shared-components/src/room/timeline/TimelineView/TimelineView.module.css b/packages/shared-components/src/room/timeline/TimelineView/TimelineView.module.css new file mode 100644 index 0000000000..1b4dd5829f --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/TimelineView.module.css @@ -0,0 +1,57 @@ +/* + * 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. + */ + +.root { + height: 100%; + width: 100%; + position: relative; +} + +.scroller { + height: 100%; + width: 100%; + overflow-y: auto; + overflow-x: hidden; + /* Turn off the browser's own scroll anchoring. It would try to hold the scroll position + when content changes, which conflicts with TanStack doing the same job in JS. */ + overflow-anchor: none; +} + +/* Applied during the first load. The list is laid out and scrolled into position, but kept + hidden (a spinner shows instead) so the user does not watch it shuffle into place. */ +.hidden { + visibility: hidden; +} + +.list { + width: 100%; + position: relative; + list-style: none; + margin: 0; +} + +/* Rows are positioned absolutely, stacked by TanStack rather than by normal document flow. + TanStack writes each row's transform and the container's height straight to the DOM (the + directDomUpdates option), so never set transform or height here — it would be overwritten + or fight with what TanStack writes. */ +.tile { + position: absolute; + top: 0; + left: 0; + width: 100%; + overflow-anchor: none; + list-style: none; +} + +/* Holds the spinner shown in the middle of the panel while the timeline is still hidden. */ +.cover { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/packages/shared-components/src/room/timeline/TimelineView/TimelineView.stories.tsx b/packages/shared-components/src/room/timeline/TimelineView/TimelineView.stories.tsx new file mode 100644 index 0000000000..0331cdc977 --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/TimelineView.stories.tsx @@ -0,0 +1,152 @@ +/* + * 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, { type JSX } from "react"; +import { fn, expect, waitFor } from "storybook/test"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { TimelineView } from "./TimelineView"; +import type { TimelineItem, TimelineViewActions, TimelineViewSnapshot } from "./types"; +import { useMockedViewModel } from "../../../core/viewmodel"; +import { withViewDocs } from "../../../../.storybook/withViewDocs"; + +// A handful of deterministic, fixed-height message rows so the virtualizer lays +// out predictably for the visual snapshot (no avatars/media to decode). +const SENDERS = ["Alice", "Bob", "Carol"]; +const LINES = [ + "Morning! Did the deploy go out?", + "Yep, green across the board.", + "Nice. I'll close the ticket then.", + "One flaky test on CI, re-running.", + "Passed on the second go.", + "Great, merging now.", +]; +const mockEvents = Array.from({ length: 12 }, (_, i) => ({ + key: `evt-${i}`, + sender: SENDERS[i % SENDERS.length], + body: LINES[i % LINES.length], +})); +const mockContent = new Map(mockEvents.map((e) => [e.key, e])); + +const mockItems: TimelineItem[] = mockEvents.map((e) => ({ + key: e.key, + kind: "event", + continuation: false, + lastInSection: true, +})); + +const renderItem = (item: TimelineItem): React.ReactNode => { + const content = mockContent.get(item.key); + if (!content) return null; + return ( +
+
+ {content.sender} +
+
{content.body}
+
+ ); +}; + +type TimelineStoryProps = TimelineViewSnapshot & TimelineViewActions; + +const TimelineViewWrapperImpl = ({ + onStartReached, + onEndReached, + onAnchorReached, + onVisibleRangeChanged, + onAtBottomStateChange, + onJumpToReadMarker, + onMarkAllAsRead, + onJumpToLive, + ...snapshot +}: TimelineStoryProps): JSX.Element => { + const vm = useMockedViewModel(snapshot, { + onStartReached, + onEndReached, + onAnchorReached, + onVisibleRangeChanged, + onAtBottomStateChange, + onJumpToReadMarker, + onMarkAllAsRead, + onJumpToLive, + }); + return ( +
+ +
+ ); +}; +const TimelineViewWrapper = withViewDocs(TimelineViewWrapperImpl, TimelineView); + +// The timeline lays out hidden behind a cover and reveals once the anchor settles +// (a couple of animation frames). Wait for that before the snapshot is captured. +const waitForReveal: NonNullable = async ({ canvasElement }) => { + const scroller = canvasElement.querySelector('[data-testid="timeline-scroller"]'); + await waitFor(() => expect(scroller).toBeVisible()); +}; + +const meta = { + title: "Timeline/TimelineView", + component: TimelineViewWrapper, + tags: ["autodocs"], + args: { + items: mockItems, + atLiveEnd: true, + pendingAnchor: null, + highlightedEventId: null, + isAtBottom: true, + canJumpToReadMarker: false, + numUnreadMessages: 0, + hasHighlights: false, + onStartReached: fn(), + onEndReached: fn(), + onAnchorReached: fn(), + onVisibleRangeChanged: fn(), + onAtBottomStateChange: fn(), + onJumpToReadMarker: fn(), + onMarkAllAsRead: fn(), + onJumpToLive: fn(), + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + play: waitForReveal, + // The virtualizer measures real DOM and reveals over a couple of frames; allow a + // little more pixel slack than the global default to absorb sub-pixel layout jitter. + parameters: { + snapshot: { + failureThreshold: 30, + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +/** A live timeline pinned to the bottom — no overlay controls. */ +export const Default: Story = {}; + +/** Scrolled up off the live end, with unread messages: the jump-to-bottom badge shows. */ +export const WithJumpToBottom: Story = { + args: { + atLiveEnd: false, + isAtBottom: false, + numUnreadMessages: 3, + }, +}; + +/** A read marker above the viewport surfaces the unread bar. */ +export const WithUnreadMarker: Story = { + args: { + canJumpToReadMarker: "above", + }, +}; diff --git a/packages/shared-components/src/room/timeline/TimelineView/TimelineView.test.tsx b/packages/shared-components/src/room/timeline/TimelineView/TimelineView.test.tsx new file mode 100644 index 0000000000..a0f6f7dc94 --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/TimelineView.test.tsx @@ -0,0 +1,169 @@ +/* + * 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 { act, render, screen, waitFor, type RenderResult } from "@test-utils"; +import userEvent from "@testing-library/user-event"; +import { describe, it, expect, vi } from "vitest"; + +import { TimelineView } from "./TimelineView"; +import type { TimelineItem, TimelineViewModel, TimelineViewSnapshot } from "./types"; + +const baseSnapshot: TimelineViewSnapshot = { + items: [], + atLiveEnd: true, + pendingAnchor: null, + highlightedEventId: null, + isAtBottom: true, + canJumpToReadMarker: false, + numUnreadMessages: 0, + hasHighlights: false, +}; + +function eventItems(count: number, offset = 0): TimelineItem[] { + return Array.from({ length: count }, (_, i) => ({ + key: `evt-${offset + i}`, + kind: "event" as const, + continuation: false, + lastInSection: true, + })); +} + +type Actions = { + onStartReached: ReturnType; + onEndReached: ReturnType; + onAnchorReached: ReturnType; + onVisibleRangeChanged: ReturnType; + onAtBottomStateChange: ReturnType; + onJumpToReadMarker: ReturnType; + onMarkAllAsRead: ReturnType; + onJumpToLive: ReturnType; +}; + +interface FakeVm { + vm: TimelineViewModel; + actions: Actions; + /** Push a new snapshot and notify subscribers (wrapped in act). */ + update: (patch: Partial) => void; +} + +function makeFakeVm(initial: Partial = {}): FakeVm { + let snapshot: TimelineViewSnapshot = { ...baseSnapshot, ...initial }; + const listeners = new Set<() => void>(); + const actions: Actions = { + onStartReached: vi.fn(), + onEndReached: vi.fn(), + onAnchorReached: vi.fn(), + onVisibleRangeChanged: vi.fn(), + onAtBottomStateChange: vi.fn(), + onJumpToReadMarker: vi.fn(), + onMarkAllAsRead: vi.fn(), + onJumpToLive: vi.fn(), + }; + // vi.fn() carries a constructable signature that trips assignability to the + // ViewModel's action method types, so cast the assembled object. + const vm = { + getSnapshot: () => snapshot, + subscribe: (listener: () => void) => { + listeners.add(listener); + return () => { + listeners.delete(listener); + }; + }, + ...actions, + } as unknown as TimelineViewModel; + const update = (patch: Partial): void => { + act(() => { + snapshot = { ...snapshot, ...patch }; + listeners.forEach((l) => l()); + }); + }; + return { vm, actions, update }; +} + +// Each row is a fixed 40px so the virtualizer measures a deterministic layout. +const ROW_HEIGHT = 40; +const renderItem = (item: TimelineItem): React.ReactNode => ( +
+ {item.key} +
+); + +// Fixed-height viewport: the TimelineView is height:100%, so its parent must size it. +const VIEWPORT_HEIGHT = 300; +function renderTimeline(vm: TimelineViewModel): RenderResult { + return render( +
+ +
, + ); +} + +describe("", () => { + it("renders each item via the renderItem callback", async () => { + const { vm } = makeFakeVm({ items: eventItems(5) }); + renderTimeline(vm); + + expect(await screen.findByTestId("row-evt-0")).toBeInTheDocument(); + expect(screen.getByTestId("row-evt-4")).toBeInTheDocument(); + }); + + it("stays hidden behind the cover then reveals after the anchor settles", async () => { + // A list taller than the viewport so there is a real scroll offset to settle on. + const { vm, actions } = makeFakeVm({ items: eventItems(30) }); + renderTimeline(vm); + + const scroller = screen.getByTestId("timeline-scroller"); + // Cover is up initially: the scroller is hidden and the anchor hasn't settled. + expect(scroller).toHaveStyle({ visibility: "hidden" }); + expect(actions.onAnchorReached).not.toHaveBeenCalled(); + + await waitFor(() => expect(actions.onAnchorReached).toHaveBeenCalledTimes(1), { timeout: 5000 }); + await waitFor(() => expect(scroller).toHaveStyle({ visibility: "visible" })); + }); + + it("reports the visible range and at-bottom state once live", async () => { + const { vm, actions } = makeFakeVm({ items: eventItems(30) }); + renderTimeline(vm); + + await waitFor(() => expect(actions.onAnchorReached).toHaveBeenCalled(), { timeout: 5000 }); + await waitFor(() => expect(actions.onVisibleRangeChanged).toHaveBeenCalled()); + await waitFor(() => expect(actions.onAtBottomStateChange).toHaveBeenCalled()); + + // Indices are 0-based into the items array. + const [start, end] = actions.onVisibleRangeChanged.mock.calls.at(-1)!; + expect(start).toBeGreaterThanOrEqual(0); + expect(end).toBeGreaterThan(start); + }); + + it("re-renders when the view model pushes a new snapshot", async () => { + const { vm, update } = makeFakeVm({ items: eventItems(5) }); + renderTimeline(vm); + await screen.findByTestId("row-evt-0"); + + update({ items: eventItems(6) }); + + expect(await screen.findByTestId("row-evt-5")).toBeInTheDocument(); + }); + + it("shows the jump-to-bottom control once revealed when scrolled off the bottom", async () => { + const { vm, actions } = makeFakeVm({ items: eventItems(30), isAtBottom: false }); + renderTimeline(vm); + + const jumpToBottom = await screen.findByRole( + "button", + { name: "Scroll to most recent messages" }, + { timeout: 5000 }, + ); + + const user = userEvent.setup(); + await user.click(jumpToBottom); + expect(actions.onJumpToLive).toHaveBeenCalledTimes(1); + // The View hands the VM its imperative scroll handle. + expect(actions.onJumpToLive.mock.calls[0][0]).toBeTypeOf("function"); + }); +}); diff --git a/packages/shared-components/src/room/timeline/TimelineView/TimelineView.tsx b/packages/shared-components/src/room/timeline/TimelineView/TimelineView.tsx new file mode 100644 index 0000000000..30a5936ee7 --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/TimelineView.tsx @@ -0,0 +1,402 @@ +/* + * 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, { useCallback, useEffect, useLayoutEffect, useRef, useState, type JSX } from "react"; +import classNames from "classnames"; +import { useVirtualizer, type VirtualItem, type Virtualizer } from "@tanstack/react-virtual"; +import { InlineSpinner } from "@vector-im/compound-web"; + +import { useViewModel } from "../../../core/viewmodel/useViewModel"; +import type { AnchorAlign, ImmediateScroll, TimelineItem, TimelineViewProps } from "./types"; +import { BACKWARD_LOADING_KEY, FORWARD_LOADING_KEY } from "./types"; +import { TimelineOverlayButtons } from "./TimelineOverlayButtons"; +import styles from "./TimelineView.module.css"; + +/** + * Renders the room timeline: a scrollable list of messages. + * + * The list is virtualised — only the rows currently on screen (plus a few just outside it) + * exist in the DOM, so a room with thousands of messages stays fast. TanStack Virtual does + * that work; we drive it from `RoomTimelineViewModel`, which supplies the rows to show and + * is told in return what the user can see. + * + * The hard part of a chat timeline is holding the scroll position steady while the list + * changes underneath the reader. Rows appear at the top when older history loads, get + * removed when the loaded window is trimmed, and loading spinners come and go. Each of + * those shifts everything below it, which without care makes the message someone is + * reading jump away mid-sentence. How each case is handled: + * + * - **Older history arrives at the top.** `anchorTo: "end"` makes TanStack remember which + * row the user is looking at and, once the new rows are inserted above it, adjust the + * scroll position by the height that was added so that row stays exactly where it was + * on screen. It does this before the browser paints, so the shift is never visible. + * + * `isValidAnchorItem` stops it picking a loading spinner as that remembered row: the + * spinner is replaced by the messages it was waiting for, so afterwards there is no + * such row left to line up against and the timeline would lurch to the top instead. + * + * - **New messages arrive at the bottom.** `followOnAppend` scrolls down to keep them in + * view, but only when we are already at the live end and not jumping somewhere else. + * + * - **Reaching either end**, which is the cue to load more, is worked out from which rows + * are currently rendered. TanStack has no "you reached the top/bottom" callback. + * + * - **Jumping to a particular message** looks up how far down that message sits and + * scrolls straight to that position. We avoid TanStack's `scrollToIndex`, which keeps + * steering towards a row *number*: if history loads while it is doing that, every row + * shifts down and it follows the wrong one to the top. See `offsetForKey`. + * + * `directDomUpdates: true` lets TanStack position rows by writing to the DOM itself rather + * than going through a React render, so measuring a row and moving it happen in the same + * frame (splitting them across two caused a visible stutter). React still re-renders when + * the set of visible rows changes. + * + * Known gap: `overscan` counts rows rather than pixels, so how far it actually reaches + * beyond the viewport varies with how tall those rows happen to be. + */ + +/** Seed height for not-yet-measured rows; kept near a typical chat row so the + * estimate→measured correction stays small. TanStack caches real heights by key thereafter. */ +const ESTIMATED_ITEM_HEIGHT = 48; +/** Rows rendered beyond the visible range each side — a COUNT, not px; ~16 ≈ a screenful. */ +const OVERSCAN = 16; +/** px from the list bottom still counted as "at the bottom". */ +const AT_BOTTOM_THRESHOLD_PX = 4; +/** + * How long we are willing to keep the timeline hidden on first load, in milliseconds. + * + * A spinner covers the list while it scrolls to the message it should start at, because that + * scroll is neither instant nor nice to watch: rows are still being measured, so the user + * would see blank white space where messages have not been placed yet, and would watch rows + * resize as their content finishes arriving — URL previews appearing, polls decrypting. We + * drop the cover as soon as the target row settles at the position it was aiming for. + * + * That content can keep changing height for a while, which shifts the target underneath the + * scroll, so settling sometimes takes longer than expected — and occasionally never quite + * completes, for instance when the requested alignment would need more content below it than + * the room has, and the browser clamps the scroll short of it. This is the point at which we + * stop waiting and show the timeline anyway: a slightly unsettled timeline beats an endless + * spinner. + */ +const REVEAL_TIMEOUT_MS = 1000; + +/** + * How far the view has got through its first load: + * - "init" — nothing rendered yet; waiting for the first batch of messages. + * - "placing" — rows are laid out but still hidden while we scroll to the right spot. + * - "live" — the timeline is visible and the user is in control of scrolling. + */ +type Phase = "init" | "placing" | "live"; + +export function TimelineView({ vm, renderItem }: TimelineViewProps): JSX.Element { + const snapshot = useViewModel(vm); + + // The effects and callbacks below run outside React's render — from scroll events and + // animation frames — so they cannot use the `snapshot` variable captured when this + // function last ran, as it may be out of date by then. These refs always hold the + // latest values for them to read. + const snapshotRef = useRef(snapshot); + snapshotRef.current = snapshot; + + // Loading spinners are ordinary entries in this list (kind: "loading") rather than + // something floating on top of it. That way each spinner occupies real space in the + // scroll area, so showing or hiding one is just another change to the list that the + // scroll anchoring described above already knows how to absorb. + const items = snapshot.items; + const itemsRef = useRef(items); + itemsRef.current = items; + + const scrollerRef = useRef(null); + + // On the first load we lay the rows out, scroll to the message we should start at, and + // only then show the result. Otherwise the user would watch the list shuffle around as + // rows are measured and the scroll position corrected. A spinner covers the gap. This + // happens once per room, as the panel is recreated when the room changes. + const [revealed, setRevealed] = useState(false); + const revealedRef = useRef(false); + + // Gives each row a stable identity (its event id). TanStack uses these to recognise the + // same row from one update to the next, which is what makes the scroll anchoring + // possible at all. It also compares the first row's key between renders to spot when + // rows have been added or removed at the top, so this must always read the current list. + const getItemKey = useCallback((index: number): string => items[index]?.key ?? String(index), [items]); + + // Refuses loading spinners as the row the scroll position is anchored to; see the note + // on `isValidAnchorItem` in the file comment above for why anchoring to one breaks. + // + // This tests the row's key rather than its position in the list. When rows are added or + // removed, TanStack is still working from the positions as they were before the change + // while `items` already reflects it, so looking a row up by position here would check + // the wrong one. The spinner keys are shared with the view model, in types.ts. + const isValidAnchorItem = useCallback( + (item: VirtualItem): boolean => item.key !== BACKWARD_LOADING_KEY && item.key !== FORWARD_LOADING_KEY, + [], + ); + + // ─── State used by the scroll reporting below ────────────────────────────── + const phaseRef = useRef("init"); + // We only want to tell the view model about things that have actually changed, so these + // hold the last values we sent and repeats are skipped. + const lastVisibleRangeRef = useRef<{ start: number; end: number } | null>(null); + const lastAtBottomRef = useRef(null); + // For the "reached the top/bottom" reports we remember a short description of the + // situation we last reported, in the form ":", and clear it + // whenever we move away from that end. This stops us reporting over and over while + // sitting still at the end, while still reporting again once more history has loaded + // and the user scrolls further into it. + const startEdgeTokenRef = useRef(""); + const endEdgeTokenRef = useRef(""); + + // Tells the view model what the user can currently see: which rows are on screen, + // whether we are at the bottom, and whether either end of the loaded messages has been + // reached (the cue for it to load more). TanStack calls this whenever it updates — + // on scroll, after measuring a row, or when the set of visible rows changes. + // + // This only reads state, it never scrolls. It stays quiet until the first load has + // finished and while we are scrolling to a message the view model asked for, because + // until then the scroll position reflects our own automatic placement rather than + // anything the user did. + const reportVisibleState = useCallback( + (v: Virtualizer): void => { + if (phaseRef.current !== "live" || snapshotRef.current.pendingAnchor !== null) return; + const itemCount = itemsRef.current.length; + const visibleRange = v.range; + + // Which rows are on screen, given as positions in the items array. + if ( + visibleRange && + (lastVisibleRangeRef.current?.start !== visibleRange.startIndex || + lastVisibleRangeRef.current?.end !== visibleRange.endIndex) + ) { + lastVisibleRangeRef.current = { start: visibleRange.startIndex, end: visibleRange.endIndex }; + vm.onVisibleRangeChanged(visibleRange.startIndex, visibleRange.endIndex); + } + + // Are we scrolled to the bottom? Worked out from figures TanStack already holds + // (how far we have scrolled, the viewport height, the total height) rather than + // measuring the DOM, which would force the browser to redo layout on every call. + const scrollOffset = v.scrollOffset ?? 0; + const viewportHeight = v.scrollRect?.height ?? 0; + const totalSize = v.getTotalSize(); + const atBottom = viewportHeight > 0 && scrollOffset + viewportHeight >= totalSize - AT_BOTTOM_THRESHOLD_PX; + if (atBottom !== lastAtBottomRef.current) { + lastAtBottomRef.current = atBottom; + vm.onAtBottomStateChange(atBottom); + } + + // Have we reached either end of the loaded messages? True once the very first or + // very last row is among those being rendered, which tells the view model it may + // need to load more history in that direction. + const renderedItems = v.getVirtualItems(); + const firstRenderedIndex = renderedItems.length ? renderedItems[0].index : -1; + const lastRenderedIndex = renderedItems.length ? renderedItems[renderedItems.length - 1].index : -1; + if (firstRenderedIndex === 0) { + const token = `${itemCount}:${visibleRange ? visibleRange.startIndex : 0}`; + if (startEdgeTokenRef.current !== token) { + startEdgeTokenRef.current = token; + vm.onStartReached(); + } + } else { + startEdgeTokenRef.current = ""; + } + if (itemCount > 0 && lastRenderedIndex === itemCount - 1) { + const token = `${itemCount}:${visibleRange ? visibleRange.endIndex : 0}`; + if (endEdgeTokenRef.current !== token) { + endEdgeTokenRef.current = token; + vm.onEndReached(); + } + } else { + endEdgeTokenRef.current = ""; + } + }, + [vm], + ); + + const virtualizer = useVirtualizer({ + count: items.length, + getScrollElement: () => scrollerRef.current, + // Only consulted for rows that have not been measured yet. TanStack measures each + // row as it renders and remembers the result by key, reusing it as rows are added, + // trimmed or reloaded, so we do not need a size cache of our own. + estimateSize: () => ESTIMATED_ITEM_HEIGHT, + getItemKey, + overscan: OVERSCAN, + // Keep whatever the user is looking at visually still when rows are added or + // removed, correcting the scroll position before the browser paints. This is the + // main thing stopping the timeline jumping; see the file comment for how it works. + anchorTo: "end", + // ...but never hold onto a loading spinner as that reference row (see above). This + // option comes from our @tanstack/virtual-core patch and is pending upstream. + isValidAnchorItem, + // Scroll down to follow newly arrived messages, but only when we are at the live end + // of the timeline and are not part-way through jumping somewhere else. + followOnAppend: snapshot.atLiveEnd && snapshot.pendingAnchor === null, + // Let TanStack place rows by writing to the DOM directly instead of re-rendering. + // Because of this, never set transform or height on a row in JSX below — it would + // fight with what TanStack writes. + directDomUpdates: true, + // Called on every TanStack update; we use it to report what is on screen upwards. + onChange: reportVisibleState, + }); + + // Works out how far down the list we would have to scroll, in pixels, to bring the row + // with `targetKey` into view — `align` saying whether it should end up at the top, + // the middle or the bottom of the viewport. Returns null if that message is not among + // the ones currently loaded, in which case the caller cannot scroll to it yet. + // + // Callers hand the result to `scrollToOffset`, which simply scrolls to a fixed pixel + // position. We deliberately do not use `scrollToIndex`: that keeps steering towards a + // row *number* as rows are measured, so if older history loads while it is still + // adjusting, every row shifts down and it follows the wrong one up to the top. + const offsetForKey = useCallback( + (targetKey: string | null, align: AnchorAlign): number | null => { + const idx = targetKey ? itemsRef.current.findIndex((i) => i.key === targetKey) : -1; + if (idx < 0) return null; + const info = virtualizer.getOffsetForIndex(idx, align); + return info ? info[0] : null; + }, + [virtualizer], + ); + // ─── First load: scroll to the starting message while hidden, then reveal ── + // Runs once, as soon as the first batch of messages arrives. + // Holds the pending animation frame from the settle loop below, so it can be cancelled + // if the panel goes away while that loop is still running — switching room part-way + // through the first load, for example. Without this the callback would carry on and + // update state on a component that no longer exists, and call a disposed view model. + const coldRafRef = useRef(undefined); + useEffect(() => { + return () => { + if (coldRafRef.current !== undefined) cancelAnimationFrame(coldRafRef.current); + }; + }, []); + useLayoutEffect(() => { + if (phaseRef.current !== "init" || items.length === 0) return; + // Move out of "init" immediately, so that if more messages arrive while we are + // still placing this effect runs again but returns here rather than starting over. + phaseRef.current = "placing"; + // Start at the message the view model asked for. If it did not ask for one, or that + // message is not in the batch we were given, start at the newest message instead. + const anchor = snapshotRef.current.pendingAnchor; + const list = itemsRef.current; + let idx = anchor ? list.findIndex((i) => i.key === anchor.targetKey) : -1; + if (idx < 0) idx = list.length - 1; + const align: AnchorAlign = anchor?.align ?? "end"; + // Let TanStack carry out this scroll: it keeps correcting the target as rows are + // measured and their real heights become known. We must not set the scroll position + // ourselves as well — two things moving the viewport at once end up fighting. + if (idx >= 0) virtualizer.scrollToIndex(idx, { align, behavior: "auto" }); + // Now watch each frame until that row actually reaches the position it was heading + // for, and reveal the timeline once it has. requestAnimationFrame passes the frame's + // timestamp, so we can measure how long we have been waiting in real time and give up + let startedAt: number | undefined; + const tick = (now: number): void => { + startedAt ??= now; + const info = virtualizer.getOffsetForIndex(idx, align); + const offset = virtualizer.scrollOffset ?? 0; + const landed = info !== undefined && Math.abs(info[0] - offset) <= 1.5; + if (landed || now - startedAt >= REVEAL_TIMEOUT_MS) { + phaseRef.current = "live"; + if (!revealedRef.current) { + revealedRef.current = true; + setRevealed(true); + } + vm.onAnchorReached(); + return; + } + coldRafRef.current = requestAnimationFrame(tick); + }; + coldRafRef.current = requestAnimationFrame(tick); + }, [items.length, virtualizer, vm]); + + // ─── Later jumps: scroll to a message the view model has asked for ───────── + // Once the first load is done, the view model can ask us to jump somewhere by setting + // `pendingAnchor`. This is used by "jump to the latest message" and "jump to the first + // unread message" when the target was not already loaded, so it had to be fetched and + // the timeline rebuilt around it first. + // + // This effect runs after every render, so it acts as soon as those messages appear, and + // it scrolls before the browser paints so the jump is never seen as a scrolling motion. + // `lastPlacedAnchorKeyRef` records which message we last jumped to, so that later + // renders do not repeat the same jump and fight the user's own scrolling. + const lastPlacedAnchorKeyRef = useRef(null); + useLayoutEffect(() => { + if (phaseRef.current !== "live") return; + const anchor = snapshotRef.current.pendingAnchor; + if (!anchor) { + lastPlacedAnchorKeyRef.current = null; + return; + } + if (lastPlacedAnchorKeyRef.current !== anchor.targetKey) { + const target = offsetForKey(anchor.targetKey, anchor.align); + if (target !== null) { + virtualizer.scrollToOffset(target); + lastPlacedAnchorKeyRef.current = anchor.targetKey; + vm.onAnchorReached(); + } + } + }); + + // Handed to the overlay buttons, and through them to the view model, so it can scroll + // us straight away when the message it wants is already loaded — no fetch needed, and + // no round trip through `pendingAnchor` above. + const scrollNow = useCallback( + (anchor) => { + const target = offsetForKey(anchor.targetKey, anchor.align); + if (target !== null) virtualizer.scrollToOffset(target); + }, + [offsetForKey, virtualizer], + ); + + const virtualItems = virtualizer.getVirtualItems(); + + return ( +
+
+ {/* An
    of
  1. rows, so screen readers announce this as a list of messages + and can say how many there are. The role="list" is stated explicitly even + though an
      already is one: Safari with VoiceOver stops treating a list + as a list once list-style is set to none, which our CSS does. The old + ScrollPanel does the same thing for the same reason. */} + {/* eslint-disable jsx-a11y/no-redundant-roles -- see comment above */} +
        + {/* eslint-enable jsx-a11y/no-redundant-roles */} + {virtualItems.map((vi) => { + const item: TimelineItem | undefined = items[vi.index]; + if (!item) return null; + return ( +
      1. + {renderItem(item)} +
      2. + ); + })} +
      +
+ {!revealed && ( +
+ +
+ )} + {revealed && } +
+ ); +} diff --git a/packages/shared-components/src/room/timeline/TimelineView/index.ts b/packages/shared-components/src/room/timeline/TimelineView/index.ts new file mode 100644 index 0000000000..c005db20ce --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { TimelineView } from "./TimelineView"; +export { BACKWARD_LOADING_KEY, FORWARD_LOADING_KEY } from "./types"; +export type { + TimelineItem, + TimelineItemKind, + TimelineViewSnapshot, + TimelineViewActions, + TimelineViewModel, + TimelineViewProps, + NavigationAnchor, + AnchorAlign, + ImmediateScroll, +} from "./types"; diff --git a/packages/shared-components/src/room/timeline/TimelineView/types.ts b/packages/shared-components/src/room/timeline/TimelineView/types.ts new file mode 100644 index 0000000000..6a4b54989d --- /dev/null +++ b/packages/shared-components/src/room/timeline/TimelineView/types.ts @@ -0,0 +1,201 @@ +/* + * 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 type { ReactNode } from "react"; +import type { ViewModel } from "../../../core/viewmodel/ViewModel"; + +// ─── Timeline item: one renderable row ───────────────────────────── + +/** Discriminated union of every row kind the timeline can render. */ +export type TimelineItemKind = "event" | "date-separator" | "read-marker" | "loading" | "gap"; + +export interface EventTimelineItem { + key: string; + kind: "event"; + /** Whether this event continues unbroken from the previous sender (suppresses avatar/name). */ + continuation: boolean; + /** + * Whether this event closes its continuation group (next event has a different + * sender / a gap / a separator, or it is the last). Rounds the group's closing + * corner — border-radius only, so it is recomputed every build, never cached. + */ + lastInSection: boolean; +} + +export interface DateSeparatorTimelineItem { + key: string; + kind: "date-separator"; + label: string; +} + +export interface ReadMarkerTimelineItem { + key: string; + kind: "read-marker"; +} + +export interface LoadingTimelineItem { + key: string; + kind: "loading"; +} + +export interface GapTimelineItem { + key: string; + kind: "gap"; +} + +export type TimelineItem = + | EventTimelineItem + | DateSeparatorTimelineItem + | ReadMarkerTimelineItem + | LoadingTimelineItem + | GapTimelineItem; + +/** + * Stable keys for the edge loading spinners. The ViewModel assigns one of these as the + * `key` of a `kind:"loading"` item; the View matches them to keep scroll anchoring off a + * spinner (whose key vanishes when the loaded batch replaces it). Shared here so the two + * sides can't drift. + */ +export const BACKWARD_LOADING_KEY = "backward-loading"; +export const FORWARD_LOADING_KEY = "forward-loading"; + +// ─── Navigation anchor ───────────────────────────────────────────── + +/** Where in the viewport to place the target when scrolling to an anchor. */ +export type AnchorAlign = "start" | "center" | "end"; + +export interface NavigationAnchor { + /** The `TimelineItem.key` to scroll to. */ + targetKey: string; + /** Where in the viewport to place the target. */ + align: AnchorAlign; +} + +/** + * Imperative scroll-to-anchor handle the View hands to ViewModel actions, to scroll + * without waiting for a data update. The VM invokes it only when the target is already + * in the loaded window; otherwise it sets `pendingAnchor` and lets a load() drive the + * scroll. Must be called synchronously inside the action — the View's closure captures + * the current items snapshot to resolve the target. + */ +export type ImmediateScroll = (anchor: NavigationAnchor) => void; + +// ─── Timeline view model contract ────────────────────────────────── + +export interface TimelineViewSnapshot { + /** The ordered list of items to render. */ + items: TimelineItem[]; + + /** + * True when the timeline window has reached the live end — i.e. there are + * no more forward events to paginate to. Used to gate follow-on-append so + * that the view only auto-scrolls to the bottom when we are actually + * viewing the live end of the room. + */ + atLiveEnd: boolean; + + /** + * Placement target for the current load. The View scrolls it into place on mount + * and re-asserts it on later loads without remounting. While set, follow-on-append + * is disabled (this also keeps a cold-loading list pinned to the anchor rather than + * snapping to the bottom). Cleared when the View reports {@link TimelineViewActions.onAnchorReached}. + */ + pendingAnchor: NavigationAnchor | null; + + /** + * The event ID that should be visually highlighted (e.g. permalink target). + * Unlike `pendingAnchor`, this is not cleared after scrolling — it persists + * so the event tile stays highlighted. + */ + highlightedEventId: string | null; + + /** True when the list is scrolled to the bottom (within a 4px threshold). */ + isAtBottom: boolean; + + /** + * Whether a read-marker is visible above (`"above"`) or below (`"below"`) the + * current viewport, or not reachable/applicable (`false`). + * - `"above"` — marker is above the viewport (or above the loaded window). + * - `"below"` — marker is below the viewport but within the loaded window. + * Controls visibility and direction of the "Jump to unread" / "Mark as read" bar. + */ + canJumpToReadMarker: "above" | "below" | false; + + /** + * Number of new messages that have arrived since the user last scrolled + * to the live bottom. Reset to zero when the user reaches the live bottom. + * Used as the badge count on the "Jump to bottom" button. + */ + numUnreadMessages: number; + + /** + * True when at least one of the new-since-leaving-bottom messages is a + * highlight (mention / keyword). Drives the highlight style on the + * "Jump to bottom" button. + */ + hasHighlights: boolean; +} + +export interface TimelineViewActions { + /** Called when the view reaches the start; VM decides whether to paginate. */ + onStartReached(): void; + + /** Called when the view reaches the end; VM decides whether to paginate. */ + onEndReached(): void; + + /** + * Report that the anchor placement has settled (the target has stabilised in + * the visible range). The VM clears `pendingAnchor`, re-enabling + * follow-on-append and normal scroll-position tracking. + */ + onAnchorReached(): void; + + /** + * Called on every visible-range change; the VM tracks the bottommost visible event + * for scroll-position persistence. Indices are 0-based into the items array. + */ + onVisibleRangeChanged(startIndex: number, endIndex: number): void; + + /** Called when the at-bottom state changes; VM uses this to decide whether to clear the saved scroll position on dispose. */ + onAtBottomStateChange(atBottom: boolean): void; + + /** + * Scroll to the read-marker item (jump to unread messages). + * + * `scrollNow` is invoked synchronously when the marker is already in the + * loaded window (no data update needed). Otherwise the VM triggers a load + * at the marker and the scroll happens via `pendingAnchor` after the load. + */ + onJumpToReadMarker(scrollNow: ImmediateScroll): void; + + /** Mark all currently-visible messages as read, clearing the read marker. */ + onMarkAllAsRead(): void; + + /** + * Navigate to the live end of the timeline. + * + * `scrollNow` is invoked synchronously when the window already reaches + * the live end (no data update needed). Otherwise the VM reloads the + * timeline window at the live end and the scroll happens via + * `pendingAnchor` after the load. + */ + onJumpToLive(scrollNow: ImmediateScroll): void; +} + +export type TimelineViewModel = ViewModel; + +// ─── Shared timeline view props ──────────────────────────────────── + +export interface TimelineViewProps { + vm: TimelineViewModel; + + /** + * Render callback for each timeline item. + * The shared container calls this for every visible item. + */ + renderItem: (item: TimelineItem) => ReactNode; +} diff --git a/patches/@tanstack__virtual-core@3.17.6.patch b/patches/@tanstack__virtual-core@3.17.6.patch new file mode 100644 index 0000000000..2604e074b5 --- /dev/null +++ b/patches/@tanstack__virtual-core@3.17.6.patch @@ -0,0 +1,47 @@ +diff --git a/dist/esm/index.d.ts b/dist/esm/index.d.ts +index 387489bd25c9ebcf6afd97df5e741706fbdec65d..0e9961bb69b411824ba91f56694774e801d599c4 100644 +--- a/dist/esm/index.d.ts ++++ b/dist/esm/index.d.ts +@@ -78,6 +78,12 @@ export interface VirtualizerOptions; + lanes?: number; + anchorTo?: ScrollAnchor; ++ /** ++ * With `anchorTo: "end"`, reject an item as the scroll anchor (e.g. a transient ++ * placeholder/sentinel row whose key does not survive the update); anchoring ++ * steps inward to the nearest accepted item. Element-web patch, pending upstream. ++ */ ++ isValidAnchorItem?: (item: VirtualItem) => boolean; + followOnAppend?: FollowOnAppend; + scrollEndThreshold?: number; + isScrollingResetDelay?: number; +diff --git a/dist/esm/index.js b/dist/esm/index.js +index 00fd4a4b0a0049193f8862c2604cb76013d605d5..37c058b25d30f42f82617d9cc4c2831bc0b13704 100644 +--- a/dist/esm/index.js ++++ b/dist/esm/index.js +@@ -295,7 +295,24 @@ class Virtualizer { + const didEdgeKeysChange = didCountChange || prevCount > 0 && nextCount > 0 && (merged.getItemKey(0) !== prevFirstKey || merged.getItemKey(nextCount - 1) !== prevLastKey); + if (didEdgeKeysChange) { + edgeKeysChanged = true; +- const item = prevCount > 0 ? this.getVirtualItemForOffset(this.getScrollOffset()) ?? measurements[0] : null; ++ let item = prevCount > 0 ? this.getVirtualItemForOffset(this.getScrollOffset()) ?? measurements[0] : null; ++ // With anchorTo: "end" the scroll is re-pinned by the key of the item ++ // under the current offset. If the consumer rejects that item as an ++ // anchor via isValidAnchorItem — e.g. a transient placeholder/sentinel ++ // row (loading spinner, "load more", skeleton) whose key does not ++ // survive the update — re-pinning by its key would fail and the ++ // prepend would go uncompensated (the list jumps to the edge). Such ++ // rows only sit at the extreme edges, so step one index inward to the ++ // nearest accepted item, whose key persists across the update. ++ const isValidAnchorItem = merged.isValidAnchorItem; ++ if (item && isValidAnchorItem && !isValidAnchorItem(item)) { ++ const step = item.index === 0 ? 1 : -1; ++ let i = item.index; ++ while (i >= 0 && i < measurements.length && measurements[i] && !isValidAnchorItem(measurements[i])) { ++ i += step; ++ } ++ item = measurements[i] ?? item; ++ } + if (item) { + anchor = [item.key, this.getScrollOffset() - item.start]; + } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1d8f0b4e1..788f4815dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -311,6 +311,7 @@ packageExtensionsChecksum: sha256-kCJccFy7l6HmjcpzmxV958fHndI9zZq246f1ttgkURI= patchedDependencies: '@arcmantle/vite-plugin-import-css-sheet': 8019aa9feca17db6bab3483612b4150c911d70b58fddc52bf2d7258a1484a747 '@matrix-org/react-sdk-module-api': 016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96 + '@tanstack/virtual-core@3.17.6': 9db9d80c4fe7a7a9911643522aef9bc9359075570c38f3d847ff75ed635d921c '@types/auto-launch': b60dc9846a11a1684ce52c55493f29ea398d3dc44b88f66ab44e9843d6c01538 '@vector-im/matrix-wysiwyg': 7bdf6150f2905bc2f055a6bcaa7b9d78fa7ffde82e800bcc454ac7b0096bd65e await-lock: b767a571946a4f8710ac54b1a7bec8a7c1570f9b85d75922392fae20d0578964 @@ -1384,6 +1385,9 @@ importers: '@matrix-org/spec': specifier: ^1.7.0 version: 1.16.0 + '@tanstack/react-virtual': + specifier: 3.14.8 + version: 3.14.8(react-dom@19.2.8)(react@19.2.8) '@vector-im/compound-design-tokens': specifier: 'catalog:' version: 10.2.1(@types/react@19.2.18)(react@19.2.8) @@ -5826,6 +5830,15 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} + '@tanstack/react-virtual@3.14.8': + resolution: {integrity: sha512-O39GJQpAYEJcIu3uN1//YtmhjSEOyw75vg9CKCatBDPiD5hKtZQoJHfferyrB/LdOD3UWaoMLWtdEjarwIwdDw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/virtual-core@3.17.6': + resolution: {integrity: sha512-h0/Ebo18CkOrChlQIhNtQkM5ySUnh/GumQ/D1st3hG2HWUPEF+ILUc2k29UtivCi/9G7w7G3/f7Xyd5cCFbKBw==} + '@testcontainers/postgresql@12.0.4': resolution: {integrity: sha512-a/pLU6j5lpKKAlUTPwqweqMGhOSjgTSb6HBX69TOrXn32ifU37nnQDmNFTj8ddOAw+BQL9oTRkeOxVbZkqhgZA==} @@ -18405,6 +18418,14 @@ snapshots: dependencies: defer-to-connect: 2.0.1 + '@tanstack/react-virtual@3.14.8(react-dom@19.2.8)(react@19.2.8)': + dependencies: + '@tanstack/virtual-core': 3.17.6(patch_hash=9db9d80c4fe7a7a9911643522aef9bc9359075570c38f3d847ff75ed635d921c) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@tanstack/virtual-core@3.17.6(patch_hash=9db9d80c4fe7a7a9911643522aef9bc9359075570c38f3d847ff75ed635d921c)': {} + '@testcontainers/postgresql@12.0.4(supports-color@10.2.2)': dependencies: testcontainers: 12.0.4(supports-color@10.2.2) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3d4b5bb72c..f82efcbaef 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -119,6 +119,8 @@ patchedDependencies: plist: patches/plist.patch # Workaround for type fails "@arcmantle/vite-plugin-import-css-sheet": patches/@arcmantle__vite-plugin-import-css-sheet.patch + # Workaround for scroll anchoring landing on transient placeholder rows (pending upstream) + "@tanstack/virtual-core@3.17.6": patches/@tanstack__virtual-core@3.17.6.patch peerDependencyRules: allowedVersions: @@ -214,3 +216,7 @@ minimumReleaseAgeExclude: # Temporary for testing purposes - "@typescript/*" - typescript@7.0.2 + # New timeline: 3.14.8/3.17.6 carry the upstream end-anchored-prepend fix + # (#1237); they were freshly published so are still inside the release-age + # window. Revisit once the release has aged past the cutoff. + - "@tanstack/*"