From 5417fce489ad794a2e8b3f912ba71ca8eca63200 Mon Sep 17 00:00:00 2001 From: rbondesson Date: Wed, 18 Feb 2026 07:28:34 +0100 Subject: [PATCH] Refactor EncryptionEvent using MVVM and move to shared-components (#32531) * Refactor EncryptionEvent using MVVM and move to shared-components * Added viewmodel and unit tests for bothe viewmodel and component. * Added test for custom-class * Update EventTileFactory and RoomView to use the new component * Clean up unused language strings from element-web * Changed how the view model is created * Make sure the initial snapshot mimics the previous component * Optimizing viewmodel initial snapshot and update * Updated playwright screenshots --- .../default-auto.png | Bin 0 -> 12679 bytes .../disable-attempt-auto.png | Bin 0 -> 8628 bytes .../enabled-direct-message-auto.png | Bin 0 -> 10799 bytes .../enabled-local-room-auto.png | Bin 0 -> 9032 bytes .../parameters-changed-auto.png | Bin 0 -> 9154 bytes .../state-encryption-enabled-auto.png | Bin 0 -> 14494 bytes .../unsupported-auto.png | Bin 0 -> 9198 bytes .../with-timestamp-auto.png | Bin 0 -> 13312 bytes .../EncryptionEventView.module.css | 10 + .../EncryptionEventView.stories.tsx | 82 ++++++ .../EncryptionEventView.test.tsx | 150 +++++++++++ .../EncryptionEventView.tsx | 100 +++++++ .../EncryptionEventView.test.tsx.snap | 245 ++++++++++++++++++ .../event-tiles/EncryptionEventView/index.ts | 13 + .../src/i18n/strings/en_EN.json | 14 +- packages/shared-components/src/index.ts | 1 + src/components/structures/RoomView.tsx | 20 +- .../views/messages/EncryptionEvent.tsx | 98 ------- src/events/EventTileFactory.tsx | 19 +- src/i18n/strings/en_EN.json | 11 - .../event-tiles/EncryptionEventViewModel.ts | 122 +++++++++ .../views/messages/EncryptionEvent-test.tsx | 145 ----------- .../EncryptionEventViewModel-test.ts | 192 ++++++++++++++ 23 files changed, 961 insertions(+), 261 deletions(-) create mode 100644 packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/default-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/disable-attempt-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/enabled-direct-message-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/enabled-local-room-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/parameters-changed-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/state-encryption-enabled-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/unsupported-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/with-timestamp-auto.png create mode 100644 packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.module.css create mode 100644 packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx create mode 100644 packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.test.tsx create mode 100644 packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.tsx create mode 100644 packages/shared-components/src/event-tiles/EncryptionEventView/__snapshots__/EncryptionEventView.test.tsx.snap create mode 100644 packages/shared-components/src/event-tiles/EncryptionEventView/index.ts delete mode 100644 src/components/views/messages/EncryptionEvent.tsx create mode 100644 src/viewmodels/event-tiles/EncryptionEventViewModel.ts delete mode 100644 test/unit-tests/components/views/messages/EncryptionEvent-test.tsx create mode 100644 test/viewmodels/event-tiles/EncryptionEventViewModel-test.ts diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/default-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..14c32d0ae440062b7f7f562a5185d4a1d06376e1 GIT binary patch literal 12679 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 E012)?L;wH) literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/disable-attempt-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/disable-attempt-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..bd68a47de28e90bf01e1b12e407e63b003b642ec GIT binary patch literal 8628 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 literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/enabled-direct-message-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/enabled-direct-message-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..edb1d6c2145ddb59099a303bc38320834bb461b9 GIT binary patch literal 10799 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 A9smFU literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/enabled-local-room-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/enabled-local-room-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..67f0f4d7f320c7b156ebac1653719c6fc0595944 GIT binary patch literal 9032 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 P0bMu?J5&G5wR`^oMIzpc literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/parameters-changed-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/parameters-changed-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c09520c0241c25df52ec72acfcd4961b803290 GIT binary patch literal 9154 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 literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/state-encryption-enabled-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/state-encryption-enabled-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..5c529852cc82665e6fc802181a178ad266f55224 GIT binary patch literal 14494 zcmeHu_g9l?*KU-VQIVn|B1jo@kWmy+giymMDoRyR=|Mz5YUmxJgTOF?s309;1Efn0 zB@iVFB2|L4ki9ch2`8ocS%eSJq0_bMJlatL&s+u`!nvR}hCl zAd;6aoxcWwh`obAwh!;z4o-$;AI(4@dmxw3pS>PcxI)~8c^3Me!@zf(Y&4&g)fu_? zuw~}keEMJenvsqh6L<+Ax6itg9 zwVDwnU@`3()>V1tD6HqYL5pyna2~hst+) z`v>RoQ^+l?Y($UEA-#afdQZRY_e9OZ!@T{+bG6*I-|MiOGVV!EFZinS4t^MOu*H3* z^e0GcW!6b&bdqQvWJQ@&OZYN!xOe*+IZa+C zqLpE1YjUIHb6uX0(BcCX1yA7>3Q1Ly)~Q6_FE1}3kulUA>^+0Bq*%M9xZx=i+ zc(f~Nw@hqhS`Sve+kjr*u^AEBR0|ihU|dZ69jl$ve}cRVd^#?_C`}SQ|GHXzNYB_r zO|8VSVLnk=!J++TwJV(Z=k7sM{u)k1@4jXy)BaT~p10DDpnM42?Aoo>$rkdgHT|A) z0z$|`ZQ)c}JH4~mBvv_m^lECyweA_R%!~wN&GdM!&M+cfDR%R#Juw!6u5oR5iT1>! z$uUM+-oBs(158pTI&L|^9^Hupn%ZWF*BQKyDPKZ zy@s*UFeF$@lzwV2Eh*Pr?k4&WNTf*?BM{70toYugaTe!tqNlU z@4g_X+sSFCXHUma1KbPX9Wm;WO>x}6oe=?!=s6;56^({1z0TAM z2${r5o3?rOWlysk{3q+THf?tv*;Au?>)k#7p);=vyH%^Ck3TgUK!nJ_+(yBPh220B&JXn2EHb&yYpB0OekZntP!zp!hqEZJKrWh0o> z9N?_~{-Sr09U3kOTGO~$)$jA0j(CqE?XRFOdEUC>?5iQ3E!B@ABgK2<2Q`e3XKT7D z+)Tj6ag`#cWi`d6(@uGGzox6|sx{e_#dc-eyMbxx$Fbs%cFk8($)ED z=Y0@JyDeNSW;I`zN5>*VTCMUGyW5HqgcnM_}C9OeLd;Mjw0xYsGPufHvA% zeC6EaFJKOtmeC5OkisOnTK}oWJH6LTVm4g(tKZkApT}|6=RQ9>WEd7?(HXZo)#Tjf z?-(_>He+kr+%Yp)XtVr&$Bwx6D2AByhEG%sZlRbLo7*xFZ(3rp8p&I&?vh1xG=01v z2hUo~EA8`CEVU6Vmd-;@y47JUa{_x$(p$?yOa7K6ps{{koLy}8VxG^3K_hzMTd&Vq z?5>`SGHXIh@XV5=!UJzz3ck*x2evhVQ!k9VT}z5T2lX}<{`s@`Yn6T(TVw90BN>Gu z%e|4L;GTYY9p}cod!HH-p7ckFOPlgP{k7K?bVOrsNe*k}i-vL0C8M{ae7mEznl~F4 z-Gn2*VLBO$uQHOJ36vMvgDt_}quiORbA%@99%a>DlejYhQ}=f$N7*qCnHFvOWGI_N z7{CN7aY1~}u5ozmz^NM*R>fxwwn5(VR<=$mQpajKUQAktdJRQ02heR{OZlC``j2E2 z;Rg>ji>yy`ZSv%{sl}Cx(i=ifrs zUu$moEe^Xg=LeLSPE$YKS)=q6v?TXl_Xk87UG3Jq!A1 zsTrs!b>j7G2RlOay;92-G-S}Na;}}&ilp3UX((0!-37IBF7S=H_LB-zZu*EfqQarP(TPMD-Q!PJn*$ zY&IGl7(RS<$Bj2Z8&l!b5%Z(*A1dr?g``+|O8d7&X;TR!OJDro4W@jNs#Z0Jw6-Cd>cf@SM z+am@>rOS$^o}WF)a*JpWtx8bhH>IvTNad@MPZ|I$d5WT8$qAGhHd)IuAs>G!klY#X zII+m1S6(+@<>*XBN3LKj@@CJq5Yr3vl9O`Wlm!_`W7AmHiguk6jL4_{QG(st>(v4c zoXU!ew9uT9DIR!`qmbZgXZWv9GJI~PHy3yHaO7N{N|BGa&cV}n?)nP(DOn_}_YfRc ztdQ>_@1;3j>+FU(&zP+gfeLlY6Ijaqe&t)LdctMWhe4mg4}(GdY--5G$r>w*qt`S0 z@(aV~<$HBglw&5cj@j*N71zmeZ*Bnv?S9SR`~m>Ukg0l?)(}v5>rir>Ow9;Zl=$jm z$LI?W9)_-7rZXTIsaiPHEFr_S%+WOAd`raj{p^;&^$Zc$oB6A#PUkr}Zl-#Tzpz|c zYk{SGF3P0#qODf2q6F~ZiWEZ@RVl63>-e9IP|}J~-_BOr*Iu4Ip{6$XTuj=S^VqD5 zD%Pv}@u9b<;v)d9;nvWcm~d*s)>2yVOgAyYxpL#-$bt~NwOknwFyPwjONYl9yW~6W ze$hgR9O`ag*bceHPUYxEh?2o@`fVXTQ*_d)@h)jP+SZscYYo-)BUXGh$Cq|+zQ38x zR$VWSTJQbx$}Ku}W#WpD>~TdRfF$?Q2oj%589^G0x-ikqeWDs|krSwfA+Mpyx_F+w zTY@f_RX^`O(C;&&eXKpKxBi$NiSX9l?C|QUylG1B43rdgbmVM#WOA(G@qUomxXmKGZxAbLM>PQbF>4aNX$?I2^6Xlc#ChD;+In%6(Evp3Czt zmh^*(_{>cza_8>dawjA*@rgU6ckNb3#aP_7p9$>(#l&2NQdHSfezi%#mmrW8SS(Ar z{K2n-93L_T0-CV#Z)+{pn zRvfo}xyX>HH17uuoCvZ=v^(S$cJ(`1w$z5VNhehMp#d<~xU^c}HH7(bOtWT9EWcja zB!(E=<64`he4}ALSar&G^v$We!_TRRmKVHuc9Rd$kUHp0B%MAVO&yD@a%|Z8AY;=) zYokWAqIS*nK<^I!dgpHWH6Ag1wmrRA@J(&E?$#f`bPqDVK@Pmx0aLo?f z&lEA-+gCY3mr{*xp1}Bo=4iS~>94(OIHX%)S7y^(@a*@i-}C)yq_nO)=$&adY8G+J zP+cLf8V0B8!!7k0mowF|f-ypq@3=3y#uYqpd}(-PiDiz7Zd_ENoo&GAV%1vyANngU z@K+GXr}j9%aZwiU03FK~ZbGMXS1%-rSa)_deL#%SYFM>cMt6T6>bD8zQQP7X!0n}B z6Igc~9#BCshvr1i43+-zEux?EUEs1af}QFwksg{}6-x z@xWxW3}X&PS<8%XjWb#^et^3mYDJI}QbkIBV^x8147FB&ZIA9WdXCl*>m{13Trj~5 z!9Lrhj-et+Kc^b603BuRT^N0{dvk^qUFJ6)m`Z+vCdIUSXTashPq=k{&$NwLAk%td zWMZ$wl|3li6qUR4Zaz{nVzCHFZel)4i-YkRaB@ZmS|=zdYwwmzc(U|<4K-ftCbD-E zcxxWoSSG3yBtx8}A?@SE;)~B+CXaNd$Evp)vm06uK?AJ#ofz1Bx@@2Qo!Bn`K=;MM zY3OSJk?q}-ia2OikW=C>MMmUIj_dAC{!$t0sL)$L$fz?6Uyj@O(nCNar2Fu&EdYK} zd8b0X9N;Vgo6sC+w6ccV7u(TJ!^~iqg9E%6$V3P0o3sA%Xf?C@5W*$wOOiWx08KJx zJdk-xl{?|E_AODYtI8YTKiUuJ&G@eNi=@ICBxyn8X0-&FU*kWu@yy;W-lP8zc&Y~L zpN`Pz!J_4_l>i?d0pmV=K^)yOmu>M(BYsV|BYtb+m}{vO(6DyvC5`l9&Hmg+irke) zN~sk+H+?4S=cbzcwpgp!6`m(`tQz|&pa2|{soWqK(OejU_3cUGE@6};Z&ZAQNGKgR z)fmnAj$qF9jRPiYXJZh}KtH6@!fZK>w?B%@+p7O$-aS)wrISxZU|>J4oE_oX6yk~M z6j)^azIX7ASqe)F5KiyGm3b(Uzvz)+yfKVJ_2=%Ds_IpfLq(6}EyX%xg(<}*pQG!t z|KkP3ChuDLle@doKic_j&M6N#o!IW(a(C^UDxo<5yHbBt9{R?i{JX5Z3y^O`lMP;S zC*dn&2hdiz+NYv5q)pealy2kC9UZF{QDv>o%)Hxfw}#9;J@jC^CGCaio-j%Lz;aY% zS#2l3Vug}ICUg)!T+Tddn?GHSCr;A13n*mge7>Kw$;R;3CKYNdXpE(|BPT~Zxy%J@ zM|h?0jmklwStIGN0w{t6*eAWS-6QXG32xhH#SLbr?a1+J(6l{|l+d;>&JpSDQ2)nx z!M^!lK}}-set~9~3Kw7ZX6leyDK)(j61Y8{9ndnX=vrIU4k-i9w-HYZITBlxP(o{K z^4lR9^ZSV`P1h~JJdWeHErdZmJLI}<&45$iCHLZV43^0xk<_9I(aE^|KLiX4*EFA4r}y zIVisW_s2`;kJ=RO@4N)%^Fu8xEWTEJ4Q!#bZ4`;->icw*y$_CFL7DIMuxG=TfG?Bvn<8x)8@3>Zk>+S<~_yR=>_2^})=es^!13P$9vpJrL$ zvbA99_Si+Gb6=<-W+&h7cF`=$2q8|Ak@Hu;So(12t7at4KW z_r9YKDHq;{eY zFXE0|l+bc`erw`65-SwjAsNMLebO^tA=~-&Bpma4F5qOc(x;AA zJ6AhxN+}sB0&527Au1V-(7$IUWx`pl^Qgtt`;C2`*F>`SS=EA&R<~<* zcFloAh309%-%5?3WvBXCq?K_r9cX!}p;OH?MM9XGMK)&kX*UHMI%uFy-L{Y*llO~G zS;40my0;02#wMy1o52_#X3EQd?z&rs+APU&+1jEa9!PfC5C8e|ZZqP%<>en}i>v9< z8F^urQbLY&RN>F`WRRQV&3AUrL&=Rnl&cjWpg`RSJ%(G59#`5xfC;c`)4w0 z^ETf{|-H43Kg=O&!> zdw`gJ6vvtzF15@)G2jEyDY_7q&E6b;SS-)oApc@2AE=SGs+BQRD6Mr2qi@#{^K~ub znnS_@F%swTD;1}&PT>H$*sG3;<9{(%*_A)w*oM(FM0$jBa>uNC&Y8;mYIJn%zIn5Qe~&ULVnU`{qs$C6AQ&>hSbgU?UY3wLee zB0mH8OmXa>)?ihu9GgOJJb8IRLD#S{md(SRU)rV4UuyhCawJ2&zat3|1t_~F9K*@L zCWlu$^`y{SRL!4~@5!BTiE^K5bYvPf`I+j3+}R3mQ1))q9?)AZxvDz1kf2w4>B-9_ zs&~ScPj0c5^2FbB8l4U;+Q#LVu!}h9G8k`~j9VXfi&p|vC7~muG+`sfRCuaiAz8_5 z>Q-O&Nq5!$_~Z;V%V!cExjAB%Ijgcd#~thMgmepraZ;|WzBed`lS0X%srx?SHjoav zV5liYHcIZMaJwsHHO$t4#{hMQ=?2{L8yCkf_pX|4PtM@2+AnivGNgBxur!)tHkYUA zImd5|L_FObv__@&NBnh~R6u!Ae|zrpnuK~)t!w4YPx)2q%FVs3d!enVX^DA#)^|GC zpi)|vUiGOcjgbKhUSq7*A=HNqr*|LYuE+BR(5Qi;*g*qt^+*T5g<+*gm-MTz;1<|t zRWmXyl~NmA8O86l%VY>pa6K&iP+FBeHUU^xU1_|wBDb6P()cjuNvLPM_6ayfpPG8T zI>w*z{o{z9=GEK4^4p;lc4cc2p!gVW_smV<;(9N2%(!#DaIyg>^JQ7LLcPpK!@h#^ zQO<4l%)KQy*kyOV-`5IRq2w`_mY`gQq*nZEx2!&jS`zY((7?-z!&b$N?AzisXV-eg z9W28hGp>wg&MVyzz5y&A`h;Z#TRlm4#PX$$zuglzFC$i~Sv+&%`i+UL#n$f-Gn`$Q z_b&WG8;qdn4(kl*&y-1;s*jh=+e$X*%A*yvpNl}vuYZ5|>S9KMTK{U&(z}mjO({F` zad@NvCv7tN%6sUSvSI~zjo|K7;|sHh!Mmki#rmeKiHgKuBtNwrwc49hK7Bt?xyHH7 zX6s?skg2E(fB^8n6z5}53NYi=)0n#jOK)*j?qm2}$52c9c&#?zl>)GoBAbJjOSKyJ zo#u2^*0ddeQp8#%sI{M5N=uehx60obK$G>1XEywymtGI_m0nbGpxN_tB*kYlrb1uD zvw9;;!5kdo_-qkQp8M+Q^yzm3ujI;$bG=Dbk%4VhW?2lQNI_@u^nD#%k2@ zTWwZ7c}r_B5CR~H#sM46B895FLCqX3RMlycWUC_lTF2I=a@4o`d#FhL<%|n|@9z!s z=}uuZR})UV<}dx)kihlkef{Yjy;nJIw|UU;{T=gKxuS-l`5zi>Hh<3^S&i$3x*7q> zVOtwZGDofW8+b>c>Ps%BD-helCAlrWUzg7&DjNbu?bPHOKB^+(Y`(0uuXS>Lac(LM zn5~$CX|vqPiHZ3Ayhj~gPYSQq3c z7`u-(-2V8tJ7pD38Z3_$uB9AVpcR@I3Tv@1Rq?^ey`R?phGCb4-&jQ2$vITqS7XXtRVCBjsV&1Gn1??fyHYmG>P0T!A;aVldO4ZZwmo z(?I;Sa<=-8$=mbAYV61OlITe$@LS^ybym$82TnSL4Zq(ZckCKx4cC_d=s{{D`Pyk6 zOxoj5i7vDpaQ#~s+7(}dGE6?qr?soON6!zUGIz^RfWs5Q(dxgFoO50eK~hk*$&=Eb z(GzuB-!aA%(gd$(t&zaiOl@3@#w^m}@Fx9=~yXA?y+`nTteRY}y}Dxw`tRjl9?mV|roeK1bzlaDOLU?vTT&MUdAZWznBA=(pAe0Q7d4x=}=0HNsA4{iC zIuo0H@ZCI+N6qD2R0;dGZ6Kf6A`N}zKlQB3_R+!EcZtUe zESiA!+9-h0F`=>SWu1uWiR3b9)zyxzL}^&A?kn8D=JMCrem@W2F=LpAOZLfFhwd4P zTK(~Iy9E-B9lkP5sqfQl7bYKic;HyfYWR5RKn_&D$!68ix&XcjhR03eor{$>(JAHA zfJ1r}wn{L4-M3eTQ}rm{YB8r*Q<&%eR=UYrTl?^B*i5=NBC+ZzdZgVx!)izNr-oVO zUnJtR36>4TA!7kG9rMdOwpaycd_tnGw%e7pgKB2f^%PIE1bcv?Mriga`meHUT0eb0 zfY7`&T@w1F>jEyc)yh^DIOh>y=7VY9J9iJ+XZ^rnaLHhrw?v~&P@b48I|H(uciOY2 zb-p51M!nSSdX+<+n3Vo=n<5d9JHB@2Rd_)&NPTunw|=N)yGEsXDxgMoWaa6*k5~`n ztHeyaaHqvB*i}uwGS8rm*CM(Lq=tOV(+)o2r;dPdvDt6Db4eDc^gp8wo}`^t_HK@J z%e6y{cj8z9gwM}3V!x-VKUml`m!U~+@UGocOGzpflh*$=1AVB*D1NKQJ#qWb;-pcG zW9n12P{Y@b4Ga7kl_6>Vcn{anyNJ7NDsuE*I22M3OB*s+D7-JGq9CGi0OR$Hn|`3< zOTswn18fj(4Iw+9UoXFw*U1-!b)oh__@p`Kc465EQNA`_rjwBZJE$LY3jks!z4ZY( zQ`;|gRd-xcN$(D-h@RcbU)R{$jQy1w8_fo-8l0T-qB}!HWz$clJrb*B+!PoV@ya+* zrTD_e5}pmb8ZQ@vkl66G(2A*~@+2MJ zw1Ca!0|5LHSFO2m!i4-GX4FUE&>Yb!sCGuvp4r0#Ce}7KbYBGKoOE`2bus-z@e{Uy z&N^k4ps?L=@3FG?im;!>6TX)=r@FKolTTB2jD1kWub&KPAl6P!95J;$; zx{Vt;RG{0gdIHILzXP~|Tfh%(BNBAuwg9W~8qCeZ-nt)aNOT1rt2Xjq#j$WBqk06W zBj8_Pjbk>l2b{aA7CxqHiTiYh%zgfml8snuT{ius-Dh;`UE&a+z+jTeX$kcdeZhRP zHJF>7Ex8J154O{7iqCK#8BHb27;FriQO9eMjF0J=ye0g?eaS<5pp(}L2y6gxiVQD&Q1fNB_I)B>+}KMr}C1aA-bo64M&X4(v5`%-NVPK$vWf zt|<}F_NFx#33}DQW}yQgjkv}fqMWa|Ssj6s94|4I7-6G&POVIHi#Hfc!*v3LJnn~l zIdu$P*lYbUG+L>Zta=J+3$; zKreVG&Qc3|u;x%{zpXyw0oZNGP!?E3g^a8>6IjGZ)D9KJx4@}A&C3ZK7Y+hds|Fm_ zB9rw4kM^lq-hAUg!8O^cPaK&pIc=n-TXvm8EEy-!3b>TQ%)rQ|<-iuhV_~1R=K*CIZmer{3U`+*G>y5D?S?9Qn z=<_274Hke(`EO>*slzV$T55A3JePpRe7PW(jO&XsrnC-wWSAJPPkP_1bq zy*?-f5FTvW!zUZB_g>3@u`Z3BwXwb|SbwOc5tq6~M^X9Sni`Sw01+LRBbH|@${>U#dzzlO_xoN~ngw_DdQe5?5wub#> z-Vt9Q^QGiUN=uVxs;<|42Z32b$5ItkZk{d^9UAefK{;WO0yuX{l9{wo7;DAAARv0} z{f-O;UEj~KbP)${>rP|k93gX|BN;|D-EXdHl^_fdB+R;bjusr_;7}7CH(=1yi z)7o^NRKEJGB72mMlj#boHlq8=jro%r4HR!|hj2`$?6wiVjoNkjgNue5&VEa@1dj9A znZP*84SUyTo$p68N)H^r{yTvR4|>mae;LeUtuCwsOev{!dLyUE-=d(0(g6e@C2Uo-G_oqSSik+;0 z$gFZcaO~P0V%uhF5#50k*c<8o!VN3rGRyQ@t1bhiD;&+rs&;O^gRzK;sEZDD`nd+) z)OIcjOynPeEMX~4-p5>dz8WkhJ?^XrN^3XF?XiE)zeEDunEl_{}PZ?q^`lsTIwOS27Cw)EJyg3Bc!eWsHGLK!NGEkms&`!@?cCTNR@oQd>@l0 zA+_+?x_-*gm<^2qL>u?by7`Sa4({f+d(Hx1WzsW8CHTYMj}PVih;28X9R8Sn!WGaH z^k5PFGQ0$|U1a3S^~SqTfSpk9T}yiLwzi#>A5n`fE|doQYk>U+fDHC=@SZM4l2F~I~|R9(eb z!F)l4(g^9XYgt55sda4(pJA@mI{UPsfPBqYB#@E>wB2!8N{Jv??NxO~2beq$%ge3~ z-0jzu^G8{`(aoysr?x@(@LA{f1Vo|oZej#3eEbGrn+($$5CNL2#BW3|6ctQV$86Fv z8R+V8dV<@)%p*TQb3f2RxpVd#RQ4aubT6Sa!OSCf|9omsh6+lUww-lc?QdBd$Xhm- zE>eAFm3_i-iu2u47&Q9?n;1hfi&&YsdIWb!9i9-qF;V9sDF=Cn?1mePN#2vcd#?kJ z6lv|<4tXcq^FQU4KmF_Q+O=!zYLJP(*QK;@Y?;cv*IA?cMHj_Z!uc~2Z^tbr%dXr9 zyPbLGeinfDITpLloOuUV<2S4b!ogB!W6n;TaStp>{3v8%_k$Qc0a*L&Lj%4yJJP&L zfFdT_Ja~_kmMS5fjGYYwV7+N~!{E>2vuqkz`*eXdNkAac3+x}+>A!!={}rhIbNJ5# z`LA^SpJMo@82%}St)KVnK_EM|e*8z^AK3hV4Vr&Y;vbaw2POVNhJTRZ|J+Rc2NnN+ rMn$0t_;aC1RK;8*gBQR2n>w7;N*F=5)%$@H$mI(*=WEX0`tyGQl|5$d literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/unsupported-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx/unsupported-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..ae08d9e2205998c78fb70c8a454dbb88f44557ac GIT binary patch literal 9198 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 literal 0 HcmV?d00001 diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.module.css b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.module.css new file mode 100644 index 0000000000..a49c7f1b05 --- /dev/null +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.module.css @@ -0,0 +1,10 @@ +/* + * 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. + */ + +.error { + color: var(--cpd-color-icon-critical-primary); +} diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx new file mode 100644 index 0000000000..04066d1dbe --- /dev/null +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.stories.tsx @@ -0,0 +1,82 @@ +/* + * 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 type { Meta, StoryFn } from "@storybook/react-vite"; +import { EncryptionEventView, EncryptionEventState, type EncryptionEventViewSnapshot } from "./EncryptionEventView"; +import { useMockedViewModel } from "../../viewmodel/useMockedViewModel"; + +type EncryptionEventProps = EncryptionEventViewSnapshot; + +const EncryptionEventViewWrapper = ({ ...rest }: EncryptionEventProps): JSX.Element => { + const vm = useMockedViewModel(rest, {}); + + return ; +}; + +export default { + title: "Event/EncryptionEvent", + component: EncryptionEventViewWrapper, + tags: ["autodocs"], + argTypes: { + state: { + options: Object.entries(EncryptionEventState) + .filter(([key, value]) => key === value) + .map(([key]) => key), + control: { type: "select" }, + }, + }, + args: { + state: EncryptionEventState.ENABLED, + encryptedStateEvents: false, + userName: "Alice", + className: "", + }, +} as Meta; + +const Template: StoryFn = (args) => ; + +export const Default = Template.bind({}); + +export const StateEncryptionEnabled = Template.bind({}); +StateEncryptionEnabled.args = { + state: EncryptionEventState.ENABLED, + encryptedStateEvents: true, +}; + +export const ParametersChanged = Template.bind({}); +ParametersChanged.args = { + state: EncryptionEventState.CHANGED, +}; + +export const DisableAttempt = Template.bind({}); +DisableAttempt.args = { + state: EncryptionEventState.DISABLE_ATTEMPT, +}; + +export const EnabledDirectMessage = Template.bind({}); +EnabledDirectMessage.args = { + state: EncryptionEventState.ENABLED_DM, + userName: "Alice", +}; + +export const EnabledLocalRoom = Template.bind({}); +EnabledLocalRoom.args = { + state: EncryptionEventState.ENABLED_LOCAL, +}; + +export const Unsupported = Template.bind({}); +Unsupported.args = { + state: EncryptionEventState.UNSUPPORTED, +}; + +export const WithTimestamp = Template.bind({}); +WithTimestamp.args = { + state: EncryptionEventState.ENABLED, + timestamp: 14:56, +}; diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.test.tsx b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.test.tsx new file mode 100644 index 0000000000..214890b8ee --- /dev/null +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.test.tsx @@ -0,0 +1,150 @@ +/* + * 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 { render, screen } from "@test-utils"; +import { composeStories } from "@storybook/react-vite"; +import { describe, it, expect } from "vitest"; +import React from "react"; + +import { EncryptionEventState, EncryptionEventView } from "./EncryptionEventView"; +import * as stories from "./EncryptionEventView.stories"; +import { MockViewModel } from "../../viewmodel"; + +const { + Default, + StateEncryptionEnabled, + ParametersChanged, + DisableAttempt, + EnabledDirectMessage, + EnabledLocalRoom, + Unsupported, + WithTimestamp, +} = composeStories(stories); + +describe("EncryptionEventView", () => { + const renderView = ( + state: EncryptionEventState, + encryptedStateEvents?: boolean, + userName?: string, + className?: string, + ): void => { + const vm = new MockViewModel({ + state, + encryptedStateEvents, + userName, + className, + }); + render(); + }; + + it("renders Default story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders StateEncryptionEnabled story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders ParametersChanged story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders DisableAttempt story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders EnabledDirectMessage story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders EnabledLocalRoom story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders Unsupported story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders WithTimestamp story", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("shows enabled room encryption copy", () => { + renderView(EncryptionEventState.ENABLED); + + expect(screen.getByText("Encryption enabled")).toBeInTheDocument(); + expect( + screen.getByText( + "Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.", + ), + ).toBeInTheDocument(); + }); + + it("shows enabled state encryption copy", () => { + renderView(EncryptionEventState.ENABLED, true); + + expect(screen.getByText("Experimental state encryption enabled")).toBeInTheDocument(); + expect( + screen.getByText( + "Messages and state events in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.", + ), + ).toBeInTheDocument(); + }); + + it("shows changed encryption parameters copy", () => { + renderView(EncryptionEventState.CHANGED); + + expect(screen.getByText("Encryption enabled")).toBeInTheDocument(); + expect(screen.getByText("Some encryption parameters have been changed.")).toBeInTheDocument(); + }); + + it("shows disable attempt copy", () => { + renderView(EncryptionEventState.DISABLE_ATTEMPT); + + expect(screen.getByText("Encryption enabled")).toBeInTheDocument(); + expect(screen.getByText("Ignored attempt to disable encryption")).toBeInTheDocument(); + }); + + it("shows unsupported encryption copy", () => { + renderView(EncryptionEventState.UNSUPPORTED); + + expect(screen.getByText("Encryption not enabled")).toBeInTheDocument(); + expect(screen.getByText("The encryption used by this room isn't supported.")).toBeInTheDocument(); + }); + + it("shows local room encryption copy", () => { + renderView(EncryptionEventState.ENABLED_LOCAL); + + expect(screen.getByText("Encryption enabled")).toBeInTheDocument(); + expect(screen.getByText("Messages in this chat will be end-to-end encrypted.")).toBeInTheDocument(); + }); + + it("shows dm room encryption copy with display name", () => { + renderView(EncryptionEventState.ENABLED_DM, false, "Alice"); + + expect(screen.getByText("Encryption enabled")).toBeInTheDocument(); + expect( + screen.getByText( + "Messages here are end-to-end encrypted. Verify Alice in their profile - tap on their profile picture.", + ), + ).toBeInTheDocument(); + }); + + it("renders additional class name on the event tile bubble", () => { + renderView(EncryptionEventState.ENABLED, false, undefined, "custom-class"); + + expect(screen.getByText("Encryption enabled").parentElement).toHaveClass("custom-class"); + }); +}); diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.tsx b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.tsx new file mode 100644 index 0000000000..218ae74da6 --- /dev/null +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.tsx @@ -0,0 +1,100 @@ +/* + * 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 { LockSolidIcon, ErrorSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; + +import { type ViewModel, useViewModel } from "../../viewmodel"; +import styles from "./EncryptionEventView.module.css"; +import { useI18n } from "../../utils/i18nContext"; +import { EventTileBubble } from "../EventTileBubble"; + +export enum EncryptionEventState { + /** Encryption settings changed while encryption stayed enabled. */ + CHANGED = "CHANGED", + /** Someone attempted to disable encryption in an encrypted room. */ + DISABLE_ATTEMPT = "DISABLE_ATTEMPT", + /** Encryption was enabled in a regular room. */ + ENABLED = "ENABLED", + /** Encryption was enabled in a DM room. */ + ENABLED_DM = "ENABLED_DM", + /** Encryption was enabled in a local room. */ + ENABLED_LOCAL = "ENABLED_LOCAL", + /** Encryption is unavailable/unsupported for this event context. */ + UNSUPPORTED = "UNSUPPORTED", +} + +export type EncryptionEventViewSnapshot = { + /** Which encryption event variant to render. */ + state: EncryptionEventState; + /** Whether state-event encryption messaging should be shown. */ + encryptedStateEvents?: boolean; + /** Display name for DM partner, used by ENABLED_DM subtitle text. */ + userName?: string; + /** Optional CSS classes passed through to EventTileBubble. */ + className?: string; + /** Optional timestamp element rendered in the EventTileBubble footer slot. */ + timestamp?: JSX.Element; +}; + +/** + * ViewModel contract consumed by {@link EncryptionEventView}. + */ +export type EncryptionEventViewModel = ViewModel; + +export interface EncryptionEventViewProps { + /** + * ViewModel providing the current encryption event snapshot. + */ + vm: ViewModel; + /** + * Ref forwarded to the root DOM element. + */ + ref?: React.RefObject; +} + +export function EncryptionEventView({ vm, ref }: Readonly): JSX.Element { + const { translate: _t } = useI18n(); + const { state, encryptedStateEvents, userName, className, timestamp } = useViewModel(vm); + + let icon = ; + let title = encryptedStateEvents ? _t("common|state_encryption_enabled") : _t("common|encryption_enabled"); + let subtitle = ""; + + switch (state) { + case EncryptionEventState.CHANGED: + subtitle = _t("timeline|m.room.encryption|parameters_changed"); + break; + case EncryptionEventState.DISABLE_ATTEMPT: + title = _t("common|encryption_enabled"); + subtitle = _t("timeline|m.room.encryption|disable_attempt"); + break; + case EncryptionEventState.ENABLED: + subtitle = encryptedStateEvents + ? _t("timeline|m.room.encryption|state_enabled") + : _t("timeline|m.room.encryption|enabled"); + break; + case EncryptionEventState.ENABLED_DM: + subtitle = _t("timeline|m.room.encryption|enabled_dm", { displayName: userName }); + break; + case EncryptionEventState.ENABLED_LOCAL: + subtitle = _t("timeline|m.room.encryption|enabled_local"); + break; + case EncryptionEventState.UNSUPPORTED: + default: + icon = ; + title = _t("timeline|m.room.encryption|disabled"); + subtitle = _t("timeline|m.room.encryption|unsupported"); + break; + } + + return ( + + {timestamp} + + ); +} diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/__snapshots__/EncryptionEventView.test.tsx.snap b/packages/shared-components/src/event-tiles/EncryptionEventView/__snapshots__/EncryptionEventView.test.tsx.snap new file mode 100644 index 0000000000..09e39927bb --- /dev/null +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/__snapshots__/EncryptionEventView.test.tsx.snap @@ -0,0 +1,245 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`EncryptionEventView > renders Default story 1`] = ` +

+
+ + + +
+ Encryption enabled +
+
+ Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture. +
+
+
+`; + +exports[`EncryptionEventView > renders DisableAttempt story 1`] = ` +
+
+ + + +
+ Encryption enabled +
+
+ Ignored attempt to disable encryption +
+
+
+`; + +exports[`EncryptionEventView > renders EnabledDirectMessage story 1`] = ` +
+
+ + + +
+ Encryption enabled +
+
+ Messages here are end-to-end encrypted. Verify Alice in their profile - tap on their profile picture. +
+
+
+`; + +exports[`EncryptionEventView > renders EnabledLocalRoom story 1`] = ` +
+
+ + + +
+ Encryption enabled +
+
+ Messages in this chat will be end-to-end encrypted. +
+
+
+`; + +exports[`EncryptionEventView > renders ParametersChanged story 1`] = ` +
+
+ + + +
+ Encryption enabled +
+
+ Some encryption parameters have been changed. +
+
+
+`; + +exports[`EncryptionEventView > renders StateEncryptionEnabled story 1`] = ` +
+
+ + + +
+ Experimental state encryption enabled +
+
+ Messages and state events in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture. +
+
+
+`; + +exports[`EncryptionEventView > renders Unsupported story 1`] = ` +
+
+ + + +
+ Encryption not enabled +
+
+ The encryption used by this room isn't supported. +
+
+
+`; + +exports[`EncryptionEventView > renders WithTimestamp story 1`] = ` +
+
+ + + +
+ Encryption enabled +
+
+ Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture. +
+ + 14:56 + +
+
+`; diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/index.ts b/packages/shared-components/src/event-tiles/EncryptionEventView/index.ts new file mode 100644 index 0000000000..746d62db4e --- /dev/null +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/index.ts @@ -0,0 +1,13 @@ +/* + * 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 { + EncryptionEventView, + EncryptionEventState, + type EncryptionEventViewSnapshot, + type EncryptionEventViewModel, +} from "./EncryptionEventView"; diff --git a/packages/shared-components/src/i18n/strings/en_EN.json b/packages/shared-components/src/i18n/strings/en_EN.json index 2ba72dd1c5..cab1f9b45b 100644 --- a/packages/shared-components/src/i18n/strings/en_EN.json +++ b/packages/shared-components/src/i18n/strings/en_EN.json @@ -20,7 +20,9 @@ "start_chat": "Start chat" }, "common": { - "preferences": "Preferences" + "encryption_enabled": "Encryption enabled", + "preferences": "Preferences", + "state_encryption_enabled": "Experimental state encryption enabled" }, "left_panel": { "open_dial_pad": "Open dial pad" @@ -159,6 +161,16 @@ "audio_player": "Audio player", "error_downloading_audio": "Error downloading audio", "unnamed_audio": "Unnamed audio" + }, + "m.room.encryption": { + "disable_attempt": "Ignored attempt to disable encryption", + "disabled": "Encryption not enabled", + "enabled": "Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.", + "enabled_dm": "Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their profile picture.", + "enabled_local": "Messages in this chat will be end-to-end encrypted.", + "parameters_changed": "Some encryption parameters have been changed.", + "state_enabled": "Messages and state events in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.", + "unsupported": "The encryption used by this room isn't supported." } }, "widget": { diff --git a/packages/shared-components/src/index.ts b/packages/shared-components/src/index.ts index 4a0a971dfd..64b92ade5b 100644 --- a/packages/shared-components/src/index.ts +++ b/packages/shared-components/src/index.ts @@ -13,6 +13,7 @@ export * from "./audio/SeekBar"; export * from "./avatar/AvatarWithDetails"; export * from "./composer/Banner"; export * from "./crypto/SasEmoji"; +export * from "./event-tiles/EncryptionEventView"; export * from "./event-tiles/EventTileBubble"; export * from "./event-tiles/TextualEventView"; export * from "./message-body/MediaBody"; diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 76177bc5c8..a8050571b2 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -46,7 +46,11 @@ import { debounce, throttle } from "lodash"; import { CryptoEvent } from "matrix-js-sdk/src/crypto-api"; import { type ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle"; import { type RoomViewProps } from "@element-hq/element-web-module-api"; -import { RoomStatusBarView, useCreateAutoDisposedViewModel } from "@element-hq/web-shared-components"; +import { + EncryptionEventView, + RoomStatusBarView, + useCreateAutoDisposedViewModel, +} from "@element-hq/web-shared-components"; import shouldHideEvent from "../../shouldHideEvent"; import { _t } from "../../languageHandler"; @@ -69,6 +73,7 @@ import { TimelineRenderingType, MainSplitContentType } from "../../contexts/Room import { E2EStatus, shieldStatusForRoom } from "../../utils/ShieldUtils"; import { Action } from "../../dispatcher/actions"; import { type IMatrixClientCreds } from "../../MatrixClientPeg"; +import { useMatrixClientContext } from "../../contexts/MatrixClientContext"; import ScrollPanel from "./ScrollPanel"; import TimelinePanel from "./TimelinePanel"; import ErrorBoundary from "../views/elements/ErrorBoundary"; @@ -111,7 +116,6 @@ import { type FocusComposerPayload } from "../../dispatcher/payloads/FocusCompos import { LocalRoom, LocalRoomState } from "../../models/LocalRoom"; import { createRoomFromLocalRoom } from "../../utils/direct-messages"; import NewRoomIntro from "../views/rooms/NewRoomIntro"; -import EncryptionEvent from "../views/messages/EncryptionEvent"; import { isLocalRoom } from "../../utils/localRoom/isLocalRoom"; import { type ShowThreadPayload } from "../../dispatcher/payloads/ShowThreadPayload"; import { LargeLoader } from "./LargeLoader"; @@ -136,6 +140,7 @@ import { type FocusMessageSearchPayload } from "../../dispatcher/payloads/FocusM import { isRoomEncrypted } from "../../hooks/useIsEncrypted"; import { type RoomViewStore } from "../../stores/RoomViewStore.tsx"; import { RoomStatusBarViewModel } from "../../viewmodels/room/RoomStatusBar.ts"; +import { EncryptionEventViewModel } from "../../viewmodels/event-tiles/EncryptionEventViewModel.ts"; const DEBUG = false; const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000; @@ -313,7 +318,7 @@ function LocalRoomView(props: LocalRoomViewProps): ReactElement { let encryptionTile: ReactNode; if (encryptionEvent) { - encryptionTile = ; + encryptionTile = ; } let statusBar: ReactElement | null = null; @@ -405,6 +410,15 @@ function RoomStatusBarWrappedView(props: ConstructorParameters; } +/** + * Wrap an EncryptionEventView and ViewModel into one component, for usage with legacy React components. + */ +function EncryptionEventWrappedView({ mxEvent }: { mxEvent: MatrixEvent }): ReactElement | null { + const cli = useMatrixClientContext(); + const vm = useCreateAutoDisposedViewModel(() => new EncryptionEventViewModel({ mxEvent, cli })); + return ; +} + export class RoomView extends React.Component { // We cache the latest computed e2eStatus per room to show as soon as we switch rooms otherwise defaulting to // unencrypted causes a flicker which can yield confusion/concern in a larger room. diff --git a/src/components/views/messages/EncryptionEvent.tsx b/src/components/views/messages/EncryptionEvent.tsx deleted file mode 100644 index a8467e0519..0000000000 --- a/src/components/views/messages/EncryptionEvent.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020 The Matrix.org Foundation C.I.C. - -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, type ReactNode } from "react"; -import { type MatrixEvent } from "matrix-js-sdk/src/matrix"; -import { ErrorSolidIcon, LockSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; -import { EventTileBubble } from "@element-hq/web-shared-components"; - -import type { RoomEncryptionEventContent } from "matrix-js-sdk/src/types"; -import { _t } from "../../../languageHandler"; -import { useMatrixClientContext } from "../../../contexts/MatrixClientContext"; -import DMRoomMap from "../../../utils/DMRoomMap"; -import { objectHasDiff } from "../../../utils/objects"; -import { isLocalRoom } from "../../../utils/localRoom/isLocalRoom"; -import { MEGOLM_ENCRYPTION_ALGORITHM } from "../../../utils/crypto"; -import { useIsEncrypted } from "../../../hooks/useIsEncrypted.ts"; - -interface IProps { - mxEvent: MatrixEvent; - timestamp?: JSX.Element; - ref?: React.RefObject; -} - -const EncryptionEvent = ({ mxEvent, timestamp, ref }: IProps): ReactNode => { - const cli = useMatrixClientContext(); - const roomId = mxEvent.getRoomId()!; - const isRoomEncrypted = useIsEncrypted(cli, cli.getRoom(roomId) || undefined); - - const prevContent = mxEvent.getPrevContent() as RoomEncryptionEventContent; - const content = mxEvent.getContent(); - - // if no change happened then skip rendering this, a shallow check is enough as all known fields are top-level. - if (!objectHasDiff(prevContent, content)) return null; // nop - - if (content.algorithm === MEGOLM_ENCRYPTION_ALGORITHM && isRoomEncrypted) { - let subtitle: string; - const dmPartner = DMRoomMap.shared().getUserIdForRoomId(roomId); - const room = cli?.getRoom(roomId); - - const stateEncrypted = content["io.element.msc4362.encrypt_state_events"] && cli.enableEncryptedStateEvents; - - if (prevContent.algorithm === MEGOLM_ENCRYPTION_ALGORITHM) { - subtitle = _t("timeline|m.room.encryption|parameters_changed"); - } else if (dmPartner) { - const displayName = room?.getMember(dmPartner)?.rawDisplayName || dmPartner; - subtitle = _t("timeline|m.room.encryption|enabled_dm", { displayName }); - } else if (room && isLocalRoom(room)) { - subtitle = _t("timeline|m.room.encryption|enabled_local"); - } else if (stateEncrypted) { - subtitle = _t("timeline|m.room.encryption|state_enabled"); - } else { - subtitle = _t("timeline|m.room.encryption|enabled"); - } - - return ( - } - className="mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon" - title={stateEncrypted ? _t("common|state_encryption_enabled") : _t("common|encryption_enabled")} - subtitle={subtitle} - > - {timestamp} - - ); - } - - if (isRoomEncrypted) { - return ( - } - className="mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon" - title={_t("common|encryption_enabled")} - subtitle={_t("timeline|m.room.encryption|disable_attempt")} - > - {timestamp} - - ); - } - - return ( - } - className="mx_EventTileBubble mx_cryptoEvent" - title={_t("timeline|m.room.encryption|disabled")} - subtitle={_t("timeline|m.room.encryption|unsupported")} - ref={ref} - > - {timestamp} - - ); -}; - -export default EncryptionEvent; diff --git a/src/events/EventTileFactory.tsx b/src/events/EventTileFactory.tsx index 3c3735ab81..41286c5075 100644 --- a/src/events/EventTileFactory.tsx +++ b/src/events/EventTileFactory.tsx @@ -17,7 +17,11 @@ import { M_POLL_END, M_POLL_START, } from "matrix-js-sdk/src/matrix"; -import { TextualEventView } from "@element-hq/web-shared-components"; +import { + EncryptionEventView, + TextualEventView, + useCreateAutoDisposedViewModel, +} from "@element-hq/web-shared-components"; import SettingsStore from "../settings/SettingsStore"; import type LegacyCallEventGrouper from "../components/structures/LegacyCallEventGrouper"; @@ -26,12 +30,12 @@ import { TimelineRenderingType } from "../contexts/RoomContext"; import MessageEvent from "../components/views/messages/MessageEvent"; import LegacyCallEvent from "../components/views/messages/LegacyCallEvent"; import { CallEvent } from "../components/views/messages/CallEvent"; -import EncryptionEvent from "../components/views/messages/EncryptionEvent"; import { RoomPredecessorTile } from "../components/views/messages/RoomPredecessorTile"; import RoomAvatarEvent from "../components/views/messages/RoomAvatarEvent"; import { WIDGET_LAYOUT_EVENT_TYPE } from "../stores/widgets/WidgetLayoutStore"; import { ALL_RULE_TYPES } from "../mjolnir/BanList"; import { MatrixClientPeg } from "../MatrixClientPeg"; +import { useMatrixClientContext } from "../contexts/MatrixClientContext"; import MKeyVerificationRequest from "../components/views/messages/MKeyVerificationRequest"; import { WidgetType } from "../widgets/WidgetType"; import MJitsiWidgetEvent from "../components/views/messages/MJitsiWidgetEvent"; @@ -42,6 +46,7 @@ import ViewSourceEvent from "../components/views/messages/ViewSourceEvent"; import { shouldDisplayAsBeaconTile } from "../utils/beacon/timeline"; import { type IBodyProps } from "../components/views/messages/IBodyProps"; import { ModuleApi } from "../modules/Api"; +import { EncryptionEventViewModel } from "../viewmodels/event-tiles/EncryptionEventViewModel"; import { TextualEventViewModel } from "../viewmodels/event-tiles/TextualEventViewModel"; import { ElementCallEventType } from "../call-types"; @@ -80,6 +85,14 @@ export const TextualEventFactory: Factory = (ref, props) => { const vm = new TextualEventViewModel(props); return ; }; +function EncryptionEventWrappedView({ mxEvent, ref }: IBodyProps): JSX.Element { + const cli = useMatrixClientContext(); + const vm = useCreateAutoDisposedViewModel(() => new EncryptionEventViewModel({ mxEvent, cli })); + return ; +} +const EncryptionEventFactory: Factory = (ref, props) => { + return ; +}; const VerificationReqFactory: Factory = (_ref, props) => ; const HiddenEventFactory: Factory = (ref, props) => ; @@ -99,7 +112,7 @@ const EVENT_TILE_TYPES = new Map([ ]); const STATE_EVENT_TILE_TYPES = new Map([ - [EventType.RoomEncryption, (ref, props) => ], + [EventType.RoomEncryption, EncryptionEventFactory], [EventType.RoomCanonicalAlias, TextualEventFactory], [EventType.RoomCreate, RoomCreateEventFactory], [EventType.RoomMember, TextualEventFactory], diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d28e456bab..cb57da8eeb 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -482,7 +482,6 @@ "email_address": "Email address", "emoji": "Emoji", "encrypted": "Encrypted", - "encryption_enabled": "Encryption enabled", "error": "Error", "faq": "FAQ", "favourites": "Favourites", @@ -3461,16 +3460,6 @@ "unknown_predecessor": "Can't find the old version of this room (room ID: %(roomId)s), and we have not been provided with 'via_servers' to look for it.", "unknown_predecessor_guess_server": "Can't find the old version of this room (room ID: %(roomId)s), and we have not been provided with 'via_servers' to look for it. It's possible that guessing the server from the room ID will work. If you want to try, click this link:" }, - "m.room.encryption": { - "disable_attempt": "Ignored attempt to disable encryption", - "disabled": "Encryption not enabled", - "enabled": "Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.", - "enabled_dm": "Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their profile picture.", - "enabled_local": "Messages in this chat will be end-to-end encrypted.", - "parameters_changed": "Some encryption parameters have been changed.", - "state_enabled": "Messages and state events in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.", - "unsupported": "The encryption used by this room isn't supported." - }, "m.room.guest_access": { "can_join": "%(senderDisplayName)s has allowed guests to join the room.", "forbidden": "%(senderDisplayName)s has prevented guests from joining the room.", diff --git a/src/viewmodels/event-tiles/EncryptionEventViewModel.ts b/src/viewmodels/event-tiles/EncryptionEventViewModel.ts new file mode 100644 index 0000000000..03a840b621 --- /dev/null +++ b/src/viewmodels/event-tiles/EncryptionEventViewModel.ts @@ -0,0 +1,122 @@ +/* + * 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 JSX } from "react"; +import { RoomStateEvent, type MatrixClient, type MatrixEvent } from "matrix-js-sdk/src/matrix"; +import { + BaseViewModel, + EncryptionEventState, + type EncryptionEventViewSnapshot as EncryptionEventViewSnapshotInterface, + type EncryptionEventViewModel as EncryptionEventViewModelInterface, +} from "@element-hq/web-shared-components"; + +import type { RoomEncryptionEventContent } from "matrix-js-sdk/src/types"; +import DMRoomMap from "../../utils/DMRoomMap"; +import { MEGOLM_ENCRYPTION_ALGORITHM } from "../../utils/crypto"; +import { isLocalRoom } from "../../utils/localRoom/isLocalRoom"; +import { isRoomEncrypted } from "../../hooks/useIsEncrypted"; +import { objectHasDiff } from "../../utils/objects"; + +export interface EncryptionEventViewModelProps { + /** Caller-provided client. */ + cli: MatrixClient; + /** Encryption state event to derive tile state from. */ + mxEvent: MatrixEvent; + /** Optional timestamp element rendered in the tile footer slot. */ + timestamp?: JSX.Element; +} + +export class EncryptionEventViewModel + extends BaseViewModel + implements EncryptionEventViewModelInterface +{ + public constructor(props: EncryptionEventViewModelProps) { + super( + props, + EncryptionEventViewModel.calculateSnapshot(props, EncryptionEventViewModel.getInitialIsEncrypted(props)), + ); + void this.refreshSnapshotFromEvent(); + + const roomId = this.props.mxEvent.getRoomId()!; + const room = this.props.cli.getRoom(roomId); + if (room) { + // Recompute when room state changes (including encryption state updates). + this.disposables.trackListener(room, RoomStateEvent.Update, () => void this.refreshSnapshotFromEvent()); + } + } + + private refreshSnapshotFromEvent = async (): Promise => { + const roomId = this.props.mxEvent.getRoomId()!; + const room = this.props.cli.getRoom(roomId); + const crypto = this.props.cli.getCrypto(); + const isEncrypted = Boolean(room && crypto && (await isRoomEncrypted(room, crypto))); + const nextSnapshot = EncryptionEventViewModel.calculateSnapshot(this.props, isEncrypted); + + if (objectHasDiff(this.snapshot.current, nextSnapshot)) { + this.snapshot.set(nextSnapshot); + } + }; + + private static getInitialIsEncrypted(props: EncryptionEventViewModelProps): boolean { + const roomId = props.mxEvent.getRoomId()!; + const room = props.cli.getRoom(roomId); + if (!room) return false; + + if (isLocalRoom(room)) { + const localRoom = room as { isEncryptionEnabled?: () => boolean }; + return Boolean(localRoom.isEncryptionEnabled?.()); + } + + return room.hasEncryptionStateEvent(); + } + + private static calculateSnapshot( + props: EncryptionEventViewModelProps, + isEncrypted: boolean, + ): EncryptionEventViewSnapshotInterface { + // Keep legacy class names for compatibility with existing timeline layout and styling. + const newSnapshot: EncryptionEventViewSnapshotInterface = { + state: EncryptionEventState.CHANGED, + encryptedStateEvents: undefined, + userName: undefined, + timestamp: props.timestamp, + className: "mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon", + }; + + const content = props.mxEvent.getContent(); + if (isEncrypted && content.algorithm === MEGOLM_ENCRYPTION_ALGORITHM) { + const roomId = props.mxEvent.getRoomId()!; + const room = props.cli.getRoom(roomId); + const isRoomLocal = isLocalRoom(room); + const prevContent = props.mxEvent.getPrevContent() as RoomEncryptionEventContent; + const dmPartner = roomId ? DMRoomMap.shared().getUserIdForRoomId(roomId) : undefined; + const stateEncrypted = Boolean( + content["io.element.msc4362.encrypt_state_events"] && props.cli.enableEncryptedStateEvents, + ); + + newSnapshot.state = EncryptionEventState.ENABLED; + newSnapshot.encryptedStateEvents = stateEncrypted; + + if (prevContent.algorithm === MEGOLM_ENCRYPTION_ALGORITHM) { + newSnapshot.state = EncryptionEventState.CHANGED; + } else if (dmPartner) { + newSnapshot.state = EncryptionEventState.ENABLED_DM; + newSnapshot.userName = room?.getMember(dmPartner)?.rawDisplayName ?? dmPartner; + } else if (isRoomLocal) { + newSnapshot.state = EncryptionEventState.ENABLED_LOCAL; + } + } else if (isEncrypted) { + newSnapshot.state = EncryptionEventState.DISABLE_ATTEMPT; + } else { + newSnapshot.state = EncryptionEventState.UNSUPPORTED; + // Unsupported branch matches legacy EncryptionEvent class usage (no icon class). + newSnapshot.className = "mx_EventTileBubble mx_cryptoEvent"; + } + + return newSnapshot; + } +} diff --git a/test/unit-tests/components/views/messages/EncryptionEvent-test.tsx b/test/unit-tests/components/views/messages/EncryptionEvent-test.tsx deleted file mode 100644 index 3375b6b0b5..0000000000 --- a/test/unit-tests/components/views/messages/EncryptionEvent-test.tsx +++ /dev/null @@ -1,145 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2022 The Matrix.org Foundation C.I.C. - -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 { mocked } from "jest-mock"; -import { type MatrixClient, type MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; -import { render, screen } from "jest-matrix-react"; -import { waitFor } from "@testing-library/dom"; - -import EncryptionEvent from "../../../../../src/components/views/messages/EncryptionEvent"; -import { createTestClient, mkMessage } from "../../../../test-utils"; -import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg"; -import { LocalRoom } from "../../../../../src/models/LocalRoom"; -import DMRoomMap from "../../../../../src/utils/DMRoomMap"; -import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext"; - -const renderEncryptionEvent = (client: MatrixClient, event: MatrixEvent) => { - render( - - - , - ); -}; - -const checkTexts = async (title: string, subTitle: string) => { - await screen.findByText(title); - await screen.findByText(subTitle); -}; - -describe("EncryptionEvent", () => { - const roomId = "!room:example.com"; - const algorithm = "m.megolm.v1.aes-sha2"; - let client: MatrixClient; - let event: MatrixEvent; - - beforeEach(() => { - jest.clearAllMocks(); - client = createTestClient(); - jest.spyOn(MatrixClientPeg, "get").mockReturnValue(client); - jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(client); - event = mkMessage({ - event: true, - room: roomId, - user: client.getUserId()!, - }); - jest.spyOn(DMRoomMap, "shared").mockReturnValue({ - getUserIdForRoomId: jest.fn(), - } as unknown as DMRoomMap); - }); - - describe("for an encrypted room", () => { - beforeEach(() => { - event.event.content!.algorithm = algorithm; - jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true); - const room = new Room(roomId, client, client.getUserId()!); - mocked(client.getRoom).mockReturnValue(room); - }); - - it("should show the expected texts", async () => { - renderEncryptionEvent(client, event); - await waitFor(() => - checkTexts( - "Encryption enabled", - "Messages in this room are end-to-end encrypted. " + - "When people join, you can verify them in their profile, just tap on their profile picture.", - ), - ); - }); - - it("should show the expected texts for experimental state event encryption", async () => { - client.enableEncryptedStateEvents = true; - event.event.content!["io.element.msc4362.encrypt_state_events"] = true; - renderEncryptionEvent(client, event); - await waitFor(() => - checkTexts( - "Experimental state encryption enabled", - "Messages and state events in this room are end-to-end encrypted. " + - "When people join, you can verify them in their profile, " + - "just tap on their profile picture.", - ), - ); - }); - - describe("with same previous algorithm", () => { - beforeEach(() => { - jest.spyOn(event, "getPrevContent").mockReturnValue({ - algorithm: algorithm, - }); - }); - - it("should show the expected texts", async () => { - renderEncryptionEvent(client, event); - await waitFor(() => checkTexts("Encryption enabled", "Some encryption parameters have been changed.")); - }); - }); - - describe("with unknown algorithm", () => { - beforeEach(() => { - event.event.content!.algorithm = "unknown"; - }); - - it("should show the expected texts", async () => { - renderEncryptionEvent(client, event); - await waitFor(() => checkTexts("Encryption enabled", "Ignored attempt to disable encryption")); - }); - }); - }); - - describe("for an unencrypted room", () => { - beforeEach(() => { - jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(false); - renderEncryptionEvent(client, event); - }); - - it("should show the expected texts", async () => { - expect(client.getCrypto()!.isEncryptionEnabledInRoom).toHaveBeenCalledWith(roomId); - await waitFor(() => - checkTexts("Encryption not enabled", "The encryption used by this room isn't supported."), - ); - }); - }); - - describe("for an encrypted local room", () => { - let localRoom: LocalRoom; - - beforeEach(() => { - event.event.content!.algorithm = algorithm; - // jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true); - localRoom = new LocalRoom(roomId, client, client.getUserId()!); - jest.spyOn(localRoom, "isEncryptionEnabled").mockReturnValue(true); - mocked(client.getRoom).mockReturnValue(localRoom); - renderEncryptionEvent(client, event); - }); - - it("should show the expected texts", async () => { - expect(localRoom.isEncryptionEnabled).toHaveBeenCalled(); - await checkTexts("Encryption enabled", "Messages in this chat will be end-to-end encrypted."); - }); - }); -}); diff --git a/test/viewmodels/event-tiles/EncryptionEventViewModel-test.ts b/test/viewmodels/event-tiles/EncryptionEventViewModel-test.ts new file mode 100644 index 0000000000..9da5bdd09c --- /dev/null +++ b/test/viewmodels/event-tiles/EncryptionEventViewModel-test.ts @@ -0,0 +1,192 @@ +/* + * 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 { waitFor } from "@testing-library/dom"; +import { mocked } from "jest-mock"; +import { RoomStateEvent, type MatrixClient, type MatrixEvent, type Room } from "matrix-js-sdk/src/matrix"; +import { EncryptionEventState } from "@element-hq/web-shared-components"; + +import type { RoomEncryptionEventContent } from "matrix-js-sdk/src/types"; +import { EncryptionEventViewModel } from "../../../src/viewmodels/event-tiles/EncryptionEventViewModel"; +import { LocalRoom } from "../../../src/models/LocalRoom"; +import DMRoomMap from "../../../src/utils/DMRoomMap"; +import { mkEvent, stubClient } from "../../test-utils"; + +describe("EncryptionEventViewModel", () => { + const roomId = "!room:example.com"; + const algorithm = "m.megolm.v1.aes-sha2"; + let client: MatrixClient; + let event: MatrixEvent; + let room: Room; + + beforeEach(() => { + jest.clearAllMocks(); + client = stubClient(); + room = client.getRoom(roomId)!; + mocked(client.getRoom).mockReturnValue(room); + event = mkEvent({ + event: true, + room: roomId, + user: client.getUserId()!, + type: "m.room.encryption", + content: { + algorithm, + }, + prev_content: {}, + }); + jest.spyOn(DMRoomMap, "shared").mockReturnValue({ + getUserIdForRoomId: jest.fn(), + } as unknown as DMRoomMap); + }); + + const setRoomEncrypted = (encrypted: boolean): void => { + const crypto = client.getCrypto()!; + mocked(crypto.isEncryptionEnabledInRoom).mockResolvedValue(encrypted); + }; + + const createVm = ( + props: Partial[0]> = {}, + ): EncryptionEventViewModel => + new EncryptionEventViewModel({ + mxEvent: event, + cli: client, + ...props, + }); + + it("sets ENABLED for encrypted room", async () => { + setRoomEncrypted(true); + + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.ENABLED)); + expect(vm.getSnapshot()).toMatchObject({ + state: EncryptionEventState.ENABLED, + className: "mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon", + encryptedStateEvents: false, + }); + }); + + it("uses synchronous room encryption state for the initial snapshot", () => { + jest.spyOn(room, "hasEncryptionStateEvent").mockReturnValue(true); + setRoomEncrypted(false); + + const vm = createVm(); + expect(vm.getSnapshot().state).toBe(EncryptionEventState.ENABLED); + }); + + it("sets ENABLED with encryptedStateEvents=true for encrypted state events", async () => { + setRoomEncrypted(true); + client.enableEncryptedStateEvents = true; + (event.getContent() as RoomEncryptionEventContent)["io.element.msc4362.encrypt_state_events"] = true; + + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.ENABLED)); + expect(vm.getSnapshot().encryptedStateEvents).toBe(true); + }); + + it("sets CHANGED when previous algorithm is already megolm", async () => { + setRoomEncrypted(true); + event = mkEvent({ + event: true, + room: roomId, + user: client.getUserId()!, + type: "m.room.encryption", + content: { + algorithm, + rotation_period_ms: 1, + }, + prev_content: { algorithm }, + }); + + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.CHANGED)); + }); + + it("sets DISABLE_ATTEMPT for unknown algorithm in encrypted room", async () => { + setRoomEncrypted(true); + event = mkEvent({ + event: true, + room: roomId, + user: client.getUserId()!, + type: "m.room.encryption", + content: { algorithm: "unknown" }, + prev_content: {}, + }); + + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.DISABLE_ATTEMPT)); + }); + + it("sets UNSUPPORTED for unencrypted room", async () => { + setRoomEncrypted(false); + + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.UNSUPPORTED)); + expect(vm.getSnapshot().className).toBe("mx_EventTileBubble mx_cryptoEvent"); + }); + + it("sets ENABLED_DM with partner display name", async () => { + setRoomEncrypted(true); + jest.spyOn(DMRoomMap, "shared").mockReturnValue({ + getUserIdForRoomId: jest.fn().mockReturnValue("@alice:example.com"), + } as unknown as DMRoomMap); + mocked(room.getMember).mockReturnValue({ + rawDisplayName: "Alice", + } as unknown as ReturnType); + + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.ENABLED_DM)); + expect(vm.getSnapshot().userName).toBe("Alice"); + }); + + it("sets ENABLED_LOCAL for encrypted local room", async () => { + const localRoomId = "local+123"; + const localRoom = new LocalRoom(localRoomId, client, client.getUserId()!); + jest.spyOn(localRoom, "isEncryptionEnabled").mockReturnValue(true); + mocked(client.getRoom).mockReturnValue(localRoom); + event = mkEvent({ + event: true, + room: localRoomId, + user: client.getUserId()!, + type: "m.room.encryption", + content: { algorithm }, + prev_content: {}, + }); + jest.spyOn(DMRoomMap, "shared").mockReturnValue({ + getUserIdForRoomId: jest.fn(), + } as unknown as DMRoomMap); + + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.ENABLED_LOCAL)); + expect(localRoom.isEncryptionEnabled).toHaveBeenCalled(); + }); + + it("recomputes snapshot on RoomStateEvent.Update", async () => { + setRoomEncrypted(false); + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.UNSUPPORTED)); + + setRoomEncrypted(true); + room.emit(RoomStateEvent.Update, room.currentState); + + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.ENABLED)); + }); + + it("does not emit updates when snapshot is unchanged", async () => { + setRoomEncrypted(true); + const vm = createVm(); + await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.ENABLED)); + + const listener = jest.fn(); + const unsubscribe = vm.subscribe(listener); + + room.emit(RoomStateEvent.Update, room.currentState); + + await waitFor(() => expect(mocked(client.getCrypto()!.isEncryptionEnabledInRoom)).toHaveBeenCalledTimes(2)); + expect(listener).not.toHaveBeenCalled(); + unsubscribe(); + }); +});