mirror of https://github.com/tasks/tasks
RepeatRow is @Composable
parent
877a2cd6a5
commit
6fd987a055
@ -0,0 +1,263 @@
|
||||
package org.tasks.repeats
|
||||
|
||||
/* This is mostly a copy of the BasicRecurrenceDialog with UI made @Composable */
|
||||
|
||||
import android.content.Context
|
||||
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.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.common.collect.Lists
|
||||
import net.fortuna.ical4j.model.Recur
|
||||
import org.tasks.R
|
||||
import org.tasks.analytics.Firebase
|
||||
import org.tasks.compose.Constants.TextButton
|
||||
import org.tasks.data.entity.Task
|
||||
import org.tasks.preferences.Preferences
|
||||
import org.tasks.repeats.RecurrenceUtils.newRecur
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun BasicRecurrencePicker (
|
||||
dismiss: () -> Unit,
|
||||
recurrence: String?,
|
||||
setRecurrence: (String?) -> Unit,
|
||||
peekCustomRecurrence: () -> Unit,
|
||||
repeatFrom: @Task.RepeatFrom Int = Task.RepeatFrom.COMPLETION_DATE,
|
||||
onRepeatFromChanged: ((@Task.RepeatFrom Int) -> Unit)? = null,
|
||||
) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val helper = remember { RecurrenceHelper(context) }
|
||||
helper.setRecurrence(recurrence)
|
||||
|
||||
fun setSelection(index: Int) {
|
||||
when (index) {
|
||||
0 -> setRecurrence(null)
|
||||
5 -> {
|
||||
peekCustomRecurrence()
|
||||
return // to avoid dismiss() call
|
||||
}
|
||||
6 -> Unit
|
||||
else -> {
|
||||
setRecurrence(
|
||||
newRecur().apply {
|
||||
interval = 1
|
||||
setFrequency(helper.selectedFrequency(index).name)
|
||||
}.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = dismiss,
|
||||
) {
|
||||
Card {
|
||||
Column (modifier = Modifier.padding(16.dp)) {
|
||||
onRepeatFromChanged?.let {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Row (modifier = Modifier.padding(start = 12.dp, top = 12.dp, bottom = 12.dp)){
|
||||
Text(
|
||||
text = stringResource(id = R.string.repeats_from),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = if (repeatFrom == Task.RepeatFrom.COMPLETION_DATE)
|
||||
R.string.repeat_type_completion
|
||||
else
|
||||
R.string.repeat_type_due
|
||||
),
|
||||
style = MaterialTheme.typography.bodyLarge.copy(
|
||||
textDecoration = TextDecoration.Underline,
|
||||
),
|
||||
modifier = Modifier.clickable { expanded = true },
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
expanded = false
|
||||
onRepeatFromChanged(Task.RepeatFrom.DUE_DATE)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.repeat_type_due),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
expanded = false
|
||||
onRepeatFromChanged(Task.RepeatFrom.COMPLETION_DATE)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.repeat_type_completion),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
if (helper.isCustomValue()) {
|
||||
SelectableText(
|
||||
text = helper.repeatRuleToString.toString(recurrence)!!,
|
||||
index = 6,
|
||||
selected = 6,
|
||||
setSelection = { setSelection(6) }
|
||||
)
|
||||
}
|
||||
for (i in 0..5) {
|
||||
SelectableText(
|
||||
text = helper.title(i),
|
||||
index = i,
|
||||
selected = helper.selectionIndex(),
|
||||
setSelection = { setSelection(i) }
|
||||
)
|
||||
}
|
||||
Row (
|
||||
modifier = Modifier.padding(bottom = 12.dp).fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
TextButton(R.string.ok, dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SelectableText (
|
||||
text: String,
|
||||
index: Int,
|
||||
selected: Int,
|
||||
setSelection: (Int) -> Unit
|
||||
) {
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.clickable{ setSelection(index) }
|
||||
) {
|
||||
RadioButton(
|
||||
selected = index == selected,
|
||||
onClick = { setSelection(index) }
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(text = text, textDecoration = TextDecoration.Underline)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper object over recurrence string, to share basic access to rrule and to avoid reloading
|
||||
* R.array.repeat_options during each recomposition
|
||||
* Intended use:
|
||||
*
|
||||
* val helper = remember { RecurrenceHelper(context) }
|
||||
* helper.setRecurrence(recurrence)
|
||||
* ...
|
||||
* Text(text = helper.title(selectedIndex), ...)
|
||||
*/
|
||||
class RecurrenceHelper (
|
||||
context: Context,
|
||||
) {
|
||||
val repeatRuleToString = RepeatRuleToString(context,Locale.getDefault(),Firebase(context, Preferences(context)))
|
||||
private var _recurrence: String? = null
|
||||
val recurrence: String? get() = _recurrence
|
||||
|
||||
private var _rrule: Recur? = null
|
||||
val rrule: Recur? get() = _rrule
|
||||
|
||||
private val titles: MutableList<String> =
|
||||
Lists.newArrayList(*context.resources.getStringArray(R.array.repeat_options))
|
||||
private val ruleTitle = if (isCustomValue()) repeatRuleToString.toString(recurrence)!! else titles[5]
|
||||
|
||||
fun title(index: Int): String =
|
||||
if (index < 5) titles[index]
|
||||
else ruleTitle
|
||||
|
||||
fun isCustomValue(): Boolean {
|
||||
if (rrule == null) {
|
||||
return false
|
||||
}
|
||||
val frequency = rrule!!.frequency
|
||||
return (frequency == Recur.Frequency.WEEKLY || frequency == Recur.Frequency.MONTHLY) && !rrule!!.dayList.isEmpty()
|
||||
|| frequency == Recur.Frequency.HOURLY
|
||||
|| frequency == Recur.Frequency.MINUTELY
|
||||
|| rrule!!.until != null
|
||||
|| rrule!!.interval > 1
|
||||
|| rrule!!.count > 0
|
||||
}
|
||||
|
||||
fun selectionIndex(): Int =
|
||||
when {
|
||||
rrule == null -> 0
|
||||
isCustomValue() -> 6
|
||||
rrule!!.frequency == Recur.Frequency.DAILY -> 1
|
||||
rrule!!.frequency == Recur.Frequency.WEEKLY -> 2
|
||||
rrule!!.frequency == Recur.Frequency.MONTHLY -> 3
|
||||
rrule!!.frequency == Recur.Frequency.YEARLY -> 4
|
||||
else -> 0
|
||||
}
|
||||
|
||||
fun selectedFrequency(index: Int): Recur.Frequency =
|
||||
when (index) {
|
||||
1 -> Recur.Frequency.DAILY
|
||||
2 -> Recur.Frequency.WEEKLY
|
||||
3 -> Recur.Frequency.MONTHLY
|
||||
4 -> Recur.Frequency.YEARLY
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
fun setRecurrence(recurrence: String?) {
|
||||
_recurrence = recurrence
|
||||
_rrule = recurrence
|
||||
.takeIf { !it.isNullOrBlank() }
|
||||
?.let {
|
||||
try {
|
||||
newRecur(it)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberRepeatRuleToString(): RepeatRuleToString {
|
||||
val context = LocalContext.current
|
||||
return remember { RepeatRuleToString(context,Locale.getDefault(),Firebase(context, Preferences(context))) }
|
||||
}
|
||||
@ -0,0 +1,581 @@
|
||||
package org.tasks.repeats
|
||||
|
||||
/*
|
||||
* This file is a copy of the CustomRecurrence.kt
|
||||
* The function CustomRecurrence is renamed to CustomRecurrenceEdit to avoid name conflicts, and
|
||||
* CustomRecurrenceEditState is used instead of the CustomRecurrenceViewModel
|
||||
*/
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||
import androidx.compose.material3.DisplayMode
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.fortuna.ical4j.model.Recur
|
||||
import net.fortuna.ical4j.model.WeekDay
|
||||
import org.tasks.R
|
||||
import org.tasks.compose.OutlinedBox
|
||||
import org.tasks.compose.OutlinedNumberInput
|
||||
import org.tasks.compose.OutlinedSpinner
|
||||
import org.tasks.compose.border
|
||||
import org.tasks.compose.pickers.DatePickerDialog
|
||||
import org.tasks.kmp.org.tasks.time.getRelativeDay
|
||||
import org.tasks.themes.TasksTheme
|
||||
import java.time.DayOfWeek
|
||||
import java.time.format.TextStyle
|
||||
import java.util.Locale
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CustomRecurrenceEdit(
|
||||
state: CustomRecurrenceEditState.ViewState,
|
||||
save: () -> Unit,
|
||||
discard: () -> Unit,
|
||||
setInterval: (Int) -> Unit,
|
||||
setSelectedFrequency: (Recur.Frequency) -> Unit,
|
||||
setEndDate: (Long) -> Unit,
|
||||
setSelectedEndType: (Int) -> Unit,
|
||||
setOccurrences: (Int) -> Unit,
|
||||
toggleDay: (DayOfWeek) -> Unit,
|
||||
setMonthSelection: (Int) -> Unit,
|
||||
calendarDisplayMode: DisplayMode,
|
||||
setDisplayMode: (DisplayMode) -> Unit,
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = { discard() },
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = true,
|
||||
usePlatformDefaultWidth = false,
|
||||
decorFitsSystemWindows = false
|
||||
)
|
||||
) {
|
||||
BackHandler { save() }
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
titleContentColor = MaterialTheme.colorScheme.onSurface,
|
||||
navigationIconContentColor = MaterialTheme.colorScheme.onSurface,
|
||||
actionIconContentColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.repeats_custom_recurrence),
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = save) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.save),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
TextButton(onClick = discard) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.cancel),
|
||||
style = MaterialTheme.typography.bodyLarge.copy(
|
||||
fontFeatureSettings = "c2sc, smcp"
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding),
|
||||
) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Header(R.string.repeats_every)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
) {
|
||||
OutlinedNumberInput(
|
||||
number = state.interval,
|
||||
onTextChanged = setInterval,
|
||||
)
|
||||
val context = LocalContext.current
|
||||
val options by remember(state.interval, state.frequency) {
|
||||
derivedStateOf {
|
||||
state.frequencyOptions.map {
|
||||
context.resources.getQuantityString(
|
||||
it.plural,
|
||||
state.interval,
|
||||
state.interval,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
OutlinedSpinner(
|
||||
text = pluralStringResource(
|
||||
id = state.frequency.plural,
|
||||
count = state.interval
|
||||
),
|
||||
options = options,
|
||||
onSelected = { setSelectedFrequency(state.frequencyOptions[it]) },
|
||||
)
|
||||
}
|
||||
if (state.frequency == Recur.Frequency.WEEKLY) {
|
||||
WeekdayPicker(
|
||||
daysOfWeek = state.daysOfWeek,
|
||||
selected = state.selectedDays,
|
||||
toggle = toggleDay,
|
||||
)
|
||||
} else if (state.frequency == Recur.Frequency.MONTHLY && !state.isMicrosoftTask) {
|
||||
MonthlyPicker(
|
||||
monthDay = state.monthDay,
|
||||
dayNumber = state.dueDayOfMonth,
|
||||
dayOfWeek = state.dueDayOfWeek,
|
||||
nthWeek = state.nthWeek,
|
||||
isLastWeek = state.lastWeekDayOfMonth,
|
||||
locale = state.locale,
|
||||
onSelected = setMonthSelection,
|
||||
)
|
||||
}
|
||||
if (!state.isMicrosoftTask) {
|
||||
Divider(
|
||||
modifier = Modifier.padding(vertical = if (state.frequency == Recur.Frequency.WEEKLY) 11.dp else 16.dp),
|
||||
color = border()
|
||||
)
|
||||
EndsPicker(
|
||||
selection = state.endSelection,
|
||||
endDate = state.endDate,
|
||||
endOccurrences = state.endCount,
|
||||
setEndDate = setEndDate,
|
||||
setSelection = setSelectedEndType,
|
||||
setOccurrences = setOccurrences,
|
||||
calendarDisplayMode = calendarDisplayMode,
|
||||
setDisplayMode = setDisplayMode,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(resId: Int) {
|
||||
Text(
|
||||
text = stringResource(id = resId),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun WeekdayPicker(
|
||||
daysOfWeek: List<DayOfWeek>,
|
||||
selected: List<DayOfWeek>,
|
||||
toggle: (DayOfWeek) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val locale = remember {
|
||||
ConfigurationCompat
|
||||
.getLocales(context.resources.configuration)
|
||||
.get(0)
|
||||
?: Locale.getDefault()
|
||||
}
|
||||
Divider(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
color = border()
|
||||
)
|
||||
Header(R.string.repeats_weekly_on)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
) {
|
||||
daysOfWeek.forEach { dayOfWeek ->
|
||||
val string = remember(dayOfWeek) {
|
||||
dayOfWeek.getDisplayName(TextStyle.NARROW, locale)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 5.dp) // hack until compose 1.5
|
||||
.size(36.dp)
|
||||
.let {
|
||||
if (selected.contains(dayOfWeek)) {
|
||||
it.background(MaterialTheme.colorScheme.secondary, shape = CircleShape)
|
||||
} else {
|
||||
it.border(1.dp, border(), shape = CircleShape)
|
||||
}
|
||||
}
|
||||
.clickable { toggle(dayOfWeek) },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = string,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = if (selected.contains(dayOfWeek)) MaterialTheme.colorScheme.onSecondary else MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MonthlyPicker(
|
||||
monthDay: WeekDay?,
|
||||
dayNumber: Int,
|
||||
dayOfWeek: DayOfWeek,
|
||||
nthWeek: Int,
|
||||
isLastWeek: Boolean,
|
||||
locale: Locale,
|
||||
onSelected: (Int) -> Unit,
|
||||
) {
|
||||
val selection = remember(monthDay) {
|
||||
when (monthDay?.offset) {
|
||||
null -> 0
|
||||
-1 -> 2
|
||||
else -> 1
|
||||
}
|
||||
}
|
||||
Divider(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
color = border()
|
||||
)
|
||||
val context = LocalContext.current
|
||||
val options = remember(dayNumber, dayOfWeek, nthWeek, isLastWeek, locale) {
|
||||
ArrayList<String>().apply {
|
||||
add(context.getString(R.string.repeat_monthly_on_day_number, dayNumber))
|
||||
val nth = context.getString(
|
||||
when (nthWeek - 1) {
|
||||
0 -> R.string.repeat_monthly_first_week
|
||||
1 -> R.string.repeat_monthly_second_week
|
||||
2 -> R.string.repeat_monthly_third_week
|
||||
3 -> R.string.repeat_monthly_fourth_week
|
||||
4 -> R.string.repeat_monthly_fifth_week
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
)
|
||||
val dayOfWeekDisplayName = dayOfWeek.getDisplayName(TextStyle.FULL, locale)
|
||||
add(
|
||||
context.getString(
|
||||
R.string.repeat_monthly_on_the_nth_weekday,
|
||||
nth,
|
||||
dayOfWeekDisplayName
|
||||
)
|
||||
)
|
||||
if (isLastWeek) {
|
||||
add(
|
||||
context.getString(
|
||||
R.string.repeat_monthly_on_the_nth_weekday,
|
||||
context.getString(R.string.repeat_monthly_last_week),
|
||||
dayOfWeekDisplayName
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
) {
|
||||
OutlinedSpinner(
|
||||
text = options[selection],
|
||||
options = options,
|
||||
onSelected = onSelected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun EndsPicker(
|
||||
selection: Int,
|
||||
endDate: Long,
|
||||
endOccurrences: Int,
|
||||
calendarDisplayMode: DisplayMode,
|
||||
setDisplayMode: (DisplayMode) -> Unit,
|
||||
setOccurrences: (Int) -> Unit,
|
||||
setEndDate: (Long) -> Unit,
|
||||
setSelection: (Int) -> Unit,
|
||||
) {
|
||||
Header(R.string.repeats_ends)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
RadioRow(selected = selection == 0, onClick = { setSelection(0) }) {
|
||||
Text(text = stringResource(id = R.string.repeats_never))
|
||||
}
|
||||
Divider(
|
||||
modifier = Modifier.padding(start = 50.dp, end = 16.dp, top = 8.dp, bottom = 8.dp),
|
||||
color = border()
|
||||
)
|
||||
RadioRow(selected = selection == 1, onClick = { setSelection(1) }) {
|
||||
Text(text = stringResource(id = R.string.repeats_on))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
val context = LocalContext.current
|
||||
val endDateString by remember(context, endDate) {
|
||||
derivedStateOf {
|
||||
runBlocking {
|
||||
getRelativeDay(endDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
var showDatePicker by remember { mutableStateOf(false) }
|
||||
if (showDatePicker) {
|
||||
DatePickerDialog(
|
||||
initialDate = endDate,
|
||||
displayMode = calendarDisplayMode,
|
||||
setDisplayMode = setDisplayMode,
|
||||
selected = {
|
||||
setEndDate(it)
|
||||
showDatePicker = false
|
||||
},
|
||||
dismiss = { showDatePicker = false },
|
||||
)
|
||||
}
|
||||
OutlinedBox(
|
||||
modifier = Modifier.clickable {
|
||||
setSelection(1)
|
||||
showDatePicker = true
|
||||
}
|
||||
) {
|
||||
Text(text = endDateString)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
}
|
||||
}
|
||||
Divider(
|
||||
modifier = Modifier.padding(start = 50.dp, end = 16.dp, top = 8.dp, bottom = 8.dp),
|
||||
color = border()
|
||||
)
|
||||
RadioRow(selected = selection == 2, onClick = { setSelection(2) }) {
|
||||
Text(text = stringResource(id = R.string.repeats_after))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
OutlinedNumberInput(
|
||||
number = endOccurrences,
|
||||
onTextChanged = setOccurrences,
|
||||
onFocus = { setSelection(2) },
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = pluralStringResource(id = R.plurals.repeat_occurrence, endOccurrences))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RadioRow(
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onClick() },
|
||||
) {
|
||||
RadioButton(selected = selected, onClick = onClick)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val Recur.Frequency.plural: Int
|
||||
get() = when (this) {
|
||||
Recur.Frequency.MINUTELY -> R.plurals.repeat_minutes
|
||||
Recur.Frequency.HOURLY -> R.plurals.repeat_hours
|
||||
Recur.Frequency.DAILY -> R.plurals.repeat_days
|
||||
Recur.Frequency.WEEKLY -> R.plurals.repeat_weeks
|
||||
Recur.Frequency.MONTHLY -> R.plurals.repeat_months
|
||||
Recur.Frequency.YEARLY -> R.plurals.repeat_years
|
||||
else -> throw RuntimeException()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun WeeklyPreview() {
|
||||
TasksTheme {
|
||||
CustomRecurrenceEdit(
|
||||
state = CustomRecurrenceEditState.ViewState(frequency = Recur.Frequency.WEEKLY),
|
||||
save = {},
|
||||
discard = {},
|
||||
setSelectedFrequency = {},
|
||||
setSelectedEndType = {},
|
||||
setEndDate = {},
|
||||
setInterval = {},
|
||||
setOccurrences = {},
|
||||
toggleDay = {},
|
||||
setMonthSelection = {},
|
||||
calendarDisplayMode = DisplayMode.Picker,
|
||||
setDisplayMode = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun MonthlyPreview() {
|
||||
TasksTheme {
|
||||
CustomRecurrenceEdit (
|
||||
state = CustomRecurrenceEditState.ViewState(frequency = Recur.Frequency.MONTHLY),
|
||||
save = {},
|
||||
discard = {},
|
||||
setSelectedFrequency = {},
|
||||
setSelectedEndType = {},
|
||||
setEndDate = {},
|
||||
setInterval = {},
|
||||
setOccurrences = {},
|
||||
toggleDay = {},
|
||||
setMonthSelection = {},
|
||||
calendarDisplayMode = DisplayMode.Picker,
|
||||
setDisplayMode = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun MinutelyPreview() {
|
||||
TasksTheme {
|
||||
CustomRecurrenceEdit(
|
||||
state = CustomRecurrenceEditState.ViewState(frequency = Recur.Frequency.MINUTELY),
|
||||
save = {},
|
||||
discard = {},
|
||||
setSelectedFrequency = {},
|
||||
setSelectedEndType = {},
|
||||
setEndDate = {},
|
||||
setInterval = {},
|
||||
setOccurrences = {},
|
||||
toggleDay = {},
|
||||
setMonthSelection = {},
|
||||
calendarDisplayMode = DisplayMode.Picker,
|
||||
setDisplayMode = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun HourlyPreview() {
|
||||
TasksTheme {
|
||||
CustomRecurrenceEdit(
|
||||
state = CustomRecurrenceEditState.ViewState(frequency = Recur.Frequency.HOURLY),
|
||||
save = {},
|
||||
discard = {},
|
||||
setSelectedFrequency = {},
|
||||
setSelectedEndType = {},
|
||||
setEndDate = {},
|
||||
setInterval = {},
|
||||
setOccurrences = {},
|
||||
toggleDay = {},
|
||||
setMonthSelection = {},
|
||||
calendarDisplayMode = DisplayMode.Picker,
|
||||
setDisplayMode = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun DailyPreview() {
|
||||
TasksTheme {
|
||||
CustomRecurrenceEdit(
|
||||
state = CustomRecurrenceEditState.ViewState(frequency = Recur.Frequency.DAILY),
|
||||
save = {},
|
||||
discard = {},
|
||||
setSelectedFrequency = {},
|
||||
setSelectedEndType = {},
|
||||
setEndDate = {},
|
||||
setInterval = {},
|
||||
setOccurrences = {},
|
||||
toggleDay = {},
|
||||
setMonthSelection = {},
|
||||
calendarDisplayMode = DisplayMode.Picker,
|
||||
setDisplayMode = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun YearlyPreview() {
|
||||
TasksTheme {
|
||||
CustomRecurrenceEdit(
|
||||
state = CustomRecurrenceEditState.ViewState(frequency = Recur.Frequency.YEARLY),
|
||||
save = {},
|
||||
discard = {},
|
||||
setSelectedFrequency = {},
|
||||
setSelectedEndType = {},
|
||||
setEndDate = {},
|
||||
setInterval = {},
|
||||
setOccurrences = {},
|
||||
toggleDay = {},
|
||||
setMonthSelection = {},
|
||||
calendarDisplayMode = DisplayMode.Picker,
|
||||
setDisplayMode = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,271 @@
|
||||
package org.tasks.repeats
|
||||
|
||||
/*
|
||||
* This is essentially a copy of the CustomRecurrenceViewModel, changed to a saveable independent
|
||||
* class to fit to @Composable environment
|
||||
* */
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import net.fortuna.ical4j.model.Date
|
||||
import net.fortuna.ical4j.model.Recur
|
||||
import net.fortuna.ical4j.model.Recur.Frequency.DAILY
|
||||
import net.fortuna.ical4j.model.Recur.Frequency.HOURLY
|
||||
import net.fortuna.ical4j.model.Recur.Frequency.MINUTELY
|
||||
import net.fortuna.ical4j.model.Recur.Frequency.MONTHLY
|
||||
import net.fortuna.ical4j.model.Recur.Frequency.WEEKLY
|
||||
import net.fortuna.ical4j.model.Recur.Frequency.YEARLY
|
||||
import net.fortuna.ical4j.model.WeekDay
|
||||
import net.fortuna.ical4j.model.WeekDayList
|
||||
import net.fortuna.ical4j.model.property.RRule
|
||||
import org.tasks.data.entity.CaldavAccount.Companion.TYPE_MICROSOFT
|
||||
import org.tasks.date.DateTimeUtils.toDateTime
|
||||
import org.tasks.time.DateTime
|
||||
import org.tasks.time.DateTimeUtils2.currentTimeMillis
|
||||
import org.tasks.time.startOfDay
|
||||
import java.time.DayOfWeek
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.temporal.WeekFields
|
||||
import java.util.Calendar
|
||||
import java.util.Calendar.DAY_OF_WEEK_IN_MONTH
|
||||
import java.util.Locale
|
||||
|
||||
class CustomRecurrenceEditState(
|
||||
rrule: String?,
|
||||
dueDate: Long?,
|
||||
val accountType: Int,
|
||||
locale: Locale = Locale.getDefault()
|
||||
) {
|
||||
data class ViewState(
|
||||
val interval: Int = 1,
|
||||
val frequency: Recur.Frequency = WEEKLY,
|
||||
val dueDate: Long = currentTimeMillis().startOfDay(),
|
||||
val endSelection: Int = 0,
|
||||
val endDate: Long = dueDate.toDateTime().plusMonths(1).startOfDay().millis,
|
||||
val endCount: Int = 1,
|
||||
val frequencyOptions: List<Recur.Frequency> = FREQ_ALL,
|
||||
val daysOfWeek: List<DayOfWeek> = Locale.getDefault().daysOfWeek(),
|
||||
val selectedDays: List<DayOfWeek> = emptyList(),
|
||||
val locale: Locale = Locale.getDefault(),
|
||||
val monthDay: WeekDay? = null,
|
||||
val isMicrosoftTask: Boolean = false,
|
||||
) {
|
||||
val dueDayOfWeek: DayOfWeek
|
||||
get() = Instant.ofEpochMilli(dueDate).atZone(ZoneId.systemDefault()).dayOfWeek
|
||||
|
||||
val dueDayOfMonth: Int
|
||||
get() = DateTime(dueDate).dayOfMonth
|
||||
|
||||
val nthWeek: Int
|
||||
get() =
|
||||
Calendar.getInstance(locale)
|
||||
.apply { timeInMillis = dueDate }
|
||||
.get(DAY_OF_WEEK_IN_MONTH)
|
||||
|
||||
val lastWeekDayOfMonth: Boolean
|
||||
get() =
|
||||
Calendar.getInstance(locale)
|
||||
.apply { timeInMillis = dueDate }
|
||||
.let { it[DAY_OF_WEEK_IN_MONTH] == it.getActualMaximum(DAY_OF_WEEK_IN_MONTH) }
|
||||
}
|
||||
|
||||
private val _state = MutableStateFlow(ViewState())
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
init {
|
||||
val daysOfWeek = locale.daysOfWeek()
|
||||
val recur = rrule
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.let { RRule(it) }
|
||||
?.recur
|
||||
val dueDate = dueDate
|
||||
?.takeIf { it > 0 }
|
||||
?: currentTimeMillis().startOfDay()
|
||||
val isMicrosoftTask = accountType == TYPE_MICROSOFT
|
||||
val frequencies = if (isMicrosoftTask) FREQ_MICROSOFT else FREQ_ALL
|
||||
_state.update { state ->
|
||||
state.copy(
|
||||
interval = recur?.interval?.takeIf { it > 0 } ?: 1,
|
||||
frequency = recur?.frequency?.takeIf { frequencies.contains(it) } ?: WEEKLY,
|
||||
dueDate = dueDate,
|
||||
endSelection = when {
|
||||
isMicrosoftTask -> 0
|
||||
recur == null -> 0
|
||||
recur.until != null -> 1
|
||||
recur.count >= 0 -> 2
|
||||
else -> 0
|
||||
},
|
||||
endDate = DateTime(dueDate).plusMonths(1).startOfDay().millis,
|
||||
endCount = recur?.count?.takeIf { it >= 0 } ?: 1,
|
||||
daysOfWeek = daysOfWeek,
|
||||
selectedDays = recur
|
||||
?.dayList
|
||||
?.takeIf { recur.frequency == WEEKLY }
|
||||
?.toDaysOfWeek()
|
||||
?: emptyList(),
|
||||
locale = locale,
|
||||
monthDay = recur
|
||||
?.dayList
|
||||
?.takeIf { recur.frequency == MONTHLY && !isMicrosoftTask }
|
||||
?.firstOrNull(),
|
||||
isMicrosoftTask = isMicrosoftTask,
|
||||
frequencyOptions = frequencies,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setEndType(endType: Int) {
|
||||
_state.update {
|
||||
it.copy(endSelection = endType)
|
||||
}
|
||||
}
|
||||
|
||||
fun setFrequency(frequency: Recur.Frequency) {
|
||||
_state.update {
|
||||
it.copy(frequency = frequency)
|
||||
}
|
||||
}
|
||||
|
||||
fun setEndDate(endDate: Long) {
|
||||
_state.update {
|
||||
it.copy(endDate = endDate)
|
||||
}
|
||||
}
|
||||
|
||||
fun setInterval(interval: Int) {
|
||||
_state.update {
|
||||
it.copy(interval = interval)
|
||||
}
|
||||
}
|
||||
|
||||
fun setOccurrences(occurrences: Int) {
|
||||
_state.update {
|
||||
it.copy(endCount = occurrences)
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleDay(dayOfWeek: DayOfWeek) {
|
||||
_state.update { state ->
|
||||
state.copy(
|
||||
selectedDays = state.selectedDays.toMutableList().also {
|
||||
if (!it.remove(dayOfWeek)) {
|
||||
it.add(dayOfWeek)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getRecur(): String {
|
||||
val state = _state.value
|
||||
val builder = Recur.Builder().frequency(state.frequency)
|
||||
if (state.frequency == WEEKLY) {
|
||||
builder.dayList(state.selectedDays.toWeekDayList())
|
||||
} else if (state.frequency == MONTHLY) {
|
||||
state.monthDay?.let { builder.dayList(WeekDayList(it)) }
|
||||
}
|
||||
if (state.interval > 1) {
|
||||
builder.interval(state.interval)
|
||||
}
|
||||
when (state.endSelection) {
|
||||
// 1 -> builder.until(Date(state.endDate))
|
||||
// builder.until expects that Date() is in local timezone and strips it, which effectively
|
||||
// equivalent to decrementing the "endDate" value by TimeZone.offset. This changes the date
|
||||
// to the previous day in timezones to the East of GMT, so this value shall be pre-shifted
|
||||
1 -> builder.until(Date(DateTime(state.endDate).let { it.millis + it.offset }))
|
||||
2 -> builder.count(state.endCount.coerceAtLeast(1))
|
||||
}
|
||||
return builder.build().toString()
|
||||
}
|
||||
|
||||
fun setMonthSelection(selection: Int) {
|
||||
_state.update {
|
||||
it.copy(
|
||||
monthDay = when (selection) {
|
||||
0 -> null
|
||||
1 -> WeekDay(it.dueDayOfWeek.weekDay, it.nthWeek)
|
||||
2 -> WeekDay(it.dueDayOfWeek.weekDay, -1)
|
||||
else -> throw IllegalArgumentException()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FREQ_ALL = listOf(MINUTELY, HOURLY, DAILY, WEEKLY, MONTHLY, YEARLY)
|
||||
val FREQ_MICROSOFT = listOf(DAILY, WEEKLY, MONTHLY, YEARLY)
|
||||
|
||||
private fun Locale.daysOfWeek(): List<DayOfWeek> {
|
||||
val values = DayOfWeek.values()
|
||||
val weekFields = WeekFields.of(this)
|
||||
var index = values.indexOf(weekFields.firstDayOfWeek)
|
||||
return (0..6).map {
|
||||
values[index].also { index = (index + 1) % 7 }
|
||||
}
|
||||
}
|
||||
|
||||
private fun WeekDayList.toDaysOfWeek(): List<DayOfWeek> = map {
|
||||
when (it) {
|
||||
WeekDay.SU -> DayOfWeek.SUNDAY
|
||||
WeekDay.MO -> DayOfWeek.MONDAY
|
||||
WeekDay.TU -> DayOfWeek.TUESDAY
|
||||
WeekDay.WE -> DayOfWeek.WEDNESDAY
|
||||
WeekDay.TH -> DayOfWeek.THURSDAY
|
||||
WeekDay.FR -> DayOfWeek.FRIDAY
|
||||
WeekDay.SA -> DayOfWeek.SATURDAY
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<DayOfWeek>.toWeekDayList(): WeekDayList =
|
||||
WeekDayList(*sortedBy { it.value }.map { it.weekDay }.toTypedArray())
|
||||
|
||||
private val DayOfWeek.weekDay: WeekDay
|
||||
get() = when (this) {
|
||||
DayOfWeek.SUNDAY -> WeekDay.SU
|
||||
DayOfWeek.MONDAY -> WeekDay.MO
|
||||
DayOfWeek.TUESDAY -> WeekDay.TU
|
||||
DayOfWeek.WEDNESDAY -> WeekDay.WE
|
||||
DayOfWeek.THURSDAY -> WeekDay.TH
|
||||
DayOfWeek.FRIDAY -> WeekDay.FR
|
||||
DayOfWeek.SATURDAY -> WeekDay.SA
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
val Saver: Saver<CustomRecurrenceEditState, Bundle> = Saver(
|
||||
save = { original: CustomRecurrenceEditState ->
|
||||
Bundle().apply {
|
||||
putString("rrule", original.getRecur())
|
||||
putLong("dueDate", original.state.value.dueDate)
|
||||
putInt("accountType", original.accountType)
|
||||
putString("locale", original.state.value.locale.toLanguageTag())
|
||||
}
|
||||
},
|
||||
restore = { bundle ->
|
||||
CustomRecurrenceEditState(
|
||||
rrule = bundle.getString("rrule"),
|
||||
dueDate = bundle.getLong("dueDate"),
|
||||
accountType = bundle.getInt("accountType"),
|
||||
locale = Locale.forLanguageTag(bundle.getString("locale"))
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun rememberCustomRecurrencePickerState(
|
||||
rrule: String?,
|
||||
dueDate: Long?,
|
||||
accountType: Int,
|
||||
locale: Locale = Locale.getDefault()
|
||||
): CustomRecurrenceEditState {
|
||||
return rememberSaveable(saver = Saver) { CustomRecurrenceEditState(rrule, dueDate, accountType, locale) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package org.tasks.repeats
|
||||
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import org.tasks.data.entity.Task
|
||||
import org.tasks.preferences.Preferences
|
||||
import java.util.Locale
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RecurrencePickerDialog (
|
||||
dismiss: () -> Unit,
|
||||
recurrence: String?,
|
||||
onRecurrenceChanged: (String?) -> Unit,
|
||||
repeatFrom: @Task.RepeatFrom Int,
|
||||
onRepeatFromChanged: (@Task.RepeatFrom Int) -> Unit,
|
||||
accountType: Int,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val preferences = remember { Preferences(context) }
|
||||
|
||||
val basicDialog = remember { mutableStateOf(true) }
|
||||
if (basicDialog.value) {
|
||||
BasicRecurrencePicker(
|
||||
dismiss = dismiss,
|
||||
recurrence = recurrence,
|
||||
setRecurrence = onRecurrenceChanged,
|
||||
repeatFrom = repeatFrom,
|
||||
onRepeatFromChanged = { onRepeatFromChanged(it) },
|
||||
peekCustomRecurrence = { basicDialog.value = false },
|
||||
)
|
||||
} else {
|
||||
val state = CustomRecurrenceEditState.Companion
|
||||
.rememberCustomRecurrencePickerState(
|
||||
rrule = recurrence,
|
||||
dueDate = null,
|
||||
accountType = accountType,
|
||||
locale = Locale.getDefault()
|
||||
)
|
||||
|
||||
CustomRecurrenceEdit(
|
||||
state = state.state.collectAsStateWithLifecycle().value,
|
||||
save = {
|
||||
onRecurrenceChanged(state.getRecur())
|
||||
dismiss()
|
||||
},
|
||||
discard = dismiss,
|
||||
setInterval = { state.setInterval(it) },
|
||||
setSelectedFrequency = { state.setFrequency(it) },
|
||||
setEndDate = { state.setEndDate(it) },
|
||||
setSelectedEndType = { state.setEndType(it) },
|
||||
setOccurrences = { state.setOccurrences(it) },
|
||||
toggleDay = { state.toggleDay(it) },
|
||||
setMonthSelection = { state.setMonthSelection(it) },
|
||||
calendarDisplayMode = preferences.calendarDisplayMode,
|
||||
setDisplayMode = { preferences.calendarDisplayMode = it }
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue