almost done

This commit is contained in:
2025-12-29 04:42:40 +01:00
parent 36d5fc5ce9
commit d07d369546
30 changed files with 380 additions and 318 deletions

View File

@@ -12,7 +12,6 @@ import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -26,6 +25,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.automirrored.filled.Logout
@@ -36,17 +36,19 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.modifier.modifierLocalConsumer
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
@@ -55,22 +57,23 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.lifecycleScope
import io.github.jan.supabase.auth.auth
import io.github.jan.supabase.auth.status.SessionStatus
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import net.tinsae.clocked.components.ShowLoadingScreen
import net.tinsae.clocked.dashboard.DashboardScreen
import net.tinsae.clocked.data.Locale
import net.tinsae.clocked.data.LogRepository
import net.tinsae.clocked.data.Theme
import net.tinsae.clocked.history.HistoryScreen
import net.tinsae.clocked.settings.SettingsScreen
import net.tinsae.clocked.settings.SettingsViewModel
import net.tinsae.clocked.ui.theme.ClockedTheme
import net.tinsae.clocked.util.SupabaseClient
import java.text.SimpleDateFormat
import java.util.Date
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.LaunchedEffect
import io.github.jan.supabase.auth.status.SessionStatus
import net.tinsae.clocked.data.LogRepository
class MainActivity : ComponentActivity() {
@@ -126,6 +129,25 @@ fun AppEntry(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel)
val settingsState by settingsViewModel.uiState.collectAsState()
val sessionStatus by authViewModel.sessionStatus.collectAsState()
/*LaunchedEffect(Unit) {
SupabaseClient.client.auth.sessionStatus.collect { status ->
if (status is SessionStatus.Authenticated &&
SupabaseClient.client.auth.currentSessionOrNull() != null
) {
LogRepository.fetchLogs()
} else {
return@collect
}
}
}*/
LaunchedEffect(sessionStatus) {
if (sessionStatus is SessionStatus.Authenticated){
LogRepository.fetchLogs()
}
}
val useDarkTheme = when (settingsState.theme) {
Theme.LIGHT -> false
Theme.DARK -> true
@@ -136,29 +158,21 @@ fun AppEntry(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel)
updateLocale(context, settingsState.locale)
ClockedTheme(darkTheme = useDarkTheme) {
// Use a 'when' statement to handle all possible states.
when (sessionStatus) {
is SessionStatus.Initializing -> {
// Only show loading for the initial session check.
ShowLoadingScreen()
}
is SessionStatus.Authenticated -> {
// If we know the user is authenticated, show the main app.
ClockedApp(settingsViewModel, authViewModel,sessionStatus)
// Just show the app. The ViewModels inside will manage their own loading UI.
ClockedApp(settingsViewModel, authViewModel)
}
is SessionStatus.NotAuthenticated -> {
// Only if we are certain the user is not logged in, show the login screen.
LoginScreen(authViewModel)
}
is SessionStatus.Initializing -> {
// While Supabase is loading the session, show a loading indicator.
// This prevents the flicker.
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
else -> {}
}
}
@@ -175,67 +189,69 @@ fun updateLocale(context: Context, locale: Locale) {
}
@Composable
fun ClockedApp(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel, sessionStatus: SessionStatus) {
fun ClockedApp(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel) {
var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) }
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
Row(
modifier = Modifier
.fillMaxWidth()
.background(colorScheme.surface)
.padding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top).asPaddingValues()),
//contentAlignment = Alignment.CenterStart
) {
Text(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
text = stringResource(R.string.app_name),
style = typography.titleLarge,
fontWeight = FontWeight.Bold
NavigationSuiteScaffold(
navigationSuiteItems = {
AppDestinations.entries.forEach { destination ->
item(
icon = { Icon(
imageVector = destination.icon,
contentDescription = stringResource(destination.label))
},
alwaysShowLabel = false,
label = { Text(stringResource(destination.label)) },
selected = destination == currentDestination,
onClick = { currentDestination = destination }
)
Spacer(modifier = Modifier.weight(1f))
// 2. Use IconButton to make the icon clickable with a ripple effect
IconButton(onClick = { authViewModel.logout() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.Logout, // Replace with your logout icon
contentDescription = "Logout"
)
}
}
},
) { innerPadding ->
val layoutDirection = LocalLayoutDirection.current
val contentPadding = PaddingValues(
start = innerPadding.calculateStartPadding(layoutDirection),
end = innerPadding.calculateEndPadding(layoutDirection),
top = innerPadding.calculateTopPadding(),
bottom = 0.dp
)
}
) {
Scaffold(
modifier = Modifier.fillMaxSize(),
NavigationSuiteScaffold(
modifier = Modifier.padding(contentPadding),
navigationSuiteItems = {
AppDestinations.entries.forEach { destination ->
item(
icon = { Icon(
imageVector = destination.icon,
contentDescription = stringResource(destination.label))
},
label = { Text(stringResource(destination.label)) },
selected = destination == currentDestination,
onClick = { currentDestination = destination }
)
topBar = {
Surface(
color = colorScheme.surfaceContainer,
shadowElevation = 2.dp, // Adds a subtle shadow to lift the bar
modifier = Modifier.fillMaxWidth()
// No padding needed here on the Surface itself
) {
Row(
// Apply safe area padding to the Row to push content down
modifier = Modifier
.fillMaxWidth()
.padding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top).asPaddingValues()),
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
) {
Text(
modifier = Modifier.padding(start = 16.dp), // Vertical padding is handled by Row's alignment
text = stringResource(R.string.app_name),
style = typography.titleLarge,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = { authViewModel.logout() }, modifier = Modifier.padding(10.dp)) {
Icon(
imageVector = Icons.AutoMirrored.Filled.Logout,
contentDescription = "Logout" // Use string resource
)
}
}
}
}
) {
},
) { innerPadding ->
val modifier = Modifier.fillMaxWidth().padding(innerPadding)
when (currentDestination) {
AppDestinations.HOME -> DashboardScreen( modifier = Modifier.fillMaxSize())
AppDestinations.HISTORY -> HistoryScreen(modifier = Modifier.fillMaxSize())
AppDestinations.HOME -> DashboardScreen( modifier = modifier)
AppDestinations.HISTORY -> HistoryScreen(modifier = modifier)
AppDestinations.SETTING -> SettingsScreen(
modifier = Modifier.fillMaxSize(),
modifier = Modifier,
viewModel = settingsViewModel,
authViewModel = authViewModel
)