progress to exporting filtered content
This commit is contained in:
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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 = { _ ->}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package net.tinsae.clocked.ui.screens.editor
|
||||||
|
|
||||||
|
class EditorViewModel {
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user