@@ -33,6 +33,7 @@ import java.time.ZonedDateTime
3333import java.time.format.DateTimeFormatter
3434import java.time.temporal.ChronoUnit
3535import java.util.Locale
36+ import kotlinx.coroutines.delay
3637
3738@OptIn(ExperimentalMaterial3Api ::class )
3839@Composable
@@ -170,6 +171,9 @@ private fun TaskItem(
170171 onDelete : () -> Unit ,
171172 onClick : () -> Unit
172173) {
174+ val ldt = remember(task.nextDueDate) { parseDueDate(task.nextDueDate) }
175+ val now by rememberTickingNow()
176+
173177 Card (
174178 modifier = Modifier
175179 .fillMaxWidth()
@@ -197,8 +201,8 @@ private fun TaskItem(
197201 verticalAlignment = Alignment .CenterVertically ,
198202 modifier = Modifier .padding(bottom = 4 .dp)
199203 ) {
200- DueDateChip (task.nextDueDate )
201- RecurrenceChip (task)
204+ DueDateChip (ldt, now )
205+ RecurrenceChip (task, ldt )
202206 if (hasActiveNotification(task)) {
203207 NotificationChip ()
204208 }
@@ -245,16 +249,8 @@ private fun TaskItem(
245249}
246250
247251@Composable
248- private fun DueDateChip (dateStr : String? ) {
249- val ldt = remember(dateStr) {
250- dateStr?.let {
251- try {
252- ZonedDateTime .parse(it).withZoneSameInstant(ZoneId .systemDefault()).toLocalDateTime()
253- } catch (_: Exception ) { null }
254- }
255- }
256- val now = LocalDateTime .now()
257- val text = if (dateStr == null ) " No Due Date" else formatDueDate(dateStr)
252+ private fun DueDateChip (ldt : LocalDateTime ? , now : LocalDateTime ) {
253+ val text = if (ldt == null ) " No Due Date" else formatDueDate(ldt, now)
258254 val (bgColor, fgColor) = when {
259255 ldt == null -> Pair (MaterialTheme .colorScheme.surfaceVariant, MaterialTheme .colorScheme.onSurfaceVariant)
260256 ldt.isBefore(now) -> Pair (MaterialTheme .colorScheme.errorContainer, MaterialTheme .colorScheme.onErrorContainer)
@@ -272,14 +268,7 @@ private fun DueDateChip(dateStr: String?) {
272268}
273269
274270@Composable
275- private fun RecurrenceChip (task : Task ) {
276- val nextDueLdt = remember(task.nextDueDate) {
277- task.nextDueDate?.let {
278- try {
279- ZonedDateTime .parse(it).withZoneSameInstant(ZoneId .systemDefault()).toLocalDateTime()
280- } catch (_: Exception ) { null }
281- }
282- }
271+ private fun RecurrenceChip (task : Task , nextDueLdt : LocalDateTime ? ) {
283272 val text = getRecurrenceText(task, nextDueLdt)
284273 val isOnce = task.frequency.type == FrequencyType .ONCE
285274 Surface (
@@ -352,22 +341,29 @@ private fun LabelChip(name: String, color: String) {
352341 }
353342}
354343
355- private fun formatDueDate (dateStr : String ): String {
356- return try {
357- val ldt = ZonedDateTime .parse(dateStr)
358- .withZoneSameInstant(ZoneId .systemDefault())
359- .toLocalDateTime()
360- val now = LocalDateTime .now()
361- val today = LocalDate .now()
362- val timeStr = ldt.format(DateTimeFormatter .ofPattern(" hh:mm a" , Locale .ENGLISH ))
363- when {
364- ldt.isBefore(now) -> " ${formatDistance(ldt, now)} ago"
365- ldt.toLocalDate() == today -> " Today at $timeStr "
366- ldt.toLocalDate() == today.plusDays(1 ) -> " Tomorrow at $timeStr "
367- else -> " in ${formatDistance(now, ldt)} "
368- }
369- } catch (_: Exception ) {
370- dateStr
344+ private fun parseDueDate (dateStr : String? ): LocalDateTime ? =
345+ dateStr?.let {
346+ try {
347+ ZonedDateTime .parse(it).withZoneSameInstant(ZoneId .systemDefault()).toLocalDateTime()
348+ } catch (_: Exception ) { null }
349+ }
350+
351+ @Composable
352+ private fun rememberTickingNow (): State <LocalDateTime > = produceState(LocalDateTime .now()) {
353+ while (true ) {
354+ delay(60_000L )
355+ value = LocalDateTime .now()
356+ }
357+ }
358+
359+ private fun formatDueDate (ldt : LocalDateTime , now : LocalDateTime ): String {
360+ val today = now.toLocalDate()
361+ val timeStr = ldt.format(DateTimeFormatter .ofPattern(" hh:mm a" , Locale .ENGLISH ))
362+ return when {
363+ ldt.isBefore(now) -> " ${formatDistance(ldt, now)} ago"
364+ ldt.toLocalDate() == today -> " Today at $timeStr "
365+ ldt.toLocalDate() == today.plusDays(1 ) -> " Tomorrow at $timeStr "
366+ else -> " in ${formatDistance(now, ldt)} "
371367 }
372368}
373369
@@ -407,10 +403,10 @@ private fun getRecurrenceText(task: Task, nextDueLdt: LocalDateTime?): String {
407403 IntervalUnit .WEEKS -> " Weekly"
408404 IntervalUnit .MONTHS -> " Monthly"
409405 IntervalUnit .YEARS -> " Yearly"
410- else -> " Every $every ${frequency.unit} "
406+ else -> " Custom "
411407 }
412408 } else {
413- " Every $every ${frequency.unit} "
409+ frequency.unit?. let { " Every $every $it " } ? : " Custom "
414410 }
415411 }
416412 RepeatOn .DAYS_OF_THE_WEEK -> {
0 commit comments