diff --git a/lib/auth/telegram_session_stub.dart b/lib/auth/telegram_session_stub.dart index c82a380..6341664 100644 --- a/lib/auth/telegram_session_stub.dart +++ b/lib/auth/telegram_session_stub.dart @@ -10,6 +10,8 @@ String telegramLoginTokenFromUrl() => ''; void saveMapflowSessionToken(String token) {} +void clearMapflowSession() {} + void savePendingTelegramLoginToken(String token) {} void clearPendingTelegramLoginToken() {} diff --git a/lib/auth/telegram_session_web.dart b/lib/auth/telegram_session_web.dart index 15a27e9..c95dda8 100644 --- a/lib/auth/telegram_session_web.dart +++ b/lib/auth/telegram_session_web.dart @@ -37,6 +37,12 @@ void saveMapflowSessionToken(String token) { web.window.localStorage.setItem(mapflowSessionStorageKey, token); } +void clearMapflowSession() { + web.window.localStorage.removeItem(mapflowSessionStorageKey); + web.window.localStorage.removeItem(telegramLoginStorageKey); + clearPendingTelegramLoginToken(); +} + void savePendingTelegramLoginToken(String token) { web.window.localStorage.setItem(pendingTelegramLoginStorageKey, token); } diff --git a/lib/screens/mapflow_shell.dart b/lib/screens/mapflow_shell.dart index e26152a..8712e01 100644 --- a/lib/screens/mapflow_shell.dart +++ b/lib/screens/mapflow_shell.dart @@ -84,7 +84,14 @@ class _MapContent extends ConsumerWidget { SafeArea( child: Align( alignment: Alignment.topLeft, - child: _UserAvatar(user: state.currentUser), + child: _UserAvatar( + user: state.currentUser, + onLogout: () { + telegram_session.clearMapflowSession(); + ref.invalidate(placeControllerProvider); + telegram_session.reloadApp(); + }, + ), ), ), if (availableTraits.isNotEmpty) @@ -140,9 +147,10 @@ class _MapContent extends ConsumerWidget { } class _UserAvatar extends StatelessWidget { - const _UserAvatar({required this.user}); + const _UserAvatar({required this.user, required this.onLogout}); final AppUser? user; + final VoidCallback onLogout; @override Widget build(BuildContext context) { @@ -151,16 +159,40 @@ class _UserAvatar extends StatelessWidget { return Padding( padding: const EdgeInsets.only(left: 12, top: 8), - child: ClipOval( - child: SizedBox.square( - dimension: 44, - child: photoUrl == null || photoUrl.isEmpty - ? _AvatarFallback(text: fallback) - : Image.network( - photoUrl, - fit: BoxFit.cover, - errorBuilder: (_, _, _) => _AvatarFallback(text: fallback), - ), + child: PopupMenuButton<_AvatarAction>( + tooltip: '', + offset: const Offset(0, 8), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + onSelected: (action) { + switch (action) { + case _AvatarAction.logout: + onLogout(); + } + }, + itemBuilder: (_) => const [ + PopupMenuItem<_AvatarAction>( + value: _AvatarAction.logout, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.logout, size: 18), + SizedBox(width: 10), + Text('Выйти'), + ], + ), + ), + ], + child: ClipOval( + child: SizedBox.square( + dimension: 44, + child: photoUrl == null || photoUrl.isEmpty + ? _AvatarFallback(text: fallback) + : Image.network( + photoUrl, + fit: BoxFit.cover, + errorBuilder: (_, _, _) => _AvatarFallback(text: fallback), + ), + ), ), ), ); @@ -181,6 +213,8 @@ class _UserAvatar extends StatelessWidget { } } +enum _AvatarAction { logout } + class _AvatarFallback extends StatelessWidget { const _AvatarFallback({required this.text});