From 7ab3b2433100af33a1b6ee16233d72436022af66 Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 26 Feb 2026 10:25:04 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=99=20=20Update=20the=20navigator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/navigator/ToggleFavoriteRoom.ts | 11 + src/api/navigator/index.ts | 1 + src/api/nitro/CreateLinkEvent.ts | 1 + src/api/nitro/index.ts | 1 + .../nitro/session/GetSessionDataManager.ts | 1 + src/api/nitro/session/index.ts | 1 + src/assets/images/navigator/avatar_icon.png | Bin 0 -> 1625 bytes src/assets/images/navigator/badge_danger.png | Bin 0 -> 1671 bytes src/assets/images/navigator/badge_empty.png | Bin 0 -> 1669 bytes src/assets/images/navigator/badge_success.png | Bin 0 -> 1671 bytes src/assets/images/navigator/badge_warning.png | Bin 0 -> 1668 bytes src/assets/images/navigator/create_room.png | Bin 0 -> 6862 bytes src/assets/images/navigator/groups.png | Bin 0 -> 461 bytes .../images/navigator/icon-room-promote.png | Bin 0 -> 674 bytes src/assets/images/navigator/info_bg.png | Bin 0 -> 1718 bytes src/assets/images/navigator/info_pointer.png | Bin 0 -> 2835 bytes src/assets/images/navigator/inline_view.png | Bin 0 -> 1824 bytes src/assets/images/navigator/minus.png | Bin 0 -> 1611 bytes src/assets/images/navigator/pen.png | Bin 0 -> 286 bytes src/assets/images/navigator/plus.png | Bin 0 -> 1623 bytes src/assets/images/navigator/profile.png | Bin 0 -> 474 bytes src/assets/images/navigator/promote_room.png | Bin 0 -> 1193 bytes src/assets/images/navigator/random_room.png | Bin 0 -> 6978 bytes src/assets/images/navigator/refresh.png | Bin 0 -> 481 bytes .../images/navigator/room_thumbnail.png | Bin 0 -> 2092 bytes .../navigator/saves-search/delete_search.png | Bin 0 -> 215 bytes .../saves-search/delete_search_click.png | Bin 0 -> 217 bytes .../saves-search/delete_search_hover.png | Bin 0 -> 214 bytes .../navigator/saves-search/search_save.png | Bin 0 -> 198 bytes .../images/navigator/similar_results.png | Bin 0 -> 381 bytes .../navigator/similar_results_active.png | Bin 0 -> 384 bytes src/assets/images/navigator/thumbnail_bg.png | Bin 0 -> 1711 bytes .../images/navigator/thumbnail_view.png | Bin 0 -> 1831 bytes src/assets/images/navigator/white_bg.png | Bin 0 -> 1661 bytes src/assets/webfonts/Volter-b.ttf | Bin 0 -> 19748 bytes src/assets/webfonts/Volter.ttf | Bin 0 -> 37840 bytes src/common/Text.tsx | 8 +- src/common/layout/LayoutSearchSavesView.tsx | 23 ++ src/common/layout/index.ts | 1 + src/components/navigator/NavigatorView.scss | 8 + src/components/navigator/NavigatorView.tsx | 77 ++++- .../navigator/views/NavigatorRoomInfoView.tsx | 240 +++++++++------- .../NavigatorSearchResultItemInfoView.tsx | 264 ++++++++++++------ .../search/NavigatorSearchResultItemView.tsx | 141 +++++++--- .../search/NavigatorSearchResultView.tsx | 93 +++--- .../NavigatorSearchSavesResultItemView.tsx | 46 +++ .../search/NavigatorSearchSavesResultView.tsx | 31 ++ src/css/icons/icons.css | 14 + src/hooks/navigator/useNavigator.ts | 36 ++- 49 files changed, 716 insertions(+), 282 deletions(-) create mode 100644 src/api/navigator/ToggleFavoriteRoom.ts create mode 100644 src/api/nitro/CreateLinkEvent.ts create mode 100644 src/api/nitro/session/GetSessionDataManager.ts create mode 100644 src/assets/images/navigator/avatar_icon.png create mode 100644 src/assets/images/navigator/badge_danger.png create mode 100644 src/assets/images/navigator/badge_empty.png create mode 100644 src/assets/images/navigator/badge_success.png create mode 100644 src/assets/images/navigator/badge_warning.png create mode 100644 src/assets/images/navigator/create_room.png create mode 100644 src/assets/images/navigator/groups.png create mode 100644 src/assets/images/navigator/icon-room-promote.png create mode 100644 src/assets/images/navigator/info_bg.png create mode 100644 src/assets/images/navigator/info_pointer.png create mode 100644 src/assets/images/navigator/inline_view.png create mode 100644 src/assets/images/navigator/minus.png create mode 100644 src/assets/images/navigator/pen.png create mode 100644 src/assets/images/navigator/plus.png create mode 100644 src/assets/images/navigator/profile.png create mode 100644 src/assets/images/navigator/promote_room.png create mode 100644 src/assets/images/navigator/random_room.png create mode 100644 src/assets/images/navigator/refresh.png create mode 100644 src/assets/images/navigator/room_thumbnail.png create mode 100644 src/assets/images/navigator/saves-search/delete_search.png create mode 100644 src/assets/images/navigator/saves-search/delete_search_click.png create mode 100644 src/assets/images/navigator/saves-search/delete_search_hover.png create mode 100644 src/assets/images/navigator/saves-search/search_save.png create mode 100644 src/assets/images/navigator/similar_results.png create mode 100644 src/assets/images/navigator/similar_results_active.png create mode 100644 src/assets/images/navigator/thumbnail_bg.png create mode 100644 src/assets/images/navigator/thumbnail_view.png create mode 100644 src/assets/images/navigator/white_bg.png create mode 100644 src/assets/webfonts/Volter-b.ttf create mode 100644 src/assets/webfonts/Volter.ttf create mode 100644 src/common/layout/LayoutSearchSavesView.tsx create mode 100644 src/components/navigator/NavigatorView.scss create mode 100644 src/components/navigator/views/search/NavigatorSearchSavesResultItemView.tsx create mode 100644 src/components/navigator/views/search/NavigatorSearchSavesResultView.tsx diff --git a/src/api/navigator/ToggleFavoriteRoom.ts b/src/api/navigator/ToggleFavoriteRoom.ts new file mode 100644 index 0000000..25295fd --- /dev/null +++ b/src/api/navigator/ToggleFavoriteRoom.ts @@ -0,0 +1,11 @@ +import { AddFavouriteRoomMessageComposer, DeleteFavouriteRoomMessageComposer } from '@nitrots/nitro-renderer'; +import { SendMessageComposer } from '..'; + +export const ToggleFavoriteRoom = (roomId: number, isFavorite: boolean): void => +{ + if(roomId <= 0) return; + + SendMessageComposer(isFavorite + ? new DeleteFavouriteRoomMessageComposer(roomId) + : new AddFavouriteRoomMessageComposer(roomId)); +}; diff --git a/src/api/navigator/index.ts b/src/api/navigator/index.ts index bceb33e..fae422b 100644 --- a/src/api/navigator/index.ts +++ b/src/api/navigator/index.ts @@ -9,4 +9,5 @@ export * from './NavigatorSearchResultViewDisplayMode'; export * from './RoomInfoData'; export * from './RoomSettingsUtils'; export * from './SearchFilterOptions'; +export * from './ToggleFavoriteRoom'; export * from './TryVisitRoom'; diff --git a/src/api/nitro/CreateLinkEvent.ts b/src/api/nitro/CreateLinkEvent.ts new file mode 100644 index 0000000..dd14c22 --- /dev/null +++ b/src/api/nitro/CreateLinkEvent.ts @@ -0,0 +1 @@ +export { CreateLinkEvent } from '@nitrots/nitro-renderer'; diff --git a/src/api/nitro/index.ts b/src/api/nitro/index.ts index 11b9d02..c2ecb72 100644 --- a/src/api/nitro/index.ts +++ b/src/api/nitro/index.ts @@ -1,3 +1,4 @@ +export * from './CreateLinkEvent'; export * from './GetConfigurationValue'; export * from './OpenUrl'; export * from './SendMessageComposer'; diff --git a/src/api/nitro/session/GetSessionDataManager.ts b/src/api/nitro/session/GetSessionDataManager.ts new file mode 100644 index 0000000..d9e19fc --- /dev/null +++ b/src/api/nitro/session/GetSessionDataManager.ts @@ -0,0 +1 @@ +export { GetSessionDataManager } from '@nitrots/nitro-renderer'; diff --git a/src/api/nitro/session/index.ts b/src/api/nitro/session/index.ts index 4c0491d..88ee45a 100644 --- a/src/api/nitro/session/index.ts +++ b/src/api/nitro/session/index.ts @@ -1,4 +1,5 @@ export * from './CanManipulateFurniture'; +export * from './GetSessionDataManager'; export * from './CreateRoomSession'; export * from './GetCanStandUp'; export * from './GetCanUseExpression'; diff --git a/src/assets/images/navigator/avatar_icon.png b/src/assets/images/navigator/avatar_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2644636cfff5d4724746351d4b5825b439eb8e11 GIT binary patch literal 1625 zcmcIkO>f*p7?Z1= z>a7xY1V?@VCvF_*Pk?`bkhmZu_yI^^yz4bp)kb?^rCE>dXP)2zvNd!{T$&F{h7Xp)5eysAmgL_dxUCCW@w9wG-(WLqxa zzV84#a1fvv5#aS4+ohiEQOBC=Qs-mIPMQKX=NF^PB~u(-jlv;eJm}dyUns?TIArHl)b~j1&`PhYo zHPap$tZB30M*BE@MfFzGxuBhng(AJA`3qPjhxL?{Vtb@2y~23G*U7KQ>E+^AYyJN! zzAY^qlYAtUfkLB62R25tXbgP0XY+S2n9>rioUYk+CaZ2bn-;mEE|m%2z52y3%D`bi z-X5%d!~VIr*m~pb=kIQ8Y<#8G#)G3j;(szWxXYl@Uq&C$|A8+$K!EgZR|0& zcavR#1f1vrA?Ss}O57@OfeRcEhYGVJ}@^{iwZ@ zvDKZWF5TInRv=!OL+3{}5X7t?XcP~U+#WgNnAe7N`B)Lr7*cFF;(Qqpt$E96foBYv zvMN!GF)}UL$6?E77CwhGtf`8sDQZK~R9kD&U@UMy}wluGYCiSzDu%9ItB^UaL1#VwW;C#S&zTJKFa zv%xsifGTXjVu0l!OszwueO~aq&+n5*{kqO3wHGo*3Z8X2A52@dJY_+eE^FwtM`)Ck zyk|;H)fVQGf;l4eO_Q)LX>~}fcEDKofpIA5G6gcV{ z+2BnUv@9Zp1l**DhAy=%AIuvxS%}r9i32iadpV2X>mh@?SxZ#`Fq(!zvDuA&YjD zL-V$)FJzUyUabZZ8r#k$v~;^8(z4Ag5Ua=0J*qdG&I#?X&wzA~=1*Wb4~rqm*nAHv zJ%hcJ`^nGA*52Z0YyJN!zA7#4lcdLBfGT2{j#3VqNn@1%_H6v_uAwxA!=x*h^^k>| zdQ7tfs52PC-w^r<4Dj`iJKt?z{rT33!*Agy-0wer?UAE*ethPQ_1j1GU3>d$Bp&yLkX@#vp7_wT}=h#+(pm)uWZxbn_F%s>oc literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/badge_empty.png b/src/assets/images/navigator/badge_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..7b105e6138efccb54c18ea90a440f2424fcacba8 GIT binary patch literal 1669 zcmcIl&x_nt98V95miAJAAiWr~iY=Pt`E#hW6EcoKE#K|wuv&~wq2UIe}P%}gd1X_vVe$jf^#pZEEGzCZHK z^{vg9PMl+t2UL6WFhmBypJFC>)M-?v@<~qnf0X{FH*I&U{SGcVD(%;S(KE#=Sxl1 z5#iH9cpBsl>&VtI?prp&h~U-dkVO!hLj$MjKuCO>3I_J#R8(}NdTMSvcJ)nqMV%2d6)=U?i_a)ma16ZD)moAqKIO-UY&~a&l zxdYt}!+}mkqp71#Er%}HUdbZ(dg%CW)>0J*M%#8l7;Wgx;Z41<8gjiw zTQE4>LDX>?JPb9JGZM-yrcfzSOozfulcA>mtEMf-G7BV!nA>GN13P%piE>EZLG^`P z5^q+saiq@5j!;%syQi@-&4O#yc65*EEvIu%I~fTeJtp~cST4h2LNl>Ggh($@UgrJe z=XC#g@yn(De-&SamW^mK6wp9Tt#rpM8_hg1=6`cGdv{k;TEL;xmD9S(!lG`|A_3|Q znlSo$qYDlEa@Sw)cdq~b^HXQ8qEr44@BZ@9Cts4z(R^FI z{MqTu>%2$hYJ7 SFJ3Nx6y1$Y|Kp2SuKfczffRTE literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/badge_success.png b/src/assets/images/navigator/badge_success.png new file mode 100644 index 0000000000000000000000000000000000000000..d597896fd9b7ac9a2f958f87a1e199e48d62476a GIT binary patch literal 1671 zcmcIk&x_nt9M6jDkKzg~DzXO~(+Vy6l9!jv>`Z7{w>zsd*sRNT(5)c!<-M1gu#>!) zWOruQpLi)ni-%r3cqj;JQ3_u5ARgM2q9BR~5B>p)J$Tn|W-@7!cA1NTB=5)PeZHUX zkN3T}zV`gWfyD!gqAYaUtqu4egZIz__rdr0;g3Fn--Ai}T&5_GJypDOosFY66=nX% zu)CRWdS@IaBb{>T3w;zNfL4?fr$z~7TOwC|(Fc*nRJ3tc z1`0xEjA&w*Ixd`=0zGekXcSfGEMMILE2Z8$5@+4rL>L<)liMj1t2-jjk4%kmR-H|@ z)4|wN&I~aS5x_DKW>%(>zRYFTmv`}_+OE7woRw72T&7(q2h&uoPgzuA=|nx|Q5MDp z@9{!Yc|^2mE?f=rMi90TAzjmQ2zG4zG&UWKCr}S+fzzDM29`Vwc4vWlp3{l5oW@La zTCN77^f2TOGyQ;BH5+jfaAaGAq9)}WVM=_01#C9zV_k5*CE0cnz~cO*bh%`}QN_p+ zb<*JVdL5a<@)5Od6ZxWUBEICCrtjO7P%&kDF%9AN(82Ajg(?n=#0q@dvKlBb8AW!Z zK~O^kO;oD|3>4b|WlNgMnG?t~qEIPeM0>(W;-047QnMllGW8^fm|I0X13P%p4l_vJ zZh1q_ir31~AXLX?=TKHuyQ{Gx&4O#?a(IX6&8BlgJM0S}-6#1ISSEvfNK>)ggGkR% zUdG+zXLM(O@w27=e-&Sbmi1}e6VN~nt#HRE8qFjz#(#4*-n*?SP2te#iec4cVN$he z5d(D!O<1{l|L@SiU+uJ(yX}jA{_^F)ckn~^etUT_U-Q|6Pd}e~`%(PL+{d>*SO2;8{-yfrn_t{` dBl~Io-1m=s_VbzZ7r>CxSzT*gJAMA$e*tsd4*>uG literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/badge_warning.png b/src/assets/images/navigator/badge_warning.png new file mode 100644 index 0000000000000000000000000000000000000000..e9335d65efceee77ec7b5572b0a88a97a0b13067 GIT binary patch literal 1668 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�K literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/groups.png b/src/assets/images/navigator/groups.png new file mode 100644 index 0000000000000000000000000000000000000000..922056b5c2d3998caad5bcccb57013276cf6b97b GIT binary patch literal 461 zcmeAS@N?(olHy`uVBq!ia0vp^{2(?5GmvyVaHJSWF%}28J29*~C-V}>VM%xNb!1@J z*w6hZkrl|72=EDU1=51OZ{G@5{M*d1;M)KHGfx6BP%zHtKnjrJC<*cl{tp2R_nP-c z0>wBBJR*UH^Bo3Z#^d=bKnf*FTq8IesvJDxT7zBWH6cA^&vw+33 zfNT&*0Ai4S7>#Br127yWursg#RTvl<8!#?_m{9puHn3K z+Eh6y|3BNY&&{nWf;Y^z_vb5r)iql?XGN#DcChEpQ*YDNjkhyRkd2W8MFE4StDnm{ Hr-UW|Ppfd> literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/icon-room-promote.png b/src/assets/images/navigator/icon-room-promote.png new file mode 100644 index 0000000000000000000000000000000000000000..ee231ed51bbaff6733214fd698d3e1c170a73a41 GIT binary patch literal 674 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFY^S7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfQS24TkI`72U@f)XXJ5hcO-X(i=}MX3w{iJ5sNdVa1U3Z{C7 zdKS`|uT}unY)g&sO!M^AV&DLBSQ(@kSs56CEH5CIhO$Af(O_f-i!%Y)hKx)M0zf(n zh%?(+z~WgzHV7mDF-Sj*MzfTGnSo&fI|B<)g@KW=0pkLQsURJ!3m_&<0kT1W31|)z zSY?o<1&{^RWoTdkl3m@PC>%V`LIY@9pr?ytNQC>_=?8NU8}PV#<_DxqdvoR9F2^an zMcL|FTq)BQDQ{?RVbbjqiak1s>kFfNfd@<7!c=!QrKWhbfVmTX&eL8M(CL0GY4Q>+ ziA+JSLe5o(`kXXf9YbHJ9Gy|L>Ox@G$x|zx(ly;qo>e%y=E;N(hYi0B7@jn{iZolu zxt2^2mbTSpbM!egM@(r}rHj2YgP~z@R0qfV`|C3$=grsK?t1Hnxx>z5T{Sgv$wgbf z1y8$_D&rBs@L)=q`hzF?rtJMGew!=k;k?$ue(w1*Sm)n3ba{J0=hV8w?E8oI9?RlC zajotY6T|Z@7L3m`|7*u49pCO8)%N;yxJ{w=eTGJ@=vTW-%=%xXb4_am(klP} literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/info_bg.png b/src/assets/images/navigator/info_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..60dc3aa06369c43bc46047cfb1901e7dd4f76bd0 GIT binary patch literal 1718 zcmcIlTZr6L7|w!S>|)hEXhjf1x=SrKlapj-GD&wV+nLoFX}0U^K&KA{PtG|r6L*r6 zO|m<)3&P@qrBYcc`k+?dq#$@H6kmk)Wf32|!Xg&BeG+`Bw1{{C75~i5BJDCS22OHL z&Ue25`!D%(W`6FOJrj>k2!gPuTq;)J`5=tF_m0DT>!U-P@Yo%cPDX<8@KiF!%9Y7m zg0Slww_1y9_A!faFNG<0SgPX%fEI-5Bb@+~MHY(=Yq)-1y7}o9NpxvmI+3xF9TZs8 zEvg1&^uLbkuwIYdKNW7SrW)g#9&7Kzv zJY=GgQe}b=5)CuupnA?>CORZ)NK+M6Q`C&Csg{YGY-M0p-ywr^2 zz*3ZUyPaz5DIPWy)ig~7X^N)FfRLkAKgJ!|kM<83iYy|b8^kX6#e@+%d^yfbpta|O z7Yu3rs2?Z@nbN_5qNY&d)BtGPJ3~Ehh>qgf6|mCRdq?7^x*9O0!XmyL5;nWS{CNMs z7$w8A!E)H@dP<4HTFe7j1j5vzsi4VY9yR%0{1|Qzy$P&B$Z*WVD(9_1s^$kQqNb-b z@d+Cf*H3s4CYt(3SP{o8FG1ck8Kq@SucC~FbW2yCM4E+A4{Ad#P#ojYz?{1E)ls0f zZI%5f#y(-?VqOAKDc7Y|My;D!WFXnVW=>Amvqa8eCk^^kN;Sj8PBzok1?P*LEGGd> z&i6`}asnK~7+IzP)k%>f8_dvUGnb~aV>nqklhceeHkm_^I$%24W zb3LhNUs<~IceEsnK>*&6Mx24BF{QaYIW9o}_ z>F8UpOg!@W*6}}YJp0_i-4{#0o)#w6ci%X6er9`nSLL^F z?XP!E-}-)oY`p*7Lz7=0nEv4Lu@_EVn7#bl+xJXfSg38d*AIMr@qBsj_SCth^v%z% sZjRSl=Q7vMjICc^`sIPmpI<(Ve>(B|w_B%PO#Uq8*}39}N1uP`AGV__bpQYW literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/info_pointer.png b/src/assets/images/navigator/info_pointer.png new file mode 100644 index 0000000000000000000000000000000000000000..42fa3d44f644daf7e61d896ec68e07987d698d9f GIT binary patch literal 2835 zcmc&$O^g&p6docP*}wr4IgqHGUPw@=>F(;EneH%a1|E=(S?%8MHSKK%#U$#UOCz4WW$8P!EG&!^D zeyKE44d&+4`NnG=6`e9+qRGp>P6X&uY5&1qMCfUrDox%B!kYT)SKp{gz-sDoyP-Ft zalQ~toQ?VH*{L~t_B3^wdhmd$Jnf>($i0T@Rk+u%;?~NP4=aj!B2g zd}CS}7co~VWdl)N*OiJ}Zt8xu$z6S)f^}?Y2G$H4VZ+0=XV^;qQNdr#d~c?HB=-hy zHFY6PBTv)1-EO&SmPOps4A*rv9cvgPK%itfOi2%g$>_jBohLL7qBIbpA}x}pSWIgw zaLqQ+i3+n}k_!crX+08YMp>7e>I0(%k&0wNtN|~kA6vv|VLV!l+kM82YP`)mJWLb7 z8ifN8x6vrzt8(dd3UrblUV?4pf~<;8=9VL_&G1Al#*`mk0*|AE-6ZJ@U(s^|%K=_2 zP6L09wQ}Y$0eCN111e_#UqAlWvV8Fvv z6-GT@#2o_B2|A?3wJ2=$Su`5nWSFERq~F>Hct%VZToE2dMmR7J*HngL2gR#;6n z^1*qTckRV|(6XTTbhHQ+pN~7|89|wrS_%(?O57=j#FJ zlVnr|EBZ4hhG(j>JFJK{5MT^KlRy?GdG|R)TKQ8uIIz0jzH4FMf#h>s6C+|Mk}b4DZ)u;{g*&_LP~`*7x6U2i`;=`7GxwX8DUn%kiwT;lOiJR%YkI3szS)Kotf9R=#P zU&-21$OflQl(4;w_LNj8E1hFaAB@MrkZ8jm3irx?chw=ls=kyFa;m`6=h4Pp7~9 z;H52h?pfC#-27!G+;RQE(coHR-@Yy9w_m<-|CLP<>3%nI<-HdkZJ#>z{1j2IZ9aA2 zH}#7PZ{PcUmOe1UU=i-DEjf-rCnNl Wk>36=xpGooTPF`s)o&d-_3mF@%adjR literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/inline_view.png b/src/assets/images/navigator/inline_view.png new file mode 100644 index 0000000000000000000000000000000000000000..acaf8eb98214295018ea51ae7f51e8760fb3997a GIT binary patch literal 1824 zcmcIlO^f727>+C~x;m$YMLdP9t1D}DDwVIUV%uT1dvqGjILtQK9t0~@mF_T|R7^6{ zJ#$(X_AuZ@#Iu4YLAFvY+<>r0yDty2De)%59zyJDQhH>@lbTF)k(FaWEti`2{ zRcn?NfHsV~o3nz8167-`8l`#1di?XRmYK?q^^qUpsOYM3x^q&h`zN~tadIH2v^L){ z@6H$yWUA)oESuyNn{})?FN1b7Y+L3WQXh1zt;V1^jP}f~E|nR!93(KtW=LBxPTH}e z_-&J5;@A$c9Ul>g5uZ7}xqK|JSIUI#_ugGvgI>oP*LA^cdpe!Arfy4@Bio^r+L+jc zAV8q%IIsB(<<-pUM&L!A+u+^u$>leoLT{+ z=wfJ=t;$Awh;}uhg9f=hzbw7oF@SfT@pC)itWQPh26s^XIUFnvQZTWp=_%FN$FGM`A&dh zr|lr@wQQcroQwnh-?OO%)W zEcq2bx>$TD+(cq3kP`?Gd9fR#SV6ccp*->12@XZDa{hl3zYMJy^L(V>0JW{>cjt1I z{NG%hySJV%7w5ZUH|OS}&g_4e;C$}P84Jmr55d9RG}m_qbk?_y${|uE+$q4OEnzr>@J{q^<!C%_qnq~d0-|(!pGn+|E-PW3ibkH-OdMW_vfWt+ecm+*h7C3F`W?;$l>~a%ml7z#uX=%yCFpdlmW#%~#dFFc#4N@d_ z2}Q0i9Fz|H46y+ZDE9{g*SD9t;Cw9ExhkMKzZzXG8F2J93b`QIb~6tNlDf!sT#j~A zDv*zf$0^3Un7M1V59=J>9y)uPRjJ~@h%?6{1A-A9U>{Vm3{m0Q$nzcF@d8S5;21h* zVJ7Q>!ldK{od~NcCx-s7ngcnLbs{;`+)?uk?2yG#-azv%yEo*4c-XCGxxO5`kTNyx zk-=1(1vk3e`7^4wna&mMd@6wSn&z)yjm+9Pt;PNXD!svYS-<~a>J!ML3I5?eJ_9hFP-ljzf)HRs!Put%-1OvY_jQ2;c ze9iuT@?`JDJHN&!TR(m|{-bH)z7qCg22T8Sx|~5VQqXvUv~;LcpE*Cx=YF>@Fep z1zKhmKzLM{1Hm$Vf`lI1HD(F~POV literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/plus.png b/src/assets/images/navigator/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b1c7e2b7441f6d706825dd9096c366b61d799e GIT binary patch literal 1623 zcmcIky^q{P6nBIYAW9HOD2h^94oG~BJu_>s*H+BwI3Kx{;^TB#DMuO@&&=*xxAqv@ zce{5b9TEi;bVx{aX{eDXkoXHI5TfCuqXbbPilFdjKTL#@(>7SLJ!Ajg@BQA#ybtzv zZ(hE5?V@2Am-{>MKD?jS_fr?(`OC)_p4FRj=k3}suDq!4bN&4nem9Ks-)4h@<{-Hh za+TXms8rgsyacpi+}N0vj33IzOywvmBI~!WzP8LvMAjWI!AZF#$Jx$lC2ybZ4*2OI z4}`Vxx_M(30zobtX3p|SQHQh0n)8OR)}I~AoI{$!$lBBn&4Xmm+)|Y^eVZVTF*g0c zPVump$^gG+QcQ_Ms6#wNNl3krc;@o5z+WYX;ePz)(i`j|Yuq$t=s45q)Sh;1RgD}H z1c8I8Ln#6Ts!xiB%}`NaUoph8=2ccUnJP@p$WnFGL>6dW1d*35ZBZ{11u1i8taONt zbx#7{9M%82Wm^j+sZ8T-LRY}&UYM|6)RjR!ei%B~+HD5{?&kD`^vevXTkuhsz zWI^31!W~5EfViOtbtA805@Ni7CeRClHEcbw64~%%9cYq-{i1GI!DT;=ED&X9nFxI< z2=BNI`GMynm-M85-**9G>m;zv4x+J4cxLUG_|a})~ZdoC3|@}v;R z^<9D>79VxE?BQP85$P~kvAtDgaC+F}VOFh50An}kQtV*+m)aS`vK~Bo}kR< z+=D}93LXwsl{4s+EN3I>l*Pz0|5dZ4CaOx5fSh-9KLa~Nv6IzMyp!b#xh>yYt|pl| zZ@Z9j-R+UZb(tmjS$xhOk-hbFF6^gc38ZHfe*vr2u$i(-ZjK<+YqXd1IQca@K3n{H zrT<^WFH_6MtQbicppK=(arB^B6vp{)&F23e8cHiTOgg=6hb(N`F)a(Au3!lN{p8gT zVSuB4yg9h~3BUi~!NwDpzWee1Grzj`YWmF&w=TuT=g*VxfBNgs&pxtVxqI`SpU)YG XUp_bR|M>Kt9{v68-S~?)-hKZc6&3k6 literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/profile.png b/src/assets/images/navigator/profile.png new file mode 100644 index 0000000000000000000000000000000000000000..83932da5277bc1d4c1489da01b917f8a00b1ca10 GIT binary patch literal 474 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 literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/room_thumbnail.png b/src/assets/images/navigator/room_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..b25d01db7fc6c17928794cea2543a1b0340b2137 GIT binary patch literal 2092 zcmcIlYitx%7+qS6(8wdEv}kG^hVt(0JoY`Zw7YEEUBj-WtUMK&ow>U^?C#8TX3B1B zNTpavc?hkbSP>|#5KPcQi_vJ6S4mV78YQFwQY=-gf>r@V*xO73NfPM1ARyGMD-@_y6l zGP-#GQgx|!0nLgrJ;R9spijgkh}P-m79=EwjQ|x1fDkX((Sc7sM-iU0qf1O)!Yj=O zVcuOOgGE(7KU)=HZ5&!K51E^wAwdkN43daN1%*!7(Lr7s&b8M#iVQ;3h#hrk29c%S zQe?g;1H_^yF_s_*#A4G2h@dq9Y(zdn5fq7&6i%8jiliwMO`4G8g+hBW7o^Kv#Yt;$ zWkDOy2E1`37@PcRZr>Iu!MVW8LhaA+)+LMv*~8fYb{_rAo6zfuBt8BoLu znFU2_fS^ttHpa23vr>f|9rToAaS#PD2veXiGG$5%i>j!E#RvG2+D>^R(eq`%sG{r_ z#prOVN{3kx%Alu^nO=tF1&w!>rYU&@xEK}KQJ6OhBa9ej@Dpa5Bxs{~4nffbF$DF( zTHqL!85vmQ_+aHopk6QS5fqgXSm1HlQ7B5!^BfHTXJsu$GiDCj7|a+TSS(kQZb@U1|wxPnn~yz6~t^oqZPARNgKvk$RKMb zf*?qlhS{EzdHD4((R*1nRUBkA1%SzD4sck&Vz5G0B#BvVK@(aZuhV0M_H?P3tRVKfXMW8%6i}J`|+0hKE zRl6N!wKM|`O}^&u6TOk?9HNbf0VI7$@`qrG7*yko3>+aC=@FC{{vi1Zv*zLAN0$2k zReUnEY?u*305(t@)!f0gMl+Nc{J%LH+`FeK4Z~rl)268=3m2(24FpIn!zO(BgI2Fj zmzL&nIsD;uzg)5lCkrQT=sot`ig&*qGiLSd(?|CE4>@vAWc17LojY=7<);OPwzf#m z=+3k)Prp@Y-Xr9egg(CX2i0+WVDA;5a^;=pbNA(re@U3U@|(j-mAm7NFVNWB)jIBW zeKEEE=J?tJwf@{0qid&c^*=V_(c0;?Ew7!bbM_UNovL>=l~1Yb3-9le>rYG1?(ZtA z?=H`8>I&3%OZm-R&ibR}tDQ};DbxDSZ*kt7Qqv=7^(l0n?fA;t;<(049osxBeJJa)2$&JIux3+9(zT-Jn+fuXW>NA^^JMMt>uN$G> z*NI7_8~y#Pr^vC{a4@6gg)HNX8yDnU zjJU2k9h*0|!?D=WGp9RJ*x+=${pRfzu7+aAiM9#Z+uKV^Z>@5V&5LF2m}_cY=g-Ul z+p;{!&SFRO=HguU;?jwCiRGCMrmQ^kp`03SQbyf2^6%{zK3wK-tTyb4NS%G~10G|+7ApQUU|1)RK91!;dvYpQVUkapHOM?7@f#UxeesALg8N^xO5n0T@ zz;_sg8IR|$NC65uc)B=-XarB~cjRJF;9$A>?SFn=qUjeQQ}LOKM|d`{@jl~yRlvXR zQG{n}$S1)qAH}+!7j*G)=Zf%b$Ow4;y^;5!E90i`S2!L6&0_F$^>bP0l+XkKogPHc literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/saves-search/delete_search_click.png b/src/assets/images/navigator/saves-search/delete_search_click.png new file mode 100644 index 0000000000000000000000000000000000000000..657ceb33321374416e87e58b21d402773d7e8542 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G|^0G|+7ARQbWJY&X;1LA)F|NjSa6SoBl0x7nVAirRs2vBVE{w4c?BAf*t zk;M!Qe1}1p@p%4<6riA;r;B5VM)1^rPd)|(jw23p|JUDa+wtINMt-h^fm$Tfz^-|G1`Yk?*)c)I$ztaD0e0st!S BMt=YR literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/saves-search/delete_search_hover.png b/src/assets/images/navigator/saves-search/delete_search_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..ad6ccf93165523851e0786972eddb8a2e824801c GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G~10G|+7ApQUU{{!NFXU?1fvh_CQE(TJpB|(0{K=JH# wNVtJ<5$gk`fObQJ!;Bs64=qF9Z{EPbFkghxGUaA>56~zEPgg&ebxsLQ08f8A8vpQaFeV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G}c0G|-o?<*Mo|Njr<{rGuN8b~ph1o;I61+Jgs1*&D>EbxddW?nAmtPE0&tPG4mmKP99L)jqLXfQH^#hHL?Lq;YB0U#X( z#F_0ZVDT&<8w3)77^ELYqgl$p%)l^#oq+|Y!obMbfN=rDRFDqV1rU>_0NEhG1T=>U ztTM>b0?2~uGBhv%$*Q`~>s@TJ^(c_3BPq-3*;Aw#1i@26gM4iAtSp00i_>zopr0N;y77XSbN literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/similar_results_active.png b/src/assets/images/navigator/similar_results_active.png new file mode 100644 index 0000000000000000000000000000000000000000..017a56cbb74db21438b07023858418d997679607 GIT binary patch literal 384 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucL5ULAh?3y^w370~qEv=}#LT=BJwMkF1yemk zJ@f8s-z|Y^wxvdRrg?g5F>nAmtPE0&tPG4mmKP99L)jqLXfQH^#hHL?Lq;YB0U#X( z#F_0ZVDT&<8w3)77^ELYqgl$p%)l^#oq+|Y!obMbfN=rDRFDqV1rU>_0NEhG1T=>U ztTM>b0?2~uGBhv%$*Q`~>s@TJ^(c_3@9E+gq7j^&v-9Iyd*+P|5l3~+_x(+G51}JS$92K=^r4f<>~6SCCMLPt zy#qyy6|`DJ;qc)eM0}}8J$-G_iccbng7_ppDLzy@p@@eFb!Imki?qkS7?@-x^PAuQ z_y5R0uguRqzJKq*y&T8wug|&*^mz*#6T9jC-pf}Yee6kQpUODyjtAJ-RbROOcaFR9 zo3PQ$o8A*PB9R0G;$x{FB^1qZhYt4>h?a29`?wv(j<9w9Q-KcyM|e{AfS1&8C!AeN z@$t2}23lJ}Rv;XHgg?}`DM5sD$oHdeoY{Rx81UM3&R)v`KY-**jxfy(@=b4^uaOk< zhNOrH0KglT-D6bCXuu)E0$%+K$TThqzExvjdR!+<7{%o;NlFWVUmX=<{2aO$#U)pRO`@*C@E>< ztOyhhncRnotVn=4H3E9x&d?|-(OEvTLah{fuSuLWRue2Q;EXJ%2+yqGIG-FD3s8AB zSx&nHPXi?5E{-THqr#NZRMH_i$vWgZew5p#H;G+KG0aKYAf!7=)%=KsS2aoHAM_v! zW5zqhG!;j%3v=uUG;gX1DxzXF6vI}4ts4&k)dpY)^=K^wFo)xTNf5SH$ANmDU5~RI z#t7G4N1&plFbr%6EfwiN7olZpV#T-EtO`WKPyvF5ZX#bB=%VLcf|gkT+4*7V1_V)# zGDfxzL91e@npmy+P*j78AzF%$M4+iALW)tXVt>STEDh<`1G`tVGF1U(1QvopRnbI6 zYXPyM85I%gW<{)GCGZW?Y$+8i@By-0B#j`glrVyAEGKbW;Qv)qBVCeuB%m?ZSUppA z>cwoB(d4ZbU&wL%Y%%MG{GjY?h*-5d0%B>#fl#~-uMxfRbPj2I9ZX4gNd6F(kyhS= zDV}cANRLro^7Z6raAjxlrFbzt1aw1LV3b4O;4W|$cHzd0N1UDcFEaN6nE zv~03;QMPFuQ|gp9;nR0sU=9CN-JNcjuYdK{(VJi3_qk7>JMr3G=)&heT$$LIt~Iy! zv@hPi`{+yWNE<)DxApA|+;YD7_0q<=``yaqmuJvtcfWn>*~RA9?=LowG{Vi>eq2BM z*B|DG#`cRBn|~gte*fI&F7Ek{PknTT+kEx(LG!1-Z(6)``JR_QnL5dB3vayn%nch8 eN8UZK&*J9R>%V>Q%hY}Bmr|dZbI(0`;^aTRuO*iN literal 0 HcmV?d00001 diff --git a/src/assets/images/navigator/thumbnail_view.png b/src/assets/images/navigator/thumbnail_view.png new file mode 100644 index 0000000000000000000000000000000000000000..b4a6116c1dfca88b5ad668428f8298da2eec1613 GIT binary patch literal 1831 zcmcIlO^f727>)?5yRw28Sr?WK*;QQDN-9wK~KUZrU-zTDhT4R)?S(6n=(r^DTPH@Y2i zm6=SaSemn}0JNsvTALM&@5x$^OMYW8DwT z7$==r2KY6dU}9S~v1}I+J0xysyZZ7nz+WknaHs#~(i?nrjd5KUp=C{{Q*%m9RgNq> z2m%Weix30|R2}9uo1whAwqoeZikE3orz+PQMi#66x@&;eMG#rh(&p7NQIIlg#tO?e zaTC-E7)7T;v#do|^~M2sS?WEJxEdZ7(%O-g+Aq1>IFNaLZRJex*0$I$C-X=Jx8y`- z0INWl-MT8qs#eulJtmI!cN>5xvf-psd3~eVQG$fsy*bXsXKqKe{!D@CK>>xsi*bi+IQvW6CX`Q4|jH zs%ANtgMQZlQD&NokWuP*z851m;VyDK7zGIy$mzs^ND}Nhc0AVw_I<_on*=uYi`Es2 z14o;qkoj)M^AisV;kziYofvgQK#@x+39#cifn%@O-Y!#EJ#2EEwNWL2(e@bjB^ZJ< zi5$lV9wIrSZpTh2jcMReLl-yMksfj8hB_QXt>1SYvC^pjy zig&nNA$R15%h4p&=WQ1<-gJA{;7ysOFqYHl3E4ZF&IRpsEP?ct;xAy8O6n;q<@yLR zeTMe39w)zI2d9gdvFo`kz=%@^3eIeam>qbC5yr7YJdeiyD)|3N{4%wC%<_?h4b(E8 zJe;$2@_%b_;@Wz$TAUn?)$E&#KC}Mag7bgJ_E;$9dH5bnJ$(4@Fypq*6&b4>qo|-J{)LW`~&;M^Ug+Rp}1XNyQ}n zm_a=3K@@y}R|S@gRB<7I8&S9$YK^h>PqFeKF)wNxuL4zyFax zuWoNWzPhruB1zI}r`_5S*So9Z`0e6+{~y<}xZIJopDrZnp3~KFTW9CgHAy;lCGPH( zd%+VP;fabV51BeiGJ%$)GiN6mCi|=eA?wAdFaP%0B^kujm!Gl%7-UV>kK2bi+dbUs zlEZ!CQu*v7;LOAm1PLoKm?VR=@Fu=I<@Ln6daTJ{3Muz}d82X&_JVED5K;s}DGt&SPn5JcxnO9qg5+^l#ykZTBM$kn^kt!S7DSTOv}rLYeU z8XK@gzzQKuuU%z*Uh<;PZxKiRx{fCEnmNNI&%2xt7Ny!=umEJJ2s{vA5~mgKgO#Sa z32Wh!`Ld`RQlP0IqYEuh?7I69L>`1QXdrrl;u0?h<}{8DmVpL=*GY>Kr-XG{zAQwk zaZEj%L1vSNsYHzsDkd_C5+d7D4AY>tf!)w??5Qqc-{NFgC9txewJzmEaMU^SD6<+A z8=+zwL{}nYLdCU1syNVbq17-$JF3~v=dt*D@Zd()N);81hJhRu*@j}du22J9uospldEvFYS7FIqy4vL(|0!vr3G9}x@uVuS#eX3X_gA= zTnu6L?mPjh3RwHt zx$o+o-+cw2yZG%d=gh0$TzvBf|I7A;yTFg1uD|C0boyBO=)FIG{`=bb_g}nx;+dCL V*N5L<8&yAu&gNF@lXK5s_y_Qv4Lbk; literal 0 HcmV?d00001 diff --git a/src/assets/webfonts/Volter-b.ttf b/src/assets/webfonts/Volter-b.ttf new file mode 100644 index 0000000000000000000000000000000000000000..219584baa87c30bb397ae96408bba95fa53334d6 GIT binary patch literal 19748 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

pdt&`l!Psp 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 literal 0 HcmV?d00001 diff --git a/src/common/Text.tsx b/src/common/Text.tsx index da5235d..ffc0a85 100644 --- a/src/common/Text.tsx +++ b/src/common/Text.tsx @@ -41,7 +41,7 @@ export const Text: FC = props => { } = props; const getClassNames = useMemo(() => { - const newClassNames: string[] = ['inline']; + const newClassNames: string[] = [truncate ? 'block' : 'inline']; if (variant) { if (variant === 'primary') newClassNames.push('text-[#1e7295]'); @@ -54,7 +54,7 @@ export const Text: FC = props => { if (variant == 'danger') newClassNames.push('text-[#a81a12]'); if (variant == 'warning') newClassNames.push('text-[#ffc107]'); } - + if (bold) newClassNames.push('font-bold'); if (fontWeight) newClassNames.push('font-' + fontWeight); if (fontSize) newClassNames.push('fs-' + fontSize); @@ -62,7 +62,7 @@ export const Text: FC = props => { if (align) newClassNames.push('text-' + align); if (underline) newClassNames.push('underline'); if (italics) newClassNames.push('italic'); - if (truncate) newClassNames.push('text-truncate'); + if (truncate) newClassNames.push('truncate', 'min-w-0'); if (center) newClassNames.push('text-center'); if (textEnd) newClassNames.push('text-end'); if (small) newClassNames.push('text-sm'); @@ -71,7 +71,7 @@ export const Text: FC = props => { if (textBreak) newClassNames.push('text-break'); return newClassNames; - }, [variant, fontWeight, fontSize, fontSizeCustom, align, bold, underline, italics, truncate, center, textEnd, small, wrap, noWrap, textBreak]); + }, [ variant, fontWeight, fontSize, fontSizeCustom, align, bold, underline, italics, truncate, center, textEnd, small, wrap, noWrap, textBreak ]); const style = fontSizeCustom ? { '--font-size': `${fontSizeCustom}px` } as React.CSSProperties : undefined; diff --git a/src/common/layout/LayoutSearchSavesView.tsx b/src/common/layout/LayoutSearchSavesView.tsx new file mode 100644 index 0000000..3c6ca88 --- /dev/null +++ b/src/common/layout/LayoutSearchSavesView.tsx @@ -0,0 +1,23 @@ +import { FC } from 'react'; +import { FaBolt } from 'react-icons/fa'; + +export interface LayoutSearchSavesViewProps +{ + title: string; + onClick?: () => void; +} + +export const LayoutSearchSavesView: FC = props => +{ + const { title = null, onClick = null } = props; + + return ( +

+ +
+ ); +}; diff --git a/src/common/layout/index.ts b/src/common/layout/index.ts index cbb0568..9723cc0 100644 --- a/src/common/layout/index.ts +++ b/src/common/layout/index.ts @@ -19,6 +19,7 @@ export * from './LayoutRarityLevelView'; export * from './LayoutRoomObjectImageView'; export * from './LayoutRoomPreviewerView'; export * from './LayoutRoomThumbnailView'; +export * from './LayoutSearchSavesView'; export * from './LayoutTrophyView'; export * from './UserProfileIconView'; export * from './limited-edition'; diff --git a/src/components/navigator/NavigatorView.scss b/src/components/navigator/NavigatorView.scss new file mode 100644 index 0000000..bdb279e --- /dev/null +++ b/src/components/navigator/NavigatorView.scss @@ -0,0 +1,8 @@ +.button-search-saves { + padding: 4px; + height: 17px; + margin-top: -1px; + font-size: 10px; + border-radius: 4px; + background-color: #FAA700; +} diff --git a/src/components/navigator/NavigatorView.tsx b/src/components/navigator/NavigatorView.tsx index 813c390..820220a 100644 --- a/src/components/navigator/NavigatorView.tsx +++ b/src/components/navigator/NavigatorView.tsx @@ -1,8 +1,13 @@ import { NitroCard } from '@layout/NitroCard'; -import { AddLinkEventTracker, ConvertGlobalRoomIdMessageComposer, HabboWebTools, ILinkEventTracker, LegacyExternalInterface, NavigatorInitComposer, NavigatorSearchComposer, RemoveLinkEventTracker, RoomSessionEvent } from '@nitrots/nitro-renderer'; +import { AddLinkEventTracker, ConvertGlobalRoomIdMessageComposer, FindNewFriendsMessageComposer, HabboWebTools, ILinkEventTracker, LegacyExternalInterface, NavigatorInitComposer, NavigatorSearchComposer, RemoveLinkEventTracker, RoomSessionEvent } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { FaPlus } from 'react-icons/fa'; -import { LocalizeText, SendMessageComposer, TryVisitRoom } from '../../api'; +import savesSearchIcon from '../../assets/images/navigator/saves-search/search_save.png'; +import createRoomImg from '../../assets/images/navigator/create_room.png'; +import randomRoomImg from '../../assets/images/navigator/random_room.png'; +import promoteRoomImg from '../../assets/images/navigator/promote_room.png'; +import { CreateLinkEvent, LocalizeText, SendMessageComposer, TryVisitRoom } from '../../api'; +import { Flex, Text } from '../../common'; import { useNavigator, useNitroEvent } from '../../hooks'; import { NavigatorDoorStateView } from './views/NavigatorDoorStateView'; import { NavigatorRoomCreatorView } from './views/NavigatorRoomCreatorView'; @@ -10,6 +15,7 @@ import { NavigatorRoomInfoView } from './views/NavigatorRoomInfoView'; import { NavigatorRoomLinkView } from './views/NavigatorRoomLinkView'; import { NavigatorRoomSettingsView } from './views/room-settings/NavigatorRoomSettingsView'; import { NavigatorSearchResultView } from './views/search/NavigatorSearchResultView'; +import { NavigatorSearchSavesResultView } from './views/search/NavigatorSearchSavesResultView'; import { NavigatorSearchView } from './views/search/NavigatorSearchView'; export const NavigatorView: FC<{}> = props => @@ -19,10 +25,11 @@ export const NavigatorView: FC<{}> = props => const [ isCreatorOpen, setCreatorOpen ] = useState(false); const [ isRoomInfoOpen, setRoomInfoOpen ] = useState(false); const [ isRoomLinkOpen, setRoomLinkOpen ] = useState(false); + const [ isOpenSavesSearches, setIsOpenSavesSearches ] = useState(false); const [ isLoading, setIsLoading ] = useState(false); const [ needsInit, setNeedsInit ] = useState(true); const [ needsSearch, setNeedsSearch ] = useState(false); - const { searchResult = null, topLevelContext = null, topLevelContexts = null, navigatorData = null } = useNavigator(); + const { searchResult = null, topLevelContext = null, topLevelContexts = null, navigatorData = null, navigatorSearches = null } = useNavigator(); const pendingSearch = useRef<{ value: string, code: string }>(null); const elementRef = useRef(); @@ -197,12 +204,18 @@ export const NavigatorView: FC<{}> = props => <> { isVisible && setIsVisible(false) } /> + setIsOpenSavesSearches(prev => !prev) }> + + { topLevelContexts && (topLevelContexts.length > 0) && topLevelContexts.map((context, index) => { return ( @@ -222,12 +235,58 @@ export const NavigatorView: FC<{}> = props => { !isCreatorOpen && - <> - -
- { (searchResult && searchResult.results.map((result, index) => )) } +
+ { isOpenSavesSearches && +
+ +
} +
+ +
+ { (searchResult && searchResult.results.map((result, index) => )) } +
+ + setCreatorOpen(true) } + > + + { LocalizeText('navigator.createroom.create') } + + + { (searchResult?.code !== 'myworld_view' && searchResult?.code !== 'roomads_view') && + SendMessageComposer(new FindNewFriendsMessageComposer()) } + > + + { LocalizeText('navigator.random.room') } + + } + { (searchResult?.code === 'myworld_view' || searchResult?.code === 'roomads_view') && + CreateLinkEvent('catalog/open/room_event') } + > + + { LocalizeText('navigator.promote.room') } + + } +
- } +
} { isCreatorOpen && } } diff --git a/src/components/navigator/views/NavigatorRoomInfoView.tsx b/src/components/navigator/views/NavigatorRoomInfoView.tsx index 35101a9..8da9d6c 100644 --- a/src/components/navigator/views/NavigatorRoomInfoView.tsx +++ b/src/components/navigator/views/NavigatorRoomInfoView.tsx @@ -1,7 +1,7 @@ -import { CreateLinkEvent, GetCustomRoomFilterMessageComposer, GetSessionDataManager, NavigatorSearchComposer, RemoveOwnRoomRightsRoomMessageComposer, RoomControllerLevel, RoomMuteComposer, RoomSettingsComposer, SecurityLevel, ToggleStaffPickMessageComposer, UpdateHomeRoomMessageComposer } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useState } from 'react'; +import { CreateLinkEvent, GetCustomRoomFilterMessageComposer, GetGuestRoomMessageComposer, GetSessionDataManager, NavigatorSearchComposer, RemoveOwnRoomRightsRoomMessageComposer, RoomControllerLevel, RoomMuteComposer, RoomSettingsComposer, SecurityLevel, ToggleStaffPickMessageComposer, UpdateHomeRoomMessageComposer } from '@nitrots/nitro-renderer'; +import { FC, useEffect, useMemo, useState } from 'react'; import { FaLink, FaSignOutAlt } from 'react-icons/fa'; -import { DispatchUiEvent, GetGroupInformation, LocalizeText, ReportType, SendMessageComposer } from '../../../api'; +import { DispatchUiEvent, GetGroupInformation, LocalizeText, ReportType, SendMessageComposer, ToggleFavoriteRoom } from '../../../api'; import { Button, Column, Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text, UserProfileIconView } from '../../../common'; import { RoomWidgetThumbnailEvent } from '../../../events'; import { useHelp, useNavigator, useRoom } from '../../../hooks'; @@ -11,16 +11,45 @@ export interface NavigatorRoomInfoViewProps { onCloseClick: () => void; } -export const NavigatorRoomInfoView: FC = props => { +export const NavigatorRoomInfoView: FC = props => +{ const { onCloseClick = null } = props; const [ isRoomPicked, setIsRoomPicked ] = useState(false); const [ isRoomMuted, setIsRoomMuted ] = useState(false); const { report = null } = useHelp(); - const { navigatorData = null } = useNavigator(); + const { navigatorData = null, favouriteRoomIds = [] } = useNavigator(); const { roomSession = null } = useRoom(); - const hasPermission = (permission: string) => { - switch(permission) { + const enteredRoomId = navigatorData?.enteredGuestRoom?.roomId ?? 0; + + useEffect(() => + { + if(!enteredRoomId) return; + SendMessageComposer(new GetGuestRoomMessageComposer(enteredRoomId, false, false)); + }, [ enteredRoomId ]); + + const isRoomInFavouritesList = useMemo(() => + { + if(!enteredRoomId) return false; + + return favouriteRoomIds.some((id: any) => + { + if(id && typeof id === 'object') + { + if('roomId' in id) return Number(id.roomId) === enteredRoomId; + if('id' in id) return Number(id.id) === enteredRoomId; + } + + return String(id) === String(enteredRoomId); + }); + }, [ favouriteRoomIds, enteredRoomId ]); + + const hasPermission = (permission: string) => + { + if(!navigatorData?.enteredGuestRoom) return false; + + switch(permission) + { case 'settings': return (GetSessionDataManager().userId === navigatorData.enteredGuestRoom.ownerId || GetSessionDataManager().isModerator); case 'staff_pick': @@ -33,17 +62,21 @@ export const NavigatorRoomInfoView: FC = props => { } }; - const processAction = (action: string, value?: string) => { - if(!navigatorData || !navigatorData.enteredGuestRoom) return; + const processAction = (action: string, value?: string) => + { + if(!navigatorData?.enteredGuestRoom) return; - switch(action) { + const roomId = navigatorData.enteredGuestRoom.roomId; + + switch(action) + { case 'set_home_room': + { let newRoomId = -1; - if(navigatorData.homeRoomId !== navigatorData.enteredGuestRoom.roomId) { - newRoomId = navigatorData.enteredGuestRoom.roomId; - } + if(navigatorData.homeRoomId !== roomId) newRoomId = roomId; if(newRoomId > 0) SendMessageComposer(new UpdateHomeRoomMessageComposer(newRoomId)); return; + } case 'navigator_search_tag': CreateLinkEvent(`navigator/search/${ value }`); SendMessageComposer(new NavigatorSearchComposer('hotel_view', `tag:${ value }`)); @@ -58,27 +91,33 @@ export const NavigatorRoomInfoView: FC = props => { CreateLinkEvent('navigator/toggle-room-link'); return; case 'open_room_settings': - SendMessageComposer(new RoomSettingsComposer(navigatorData.enteredGuestRoom.roomId)); + SendMessageComposer(new RoomSettingsComposer(roomId)); return; case 'toggle_pick': - setIsRoomPicked(value => !value); - SendMessageComposer(new ToggleStaffPickMessageComposer(navigatorData.enteredGuestRoom.roomId)); + setIsRoomPicked(prev => !prev); + SendMessageComposer(new ToggleStaffPickMessageComposer(roomId)); + SendMessageComposer(new GetGuestRoomMessageComposer(roomId, false, false)); return; case 'toggle_mute': - setIsRoomMuted(value => !value); + setIsRoomMuted(prev => !prev); SendMessageComposer(new RoomMuteComposer()); + SendMessageComposer(new GetGuestRoomMessageComposer(roomId, false, false)); return; case 'room_filter': - SendMessageComposer(new GetCustomRoomFilterMessageComposer(navigatorData.enteredGuestRoom.roomId)); + SendMessageComposer(new GetCustomRoomFilterMessageComposer(roomId)); return; case 'open_floorplan_editor': CreateLinkEvent('floor-editor/toggle'); return; case 'report_room': - report(ReportType.ROOM, { roomId: navigatorData.enteredGuestRoom.roomId, roomName: navigatorData.enteredGuestRoom.roomName }); + report(ReportType.ROOM, { roomId, roomName: navigatorData.enteredGuestRoom.roomName }); + return; + case 'room_favourite': + ToggleFavoriteRoom(roomId, isRoomInFavouritesList); + SendMessageComposer(new GetGuestRoomMessageComposer(roomId, false, false)); return; case 'remove_rights': - SendMessageComposer(new RemoveOwnRoomRightsRoomMessageComposer(navigatorData.enteredGuestRoom.roomId)); + SendMessageComposer(new RemoveOwnRoomRightsRoomMessageComposer(roomId)); return; case 'close': onCloseClick(); @@ -86,96 +125,105 @@ export const NavigatorRoomInfoView: FC = props => { } }; - useEffect(() => { + useEffect(() => + { if(!navigatorData) return; - setIsRoomPicked(navigatorData.currentRoomIsStaffPick); if(navigatorData.enteredGuestRoom) setIsRoomMuted(navigatorData.enteredGuestRoom.allInRoomMuted); }, [ navigatorData ]); - if(!navigatorData.enteredGuestRoom) return null; + if(!navigatorData?.enteredGuestRoom) return null; return ( processAction('close') } /> - { navigatorData.enteredGuestRoom && - <> - - - { hasPermission('settings') && processAction('open_room_thumbnail_camera') } /> } - - + + + { hasPermission('settings') && processAction('open_room_thumbnail_camera') } /> } + + +
+
- -
- { navigatorData.enteredGuestRoom.roomName } -
- { navigatorData.enteredGuestRoom.showOwner && -
- { LocalizeText('navigator.roomownercaption') } -
- - { navigatorData.enteredGuestRoom.ownerName } -
-
} -
- { LocalizeText('navigator.roomrating') } - { navigatorData.currentRoomRating } -
- { (navigatorData.enteredGuestRoom.tags.length > 0) && -
- { navigatorData.enteredGuestRoom.tags.map(tag => - processAction('navigator_search_tag', tag) }>#{ tag } - ) } -
} -
- - processAction('set_home_room') } /> - { hasPermission('settings') && - processAction('open_room_settings') } /> } - processAction('toggle_room_link') } /> - { hasPermission('guest') && - processAction('remove_rights') } /> } - + { navigatorData.enteredGuestRoom.roomName }
- { navigatorData.enteredGuestRoom.description } - { (navigatorData.enteredGuestRoom.habboGroupId > 0) && - processAction('open_group_info') }> - - - { LocalizeText('navigator.guildbase', [ 'groupName' ], [ navigatorData.enteredGuestRoom.groupName ]) } - - } + { navigatorData.enteredGuestRoom.showOwner && +
+ { LocalizeText('navigator.roomownercaption') } +
+ + { navigatorData.enteredGuestRoom.ownerName } +
+
} +
+ { LocalizeText('navigator.roomrating') } + { navigatorData.currentRoomRating } +
+ { (navigatorData.enteredGuestRoom.tags.length > 0) && +
+ { navigatorData.enteredGuestRoom.tags.map(tag => ( + processAction('navigator_search_tag', tag) }> + #{ tag } + + )) } +
} +
+ + processAction('set_home_room') } + /> + { GetSessionDataManager().userId !== navigatorData.enteredGuestRoom.ownerId && + processAction('room_favourite') } + /> } + { hasPermission('settings') && + processAction('open_room_settings') } /> } + processAction('toggle_room_link') } /> + { hasPermission('guest') && + processAction('remove_rights') } /> } - -
- { hasPermission('staff_pick') && - } - - { hasPermission('settings') && - <> - - - - } - { hasPermission('floor') && !hasPermission('settings') && - }
- } + { navigatorData.enteredGuestRoom.description } + { (navigatorData.enteredGuestRoom.habboGroupId > 0) && + processAction('open_group_info') }> + + + { LocalizeText('navigator.guildbase', [ 'groupName' ], [ navigatorData.enteredGuestRoom.groupName ]) } + + } + + +
+ { hasPermission('staff_pick') && + } + + { hasPermission('settings') && + <> + + + + } + { hasPermission('floor') && !hasPermission('settings') && + } +
); -}; \ No newline at end of file +}; diff --git a/src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx b/src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx index 5c52c8b..dc548ce 100644 --- a/src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx +++ b/src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx @@ -1,106 +1,196 @@ -import { RoomDataParser } from '@nitrots/nitro-renderer'; -import { FC, useRef, useState } from 'react'; +import { RoomDataParser, RoomSettingsComposer, UpdateHomeRoomMessageComposer } from '@nitrots/nitro-renderer'; +import React, { FC, useRef, useState } from 'react'; import { FaUser } from 'react-icons/fa'; - import { ArrowContainer, Popover } from 'react-tiny-popover'; -import { LocalizeText } from '../../../../api'; -import { Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, Text, UserProfileIconView } from '../../../../common'; +import { GetGroupInformation, GetSessionDataManager, GetUserProfile, LocalizeText, ReportType, SendMessageComposer, ToggleFavoriteRoom } from '../../../../api'; +import { Column, Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, Text, UserProfileIconView } from '../../../../common'; +import { useHelp, useNavigator } from '../../../../hooks'; +import { classNames } from '../../../../layout'; -export const NavigatorSearchResultItemInfoView: FC<{ - roomData: RoomDataParser; -}> = props => +interface NavigatorSearchResultItemInfoViewProps { - const { roomData = null } = props; - const [ isVisible, setIsVisible ] = useState(false); - const elementRef = useRef(); + roomData: RoomDataParser; + isVisible?: boolean; + onToggle?: (e: React.MouseEvent) => void; + setIsPopoverActive?: React.Dispatch>; +} + +export const NavigatorSearchResultItemInfoView: FC = props => +{ + const { roomData = null, isVisible = undefined, onToggle, setIsPopoverActive } = props; + const elementRef = useRef(null); + const [ internalVisible, setInternalVisible ] = useState(false); + const { navigatorData = null, favouriteRoomIds = [] } = useNavigator(); + const { report = null } = useHelp(); + + const isControlled = isVisible !== undefined; + const popoverOpen = isControlled ? isVisible : internalVisible; const getUserCounterColor = () => { const num: number = (100 * (roomData.userCount / roomData.maxUserCount)); - let bg = 'bg-primary'; + if(num >= 92) return 'bg-danger'; + if(num >= 50) return 'bg-warning'; + if(num > 0) return 'bg-success'; - if(num >= 92) - { - bg = 'bg-danger'; - } - else if(num >= 50) - { - bg = 'bg-warning'; - } - else if(num > 0) - { - bg = 'bg-success'; - } - - return bg; + return 'bg-primary'; }; - function dispatch(arg0: string): void + const processAction = (action: string) => { - throw new Error('Function not implemented.'); - } + if(!navigatorData || !roomData) return; + switch(action) + { + case 'set_home_room': + { + let newRoomId = -1; + if(navigatorData.homeRoomId !== roomData.roomId) newRoomId = roomData.roomId; + if(newRoomId > 0) SendMessageComposer(new UpdateHomeRoomMessageComposer(newRoomId)); + return; + } + case 'open_room_settings': + SendMessageComposer(new RoomSettingsComposer(roomData.roomId)); + return; + case 'report_room': + report(ReportType.ROOM, { roomId: roomData.roomId, roomName: roomData.roomName }); + return; + case 'room_favourite': + ToggleFavoriteRoom(roomData.roomId, favouriteRoomIds.includes(roomData.roomId)); + return; + } + }; + + const handleOwnerClick = (e: React.MouseEvent) => + { + e.stopPropagation(); + GetUserProfile(roomData.ownerId); + }; + + const handleGroupClick = (e: React.MouseEvent) => + { + e.stopPropagation(); + GetGroupInformation(roomData.habboGroupId); + }; + + const getTradeModeText = (): string => + { + if(roomData.tradeMode === 1) return LocalizeText('trading.mode.free'); + return LocalizeText('trading.mode.not.allowed'); + }; + + const handleIconClick = (e: React.MouseEvent) => + { + e.preventDefault(); + e.stopPropagation(); + if(onToggle) onToggle(e); + }; return ( - <> - - - ( - - - - - { roomData.habboGroupId > 0 && ( - ) } - { roomData.doorMode !== RoomDataParser.OPEN_STATE && ( - ) } - -
- - { roomData.roomName } - -
- - { LocalizeText('navigator.roomownercaption') } - -
- - { roomData.ownerName } -
-
- - { roomData.description } - - - - { roomData.userCount } - -
+ ( + + e.stopPropagation() }> + + + { roomData.habboGroupId > 0 && ( + ) } + { roomData.doorMode !== RoomDataParser.OPEN_STATE && ( + ) } + + + + { roomData.roomName.length > 35 ? roomData.roomName.substring(0, 35) + '…' : roomData.roomName } + + { roomData.description } + + + + + { roomData.ownerName && roomData.ownerName.length > 0 && + + + { roomData.ownerName } + } + { roomData.habboGroupId > 0 && + + + { roomData.groupName } + } - - - ) } - isOpen={ isVisible } - padding={ 10 } - positions={ [ 'right' ] } - > - -
setIsVisible(false) } onMouseOver={ event => setIsVisible(true) } /> - - - - + + + + { LocalizeText('navigator.roompopup.property.trading') } + { getTradeModeText() } + + + { LocalizeText('navigator.roompopup.property.max_users') } + { roomData.maxUserCount } + + + + + { roomData.userCount } + + + + + processAction('room_favourite') }> + + { LocalizeText('navigator.room.popup.room.info.favorite') } + + processAction('set_home_room') }> + + { LocalizeText('navigator.room.popup.room.info.home') } + + { GetSessionDataManager().userId === roomData.ownerId && + processAction('open_room_settings') }> + + { LocalizeText('navigator.room.popup.info.room.settings') } + } + { GetSessionDataManager().userId !== roomData.ownerId && + processAction('report_room') }> + + { LocalizeText('navigator.room.popup.report.room') } + } + + + { roomData.tags && roomData.tags.length > 0 && + + { roomData.tags.map((tag, i) => ( + #{ tag } + )) } + } + + + + ) } + isOpen={ popoverOpen } + onClickOutside={ () => + { + if(!isControlled) setInternalVisible(false); + if(setIsPopoverActive) setIsPopoverActive(false); + } } + padding={ 10 } + positions={ [ 'right', 'left', 'top', 'bottom' ] } + > +
{ if(!isControlled) setInternalVisible(true); } } + onMouseLeave={ () => { if(!isControlled) setInternalVisible(false); } } + /> + ); }; diff --git a/src/components/navigator/views/search/NavigatorSearchResultItemView.tsx b/src/components/navigator/views/search/NavigatorSearchResultItemView.tsx index 2213f45..2f746e9 100644 --- a/src/components/navigator/views/search/NavigatorSearchResultItemView.tsx +++ b/src/components/navigator/views/search/NavigatorSearchResultItemView.tsx @@ -1,5 +1,5 @@ import { GetSessionDataManager, RoomDataParser } from '@nitrots/nitro-renderer'; -import { FC, MouseEvent } from 'react'; +import React, { FC, MouseEvent, useEffect } from 'react'; import { FaUser } from 'react-icons/fa'; import { CreateRoomSession, DoorStateType, TryVisitRoom } from '../../../../api'; import { Column, Flex, LayoutBadgeImageView, LayoutGridItemProps, LayoutRoomThumbnailView, Text } from '../../../../common'; @@ -8,35 +8,84 @@ import { NavigatorSearchResultItemInfoView } from './NavigatorSearchResultItemIn export interface NavigatorSearchResultItemViewProps extends LayoutGridItemProps { - roomData: RoomDataParser - thumbnail?: boolean + roomData: RoomDataParser; + thumbnail?: boolean; + selectedRoomId?: number | null; + setSelectedRoomId?: React.Dispatch>; + isPopoverActive?: boolean; + setIsPopoverActive?: React.Dispatch>; } export const NavigatorSearchResultItemView: FC = props => { - const { roomData = null, children = null, thumbnail = false, ...rest } = props; + const { roomData = null, children = null, thumbnail = false, selectedRoomId, setSelectedRoomId, isPopoverActive, setIsPopoverActive, ...rest } = props; const { setDoorData = null } = useNavigator(); + const handleMouseEnter = () => + { + if(isPopoverActive && setSelectedRoomId) setSelectedRoomId(roomData.roomId); + }; + + const handleMouseLeave = () => + { + if(setSelectedRoomId && setIsPopoverActive) + { + setSelectedRoomId(null); + setIsPopoverActive(false); + } + }; + + const handleInfoClick = (e: React.MouseEvent) => + { + e.preventDefault(); + e.stopPropagation(); + + if(setIsPopoverActive && setSelectedRoomId) + { + if(!isPopoverActive) + { + setSelectedRoomId(roomData.roomId); + setIsPopoverActive(true); + } + else if(selectedRoomId === roomData.roomId) + { + setSelectedRoomId(null); + setIsPopoverActive(false); + } + else + { + setSelectedRoomId(roomData.roomId); + } + } + }; + + useEffect(() => + { + const handleClickOutside = (event: Event) => + { + const target = event.target as HTMLElement; + const navigatorItem = target.closest('.navigator-item'); + + if(!navigatorItem && setIsPopoverActive && setSelectedRoomId) + { + setIsPopoverActive(false); + setSelectedRoomId(null); + } + }; + + document.addEventListener('click', handleClickOutside); + return () => document.removeEventListener('click', handleClickOutside); + }, [ setIsPopoverActive, setSelectedRoomId ]); + const getUserCounterColor = () => { const num: number = (100 * (roomData.userCount / roomData.maxUserCount)); - let bg = 'bg-primary'; + if(num >= 92) return 'bg-danger'; + if(num >= 50) return 'bg-warning'; + if(num > 0) return 'bg-success'; - if(num >= 92) - { - bg = 'bg-danger'; - } - else if(num >= 50) - { - bg = 'bg-warning'; - } - else if(num > 0) - { - bg = 'bg-success'; - } - - return bg; + return 'bg-primary'; }; const visitRoom = (event: MouseEvent) => @@ -46,7 +95,6 @@ export const NavigatorSearchResultItemView: FC { const newValue = { ...prevValue }; - newValue.roomInfo = roomData; newValue.state = DoorStateType.START_DOORBELL; - return newValue; }); return; @@ -67,10 +113,8 @@ export const NavigatorSearchResultItemView: FC { const newValue = { ...prevValue }; - newValue.roomInfo = roomData; newValue.state = DoorStateType.START_PASSWORD; - return newValue; }); return; @@ -81,10 +125,20 @@ export const NavigatorSearchResultItemView: FC + - { roomData.habboGroupId > 0 && } - + { roomData.habboGroupId > 0 && } + { roomData.userCount } @@ -94,23 +148,42 @@ export const NavigatorSearchResultItemView: FC { roomData.roomName } - + { children } - ); return ( - - + + { roomData.userCount } - { roomData.roomName } - - + { roomData.roomName } + + { roomData.habboGroupId > 0 && } { (roomData.doorMode !== RoomDataParser.OPEN_STATE) && } diff --git a/src/components/navigator/views/search/NavigatorSearchResultView.tsx b/src/components/navigator/views/search/NavigatorSearchResultView.tsx index d7cd752..fcbe7d7 100644 --- a/src/components/navigator/views/search/NavigatorSearchResultView.tsx +++ b/src/components/navigator/views/search/NavigatorSearchResultView.tsx @@ -1,8 +1,8 @@ -import { NavigatorSearchComposer, NavigatorSearchResultList } from '@nitrots/nitro-renderer'; +import { NavigatorSearchComposer, NavigatorSearchResultList, NavigatorSearchSaveComposer } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; import { FaBars, FaMinus, FaPlus, FaTh, FaWindowMaximize, FaWindowRestore } from 'react-icons/fa'; import { LocalizeText, NavigatorSearchResultViewDisplayMode, SendMessageComposer } from '../../../../api'; -import { AutoGrid, AutoGridProps, Column, Flex, Grid, Text } from '../../../../common'; +import { AutoGrid, AutoGridProps, Column, Flex, Grid, LayoutSearchSavesView, Text } from '../../../../common'; import { useNavigator } from '../../../../hooks'; import { NavigatorSearchResultItemView } from './NavigatorSearchResultItemView'; @@ -16,6 +16,8 @@ export const NavigatorSearchResultView: FC = pro const { searchResult = null, ...rest } = props; const [ isExtended, setIsExtended ] = useState(true); const [ displayMode, setDisplayMode ] = useState(0); + const [ selectedRoomId, setSelectedRoomId ] = useState(null); + const [ isPopoverActive, setIsPopoverActive ] = useState(false); const { topLevelContext = null } = useNavigator(); @@ -23,7 +25,7 @@ export const NavigatorSearchResultView: FC = pro { let name = searchResult.code; - if(!name || !name.length || LocalizeText('navigator.searchcode.title.' + name) == ('navigator.searchcode.title.' + name)) return searchResult.data; + if(!name || !name.length || LocalizeText('navigator.searchcode.title.' + name) === ('navigator.searchcode.title.' + name)) return searchResult.data; if(name.startsWith('${')) return name.slice(2, (name.length - 1)); @@ -51,7 +53,6 @@ export const NavigatorSearchResultView: FC = pro if(!searchResult) return; setIsExtended(!searchResult.closed); - setDisplayMode(searchResult.mode); }, [ searchResult ]); @@ -65,54 +66,46 @@ export const NavigatorSearchResultView: FC = pro { !isExtended && } { LocalizeText(getResultTitle()) } -
- { (displayMode === NavigatorSearchResultViewDisplayMode.LIST) && } - { (displayMode >= NavigatorSearchResultViewDisplayMode.THUMBNAILS) && } - { (searchResult.action > 0) && (searchResult.action === 1) && } - { (searchResult.action > 0) && (searchResult.action !== 1) && } +
+ { (displayMode === NavigatorSearchResultViewDisplayMode.LIST) && } + { (displayMode >= NavigatorSearchResultViewDisplayMode.THUMBNAILS) && } + { (searchResult.action > 0) && (searchResult.action === 1) && } + { (searchResult.action > 0) && (searchResult.action !== 1) && } + SendMessageComposer(new NavigatorSearchSaveComposer(getResultTitle(), searchResult.data)) } + />
- - { isExtended && + + { isExtended && <> - { - gridHasTwoColumns ? - { searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => ) } - : - { searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => ) } - - } - - } + { gridHasTwoColumns + ? + { searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => ( + + )) } + + : + { searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => ( + + )) } + } + } - //
- //
- //
- // - //
{ LocalizeText(getResultTitle()) }
- // = NavigatorSearchResultViewDisplayMode.THUMBNAILS })}> - //
- // { isExtended && - //
= NavigatorSearchResultViewDisplayMode.THUMBNAILS) }) }> - // { searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => - // { - // return - // }) } - //
} - //
- //
- //
- //
- // - //
{ LocalizeText(getListCode()) }
- // = NavigatorResultListViewDisplayMode.THUMBNAILS })} onClick={ toggleDisplayMode }> - //
- //
= NavigatorResultListViewDisplayMode.THUMBNAILS }) }> - // { isExtended && resultList && resultList.rooms.map((room, index) => - // { - // return - // }) - // } - //
- //
); }; diff --git a/src/components/navigator/views/search/NavigatorSearchSavesResultItemView.tsx b/src/components/navigator/views/search/NavigatorSearchSavesResultItemView.tsx new file mode 100644 index 0000000..2c11dac --- /dev/null +++ b/src/components/navigator/views/search/NavigatorSearchSavesResultItemView.tsx @@ -0,0 +1,46 @@ +import { NavigatorDeleteSavedSearchComposer, NavigatorSavedSearch, NavigatorSearchComposer } from '@nitrots/nitro-renderer'; +import { FC, useState } from 'react'; +import { LocalizeText, SendMessageComposer } from '../../../../api'; +import { Flex, Text } from '../../../../common'; + +export interface NavigatorSearchSavesResultItemViewProps +{ + search: NavigatorSavedSearch; +} + +export const NavigatorSearchSavesResultItemView: FC = props => +{ + const { search = null } = props; + const [ isHovered, setIsHovered ] = useState(false); + + const getResultTitle = () => + { + let name = search.code; + + if(!name || !name.length || LocalizeText('navigator.searchcode.title.' + name) === ('navigator.searchcode.title.' + name)) return search.code; + + if(name.startsWith('${')) return name.slice(2, (name.length - 1)); + + return ('navigator.searchcode.title.' + name); + }; + + return ( + setIsHovered(true) } onMouseLeave={ () => setIsHovered(false) }> + { isHovered && + SendMessageComposer(new NavigatorDeleteSavedSearchComposer(search.id)) } + /> } + SendMessageComposer(new NavigatorSearchComposer(search.code.split('.').reverse()[0], search.filter)) } + > + { LocalizeText(getResultTitle()) } + + + ); +}; diff --git a/src/components/navigator/views/search/NavigatorSearchSavesResultView.tsx b/src/components/navigator/views/search/NavigatorSearchSavesResultView.tsx new file mode 100644 index 0000000..9fbc9fa --- /dev/null +++ b/src/components/navigator/views/search/NavigatorSearchSavesResultView.tsx @@ -0,0 +1,31 @@ +import { NavigatorSavedSearch } from '@nitrots/nitro-renderer'; +import { FC } from 'react'; +import { FaBolt } from 'react-icons/fa'; +import { LocalizeText } from '../../../../api'; +import { Column, Flex, Text } from '../../../../common'; +import { NavigatorSearchSavesResultItemView } from './NavigatorSearchSavesResultItemView'; + +export interface NavigatorSearchSavesResultViewProps +{ + searches: NavigatorSavedSearch[]; +} + +export const NavigatorSearchSavesResultView: FC = props => +{ + const { searches = [] } = props; + + return ( + + + + { LocalizeText('navigator.quick.links.title') } + + + { (searches && searches.length > 0) && + searches.map((search: NavigatorSavedSearch) => ( + + )) } + + + ); +}; diff --git a/src/css/icons/icons.css b/src/css/icons/icons.css index e5c0fe2..e8382b2 100644 --- a/src/css/icons/icons.css +++ b/src/css/icons/icons.css @@ -668,4 +668,18 @@ .nitro-icon.icon-loading.with-size { width: 16px; height: 16px; +} + +.nitro-icon.icon-navigator-search-delete { + background-image: url("@/assets/images/navigator/saves-search/delete_search.png"); + width: 18px; + height: 18px; +} + +.nitro-icon.icon-navigator-search-delete:hover { + background-image: url("@/assets/images/navigator/saves-search/delete_search_hover.png"); +} + +.nitro-icon.icon-navigator-search-delete:active { + background-image: url("@/assets/images/navigator/saves-search/delete_search_click.png"); } \ No newline at end of file diff --git a/src/hooks/navigator/useNavigator.ts b/src/hooks/navigator/useNavigator.ts index 60f949e..90df9fc 100644 --- a/src/hooks/navigator/useNavigator.ts +++ b/src/hooks/navigator/useNavigator.ts @@ -1,4 +1,4 @@ -import { CanCreateRoomEventEvent, CantConnectMessageParser, CreateLinkEvent, DoorbellMessageEvent, FlatAccessDeniedMessageEvent, FlatCreatedEvent, FollowFriendMessageComposer, GenericErrorEvent, GetGuestRoomMessageComposer, GetGuestRoomResultEvent, GetSessionDataManager, GetUserEventCatsMessageComposer, GetUserFlatCatsMessageComposer, HabboWebTools, LegacyExternalInterface, NavigatorCategoryDataParser, NavigatorEventCategoryDataParser, NavigatorHomeRoomEvent, NavigatorMetadataEvent, NavigatorOpenRoomCreatorEvent, NavigatorSearchEvent, NavigatorSearchResultSet, NavigatorTopLevelContext, RoomDataParser, RoomDoorbellAcceptedEvent, RoomEnterErrorEvent, RoomEntryInfoMessageEvent, RoomForwardEvent, RoomScoreEvent, RoomSettingsUpdatedEvent, SecurityLevel, UserEventCatsEvent, UserFlatCatsEvent, UserInfoEvent, UserPermissionsEvent } from '@nitrots/nitro-renderer'; +import { CanCreateRoomEventEvent, CantConnectMessageParser, CreateLinkEvent, DoorbellMessageEvent, FavouriteChangedEvent, FavouritesEvent, FlatAccessDeniedMessageEvent, FlatCreatedEvent, FollowFriendMessageComposer, GenericErrorEvent, GetGuestRoomMessageComposer, GetGuestRoomResultEvent, GetSessionDataManager, GetUserEventCatsMessageComposer, GetUserFlatCatsMessageComposer, HabboWebTools, LegacyExternalInterface, NavigatorCategoryDataParser, NavigatorEventCategoryDataParser, NavigatorHomeRoomEvent, NavigatorMetadataEvent, NavigatorOpenRoomCreatorEvent, NavigatorSavedSearch, NavigatorSearchesEvent, NavigatorSearchEvent, NavigatorSearchResultSet, NavigatorTopLevelContext, RoomDataParser, RoomDoorbellAcceptedEvent, RoomEnterErrorEvent, RoomEntryInfoMessageEvent, RoomForwardEvent, RoomScoreEvent, RoomSettingsUpdatedEvent, SecurityLevel, UserEventCatsEvent, UserFlatCatsEvent, UserInfoEvent, UserPermissionsEvent } from '@nitrots/nitro-renderer'; import { useState } from 'react'; import { useBetween } from 'use-between'; import { CreateRoomSession, DoorStateType, GetConfigurationValue, INavigatorData, LocalizeText, NotificationAlertType, SendMessageComposer, TryVisitRoom, VisitDesktop } from '../../api'; @@ -9,10 +9,12 @@ const useNavigatorState = () => { const [ categories, setCategories ] = useState(null); const [ eventCategories, setEventCategories ] = useState(null); + const [ favouriteRoomIds, setFavouriteRoomIds ] = useState([]); const [ topLevelContext, setTopLevelContext ] = useState(null); const [ topLevelContexts, setTopLevelContexts ] = useState(null); const [ doorData, setDoorData ] = useState<{ roomInfo: RoomDataParser, state: number }>({ roomInfo: null, state: DoorStateType.NONE }); const [ searchResult, setSearchResult ] = useState(null); + const [ navigatorSearches, setNavigatorSearches ] = useState(null); const [ navigatorData, setNavigatorData ] = useState({ settingsReceived: false, homeRoomId: 0, @@ -29,6 +31,29 @@ const useNavigatorState = () => }); const { simpleAlert = null } = useNotification(); + useMessageEvent(FavouritesEvent, event => + { + const parser = event.getParser(); + const favoriteIds = (parser.favoriteRoomIds || []).map((x: any) => Number(x)); + setFavouriteRoomIds(favoriteIds); + }); + + useMessageEvent(FavouriteChangedEvent, event => + { + const parser = event.getParser(); + const roomId = Number(parser.flatId); + const added = !!parser.added; + + setFavouriteRoomIds(prev => + { + const ids = (prev || []).map((x: any) => Number(x)); + + if(added) return ids.includes(roomId) ? ids : [ ...ids, roomId ]; + + return ids.filter(id => id !== roomId); + }); + }); + useMessageEvent(RoomSettingsUpdatedEvent, event => { const parser = event.getParser(); @@ -436,7 +461,14 @@ const useNavigatorState = () => useMessageEvent(NavigatorOpenRoomCreatorEvent, event => CreateLinkEvent('navigator/show')); - return { categories, doorData, setDoorData, topLevelContext, topLevelContexts, searchResult, navigatorData }; + useMessageEvent(NavigatorSearchesEvent, event => + { + const parser = event.getParser(); + if(!parser) return; + setNavigatorSearches(parser.searches); + }); + + return { categories, doorData, setDoorData, topLevelContext, topLevelContexts, searchResult, navigatorData, favouriteRoomIds, navigatorSearches }; }; export const useNavigator = () => useBetween(useNavigatorState);