progress to exporting filtered content

This commit is contained in:
2026-01-30 22:21:01 +01:00
parent 61002cada9
commit 29f5716664
7 changed files with 120 additions and 54 deletions

View File

@@ -27,7 +27,7 @@ fun LoginScreen(viewModel: AuthViewModel) {
modifier = modifier, modifier = modifier,
uiState = uiState, uiState = uiState,
onRegister = viewModel::signUp, onRegister = viewModel::signUp,
onTogleForm = viewModel::setFormType onToggleForm = viewModel::setFormType
) )
} }

View File

@@ -23,10 +23,8 @@ import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -36,7 +34,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.capitalize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@@ -45,12 +42,10 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.tinsae.clocked.biometric.GlobalAuthenticator import net.tinsae.clocked.biometric.GlobalAuthenticator
import net.tinsae.clocked.ui.components.ShowLoadingScreen
import net.tinsae.clocked.ui.screens.dashboard.DashboardScreen
import net.tinsae.clocked.data.Locale import net.tinsae.clocked.data.Locale
import net.tinsae.clocked.data.LogRepository
import net.tinsae.clocked.data.Theme import net.tinsae.clocked.data.Theme
import net.tinsae.clocked.ui.components.TopBar import net.tinsae.clocked.ui.components.TopBar
import net.tinsae.clocked.ui.screens.dashboard.DashboardScreen
import net.tinsae.clocked.ui.screens.history.HistoryScreen import net.tinsae.clocked.ui.screens.history.HistoryScreen
import net.tinsae.clocked.ui.screens.settings.SettingsScreen import net.tinsae.clocked.ui.screens.settings.SettingsScreen
import net.tinsae.clocked.ui.screens.settings.SettingsViewModel import net.tinsae.clocked.ui.screens.settings.SettingsViewModel
@@ -92,17 +87,13 @@ class MainActivity : AppCompatActivity() {
settingsViewModel.uiState settingsViewModel.uiState
.map { it.pendingCsvContent } .map { it.pendingCsvContent }
.distinctUntilChanged() .distinctUntilChanged()
.collect { csvContent -> .collect {
csvContent ->
if (csvContent != null) { if (csvContent != null) {
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", java.util.Locale.US).format(Date()) val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", java.util.Locale.US).format(Date())
createDocumentLauncher.launch("Clocked-Export-$timestamp.csv") createDocumentLauncher.launch("Clocked-Export-$timestamp.csv")
} }
} }
/*authViewModel.sessionStatus.collect {
if (it is SessionStatus.Authenticated) {
LogRepository.subscribeRealTime()
}
}*/
} }
enableEdgeToEdge() enableEdgeToEdge()
@@ -114,23 +105,14 @@ class MainActivity : AppCompatActivity() {
} }
/*override fun onDestroy() {
super.onDestroy()
// When the app is being completely destroyed, ensure the connection is closed.
LogRepository.unsubscribeRealTime()
}*/
} }
@Composable @Composable
fun AppEntry(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel) { fun AppEntry(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel) {
val settingsState by settingsViewModel.uiState.collectAsState() val settingsState by settingsViewModel.uiState.collectAsState()
val sessionStatus by authViewModel.sessionStatus.collectAsState() val sessionStatus by authViewModel.sessionStatus.collectAsState()
// Trigger fetching of logs when the session status changes.
LaunchedEffect(sessionStatus) {
if (sessionStatus is SessionStatus.Authenticated){
//LogRepository.
}
}
val useDarkTheme = when (settingsState.theme) { val useDarkTheme = when (settingsState.theme) {
@@ -148,21 +130,6 @@ fun AppEntry(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel)
}else{ }else{
ClockedApp(settingsViewModel, authViewModel) ClockedApp(settingsViewModel, authViewModel)
} }
/*when (sessionStatus) {
/*is SessionStatus.Initializing -> {
// Only show loading for the initial session check.
ShowLoadingScreen()
}*/
is SessionStatus.Authenticated -> {
// Just show the app. The ViewModels inside will manage their own loading UI.
ClockedApp(settingsViewModel, authViewModel)
}
else -> {
LoginScreen(authViewModel)
}
}*/
} }
} }
@@ -180,7 +147,6 @@ fun updateLocale(context: Context, locale: Locale) {
fun ClockedApp(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel) { fun ClockedApp(settingsViewModel: SettingsViewModel, authViewModel: AuthViewModel) {
var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) } var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) }
NavigationSuiteScaffold( NavigationSuiteScaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
navigationSuiteItems = { navigationSuiteItems = {
@@ -190,16 +156,18 @@ fun ClockedApp(settingsViewModel: SettingsViewModel, authViewModel: AuthViewMode
Icon( Icon(
imageVector = destination.icon, imageVector = destination.icon,
contentDescription = stringResource(destination.label), contentDescription = stringResource(destination.label),
modifier = Modifier.padding(1.dp).size(24.dp) modifier = Modifier
//.padding(1.dp)
.size(24.dp)
) )
}, },
alwaysShowLabel = false, alwaysShowLabel = false,
label = { Text(stringResource(destination.label)) }, //label = { Text(stringResource(destination.label)) },
selected = destination == currentDestination, selected = destination == currentDestination,
onClick = { currentDestination = destination } onClick = { currentDestination = destination }
) )
} }
} },
) { ) {
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@@ -235,10 +203,6 @@ fun ClockedApp(settingsViewModel: SettingsViewModel, authViewModel: AuthViewMode
modifier = modifier, modifier = modifier,
viewModel = settingsViewModel viewModel = settingsViewModel
) )
/*AppDestinations.LOGOUT -> {
authViewModel.logout()
}*/
} }
} }
} }

View File

@@ -41,9 +41,6 @@ class BiometricAuthenticator(private val activity: FragmentActivity) {
onSuccess() onSuccess()
} }
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
}
}) })
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) { when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) {

View File

@@ -42,11 +42,14 @@ fun RegistrationForm(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
uiState: AuthUiState, uiState: AuthUiState,
onRegister: (String, String, String) -> Unit, onRegister: (String, String, String) -> Unit,
onTogleForm: (FormType) -> Unit onToggleForm: (FormType) -> Unit
) { ) {
var name by rememberSaveable { mutableStateOf("") } var name by rememberSaveable { mutableStateOf("") }
var email by rememberSaveable { mutableStateOf("") } var email by rememberSaveable { mutableStateOf("") }
var password by rememberSaveable { mutableStateOf("") } var password by rememberSaveable { mutableStateOf("") }
var cPassword by rememberSaveable { mutableStateOf("") }
var error: String? by rememberSaveable { mutableStateOf(null) }
Scaffold(modifier = modifier.fillMaxSize()) { innerPadding -> Scaffold(modifier = modifier.fillMaxSize()) { innerPadding ->
Column( Column(
@@ -103,6 +106,31 @@ fun RegistrationForm(
singleLine = true, singleLine = true,
isError = uiState.error != null isError = uiState.error != null
) )
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = cPassword,
onValueChange = {
run {
cPassword = it
error = if (it != password) {
"Passwords do not match"
} else {
null
}
}
},
label = { Text(stringResource(R.string.confirm_password)) },
modifier = Modifier.fillMaxWidth(),
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
singleLine = true,
isError = error != null,
supportingText = {
error?.let {
Text(text = it, color = MaterialTheme.colorScheme.error)
}
}
)
uiState.error?.let { uiState.error?.let {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
@@ -112,7 +140,12 @@ fun RegistrationForm(
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
Button( Button(
onClick = { onRegister(name,email, password) }, onClick = {
if (password == cPassword) {
onRegister(name, email, password)
error = null
}
},
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
enabled = !uiState.isLoading enabled = !uiState.isLoading
) { ) {
@@ -128,7 +161,7 @@ fun RegistrationForm(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Text("Have an account?") Text("Have an account?")
TextButton(onClick = { onTogleForm(FormType.LOGIN) }) { TextButton(onClick = { onToggleForm(FormType.LOGIN) }) {
Text("Sign in", color = Color.Blue) Text("Sign in", color = Color.Blue)
} }
} }
@@ -144,7 +177,7 @@ fun RegisterPreview(){
RegistrationForm( RegistrationForm(
uiState = AuthUiState(), uiState = AuthUiState(),
onRegister = { _, _, _ -> }, onRegister = { _, _, _ -> },
onTogleForm = { _ ->} onToggleForm = { _ ->}
) )
} }
} }

View File

@@ -0,0 +1,67 @@
package net.tinsae.clocked.ui.screens.editor
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import net.tinsae.clocked.data.Log
import net.tinsae.clocked.ui.components.ListItem
@Composable
private fun EditorScreen(modifier: Modifier = Modifier){
val viewModel = EditorViewModel()
Surface(modifier = modifier.padding(16.dp).fillMaxSize()) {
FilterLogs(modifier = modifier, viewModel = viewModel)
ShowLogs(modifier = modifier, logs = listOf())
}
}
@Composable
private fun FilterLogs(modifier: Modifier = Modifier, viewModel: EditorViewModel){
Column(modifier = modifier) {
Text(modifier = Modifier.fillMaxWidth().padding(16.dp), text = "Filter")
Row(modifier = Modifier.fillMaxWidth()) {
Text(modifier = Modifier.weight(1f).padding(16.dp), text = "By date")
Text(modifier = Modifier.weight(1f).padding(16.dp), text = "from")
Text(modifier = Modifier.weight(1f).padding(16.dp), text = "to")
}
}
}
@Composable
private fun ShowLogs(logs: List<Log>, modifier: Modifier = Modifier){
LazyColumn {
items(logs, key = { it.id }) { log ->
ListItem(
modifier = Modifier.padding(horizontal = 16.dp),
log = log,
onEdit = { },
onDelete = { },
// When the item is clicked, set it as the selected log for the dialog
onClick = { }
)
}
}
}
@Preview
@Composable
private fun EditorPreview () {
EditorScreen();
}

View File

@@ -0,0 +1,4 @@
package net.tinsae.clocked.ui.screens.editor
class EditorViewModel {
}

View File

@@ -74,5 +74,6 @@
<string name="add_entry">Add a new entry</string> <string name="add_entry">Add a new entry</string>
<string name="logout">Log out</string> <string name="logout">Log out</string>
<string name="loading">loading</string> <string name="loading">loading</string>
<string name="confirm_password">Confirm password</string>
</resources> </resources>