Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4e50bd161 |
@@ -15,6 +15,7 @@ import de.jeanlucmakiola.agendula.domain.Task
|
|||||||
import de.jeanlucmakiola.agendula.domain.TaskFilter
|
import de.jeanlucmakiola.agendula.domain.TaskFilter
|
||||||
import de.jeanlucmakiola.agendula.domain.TaskFormField
|
import de.jeanlucmakiola.agendula.domain.TaskFormField
|
||||||
import de.jeanlucmakiola.agendula.domain.TaskReminder
|
import de.jeanlucmakiola.agendula.domain.TaskReminder
|
||||||
|
import de.jeanlucmakiola.agendula.domain.TaskSection
|
||||||
import de.jeanlucmakiola.agendula.domain.asTaskReminder
|
import de.jeanlucmakiola.agendula.domain.asTaskReminder
|
||||||
import de.jeanlucmakiola.agendula.domain.asReminderLead
|
import de.jeanlucmakiola.agendula.domain.asReminderLead
|
||||||
import de.jeanlucmakiola.agendula.domain.TaskSortOrder
|
import de.jeanlucmakiola.agendula.domain.TaskSortOrder
|
||||||
@@ -43,6 +44,7 @@ fun TimeFormatPref.is24Hour(systemIs24Hour: Boolean): Boolean = when (this) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const val DEFAULT_SNOOZE_MINUTES = 10
|
const val DEFAULT_SNOOZE_MINUTES = 10
|
||||||
|
/** Done starts folded away, to keep the open tasks in focus. */ val DEFAULT_COLLAPSED_SECTIONS: Set<TaskSection> = setOf(TaskSection.COMPLETED)
|
||||||
|
|
||||||
/** The snooze lengths Settings offers. */
|
/** The snooze lengths Settings offers. */
|
||||||
val SNOOZE_PRESETS = listOf(5, 10, 15, 30, 60)
|
val SNOOZE_PRESETS = listOf(5, 10, 15, 30, 60)
|
||||||
@@ -103,6 +105,8 @@ data class Settings(
|
|||||||
val hiddenFromSmartLists: Set<Long> = emptySet(),
|
val hiddenFromSmartLists: Set<Long> = emptySet(),
|
||||||
/** Pre-fill a new task's start with today's date. */
|
/** Pre-fill a new task's start with today's date. */
|
||||||
val defaultStartToday: Boolean = false,
|
val defaultStartToday: Boolean = false,
|
||||||
|
/** Task-list sections folded away; Done starts folded. */
|
||||||
|
val collapsedSections: Set<TaskSection> = DEFAULT_COLLAPSED_SECTIONS,
|
||||||
) {
|
) {
|
||||||
/** [tasks] as [filter] shows them: a smart list drops the lists kept out of it. */
|
/** [tasks] as [filter] shows them: a smart list drops the lists kept out of it. */
|
||||||
fun visibleIn(filter: TaskFilter, tasks: List<Task>): List<Task> =
|
fun visibleIn(filter: TaskFilter, tasks: List<Task>): List<Task> =
|
||||||
@@ -168,9 +172,18 @@ class SettingsPrefs @Inject constructor(
|
|||||||
.toSet()
|
.toSet()
|
||||||
},
|
},
|
||||||
defaultStartToday = p[DEFAULT_START_TODAY] ?: false,
|
defaultStartToday = p[DEFAULT_START_TODAY] ?: false,
|
||||||
|
collapsedSections = p[COLLAPSED_SECTIONS]
|
||||||
|
?.mapNotNull { name -> runCatching { TaskSection.valueOf(name) }.getOrNull() }
|
||||||
|
?.toSet()
|
||||||
|
?: DEFAULT_COLLAPSED_SECTIONS,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun setSectionCollapsed(section: TaskSection, collapsed: Boolean) = dataStore.edit { p ->
|
||||||
|
val current = p[COLLAPSED_SECTIONS] ?: DEFAULT_COLLAPSED_SECTIONS.mapTo(HashSet()) { it.name }
|
||||||
|
p[COLLAPSED_SECTIONS] = if (collapsed) current + section.name else current - section.name
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun setSortOrder(order: TaskSortOrder) = dataStore.edit { it[SORT_ORDER] = order.name }
|
suspend fun setSortOrder(order: TaskSortOrder) = dataStore.edit { it[SORT_ORDER] = order.name }
|
||||||
|
|
||||||
suspend fun setSnoozeMinutes(minutes: Int) = dataStore.edit { it[SNOOZE_MINUTES] = minutes.coerceAtLeast(1) }
|
suspend fun setSnoozeMinutes(minutes: Int) = dataStore.edit { it[SNOOZE_MINUTES] = minutes.coerceAtLeast(1) }
|
||||||
@@ -328,6 +341,7 @@ class SettingsPrefs @Inject constructor(
|
|||||||
val PUSH_ENABLED = booleanPreferencesKey("push_enabled")
|
val PUSH_ENABLED = booleanPreferencesKey("push_enabled")
|
||||||
val SMART_LIST_HIDDEN = stringSetPreferencesKey("smart_list_hidden")
|
val SMART_LIST_HIDDEN = stringSetPreferencesKey("smart_list_hidden")
|
||||||
val DEFAULT_START_TODAY = booleanPreferencesKey("default_start_today")
|
val DEFAULT_START_TODAY = booleanPreferencesKey("default_start_today")
|
||||||
|
val COLLAPSED_SECTIONS = stringSetPreferencesKey("collapsed_sections")
|
||||||
val SAVED_FILTERS = stringPreferencesKey("saved_filters")
|
val SAVED_FILTERS = stringPreferencesKey("saved_filters")
|
||||||
val SAVED_FILTER_NEXT_ID = longPreferencesKey("saved_filter_next_id")
|
val SAVED_FILTER_NEXT_ID = longPreferencesKey("saved_filter_next_id")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -371,8 +371,6 @@ private fun TaskListBody(
|
|||||||
// rows; inside a single real list both are redundant.
|
// rows; inside a single real list both are redundant.
|
||||||
val showListName = filter is TaskFilter.Smart || filter is TaskFilter.Tagged ||
|
val showListName = filter is TaskFilter.Smart || filter is TaskFilter.Tagged ||
|
||||||
(filter is TaskFilter.Saved && state.savedFilter?.listIds?.size != 1)
|
(filter is TaskFilter.Saved && state.savedFilter?.listIds?.size != 1)
|
||||||
// Completed tasks collapse away by default to keep the active list in focus.
|
|
||||||
var completedExpanded by rememberSaveable { mutableStateOf(false) }
|
|
||||||
// Parents the user expanded to reveal their subtasks inline. Children are
|
// Parents the user expanded to reveal their subtasks inline. Children are
|
||||||
// grouped from the same flat task set the list already holds.
|
// grouped from the same flat task set the list already holds.
|
||||||
var expandedParents by rememberSaveable { mutableStateOf(emptySet<Long>()) }
|
var expandedParents by rememberSaveable { mutableStateOf(emptySet<Long>()) }
|
||||||
@@ -420,23 +418,20 @@ private fun TaskListBody(
|
|||||||
}
|
}
|
||||||
|
|
||||||
sections.forEach { section ->
|
sections.forEach { section ->
|
||||||
val collapsible = showHeaders && section.section == TaskSection.COMPLETED
|
// Every header folds its section away (#91); Done starts folded.
|
||||||
|
val expanded = !showHeaders || section.section !in state.collapsedSections
|
||||||
if (showHeaders) {
|
if (showHeaders) {
|
||||||
stickyHeader(key = "hdr-${section.section}") {
|
stickyHeader(key = "hdr-${section.section}") {
|
||||||
if (collapsible) {
|
CollapsibleSectionHeader(
|
||||||
CollapsibleSectionHeader(
|
label = sectionLabel(section.section),
|
||||||
label = sectionLabel(section.section),
|
count = section.tasks.size,
|
||||||
count = section.tasks.size,
|
expanded = expanded,
|
||||||
expanded = completedExpanded,
|
onToggle = { viewModel.setSectionCollapsed(section.section, expanded) },
|
||||||
onToggle = { completedExpanded = !completedExpanded },
|
onClear = onClearCompleted.takeIf { section.section == TaskSection.COMPLETED },
|
||||||
onClear = onClearCompleted,
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
SectionHeader(sectionLabel(section.section))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!collapsible || completedExpanded) {
|
if (expanded) {
|
||||||
// Flatten the section into one grouped run: each parent, followed
|
// Flatten the section into one grouped run: each parent, followed
|
||||||
// by its subtasks when expanded. Each row's corners then come from
|
// by its subtasks when expanded. Each row's corners then come from
|
||||||
// its place in the whole run (positionOf below) — so the children
|
// its place in the whole run (positionOf below) — so the children
|
||||||
@@ -1127,19 +1122,6 @@ private fun cardShape(position: Position, full: androidx.compose.ui.unit.Dp, sma
|
|||||||
Position.Bottom -> RoundedCornerShape(topStart = small, topEnd = small, bottomStart = full, bottomEnd = full)
|
Position.Bottom -> RoundedCornerShape(topStart = small, topEnd = small, bottomStart = full, bottomEnd = full)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SectionHeader(text: String) {
|
|
||||||
// Opaque surface so rows scroll cleanly under the pinned header.
|
|
||||||
Surface(color = MaterialTheme.colorScheme.surface, modifier = Modifier.fillMaxWidth()) {
|
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
style = MaterialTheme.typography.titleSmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
modifier = Modifier.padding(start = 28.dp, end = 28.dp, top = 16.dp, bottom = 8.dp),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A tappable section header with a count and a chevron that rotates on expand. */
|
/** A tappable section header with a count and a chevron that rotates on expand. */
|
||||||
@Composable
|
@Composable
|
||||||
private fun CollapsibleSectionHeader(
|
private fun CollapsibleSectionHeader(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package de.jeanlucmakiola.agendula.ui.tasklist
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import de.jeanlucmakiola.agendula.data.prefs.DEFAULT_COLLAPSED_SECTIONS
|
||||||
import de.jeanlucmakiola.agendula.data.prefs.SettingsPrefs
|
import de.jeanlucmakiola.agendula.data.prefs.SettingsPrefs
|
||||||
import de.jeanlucmakiola.agendula.data.sync.RemoteLists
|
import de.jeanlucmakiola.agendula.data.sync.RemoteLists
|
||||||
import de.jeanlucmakiola.agendula.data.tasks.TasksRepository
|
import de.jeanlucmakiola.agendula.data.tasks.TasksRepository
|
||||||
@@ -14,6 +15,7 @@ import de.jeanlucmakiola.agendula.domain.TaskFiltering
|
|||||||
import de.jeanlucmakiola.agendula.domain.tagsInUse
|
import de.jeanlucmakiola.agendula.domain.tagsInUse
|
||||||
import de.jeanlucmakiola.agendula.domain.TaskForm
|
import de.jeanlucmakiola.agendula.domain.TaskForm
|
||||||
import de.jeanlucmakiola.agendula.domain.TaskList
|
import de.jeanlucmakiola.agendula.domain.TaskList
|
||||||
|
import de.jeanlucmakiola.agendula.domain.TaskSection
|
||||||
import de.jeanlucmakiola.agendula.domain.TaskSortOrder
|
import de.jeanlucmakiola.agendula.domain.TaskSortOrder
|
||||||
import de.jeanlucmakiola.agendula.domain.comparator
|
import de.jeanlucmakiola.agendula.domain.comparator
|
||||||
import de.jeanlucmakiola.agendula.ui.lists.ListWriteFailure
|
import de.jeanlucmakiola.agendula.ui.lists.ListWriteFailure
|
||||||
@@ -56,6 +58,8 @@ sealed interface TaskListUiState {
|
|||||||
/** Whether a real list uses the bottom quick-add bar instead of the FAB. */
|
/** Whether a real list uses the bottom quick-add bar instead of the FAB. */
|
||||||
val bottomAddBar: Boolean = false,
|
val bottomAddBar: Boolean = false,
|
||||||
val sortOrder: TaskSortOrder = TaskSortOrder.DUE,
|
val sortOrder: TaskSortOrder = TaskSortOrder.DUE,
|
||||||
|
/** Sections whose rows are folded under their header. */
|
||||||
|
val collapsedSections: Set<TaskSection> = DEFAULT_COLLAPSED_SECTIONS,
|
||||||
/** This real list is kept out of the smart lists. */
|
/** This real list is kept out of the smart lists. */
|
||||||
val hiddenFromSmartLists: Boolean = false,
|
val hiddenFromSmartLists: Boolean = false,
|
||||||
/** Tags the unnarrowed view carries, offered as filter chips. */
|
/** Tags the unnarrowed view carries, offered as filter chips. */
|
||||||
@@ -144,6 +148,7 @@ class TaskListViewModel @Inject constructor(
|
|||||||
showAddSubtaskRow = settings.showAddSubtaskRow,
|
showAddSubtaskRow = settings.showAddSubtaskRow,
|
||||||
bottomAddBar = settings.bottomAddBar,
|
bottomAddBar = settings.bottomAddBar,
|
||||||
sortOrder = settings.sortOrder,
|
sortOrder = settings.sortOrder,
|
||||||
|
collapsedSections = settings.collapsedSections,
|
||||||
hiddenFromSmartLists = st.list?.id?.let { it in settings.hiddenFromSmartLists } ?: false,
|
hiddenFromSmartLists = st.list?.id?.let { it in settings.hiddenFromSmartLists } ?: false,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -277,6 +282,9 @@ class TaskListViewModel @Inject constructor(
|
|||||||
fun setHiddenFromSmartLists(listId: Long, hidden: Boolean) =
|
fun setHiddenFromSmartLists(listId: Long, hidden: Boolean) =
|
||||||
viewModelScope.launch { settingsPrefs.setHiddenFromSmartLists(listId, hidden) }
|
viewModelScope.launch { settingsPrefs.setHiddenFromSmartLists(listId, hidden) }
|
||||||
|
|
||||||
|
fun setSectionCollapsed(section: TaskSection, collapsed: Boolean) =
|
||||||
|
viewModelScope.launch { settingsPrefs.setSectionCollapsed(section, collapsed) }
|
||||||
|
|
||||||
fun setSortOrder(order: TaskSortOrder) = viewModelScope.launch { settingsPrefs.setSortOrder(order) }
|
fun setSortOrder(order: TaskSortOrder) = viewModelScope.launch { settingsPrefs.setSortOrder(order) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user