android: add settings and profile shells and move logout action
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
This commit is contained in:
@@ -313,3 +313,8 @@
|
|||||||
- Added `SessionCleanupRepository` + `DefaultSessionCleanupRepository` to wipe Room tables and clear per-chat notification overrides.
|
- 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 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.
|
- 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`.
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import androidx.compose.material3.Surface
|
|||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.TabRow
|
import androidx.compose.material3.TabRow
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -51,7 +50,8 @@ import java.time.format.DateTimeFormatter
|
|||||||
@Composable
|
@Composable
|
||||||
fun ChatListRoute(
|
fun ChatListRoute(
|
||||||
onOpenChat: (Long) -> Unit,
|
onOpenChat: (Long) -> Unit,
|
||||||
onLogout: () -> Unit,
|
onOpenSettings: () -> Unit,
|
||||||
|
onOpenProfile: () -> Unit,
|
||||||
inviteToken: String?,
|
inviteToken: String?,
|
||||||
onInviteTokenConsumed: () -> Unit,
|
onInviteTokenConsumed: () -> Unit,
|
||||||
viewModel: ChatListViewModel = hiltViewModel(),
|
viewModel: ChatListViewModel = hiltViewModel(),
|
||||||
@@ -74,7 +74,8 @@ fun ChatListRoute(
|
|||||||
onFilterSelected = viewModel::onFilterSelected,
|
onFilterSelected = viewModel::onFilterSelected,
|
||||||
onSearchChanged = viewModel::onSearchChanged,
|
onSearchChanged = viewModel::onSearchChanged,
|
||||||
onRefresh = viewModel::onPullToRefresh,
|
onRefresh = viewModel::onPullToRefresh,
|
||||||
onLogout = onLogout,
|
onOpenSettings = onOpenSettings,
|
||||||
|
onOpenProfile = onOpenProfile,
|
||||||
onOpenChat = onOpenChat,
|
onOpenChat = onOpenChat,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,8 @@ fun ChatListScreen(
|
|||||||
onFilterSelected: (ChatListFilter) -> Unit,
|
onFilterSelected: (ChatListFilter) -> Unit,
|
||||||
onSearchChanged: (String) -> Unit,
|
onSearchChanged: (String) -> Unit,
|
||||||
onRefresh: () -> Unit,
|
onRefresh: () -> Unit,
|
||||||
onLogout: () -> Unit,
|
onOpenSettings: () -> Unit,
|
||||||
|
onOpenProfile: () -> Unit,
|
||||||
onOpenChat: (Long) -> Unit,
|
onOpenChat: (Long) -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -119,16 +121,6 @@ fun ChatListScreen(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
)
|
)
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp),
|
|
||||||
horizontalArrangement = Arrangement.End,
|
|
||||||
) {
|
|
||||||
TextButton(onClick = onLogout) {
|
|
||||||
Text("Logout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -224,8 +216,8 @@ fun ChatListScreen(
|
|||||||
) {
|
) {
|
||||||
BottomNavPill(label = "Chats", selected = true, onClick = {})
|
BottomNavPill(label = "Chats", selected = true, onClick = {})
|
||||||
BottomNavPill(label = "Contacts", selected = false, onClick = {})
|
BottomNavPill(label = "Contacts", selected = false, onClick = {})
|
||||||
BottomNavPill(label = "Settings", selected = false, onClick = {})
|
BottomNavPill(label = "Settings", selected = false, onClick = onOpenSettings)
|
||||||
BottomNavPill(label = "Profile", selected = false, onClick = {})
|
BottomNavPill(label = "Profile", selected = false, onClick = onOpenProfile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,11 +33,15 @@ import ru.daemonlord.messenger.ui.auth.AuthViewModel
|
|||||||
import ru.daemonlord.messenger.ui.auth.LoginScreen
|
import ru.daemonlord.messenger.ui.auth.LoginScreen
|
||||||
import ru.daemonlord.messenger.ui.chat.ChatRoute
|
import ru.daemonlord.messenger.ui.chat.ChatRoute
|
||||||
import ru.daemonlord.messenger.ui.chats.ChatListRoute
|
import ru.daemonlord.messenger.ui.chats.ChatListRoute
|
||||||
|
import ru.daemonlord.messenger.ui.profile.ProfileRoute
|
||||||
|
import ru.daemonlord.messenger.ui.settings.SettingsRoute
|
||||||
|
|
||||||
private object Routes {
|
private object Routes {
|
||||||
const val AuthGraph = "auth_graph"
|
const val AuthGraph = "auth_graph"
|
||||||
const val Login = "login"
|
const val Login = "login"
|
||||||
const val Chats = "chats"
|
const val Chats = "chats"
|
||||||
|
const val Settings = "settings"
|
||||||
|
const val Profile = "profile"
|
||||||
const val Chat = "chat"
|
const val Chat = "chat"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,13 +129,29 @@ fun MessengerNavHost(
|
|||||||
ChatListRoute(
|
ChatListRoute(
|
||||||
inviteToken = inviteToken,
|
inviteToken = inviteToken,
|
||||||
onInviteTokenConsumed = onInviteTokenConsumed,
|
onInviteTokenConsumed = onInviteTokenConsumed,
|
||||||
onLogout = viewModel::logout,
|
onOpenSettings = { navController.navigate(Routes.Settings) },
|
||||||
|
onOpenProfile = { navController.navigate(Routes.Profile) },
|
||||||
onOpenChat = { chatId ->
|
onOpenChat = { chatId ->
|
||||||
navController.navigate("${Routes.Chat}/$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(
|
composable(
|
||||||
route = "${Routes.Chat}/{chatId}",
|
route = "${Routes.Chat}/{chatId}",
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user