From 5116bc21ae9b8b457cffeb6d11f80526afaf4d15 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 20:11:43 +0000 Subject: [PATCH] refactor(calendars): extract DAL to commons + a11y + inline-FQN cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DAL extraction (commons/src/jvmAndroid/.../model/nip52Calendar/): - CalendarSortKeys, CalendarAppointmentView, MonthGridBars, IcsExport now live in commons so desktop and the future CLI can consume them. Package changed to com.vitorpamplona.amethyst.commons.model.nip52Calendar. - IcsExportTest moved to commons/jvmTest so it can see the `internal` escapeText helper without exposing it as public API. - Feed-filter classes (CalendarAppointmentsFeedFilter, CalendarCollectionsFeedFilter) stay in amethyst — they depend on Account and LocalCache. The ViewModels stay for the same reason; lifting them requires moving Account/LocalCache too, out of scope here. - A few smart-cast call sites needed local bindings because cross-module properties don't support implicit smart-cast. Accessibility: - Each month-grid cell announces a full content description ("Wednesday, January 15, 2025, 2 events, today, selected") via mergeDescendants so TalkBack reads the cell as one item with role=Button. - Week-strip cells get the same treatment with role=Tab. - Header title now announces both the title and "jump to today" so the affordance is discoverable. - The expanding FAB describes itself as a toggle, and each sub-FAB as the concrete create action. Inline-FQN cleanup pass: - CalendarEventDetailScreen (already in a prior pass), CalendarCollectionsView, NewCalendarEventScreen, NewCalendarCollectionScreen: hoisted fully-qualified androidx.compose / com.vitorpamplona references into proper imports per the codebase style. All 56 calendar tests still pass (48 in amethyst + 8 IcsExport in commons). --- .../calendar/CalendarReminderWorker.kt | 2 +- .../loggedIn/calendars/AddToPhoneCalendar.kt | 2 +- .../calendars/CalendarCollectionsView.kt | 41 ++++++------ .../loggedIn/calendars/CalendarDayView.kt | 11 ++-- .../calendars/CalendarEventListCard.kt | 12 ++-- .../loggedIn/calendars/CalendarFeedView.kt | 4 +- .../loggedIn/calendars/CalendarMonthView.kt | 40 ++++++++++-- .../calendars/CalendarNavigationHeader.kt | 15 ++++- .../calendars/CalendarRelativeTime.kt | 2 +- .../loggedIn/calendars/CalendarTimeFormat.kt | 2 +- .../loggedIn/calendars/CalendarWeekView.kt | 22 ++++++- .../loggedIn/calendars/NewCalendarButton.kt | 11 ++-- .../create/NewCalendarCollectionScreen.kt | 17 ++--- .../create/NewCalendarEventScreen.kt | 64 ++++++++++--------- .../create/NewCalendarEventViewModel.kt | 2 +- .../dal/CalendarAppointmentsFeedFilter.kt | 1 + .../detail/CalendarEventDetailScreen.kt | 4 +- amethyst/src/main/res/values/strings.xml | 11 ++++ .../calendar/CalendarFeedGroupingTest.kt | 6 +- .../amethyst/calendar/CalendarSortKeysTest.kt | 12 ++-- .../amethyst/calendar/MonthGridBarsTest.kt | 2 +- .../nip52Calendar}/CalendarAppointmentView.kt | 4 +- .../model/nip52Calendar}/CalendarSortKeys.kt | 4 +- .../commons/model/nip52Calendar}/IcsExport.kt | 2 +- .../model/nip52Calendar}/MonthGridBars.kt | 4 +- .../model/nip52Calendar}/IcsExportTest.kt | 3 +- 26 files changed, 187 insertions(+), 113 deletions(-) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar}/CalendarAppointmentView.kt (96%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar}/CalendarSortKeys.kt (98%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar}/IcsExport.kt (99%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal => commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar}/MonthGridBars.kt (97%) rename {amethyst/src/test/java/com/vitorpamplona/amethyst/calendar => commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar}/IcsExportTest.kt (98%) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/calendar/CalendarReminderWorker.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/calendar/CalendarReminderWorker.kt index 39a75428a..4f1f30f7e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/calendar/CalendarReminderWorker.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/calendar/CalendarReminderWorker.kt @@ -27,8 +27,8 @@ import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerParameters import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.appointmentView import com.vitorpamplona.amethyst.model.LocalCache -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.appointmentView import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.tags.RSVPStatusTag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/AddToPhoneCalendar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/AddToPhoneCalendar.kt index e0b1ba1fe..11a93892f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/AddToPhoneCalendar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/AddToPhoneCalendar.kt @@ -24,7 +24,7 @@ import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.provider.CalendarContract -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.parseIsoDateToUnixSeconds +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.parseIsoDateToUnixSeconds import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarCollectionsView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarCollectionsView.kt index 0cbd85cfd..986ba67a8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarCollectionsView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarCollectionsView.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -41,6 +42,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -48,15 +50,22 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.IcsExport import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState +import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader import com.vitorpamplona.amethyst.ui.stringRes +import com.vitorpamplona.quartz.nip01Core.core.Address +import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent +import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent +import com.vitorpamplona.quartz.utils.TimeUtils @Composable fun CalendarCollectionsView( @@ -118,7 +127,7 @@ fun CalendarCollectionCard( val title = remember(note.idHex) { event.title() } val description = remember(note.idHex) { event.content.take(180) } val count = remember(note.idHex) { event.calendarEventAddresses().size } - val context = androidx.compose.ui.platform.LocalContext.current + val context = LocalContext.current Card( modifier = @@ -131,7 +140,7 @@ fun CalendarCollectionCard( elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp), ) { // Author header matches every other social card in the app. - com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader( + UserCardHeader( baseNote = note, accountViewModel = accountViewModel, nav = nav, @@ -172,19 +181,10 @@ fun CalendarCollectionCard( color = MaterialTheme.colorScheme.primary, ) } - androidx.compose.material3.IconButton(onClick = { + IconButton(onClick = { val members = collectMembers(event) - val ics = - com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.IcsExport - .calendarToIcs( - event, - members, - com.vitorpamplona.quartz.utils.TimeUtils - .now(), - ) - val filename = - com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.IcsExport - .calendarFilename(event) + val ics = IcsExport.calendarToIcs(event, members, TimeUtils.now()) + val filename = IcsExport.calendarFilename(event) shareIcs(context, filename, ics) }) { Icon( @@ -201,19 +201,14 @@ fun CalendarCollectionCard( /** * Resolves a calendar's member addresses to their cached events, skipping members that haven't * arrived from relays yet or aren't appointments. Returns a list compatible with - * [com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.IcsExport.calendarToIcs]. + * [IcsExport.calendarToIcs]. */ -private fun collectMembers(calendar: CalendarEvent): List> = +private fun collectMembers(calendar: CalendarEvent): List> = calendar .calendarEventAddresses() .mapNotNull { addr -> - val cachedEvent = - com.vitorpamplona.amethyst.model.LocalCache.addressables - .get(addr) - ?.event - if (cachedEvent is com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent || - cachedEvent is com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent - ) { + val cachedEvent = LocalCache.addressables.get(addr)?.event + if (cachedEvent is CalendarTimeSlotEvent || cachedEvent is CalendarDateSlotEvent) { addr to cachedEvent } else { null diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarDayView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarDayView.kt index c2829fe61..e42ea4bef 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarDayView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarDayView.kt @@ -51,15 +51,15 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.appointmentView +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.calendarLocalDayKeyRange +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.groupByDayKeyExpanded import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.appointmentView -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.calendarLocalDayKeyRange -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.groupByDayKeyExpanded import com.vitorpamplona.amethyst.ui.stringRes import java.time.LocalDate import java.time.ZoneId @@ -164,15 +164,16 @@ private fun DayRow( null } + val startSeconds = view.startSeconds val timeLabel = when { view.isAllDay -> stringRes(R.string.calendar_all_day) - view.startSeconds != null && visibleEpochDay > (range?.first ?: visibleEpochDay) -> + startSeconds != null && visibleEpochDay > (range?.first ?: visibleEpochDay) -> // Continuation day of a multi-day timed event — the "9:00 AM" of day 1 is // misleading on day 2 since the event has been ongoing overnight. Show a // continuation marker so the user reads it as "still happening". stringRes(R.string.calendar_continues) - view.startSeconds != null -> formatTimeOfDay(view.startSeconds) + startSeconds != null -> formatTimeOfDay(startSeconds) else -> "—" } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarEventListCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarEventListCard.kt index b6f5600b9..f5d043bbe 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarEventListCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarEventListCard.kt @@ -46,12 +46,12 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.appointmentView import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.components.MyAsyncImage import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.appointmentView import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader import com.vitorpamplona.quartz.utils.TimeUtils import java.time.Instant @@ -155,10 +155,12 @@ fun CalendarEventListCard( ) } } - if (!view.image.isNullOrBlank()) { + val image = view.image + val summary = view.summary + if (!image.isNullOrBlank()) { Spacer(modifier = Modifier.size(4.dp)) MyAsyncImage( - imageUrl = view.image, + imageUrl = image, contentDescription = view.title, contentScale = ContentScale.Crop, mainImageModifier = Modifier.fillMaxWidth().height(120.dp), @@ -168,9 +170,9 @@ fun CalendarEventListCard( onError = { Box(modifier = Modifier.fillMaxWidth().height(120.dp)) }, ) } - if (!view.summary.isNullOrBlank() && view.image.isNullOrBlank()) { + if (!summary.isNullOrBlank() && image.isNullOrBlank()) { Text( - text = view.summary, + text = summary, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 2, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarFeedView.kt index d17613b72..490c5807b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarFeedView.kt @@ -38,6 +38,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.calendarEndSeconds +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.calendarStartSeconds import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.model.Note @@ -45,8 +47,6 @@ import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.calendarEndSeconds -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.calendarStartSeconds import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.FeedPadding import com.vitorpamplona.quartz.utils.TimeUtils diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarMonthView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarMonthView.kt index fa26e2b4b..98f4ecd40 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarMonthView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarMonthView.kt @@ -46,22 +46,27 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.MONTH_GRID_MAX_LANES +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.MonthGridBarSegment +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.computeMonthGridBars +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.groupByDayKeyExpanded import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.MONTH_GRID_MAX_LANES -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.MonthGridBarSegment -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.computeMonthGridBars -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.groupByDayKeyExpanded import com.vitorpamplona.amethyst.ui.stringRes import java.time.LocalDate import java.time.YearMonth +import java.time.ZoneId @Composable fun CalendarMonthView( @@ -197,17 +202,24 @@ private fun MonthGrid( val date = visibleMonth.atDay(dayNumber) val dayKey = date.toEpochDay() val cellBars = barsByDay[dayKey].orEmpty() + val isToday = isCurrentMonth && date == today + val isSelected = selectedDayKey == dayKey DayCell( modifier = Modifier.weight(1f), dayNumber = dayNumber, - isToday = isCurrentMonth && date == today, - isSelected = selectedDayKey == dayKey, + isToday = isToday, + isSelected = isSelected, bars = cellBars, // Anything past the visible lane cap collapses into a "+N" tail — // keeps each cell readable when a day has more than three events. extraEventCount = cellBars.count { it.lane >= MONTH_GRID_MAX_LANES }, isWeekStart = c == 0, isWeekEnd = c == 6, + // Full date label fed to the screen-reader content description so + // TalkBack reads "Wednesday January 15 2025, 2 events" instead of + // just "15". + dateLabel = formatLongDate(date.atStartOfDay(ZoneId.systemDefault()).toEpochSecond()), + totalEventCount = cellBars.size, onClick = { onDayClick(dayKey) }, ) } else { @@ -231,6 +243,8 @@ private fun DayCell( extraEventCount: Int, isWeekStart: Boolean, isWeekEnd: Boolean, + dateLabel: String, + totalEventCount: Int, onClick: () -> Unit, ) { val bg = @@ -240,6 +254,17 @@ private fun DayCell( MaterialTheme.colorScheme.surface } + val baseDescription = + pluralStringResource(R.plurals.calendar_day_a11y_events, totalEventCount, dateLabel, totalEventCount) + val todaySuffix = stringRes(R.string.calendar_day_a11y_today_suffix) + val selectedSuffix = stringRes(R.string.calendar_day_a11y_selected_suffix) + val a11y = + buildString { + append(baseDescription) + if (isToday) append(", ").append(todaySuffix) + if (isSelected) append(", ").append(selectedSuffix) + } + Box( modifier = modifier @@ -253,7 +278,8 @@ private fun DayCell( width = if (isToday) 1.5.dp else 0.5.dp, color = if (isToday) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outlineVariant, shape = RoundedCornerShape(8.dp), - ).clickable(onClick = onClick), + ).clickable(role = Role.Button, onClick = onClick) + .semantics(mergeDescendants = true) { contentDescription = a11y }, ) { Column( modifier = Modifier.fillMaxSize().padding(horizontal = 2.dp, vertical = 4.dp), diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarNavigationHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarNavigationHeader.kt index 75920ba6a..c7107c56e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarNavigationHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarNavigationHeader.kt @@ -31,11 +31,16 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.ui.stringRes /** * Shared `[◀] title [▶]` header used by month / week / day view bodies. Tapping the title @@ -62,10 +67,18 @@ fun CalendarNavigationHeader( tint = MaterialTheme.colorScheme.onSurface, ) } + // Title doubles as the "jump to today" affordance. Adding a TalkBack-only contentDescription + // makes that role discoverable for screen-reader users — the bare text alone reads as a + // label, not an action. + val jumpToToday = stringRes(R.string.calendar_nav_jump_to_today) Text( text = title, style = MaterialTheme.typography.titleLarge, - modifier = Modifier.weight(1f).clickable(onClick = onToday), + modifier = + Modifier + .weight(1f) + .clickable(role = Role.Button, onClickLabel = jumpToToday, onClick = onToday) + .semantics { contentDescription = "$title, $jumpToToday" }, textAlign = TextAlign.Center, fontWeight = FontWeight.Bold, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarRelativeTime.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarRelativeTime.kt index 830f38aae..ebc8caa32 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarRelativeTime.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarRelativeTime.kt @@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars import android.content.Context import android.text.format.DateUtils import com.vitorpamplona.amethyst.R -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.CalendarAppointmentView +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.CalendarAppointmentView /** * Localised "starts in 2 hours" / "started 5 minutes ago" / "Happening now · ends in 2 hours" diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarTimeFormat.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarTimeFormat.kt index 5e270f1bc..f24a7cd20 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarTimeFormat.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarTimeFormat.kt @@ -20,8 +20,8 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.appointmentView import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.appointmentView import java.text.SimpleDateFormat import java.util.Calendar import java.util.Date diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarWeekView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarWeekView.kt index 2eb975447..9c73425a9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarWeekView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/CalendarWeekView.kt @@ -44,17 +44,21 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.groupByDayKeyExpanded import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.groupByDayKeyExpanded import com.vitorpamplona.amethyst.ui.stringRes import java.time.LocalDate import java.time.ZoneId @@ -182,6 +186,17 @@ private fun WeekStrip( else -> MaterialTheme.colorScheme.onSurface } + val dateLabel = formatLongDate(date.atStartOfDay(ZoneId.systemDefault()).toEpochSecond()) + val baseA11y = pluralStringResource(R.plurals.calendar_day_a11y_events, count, dateLabel, count) + val todaySuffix = stringRes(R.string.calendar_day_a11y_today_suffix) + val selectedSuffix = stringRes(R.string.calendar_day_a11y_selected_suffix) + val a11y = + buildString { + append(baseA11y) + if (isToday) append(", ").append(todaySuffix) + if (isSelected) append(", ").append(selectedSuffix) + } + Column( modifier = Modifier @@ -192,8 +207,9 @@ private fun WeekStrip( width = 0.5.dp, color = MaterialTheme.colorScheme.outlineVariant, shape = RoundedCornerShape(10.dp), - ).clickable { onSelect(i) } - .padding(vertical = 6.dp), + ).clickable(role = Role.Tab) { onSelect(i) } + .padding(vertical = 6.dp) + .semantics(mergeDescendants = true) { contentDescription = a11y }, horizontalAlignment = Alignment.CenterHorizontally, ) { Text( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/NewCalendarButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/NewCalendarButton.kt index f4366a292..c4b0981c2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/NewCalendarButton.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/NewCalendarButton.kt @@ -70,7 +70,9 @@ fun NewCalendarButton(nav: INav) { ) { Icon( symbol = MaterialSymbols.CalendarMonth, - contentDescription = stringRes(R.string.new_calendar_collection), + // Spell out the full intent for screen readers — the bare "New collection" + // string is ambiguous out of context. + contentDescription = stringRes(R.string.calendar_fab_new_collection), modifier = Size26Modifier, tint = Color.White, ) @@ -89,7 +91,7 @@ fun NewCalendarButton(nav: INav) { ) { Icon( symbol = MaterialSymbols.Add, - contentDescription = stringRes(R.string.new_calendar_event), + contentDescription = stringRes(R.string.calendar_fab_new_event), modifier = Size26Modifier, tint = Color.White, ) @@ -112,7 +114,7 @@ fun NewCalendarButton(nav: INav) { ) { Icon( symbol = MaterialSymbols.Close, - contentDescription = stringRes(R.string.new_calendar_event), + contentDescription = stringRes(R.string.calendar_fab_toggle), modifier = Size26Modifier, tint = Color.White, ) @@ -125,7 +127,8 @@ fun NewCalendarButton(nav: INav) { ) { Icon( symbol = MaterialSymbols.Add, - contentDescription = stringRes(R.string.new_calendar_event), + // Top FAB toggles the sub-FABs in/out — describe that, not the sub-action. + contentDescription = stringRes(R.string.calendar_fab_toggle), modifier = Size26Modifier, tint = Color.White, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarCollectionScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarCollectionScreen.kt index ca52494ba..5f60418e0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarCollectionScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarCollectionScreen.kt @@ -31,13 +31,17 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -140,24 +144,21 @@ private fun DeleteCalendarRow( ) { var confirming by rememberSaveable { mutableStateOf(false) } - androidx.compose.material3.OutlinedButton( + OutlinedButton( onClick = { confirming = true }, modifier = Modifier.fillMaxWidth(), - colors = - androidx.compose.material3.ButtonDefaults.outlinedButtonColors( - contentColor = MaterialTheme.colorScheme.error, - ), + colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.error), ) { Text(text = stringRes(R.string.calendar_collection_delete)) } if (confirming) { - androidx.compose.material3.AlertDialog( + AlertDialog( onDismissRequest = { confirming = false }, title = { Text(stringRes(R.string.calendar_collection_delete_confirm_title)) }, text = { Text(stringRes(R.string.calendar_collection_delete_confirm_message)) }, confirmButton = { - androidx.compose.material3.TextButton(onClick = { + TextButton(onClick = { confirming = false accountViewModel.launchSigner { if (vm.deleteLoaded()) onDeleted() @@ -170,7 +171,7 @@ private fun DeleteCalendarRow( } }, dismissButton = { - androidx.compose.material3.TextButton(onClick = { confirming = false }) { + TextButton(onClick = { confirming = false }) { Text(stringRes(R.string.cancel)) } }, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarEventScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarEventScreen.kt index 8730f96fe..0afcc6245 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarEventScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarEventScreen.kt @@ -33,26 +33,41 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable 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.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.commons.icons.symbols.Icon +import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar +import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture +import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser import com.vitorpamplona.amethyst.ui.stringRes +import com.vitorpamplona.amethyst.ui.theme.Size30dp +import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser +import com.vitorpamplona.quartz.nip19Bech32.entities.NPub import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @@ -227,8 +242,8 @@ private fun ImageRow( vm: NewCalendarEventViewModel, accountViewModel: AccountViewModel, ) { - val context = androidx.compose.ui.platform.LocalContext.current - val scope = androidx.compose.runtime.rememberCoroutineScope() + val context = LocalContext.current + val scope = rememberCoroutineScope() val launcher = androidx.activity.compose.rememberLauncherForActivityResult( contract = @@ -241,8 +256,8 @@ private fun ImageRow( val ok = vm.uploadAndSetImage(uri, mime, context) if (!ok) { accountViewModel.toastManager.toast( - com.vitorpamplona.amethyst.R.string.calendar_event_image_upload_failed, - com.vitorpamplona.amethyst.R.string.calendar_event_image_upload_failed_body, + R.string.calendar_event_image_upload_failed, + R.string.calendar_event_image_upload_failed_body, ) } } @@ -258,14 +273,14 @@ private fun ImageRow( enabled = !vm.isUploadingImage.value, ) if (vm.isUploadingImage.value) { - androidx.compose.material3.CircularProgressIndicator( + CircularProgressIndicator( modifier = Modifier.padding(start = 8.dp).size(20.dp), strokeWidth = 2.dp, ) } else { - androidx.compose.material3.IconButton(onClick = { launcher.launch("image/*") }) { - com.vitorpamplona.amethyst.commons.icons.symbols.Icon( - symbol = com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols.AddPhotoAlternate, + IconButton(onClick = { launcher.launch("image/*") }) { + Icon( + symbol = MaterialSymbols.AddPhotoAlternate, contentDescription = stringRes(R.string.calendar_event_pick_image), modifier = Modifier.size(22.dp), tint = MaterialTheme.colorScheme.primary, @@ -289,10 +304,8 @@ private fun ParticipantsRow( vm: NewCalendarEventViewModel, accountViewModel: AccountViewModel, ) { - var draft by androidx.compose.runtime.saveable - .rememberSaveable { androidx.compose.runtime.mutableStateOf("") } - var error by androidx.compose.runtime.saveable - .rememberSaveable { androidx.compose.runtime.mutableStateOf(null) } + var draft by rememberSaveable { mutableStateOf("") } + var error by rememberSaveable { mutableStateOf(null) } Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { FieldLabel(stringRes(R.string.calendar_event_participants_section, vm.participants.size)) @@ -308,7 +321,7 @@ private fun ParticipantsRow( singleLine = true, isError = error != null, ) - androidx.compose.material3.TextButton( + TextButton( onClick = { val resolved = resolvePubKeyOrNull(draft.trim()) if (resolved == null) { @@ -320,7 +333,7 @@ private fun ParticipantsRow( }, enabled = draft.isNotBlank(), ) { - Text(stringRes(com.vitorpamplona.amethyst.R.string.add)) + Text(stringRes(R.string.add)) } } if (error != null) { @@ -332,17 +345,14 @@ private fun ParticipantsRow( } vm.participants.forEach { pubKey -> Row(verticalAlignment = Alignment.CenterVertically) { - com.vitorpamplona.amethyst.ui.note.ClickableUserPicture( + ClickableUserPicture( baseUserHex = pubKey, - size = com.vitorpamplona.amethyst.ui.theme.Size30dp, + size = Size30dp, accountViewModel = accountViewModel, ) - com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser( - baseUserHex = pubKey, - accountViewModel = accountViewModel, - ) { user -> + LoadUser(baseUserHex = pubKey, accountViewModel = accountViewModel) { user -> if (user != null) { - com.vitorpamplona.amethyst.ui.note.UsernameDisplay( + UsernameDisplay( baseUser = user, weight = Modifier.weight(1f).padding(horizontal = 8.dp), accountViewModel = accountViewModel, @@ -355,9 +365,9 @@ private fun ParticipantsRow( ) } } - androidx.compose.material3.IconButton(onClick = { vm.removeParticipant(pubKey) }) { - com.vitorpamplona.amethyst.commons.icons.symbols.Icon( - symbol = com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols.Close, + IconButton(onClick = { vm.removeParticipant(pubKey) }) { + Icon( + symbol = MaterialSymbols.Close, contentDescription = stringRes(R.string.calendar_event_participant_remove), modifier = Modifier.size(18.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant, @@ -378,11 +388,7 @@ private fun resolvePubKeyOrNull(input: String): String? { } if (input.startsWith("npub")) { return runCatching { - ( - com.vitorpamplona.quartz.nip19Bech32.Nip19Parser - .uriToRoute(input) - ?.entity as? com.vitorpamplona.quartz.nip19Bech32.entities.NPub - )?.hex + (Nip19Parser.uriToRoute(input)?.entity as? NPub)?.hex }.getOrNull() } return null diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarEventViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarEventViewModel.kt index f4f8fef13..637f0f0d4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarEventViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/create/NewCalendarEventViewModel.kt @@ -24,6 +24,7 @@ import android.content.Context import android.net.Uri import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.parseIsoDateToUnixSeconds import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.service.uploads.CompressorQuality @@ -31,7 +32,6 @@ import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator import com.vitorpamplona.amethyst.service.uploads.UploadingState import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.parseIsoDateToUnixSeconds import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.people.PTag diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentsFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentsFeedFilter.kt index dccd985a5..2b8acb82b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentsFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentsFeedFilter.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.upcomingFirstCalendarOrder import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/detail/CalendarEventDetailScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/detail/CalendarEventDetailScreen.kt index 790a56a42..828b53860 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/detail/CalendarEventDetailScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/detail/CalendarEventDetailScreen.kt @@ -61,6 +61,8 @@ import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.IcsExport +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.appointmentView import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote @@ -73,8 +75,6 @@ import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.note.types.CalendarRsvpRow import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.addToPhoneCalendar -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.IcsExport -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.appointmentView import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.datasource.CalendarsFilterAssemblerSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.formatCalendarRange import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.relativeTimeLabel diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 85674fe4b..30eaee2f5 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -1969,6 +1969,17 @@ Continues Day %1$d of %2$d Add to phone calendar + Jump to today + + %1$s, no events + %1$s, %2$d event + %1$s, %2$d events + + today + selected + Create a new calendar event + Create a new calendar collection + Show create options (untitled) All-day ✓ Going diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarFeedGroupingTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarFeedGroupingTest.kt index 0bfd1c96c..313a966b4 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarFeedGroupingTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarFeedGroupingTest.kt @@ -20,10 +20,10 @@ */ package com.vitorpamplona.amethyst.calendar +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.calendarLocalDayKeyRange +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.groupByDayKey +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.groupByDayKeyExpanded import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.calendarLocalDayKeyRange -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.groupByDayKey -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.groupByDayKeyExpanded import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.partitionUpcomingPast import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarSortKeysTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarSortKeysTest.kt index 0bb3f1d0d..fded0c4bc 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarSortKeysTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/CalendarSortKeysTest.kt @@ -20,13 +20,13 @@ */ package com.vitorpamplona.amethyst.calendar +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.appointmentView +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.calendarEndSeconds +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.calendarLocalDayKey +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.calendarStartSeconds +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.parseIsoDateToUnixSeconds +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.upcomingFirstCalendarOrder import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.appointmentView -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.calendarEndSeconds -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.calendarLocalDayKey -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.calendarStartSeconds -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.parseIsoDateToUnixSeconds -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.upcomingFirstCalendarOrder import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent import org.junit.Assert.assertEquals diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/MonthGridBarsTest.kt b/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/MonthGridBarsTest.kt index c6617dd47..6307d8066 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/MonthGridBarsTest.kt +++ b/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/MonthGridBarsTest.kt @@ -20,8 +20,8 @@ */ package com.vitorpamplona.amethyst.calendar +import com.vitorpamplona.amethyst.commons.model.nip52Calendar.computeMonthGridBars import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.computeMonthGridBars import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentView.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/CalendarAppointmentView.kt similarity index 96% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentView.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/CalendarAppointmentView.kt index 2282cbba9..51f1aa22b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarAppointmentView.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/CalendarAppointmentView.kt @@ -18,10 +18,10 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal +package com.vitorpamplona.amethyst.commons.model.nip52Calendar import androidx.compose.runtime.Immutable -import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarSortKeys.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/CalendarSortKeys.kt similarity index 98% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarSortKeys.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/CalendarSortKeys.kt index 9fbb6a1ae..a09f17880 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/CalendarSortKeys.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/CalendarSortKeys.kt @@ -18,9 +18,9 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal +package com.vitorpamplona.amethyst.commons.model.nip52Calendar -import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent import com.vitorpamplona.quartz.utils.TimeUtils diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/IcsExport.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/IcsExport.kt similarity index 99% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/IcsExport.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/IcsExport.kt index 352913696..ecf32fda4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/IcsExport.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/IcsExport.kt @@ -18,7 +18,7 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal +package com.vitorpamplona.amethyst.commons.model.nip52Calendar import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/MonthGridBars.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/MonthGridBars.kt similarity index 97% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/MonthGridBars.kt rename to commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/MonthGridBars.kt index 3a34b1d25..509cc03a1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/calendars/dal/MonthGridBars.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/MonthGridBars.kt @@ -18,10 +18,10 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal +package com.vitorpamplona.amethyst.commons.model.nip52Calendar import androidx.compose.runtime.Immutable -import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.commons.model.Note /** * One bar drawn into one day cell, with `lane` controlling its vertical position so two diff --git a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/IcsExportTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/IcsExportTest.kt similarity index 98% rename from amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/IcsExportTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/IcsExportTest.kt index 3487143fb..f6e5ec7e8 100644 --- a/amethyst/src/test/java/com/vitorpamplona/amethyst/calendar/IcsExportTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip52Calendar/IcsExportTest.kt @@ -18,9 +18,8 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.amethyst.calendar +package com.vitorpamplona.amethyst.commons.model.nip52Calendar -import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.dal.IcsExport import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent