refactor(calendars): extract DAL to commons + a11y + inline-FQN cleanup
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).
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+18
-23
@@ -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<Pair<com.vitorpamplona.quartz.nip01Core.core.Address, Any>> =
|
||||
private fun collectMembers(calendar: CalendarEvent): List<Pair<Address, Any>> =
|
||||
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
|
||||
|
||||
+6
-5
@@ -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 -> "—"
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -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,
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+33
-7
@@ -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),
|
||||
|
||||
+14
-1
@@ -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,
|
||||
)
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+19
-3
@@ -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(
|
||||
|
||||
+7
-4
@@ -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,
|
||||
)
|
||||
|
||||
+9
-8
@@ -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))
|
||||
}
|
||||
},
|
||||
|
||||
+35
-29
@@ -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<String?>(null) }
|
||||
var draft by rememberSaveable { mutableStateOf("") }
|
||||
var error by rememberSaveable { mutableStateOf<String?>(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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
@@ -1969,6 +1969,17 @@
|
||||
<string name="calendar_continues">Continues</string>
|
||||
<string name="calendar_day_of_total">Day %1$d of %2$d</string>
|
||||
<string name="calendar_add_to_phone_calendar">Add to phone calendar</string>
|
||||
<string name="calendar_nav_jump_to_today">Jump to today</string>
|
||||
<plurals name="calendar_day_a11y_events">
|
||||
<item quantity="zero">%1$s, no events</item>
|
||||
<item quantity="one">%1$s, %2$d event</item>
|
||||
<item quantity="other">%1$s, %2$d events</item>
|
||||
</plurals>
|
||||
<string name="calendar_day_a11y_today_suffix">today</string>
|
||||
<string name="calendar_day_a11y_selected_suffix">selected</string>
|
||||
<string name="calendar_fab_new_event">Create a new calendar event</string>
|
||||
<string name="calendar_fab_new_collection">Create a new calendar collection</string>
|
||||
<string name="calendar_fab_toggle">Show create options</string>
|
||||
<string name="calendar_untitled">(untitled)</string>
|
||||
<string name="calendar_all_day">All-day</string>
|
||||
<string name="calendar_rsvp_going_prefixed">✓ Going</string>
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+2
-2
@@ -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
|
||||
+1
-2
@@ -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
|
||||
Reference in New Issue
Block a user