diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index a293cf5..a86d691 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -107,6 +107,8 @@ dependencies { implementation("com.github.PhilJay:MPAndroidChart:v3.1.0") implementation ("androidx.glance:glance-appwidget:1.0.0") implementation ("androidx.glance:glance-material3:1.0.0") + implementation ("androidx.lifecycle:lifecycle-livedata-ktx:2.8.6") + } kapt{ diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/MainActivity.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/MainActivity.kt index 28ac87d..5647e38 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/MainActivity.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/MainActivity.kt @@ -10,8 +10,10 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.annotation.RequiresApi +import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.collectAsState +import androidx.core.os.LocaleListCompat import androidx.navigation.compose.rememberNavController import dagger.hilt.android.AndroidEntryPoint import rocks.poopjournal.fucksgiven.presentation.navigation.NavGraph @@ -30,16 +32,6 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { - if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - val localeManager = applicationContext - .getSystemService(LocaleManager::class.java) - localeManager.overrideLocaleConfig = LocaleConfig( - LocaleList.forLanguageTags("en-US,de,ur,fr") - ) - - val overrideLocaleConfig = localeManager.overrideLocaleConfig - val supportedLocales = overrideLocaleConfig?.supportedLocales - } val theme = themeSetting.themeFlow.collectAsState() val useDarkColors = when (theme.value) { AppTheme.LIGHT -> false diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/data/FuckDao.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/data/FuckDao.kt index 3c32c3f..9d4a400 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/data/FuckDao.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/data/FuckDao.kt @@ -2,9 +2,11 @@ package rocks.poopjournal.fucksgiven.data import androidx.lifecycle.LiveData import androidx.room.Dao +import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Update import kotlinx.coroutines.flow.Flow import rocks.poopjournal.fucksgiven.presentation.ui.utils.THETABLE_TABLENAME @@ -19,6 +21,12 @@ interface FuckDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(data: FuckData) + @Update + suspend fun update(data: FuckData) + + @Delete + suspend fun delete(data: FuckData) + @Query("SELECT * FROM $THETABLE_TABLENAME WHERE date BETWEEN :startDate AND :endDate") fun getDataBetweenDates(startDate: Long, endDate: Long): LiveData> diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/data/FuckRepository.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/data/FuckRepository.kt index 84d0d27..75ad06a 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/data/FuckRepository.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/data/FuckRepository.kt @@ -2,6 +2,7 @@ package rocks.poopjournal.fucksgiven.data import android.util.Log import androidx.lifecycle.LiveData +import androidx.lifecycle.map import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.conflate @@ -15,6 +16,8 @@ class FuckRepository @Inject constructor( fun getAllFucks() : Flow> = fuckDao.getAllData().flowOn(Dispatchers.IO).conflate() fun getFuck(id: Int) : Flow = fuckDao.getData(id).flowOn(Dispatchers.IO).conflate() suspend fun insertFuck(fuckData: FuckData) = fuckDao.insert(fuckData) + suspend fun updateFuck(fuckData: FuckData) = fuckDao.update(fuckData) + suspend fun deleteFuck(fuckData: FuckData) = fuckDao.delete(fuckData) fun getWeeklyData(): LiveData> { val calendar = Calendar.getInstance().apply { @@ -29,7 +32,9 @@ class FuckRepository @Inject constructor( calendar.add(Calendar.DAY_OF_WEEK, 7) val endOfWeek = calendar.timeInMillis - return fuckDao.getDataBetweenDates(startOfWeek, endOfWeek) + return fuckDao.getDataBetweenDates(startOfWeek, endOfWeek).map { + it ?: emptyList() // Convert null to empty list + } } fun getMonthlyData(): LiveData> { diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/component/Chart.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/component/Chart.kt index 6d796a8..f93b9a2 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/component/Chart.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/component/Chart.kt @@ -1,24 +1,17 @@ package rocks.poopjournal.fucksgiven.presentation.component -import android.content.res.Configuration -import android.graphics.Typeface -import android.view.View import android.view.ViewGroup -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.core.content.res.ResourcesCompat -import androidx.core.text.layoutDirection import com.github.mikephil.charting.charts.LineChart import com.github.mikephil.charting.components.XAxis import com.github.mikephil.charting.data.Entry @@ -30,7 +23,6 @@ import rocks.poopjournal.fucksgiven.R import rocks.poopjournal.fucksgiven.presentation.ui.theme.FuckGreen import rocks.poopjournal.fucksgiven.presentation.ui.utils.AppTheme import rocks.poopjournal.fucksgiven.presentation.ui.utils.ThemeSetting -import java.util.Locale @Composable fun LineChartComposable( @@ -171,8 +163,8 @@ fun LineChartComposable( chart.axisRight.isGranularityEnabled = true chart.notifyDataSetChanged() chart.fitScreen() - chart.viewPortHandler.setMaximumScaleX(2f); - chart.viewPortHandler.setMaximumScaleY(2f); + chart.viewPortHandler.setMaximumScaleX(2f) + chart.viewPortHandler.setMaximumScaleY(2f) chart.isDoubleTapToZoomEnabled = false chart.invalidate() // Refresh the chart } diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/component/Dialogs.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/component/Dialogs.kt new file mode 100644 index 0000000..55b9902 --- /dev/null +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/component/Dialogs.kt @@ -0,0 +1,349 @@ +package rocks.poopjournal.fucksgiven.presentation.component + +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DateRange +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDefaults +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberDatePickerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import rocks.poopjournal.fucksgiven.R +import rocks.poopjournal.fucksgiven.data.FuckData +import rocks.poopjournal.fucksgiven.presentation.ui.theme.FuckRed +import rocks.poopjournal.fucksgiven.presentation.ui.utils.getFormattedDate + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AddDialog( + onDismiss: () -> Unit, + onAdd: (FuckData) -> Unit, +) { + var description by remember { mutableStateOf("") } + var dateDialogOpen by remember { + mutableStateOf(false) + } + var date by remember { + mutableLongStateOf(0) + } + + val datePickerState = + rememberDatePickerState(initialSelectedDateMillis = System.currentTimeMillis()) + val selectedDate = if (date == 0L) System.currentTimeMillis() else date + + AlertDialog(onDismissRequest = onDismiss, confirmButton = { + Button( + onClick = { + onAdd( + FuckData(description = description, date = selectedDate) + ) + onDismiss() + }, colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.background + ) + ) { + Text(text = stringResource(id = R.string.add)) + } + }, dismissButton = { + TextButton( + onClick = onDismiss, colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.primary + ) + ) { + Text(text = stringResource(id = R.string.cancel)) + } + }, containerColor = MaterialTheme.colorScheme.background, text = { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(10.dp) + ) { + OutlinedTextField( + value = description, onValueChange = { + description = it + }, colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.primary + ), + placeholder = { + Text( + text = stringResource(id = R.string.description), + style = MaterialTheme.typography.bodyLarge + ) + } + ) + Spacer(modifier = Modifier.height(10.dp)) + + Row( + modifier = Modifier + .fillMaxWidth() + .border( + 1.dp, + shape = RoundedCornerShape(5.dp), + color = MaterialTheme.colorScheme.primary + ) + .padding(5.dp) + .clickable { dateDialogOpen = true } + .height(50.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = if (date == 0L) getFormattedDate(selectedDate) else getFormattedDate( + date + ), + modifier = Modifier.padding(start = 8.dp), + style = MaterialTheme.typography.bodyLarge + ) + Icon( + imageVector = Icons.Filled.DateRange, contentDescription = stringResource( + id = R.string.select_date + ) + ) + } + + + if (dateDialogOpen) { + DatePickerDialog(onDismissRequest = { dateDialogOpen = false }, + confirmButton = { + TextButton( + onClick = { + date = datePickerState.selectedDateMillis ?: 0L + dateDialogOpen = false + }) { + Text(text = stringResource(id = R.string.ok)) + } + }, + dismissButton = { + TextButton( + onClick = { dateDialogOpen = false }) { + Text(text = stringResource(id = R.string.cancel)) + } + } + ) { + DatePicker( + state = datePickerState, colors = DatePickerDefaults.colors( + containerColor = MaterialTheme.colorScheme.background, + headlineContentColor = MaterialTheme.colorScheme.primary, + dayContentColor = MaterialTheme.colorScheme.primary, + yearContentColor = MaterialTheme.colorScheme.primary, + todayContentColor = MaterialTheme.colorScheme.primary, + selectedDayContainerColor = MaterialTheme.colorScheme.primary, + ) + ) + } + } + } + }) +} + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun UpdateDialog( + fuckData: FuckData, + onDismiss: () -> Unit, + onUpdate: (FuckData) -> Unit, +) { + var description by remember { mutableStateOf(fuckData.description) } + var dateDialogOpen by remember { mutableStateOf(false) } + var date by remember { mutableLongStateOf(fuckData.date) } + + val datePickerState = rememberDatePickerState(initialSelectedDateMillis = fuckData.date) + + AlertDialog( + onDismissRequest = onDismiss, + confirmButton = { + Button( + onClick = { + onUpdate(FuckData(description = description, date = date, id = fuckData.id)) + onDismiss() + }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.background + ) + ) { + Text(text = stringResource(id = R.string.update)) + } + }, + dismissButton = { + TextButton( + onClick = onDismiss, + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.primary + ) + ) { + Text(text = stringResource(id = R.string.cancel)) + } + }, + containerColor = MaterialTheme.colorScheme.background, + text = { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(10.dp) + ) { + OutlinedTextField( + value = description, + onValueChange = { description = it }, + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.primary + ), + placeholder = { + Text( + text = stringResource(id = R.string.description), + style = MaterialTheme.typography.bodyLarge + ) + } + ) + Spacer(modifier = Modifier.height(10.dp)) + + Row( + modifier = Modifier + .fillMaxWidth() + .border( + 1.dp, + shape = RoundedCornerShape(5.dp), + color = MaterialTheme.colorScheme.primary + ) + .padding(5.dp) + .clickable { dateDialogOpen = true } + .height(50.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = if (date == 0L) stringResource(id = R.string.select_date) else getFormattedDate( + date + ), + modifier = Modifier.padding(start = 8.dp), + style = MaterialTheme.typography.bodyLarge + ) + Icon( + imageVector = Icons.Filled.DateRange, + contentDescription = stringResource(id = R.string.select_date) + ) + } + + if (dateDialogOpen) { + DatePickerDialog(onDismissRequest = { dateDialogOpen = false }, + confirmButton = { + TextButton( + onClick = { + date = datePickerState.selectedDateMillis ?: 0L + dateDialogOpen = false + }) { + Text(text = stringResource(id = R.string.ok)) + } + }, + dismissButton = { + TextButton( + onClick = { dateDialogOpen = false }) { + Text(text = stringResource(id = R.string.cancel)) + } + } + ) { + DatePicker( + state = datePickerState, + colors = DatePickerDefaults.colors( + containerColor = MaterialTheme.colorScheme.background, + headlineContentColor = MaterialTheme.colorScheme.primary, + dayContentColor = MaterialTheme.colorScheme.primary, + yearContentColor = MaterialTheme.colorScheme.primary, + todayContentColor = MaterialTheme.colorScheme.primary, + selectedDayContainerColor = MaterialTheme.colorScheme.primary, + ) + ) + } + } + } + } + ) +} + +@Composable +fun DeleteDialog( + fuckData: FuckData, + onDismiss: () -> Unit, + onDelete: (FuckData) -> Unit +) { + val description by remember { mutableStateOf(fuckData.description) } + val date by remember { mutableLongStateOf(fuckData.date) } + AlertDialog( + onDismissRequest = onDismiss, confirmButton = { + Button( + onClick = { + onDelete(FuckData(description = description, date = date, id = fuckData.id)) + onDismiss() + }, + colors = ButtonDefaults.buttonColors( + containerColor = FuckRed, + contentColor = MaterialTheme.colorScheme.background + ) + ) { + Text(text = stringResource(id = R.string.delete)) + } + }, + dismissButton = { + TextButton( + onClick = onDismiss, + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.primary + ) + ) { + Text(text = stringResource(id = R.string.cancel)) + } + }, + containerColor = MaterialTheme.colorScheme.background, + text = { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = stringResource(id = R.string.delete_dialog)) + Spacer(modifier = Modifier.height(8.dp)) + Row { + Text(text = "Title:", fontWeight = FontWeight.Bold) + Spacer(modifier = Modifier.width(5.dp)) + Text(text = fuckData.description, fontWeight = FontWeight.Bold) + } + } + } + ) +} \ No newline at end of file diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/screens/HomeScreen.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/screens/HomeScreen.kt index 7e43121..f5f9e06 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/screens/HomeScreen.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/screens/HomeScreen.kt @@ -3,13 +3,12 @@ package rocks.poopjournal.fucksgiven.presentation.screens import android.Manifest import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -17,32 +16,18 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.DateRange -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.DatePicker -import androidx.compose.material3.DatePickerDefaults -import androidx.compose.material3.DatePickerDialog import androidx.compose.material3.Divider -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -59,13 +44,15 @@ import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.shouldShowRationale import rocks.poopjournal.fucksgiven.R import rocks.poopjournal.fucksgiven.data.FuckData +import rocks.poopjournal.fucksgiven.presentation.component.AddDialog import rocks.poopjournal.fucksgiven.presentation.component.AppBar import rocks.poopjournal.fucksgiven.presentation.component.BottomBar import rocks.poopjournal.fucksgiven.presentation.component.BottomNavBar +import rocks.poopjournal.fucksgiven.presentation.component.DeleteDialog +import rocks.poopjournal.fucksgiven.presentation.component.UpdateDialog +import rocks.poopjournal.fucksgiven.presentation.ui.utils.getFormattedDate +import rocks.poopjournal.fucksgiven.presentation.ui.utils.isToday import rocks.poopjournal.fucksgiven.presentation.viewmodel.HomeViewModel -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale @OptIn(ExperimentalPermissionsApi::class) @Composable @@ -137,7 +124,12 @@ fun HomeScreen( } } if (uiState.fuckList.isNotEmpty()) { - FucksList(fuckList = uiState.fuckList) + FucksList(fuckList = uiState.fuckList, onUpdate = { data -> + viewModel.updateFuck(data, context) + }, + onDelete = { data -> + viewModel.deleteFuck(data) + }) } else { Text( text = stringResource(id = R.string.no_fucks), @@ -148,11 +140,19 @@ fun HomeScreen( } } +@OptIn(ExperimentalFoundationApi::class) @Composable fun FucksList( - fuckList: List + fuckList: List, + onUpdate: (FuckData) -> Unit, + onDelete: (FuckData) -> Unit ) { - val sortedFucks = fuckList.sortedWith(compareBy({ !isToday(getFormattedDate(it.date)) }, { -it.date })) + var updateTaskDialogOpen by remember { mutableStateOf(false) } + var deleteTaskDialogOpen by remember { mutableStateOf(false) } + var selectedFuck by remember { mutableStateOf(null) } + var longSelectedFuck by remember { mutableStateOf(null) } + val sortedFucks = + fuckList.sortedWith(compareBy({ !isToday(getFormattedDate(it.date)) }, { -it.date })) // Group the sorted list by date val groupedFucks = sortedFucks.groupBy { getFormattedDate(it.date) } @@ -191,7 +191,17 @@ fun FucksList( Row( modifier = Modifier .fillMaxWidth() - .height(50.dp), + .height(50.dp) + .combinedClickable( + onClick = { + selectedFuck = fuck + updateTaskDialogOpen = true + }, + onLongClick = { + longSelectedFuck = fuck + deleteTaskDialogOpen = true + } + ), verticalAlignment = Alignment.CenterVertically ) { Text( @@ -209,141 +219,38 @@ fun FucksList( } } } -} - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun AddDialog( - onDismiss: () -> Unit, - onAdd: (FuckData) -> Unit, -) { - var description by remember { mutableStateOf("") } - var dateDialogOpen by remember { - mutableStateOf(false) + selectedFuck?.let { fuckData -> + if (updateTaskDialogOpen) { + UpdateDialog( + fuckData = fuckData, + onDismiss = { updateTaskDialogOpen = false }, + onUpdate = { updatedFuck -> + onUpdate(updatedFuck) + updateTaskDialogOpen = false + } + ) + } } - var date by remember { - mutableLongStateOf(0) + + longSelectedFuck?.let { fuckData -> + if (deleteTaskDialogOpen) { + DeleteDialog( + fuckData = fuckData, + onDismiss = { deleteTaskDialogOpen = false }, + onDelete = { deleteFuck -> + onDelete(deleteFuck) + deleteTaskDialogOpen = false + }) + } } +} + - val datePickerState = rememberDatePickerState() - AlertDialog(onDismissRequest = onDismiss, confirmButton = { - Button( - onClick = { - onAdd( - FuckData(description = description, date = date) - ) - onDismiss() - }, colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.primary, - contentColor = MaterialTheme.colorScheme.background - ) - ) { - Text(text = stringResource(id = R.string.add)) - } - }, dismissButton = { - TextButton( - onClick = onDismiss, colors = ButtonDefaults.textButtonColors( - contentColor = MaterialTheme.colorScheme.primary - ) - ) { - Text(text = stringResource(id = R.string.cancel)) - } - }, containerColor = MaterialTheme.colorScheme.background, text = { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(10.dp) - ) { - OutlinedTextField( - value = description, onValueChange = { - description = it - }, colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.primary, - unfocusedBorderColor = MaterialTheme.colorScheme.primary - ), - placeholder = { - Text( - text = stringResource(id = R.string.description), - style = MaterialTheme.typography.bodyLarge - ) - } - ) - Spacer(modifier = Modifier.height(10.dp)) - Row( - modifier = Modifier - .fillMaxWidth() - .border( - 1.dp, - shape = RoundedCornerShape(5.dp), - color = MaterialTheme.colorScheme.primary - ) - .padding(5.dp) - .clickable { dateDialogOpen = true } - .height(50.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = if (date == 0L) stringResource(id = R.string.select_date) else getFormattedDate( - date - ), - modifier = Modifier.padding(start = 8.dp), - style = MaterialTheme.typography.bodyLarge - ) - Icon( - imageVector = Icons.Filled.DateRange, contentDescription = stringResource( - id = R.string.select_date - ) - ) - } - if (dateDialogOpen) { - DatePickerDialog(onDismissRequest = { dateDialogOpen = false }, - confirmButton = { - TextButton( - onClick = { - date = datePickerState.selectedDateMillis ?: 0L - dateDialogOpen = false - }) { - Text(text = stringResource(id = R.string.ok)) - } - }, - dismissButton = { - TextButton( - onClick = { dateDialogOpen = false }) { - Text(text = stringResource(id = R.string.cancel)) - } - } - ) { - DatePicker( - state = datePickerState, colors = DatePickerDefaults.colors( - containerColor = MaterialTheme.colorScheme.background, - headlineContentColor = MaterialTheme.colorScheme.primary, - dayContentColor = MaterialTheme.colorScheme.primary, - yearContentColor = MaterialTheme.colorScheme.primary, - todayContentColor = MaterialTheme.colorScheme.primary, - selectedDayContainerColor = MaterialTheme.colorScheme.primary, - ) - ) - } - } - } - }) -} -fun getFormattedDate(timestamp: Long): String { - val date = Date(timestamp) - val sdf = SimpleDateFormat("MMMM dd", Locale.getDefault()) - return sdf.format(date) -} -fun isToday(dateString: String): Boolean { - val todayString = getFormattedDate(System.currentTimeMillis()) - return dateString == todayString -} diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/ui/theme/Color.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/ui/theme/Color.kt index f879807..0ac52ca 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/ui/theme/Color.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/ui/theme/Color.kt @@ -14,4 +14,5 @@ val FuckGreen = Color(0xFF29A331) val FuckSecondary = Color(0xFFEEF8EF) val WatchGrey = Color(0xFFC8C8D0) val FuckSecondaryDark = Color(0xFF50CE58).copy(alpha = 0.4f) -val FuckGreenDark = Color(0xFF50CE58) \ No newline at end of file +val FuckGreenDark = Color(0xFF50CE58) +val FuckRed = Color(0xFFD22B2B) \ No newline at end of file diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/ui/utils/ext.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/ui/utils/ext.kt new file mode 100644 index 0000000..b9b32f2 --- /dev/null +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/ui/utils/ext.kt @@ -0,0 +1,16 @@ +package rocks.poopjournal.fucksgiven.presentation.ui.utils + +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +fun getFormattedDate(timestamp: Long): String { + val date = Date(timestamp) + val sdf = SimpleDateFormat("MMMM dd", Locale.getDefault()) + return sdf.format(date) +} + +fun isToday(dateString: String): Boolean { + val todayString = getFormattedDate(System.currentTimeMillis()) + return dateString == todayString +} \ No newline at end of file diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/viewmodel/HomeViewModel.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/viewmodel/HomeViewModel.kt index 01684ac..44c75d4 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/viewmodel/HomeViewModel.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/viewmodel/HomeViewModel.kt @@ -53,9 +53,9 @@ class HomeViewModel @Inject constructor( } } - fun addFuck(fuck: FuckData,context: Context) { - if(fuck.description.isEmpty() && fuck.date == 0L){ - Toast.makeText(context,"Fill all information",Toast.LENGTH_SHORT).show() + fun addFuck(fuck: FuckData, context: Context) { + if (fuck.description.isEmpty() && fuck.date == 0L) { + Toast.makeText(context, "Fill all information", Toast.LENGTH_SHORT).show() return } viewModelScope.launch { @@ -63,5 +63,17 @@ class HomeViewModel @Inject constructor( } } + fun updateFuck(fuck: FuckData, context: Context) { + viewModelScope.launch { + fuckRepository.updateFuck(fuck) + } + } + + fun deleteFuck(fuck: FuckData) { + viewModelScope.launch { + fuckRepository.deleteFuck(fuck) + } + } + } \ No newline at end of file diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/viewmodel/StatsViewModel.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/viewmodel/StatsViewModel.kt index c2c7621..1e546d6 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/viewmodel/StatsViewModel.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/viewmodel/StatsViewModel.kt @@ -4,6 +4,7 @@ import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.map import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -17,8 +18,16 @@ import javax.inject.Inject class StatsViewModel @Inject constructor(private val repository: FuckRepository) : ViewModel() { - val weeklyData: LiveData> = repository.getWeeklyData() - val monthlyData: LiveData> = repository.getMonthlyData() - val yearlyData: LiveData> = repository.getYearlyData() + val weeklyData: LiveData> = repository.getWeeklyData().map { + it ?: emptyList() // Handle null by returning an empty list + } + + val monthlyData: LiveData> = repository.getMonthlyData().map { + it ?: emptyList() // Handle null by returning an empty list + } + + val yearlyData: LiveData> = repository.getYearlyData().map { + it ?: emptyList() // Handle null by returning an empty list + } } \ No newline at end of file diff --git a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/widget/TaskWidgetProvider.kt b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/widget/TaskWidgetProvider.kt index a43bd1a..315dc43 100644 --- a/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/widget/TaskWidgetProvider.kt +++ b/android/app/src/main/java/rocks/poopjournal/fucksgiven/presentation/widget/TaskWidgetProvider.kt @@ -36,8 +36,8 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import rocks.poopjournal.fucksgiven.MainActivity import rocks.poopjournal.fucksgiven.R -import rocks.poopjournal.fucksgiven.presentation.screens.getFormattedDate -import rocks.poopjournal.fucksgiven.presentation.screens.isToday +import rocks.poopjournal.fucksgiven.presentation.ui.utils.getFormattedDate +import rocks.poopjournal.fucksgiven.presentation.ui.utils.isToday class MyAppWidget : GlanceAppWidget() { override val sizeMode: SizeMode diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 44ae703..0f5d397 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -87,6 +87,9 @@ Report a problem View Source Open-source Licenses + Update + Delete + Are you Sure you want to delete? Feather Icons MIT License Android Jetpack