android: add settings and profile shells and move logout action
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
Codex
2026-03-09 15:13:18 +03:00
parent 6afde15a2c
commit 7fcdc28015
5 changed files with 177 additions and 17 deletions

View File

@@ -313,3 +313,8 @@
- Added `SessionCleanupRepository` + `DefaultSessionCleanupRepository` to wipe Room tables and clear per-chat notification overrides.
- Added logout action in chat list UI and wired it to `AuthViewModel`, with automatic navigation back to login via auth state.
- Added unit tests for logout use case orchestration and notification override cleanup.
### Step 52 - Settings/Profile shell and logout relocation
- Added dedicated `Settings` and `Profile` routes/screens with mobile-safe insets and placeholder content.
- Removed direct logout action from chat list and moved logout action to `Settings`.
- Wired bottom navigation pills in chats to open `Settings` and `Profile`.

View File

@@ -29,7 +29,6 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -51,7 +50,8 @@ import java.time.format.DateTimeFormatter
@Composable
fun ChatListRoute(
onOpenChat: (Long) -> Unit,
onLogout: () -> Unit,
onOpenSettings: () -> Unit,
onOpenProfile: () -> Unit,
inviteToken: String?,
onInviteTokenConsumed: () -> Unit,
viewModel: ChatListViewModel = hiltViewModel(),
@@ -74,7 +74,8 @@ fun ChatListRoute(
onFilterSelected = viewModel::onFilterSelected,
onSearchChanged = viewModel::onSearchChanged,
onRefresh = viewModel::onPullToRefresh,
onLogout = onLogout,
onOpenSettings = onOpenSettings,
onOpenProfile = onOpenProfile,
onOpenChat = onOpenChat,
)
}
@@ -87,7 +88,8 @@ fun ChatListScreen(
onFilterSelected: (ChatListFilter) -> Unit,
onSearchChanged: (String) -> Unit,
onRefresh: () -> Unit,
onLogout: () -> Unit,
onOpenSettings: () -> Unit,
onOpenProfile: () -> Unit,
onOpenChat: (Long) -> Unit,
) {
Column(
@@ -119,16 +121,6 @@ fun ChatListScreen(
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.End,
) {
TextButton(onClick = onLogout) {
Text("Logout")
}
}
Row(
modifier = Modifier
.fillMaxWidth()
@@ -224,8 +216,8 @@ fun ChatListScreen(
) {
BottomNavPill(label = "Chats", selected = true, onClick = {})
BottomNavPill(label = "Contacts", selected = false, onClick = {})
BottomNavPill(label = "Settings", selected = false, onClick = {})
BottomNavPill(label = "Profile", selected = false, onClick = {})
BottomNavPill(label = "Settings", selected = false, onClick = onOpenSettings)
BottomNavPill(label = "Profile", selected = false, onClick = onOpenProfile)
}
}
}

View File

@@ -33,11 +33,15 @@ import ru.daemonlord.messenger.ui.auth.AuthViewModel
import ru.daemonlord.messenger.ui.auth.LoginScreen
import ru.daemonlord.messenger.ui.chat.ChatRoute
import ru.daemonlord.messenger.ui.chats.ChatListRoute
import ru.daemonlord.messenger.ui.profile.ProfileRoute
import ru.daemonlord.messenger.ui.settings.SettingsRoute
private object Routes {
const val AuthGraph = "auth_graph"
const val Login = "login"
const val Chats = "chats"
const val Settings = "settings"
const val Profile = "profile"
const val Chat = "chat"
}
@@ -125,13 +129,29 @@ fun MessengerNavHost(
ChatListRoute(
inviteToken = inviteToken,
onInviteTokenConsumed = onInviteTokenConsumed,
onLogout = viewModel::logout,
onOpenSettings = { navController.navigate(Routes.Settings) },
onOpenProfile = { navController.navigate(Routes.Profile) },
onOpenChat = { chatId ->
navController.navigate("${Routes.Chat}/$chatId")
}
)
}
composable(route = Routes.Settings) {
SettingsRoute(
onBackToChats = { navController.popBackStack() },
onOpenProfile = { navController.navigate(Routes.Profile) },
onLogout = viewModel::logout,
)
}
composable(route = Routes.Profile) {
ProfileRoute(
onBackToChats = { navController.popBackStack() },
onOpenSettings = { navController.navigate(Routes.Settings) },
)
}
composable(
route = "${Routes.Chat}/{chatId}",
arguments = listOf(

View File

@@ -0,0 +1,64 @@
package ru.daemonlord.messenger.ui.profile
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun ProfileRoute(
onBackToChats: () -> Unit,
onOpenSettings: () -> Unit,
) {
ProfileScreen(
onBackToChats = onBackToChats,
onOpenSettings = onOpenSettings,
)
}
@Composable
fun ProfileScreen(
onBackToChats: () -> Unit,
onOpenSettings: () -> Unit,
) {
Column(
modifier = Modifier
.fillMaxSize()
.windowInsetsPadding(WindowInsets.safeDrawing)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = "Profile",
style = MaterialTheme.typography.headlineSmall,
)
Text(
text = "Profile editing screen placeholder. Telegram-like account editor will be implemented in a dedicated step.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
OutlinedButton(
onClick = onOpenSettings,
modifier = Modifier.fillMaxWidth(),
) {
Text("Open settings")
}
OutlinedButton(
onClick = onBackToChats,
modifier = Modifier.fillMaxWidth(),
) {
Text("Back to chats")
}
}
}

View File

@@ -0,0 +1,79 @@
package ru.daemonlord.messenger.ui.settings
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun SettingsRoute(
onBackToChats: () -> Unit,
onOpenProfile: () -> Unit,
onLogout: () -> Unit,
) {
SettingsScreen(
onBackToChats = onBackToChats,
onOpenProfile = onOpenProfile,
onLogout = onLogout,
)
}
@Composable
fun SettingsScreen(
onBackToChats: () -> Unit,
onOpenProfile: () -> Unit,
onLogout: () -> Unit,
) {
Column(
modifier = Modifier
.fillMaxSize()
.windowInsetsPadding(WindowInsets.safeDrawing)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = "Settings",
style = MaterialTheme.typography.headlineSmall,
)
Text(
text = "Core account actions are here. More Telegram-like settings will be added in next iterations.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
OutlinedButton(
onClick = onOpenProfile,
modifier = Modifier.fillMaxWidth(),
) {
Text("Open profile")
}
Button(
onClick = onLogout,
modifier = Modifier.fillMaxWidth(),
) {
Text("Logout")
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
) {
OutlinedButton(onClick = onBackToChats) {
Text("Back to chats")
}
}
}
}