1- import { Box , toRem , Text , color , config , Menu , Icon , Icons , Line , MenuItem } from 'folds' ;
2- import { SquaresFour , sizedIcon } from '$components/icons/phosphor' ;
1+ import {
2+ Box ,
3+ toRem ,
4+ Text ,
5+ color ,
6+ config ,
7+ Menu ,
8+ Icon ,
9+ Icons ,
10+ Line ,
11+ MenuItem ,
12+ OverlayBackdrop ,
13+ Overlay ,
14+ OverlayCenter ,
15+ } from 'folds' ;
16+ import { SquaresFour , menuIcon , settingsNavIcon , sizedIcon } from '$components/icons/phosphor' ;
317import { PageNav , PageNavHeader } from '$components/page' ;
4- import { useEffect , useState } from 'react' ;
18+ import { useEffect , useMemo , useState } from 'react' ;
519import { ScreenSize , useScreenSizeContext } from '$hooks/useScreenSize' ;
620import { useSetting } from '$state/hooks/settings' ;
721import { settingsAtom } from '$state/settings' ;
@@ -19,8 +33,14 @@ import { getMxIdLocalPart, mxcUrlToHttp } from '$utils/matrix';
1933import { useMediaAuthentication } from '$hooks/useMediaAuthentication' ;
2034import { useUserPresence } from '$hooks/useUserPresence' ;
2135import { useUserProfile } from '$hooks/useUserProfile' ;
22- import { useOpenSettings } from '$features/settings' ;
36+ import type { SettingsMenuItem } from '$features/settings' ;
37+ import { settingsMenuIcons , settingsSections , useOpenSettings } from '$features/settings' ;
2338import { UserQuickTools } from '../sidebar/UserQuickTools' ;
39+ import { SignOutIcon } from '@phosphor-icons/react' ;
40+ import { UseStateProvider } from '$components/UseStateProvider' ;
41+ import { FocusTrap } from 'focus-trap-react' ;
42+ import { LogoutDialog } from '$components/LogoutDialog' ;
43+ import { stopPropagation } from '$utils/keyboard' ;
2444
2545export function ProfileMobile ( ) {
2646 const mx = useMatrixClient ( ) ;
@@ -31,6 +51,7 @@ export function ProfileMobile() {
3151 const userId = mx . getUserId ( ) ?? '' ;
3252 const profile = useUserProfile ( userId ) ;
3353 const presence = useUserPresence ( userId ) ;
54+ const [ isSettingsOpen , setIsSettingsOpen ] = useState ( true ) ;
3455
3556 const displayName = profile . displayName ?? getMxIdLocalPart ( userId ) ?? userId ;
3657 const heroAvatarUrl = profile . avatarUrl
@@ -54,6 +75,18 @@ export function ProfileMobile() {
5475 const isMobile = screenSize === ScreenSize . Mobile ;
5576 const hideText = curWidth <= 80 && ! isMobile ;
5677
78+ const [ showPersona ] = useSetting ( settingsAtom , 'showPersonaSetting' ) ;
79+ const menuItems = useMemo < SettingsMenuItem [ ] > (
80+ ( ) =>
81+ settingsSections
82+ . filter ( ( section ) => showPersona || section . id !== 'persona' )
83+ . map ( ( section ) => {
84+ const icon = settingsMenuIcons [ section . id ] ;
85+ return { id : section . id , name : section . label , ...icon } ;
86+ } ) ,
87+ [ showPersona ]
88+ ) ;
89+
5790 return (
5891 < >
5992 { ! isMobile && (
@@ -99,9 +132,18 @@ export function ProfileMobile() {
99132 direction = "Column"
100133 gap = "0"
101134 alignItems = "Center"
135+ justifyContent = "SpaceBetween"
102136 style = { { width : '100%' , minWidth : '100%' } }
103137 >
104- < Menu style = { { minWidth : '100%' , overflowY : 'scroll' } } >
138+ < Menu
139+ style = { {
140+ minWidth : '100%' ,
141+ minHeight : '100%' ,
142+ overflowY : 'scroll' ,
143+ border : 'none' ,
144+ background : color . Background . Container ,
145+ } }
146+ >
105147 < UserHero
106148 userId = { userId }
107149 avatarUrl = { heroAvatarUrl }
@@ -119,7 +161,10 @@ export function ProfileMobile() {
119161 < UnverifiedMenuOption />
120162 </ Box >
121163 < Line variant = "Surface" size = "300" />
122- < PresenceMenuOption initialOpen isMobile />
164+
165+ < Box direction = "Column" gap = "100" style = { { padding : config . space . S100 } } >
166+ < PresenceMenuOption isMobile />
167+ </ Box >
123168 < AccountMenuOption isMobile />
124169
125170 < Line variant = "Surface" size = "300" />
@@ -128,13 +173,73 @@ export function ProfileMobile() {
128173 < MenuItem
129174 size = "300"
130175 radii = "300"
176+ variant = "Background"
131177 before = { < Icon size = "100" src = { Icons . Setting } /> }
132- onClick = { ( ) => openSettings ( ) }
178+ after = {
179+ < Icon
180+ size = "100"
181+ src = { isSettingsOpen && isMobile ? Icons . ChevronBottom : Icons . ChevronRight }
182+ />
183+ }
184+ onClick = { ( ) => setIsSettingsOpen ( ! isSettingsOpen ) }
133185 >
134186 < Text style = { { flexGrow : 1 } } size = "T300" >
135187 Settings
136188 </ Text >
137189 </ MenuItem >
190+ { isSettingsOpen && (
191+ < div style = { { paddingLeft : config . space . S100 } } >
192+ { menuItems . map ( ( item ) => {
193+ const IconComponent = item . icon ;
194+
195+ return (
196+ < MenuItem
197+ key = { item . id }
198+ radii = "400"
199+ size = "300"
200+ variant = "Background"
201+ before = { settingsNavIcon ( IconComponent , false ) }
202+ onClick = { ( ) => openSettings ( item . id ) }
203+ >
204+ < Text size = "T300" truncate >
205+ { item . name }
206+ </ Text >
207+ </ MenuItem >
208+ ) ;
209+ } ) }
210+
211+ < UseStateProvider initial = { false } >
212+ { ( logout , setLogout ) => (
213+ < >
214+ < MenuItem
215+ size = "300"
216+ variant = "Background"
217+ style = { { color : color . Critical . OnContainer } }
218+ before = { menuIcon ( SignOutIcon ) }
219+ onClick = { ( ) => setLogout ( true ) }
220+ >
221+ < Text size = "B400" > Logout</ Text >
222+ </ MenuItem >
223+ { logout && (
224+ < Overlay open backdrop = { < OverlayBackdrop /> } >
225+ < OverlayCenter >
226+ < FocusTrap
227+ focusTrapOptions = { {
228+ onDeactivate : ( ) => setLogout ( false ) ,
229+ clickOutsideDeactivates : true ,
230+ escapeDeactivates : stopPropagation ,
231+ } }
232+ >
233+ < LogoutDialog handleClose = { ( ) => setLogout ( false ) } />
234+ </ FocusTrap >
235+ </ OverlayCenter >
236+ </ Overlay >
237+ ) }
238+ </ >
239+ ) }
240+ </ UseStateProvider >
241+ </ div >
242+ ) }
138243 </ Box >
139244 </ Menu >
140245 </ Box >
0 commit comments