mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 16:06:32 +00:00
feat(calendar): lay out the agenda as a schedule with dates on the left
The agenda showed each day as a grey header bar above its events. It now reads like a schedule: the weekday and date sit in a column on the left of the day's first event (today in a filled circle), a heading marks where each month starts and a quiet line names each week's range. An empty today says "Nothing planned. Tap to create." and tapping it opens a new event. In the drawer the view is now called Schedule. Events that fill a whole day are listed as all day instead of "00:00 – 00:00".
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "رابط",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "لا شيء مخطط. انقر للإنشاء.",
|
||||
"tap_to_create": "اضغط + لإنشاء حدث"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Enllaç",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Res planificat. Toca per crear.",
|
||||
"tap_to_create": "Toqueu + per crear-ne un"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Odkaz",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nic naplánováno. Klepnutím vytvoříte.",
|
||||
"tap_to_create": "Událost vytvoříte klepnutím na +"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Intet planlagt. Tryk for at oprette.",
|
||||
"tap_to_create": "Tryk på + for at oprette en"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nichts geplant. Zum Erstellen tippen.",
|
||||
"tap_to_create": "Tippen Sie auf +, um einen zu erstellen"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nothing planned. Tap to create.",
|
||||
"tap_to_create": "Tap + to create one"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Enlace",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nada planificado. Toca para crear.",
|
||||
"tap_to_create": "Toca + para crear uno"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "لینک",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "برنامهای نیست. برای ایجاد ضربه بزنید.",
|
||||
"tap_to_create": "برای ایجاد، روی + ضربه بزنید"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Lien",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Rien de prévu. Touchez pour créer.",
|
||||
"tap_to_create": "Appuyez sur + pour en créer un"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "קישור",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "אין תוכניות. הקש כדי ליצור.",
|
||||
"tap_to_create": "הקש על + כדי ליצור"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Hivatkozás",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nincs terv. Koppintson a létrehozáshoz.",
|
||||
"tap_to_create": "Esemény létrehozásához koppints a + gombra"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Niente in programma. Tocca per creare.",
|
||||
"tap_to_create": "Tocca + per crearne uno"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "リンク",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "予定はありません。タップして作成",
|
||||
"tap_to_create": "+ をタップして作成"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "링크",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "계획된 일정이 없습니다. 탭하여 만드세요.",
|
||||
"tap_to_create": "+를 눌러 만들어 보세요"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Saite",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nekas nav ieplānots. Pieskarieties, lai izveidotu.",
|
||||
"tap_to_create": "Pieskarieties +, lai izveidotu notikumu"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Холбоос",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Төлөвлөсөн зүйл алга. Үүсгэхийн тулд товшино уу.",
|
||||
"tap_to_create": "Үүсгэхийн тулд + товшино уу"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Lenke",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Ingenting planlagt. Trykk for å opprette.",
|
||||
"tap_to_create": "Trykk på + for å opprette en"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Niets gepland. Tik om aan te maken.",
|
||||
"tap_to_create": "Tik op + om er een te maken"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nic nie zaplanowano. Dotknij, aby utworzyć.",
|
||||
"tap_to_create": "Stuknij +, aby utworzyć wydarzenie"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nada planejado. Toque para criar.",
|
||||
"tap_to_create": "Toque em + para criar um"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Link",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nimic planificat. Atinge pentru a crea.",
|
||||
"tap_to_create": "Atingeți + pentru a crea unul"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Ссылка",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Ничего не запланировано. Нажмите, чтобы создать.",
|
||||
"tap_to_create": "Нажмите +, чтобы создать"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Odkaz",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Nič nie je naplánované. Ťuknutím vytvoríte.",
|
||||
"tap_to_create": "Udalosť vytvoríte klepnutím na +"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Bağlantı",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Planlanmış bir şey yok. Oluşturmak için dokunun.",
|
||||
"tap_to_create": "Etkinlik oluşturmak için + simgesine dokunun"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "Посилання",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "Нічого не заплановано. Торкніться, щоб створити.",
|
||||
"tap_to_create": "Натисніть +, щоб створити"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "連結",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "沒有安排。輕觸即可建立。",
|
||||
"tap_to_create": "點選 + 即可建立"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"events": {
|
||||
"copy_link_short": "链接",
|
||||
"export_ics_short": ".ics",
|
||||
"nothing_planned": "暂无安排。点按即可创建。",
|
||||
"tap_to_create": "点按 + 创建"
|
||||
},
|
||||
"fab": {
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
type LayoutChangeEvent,
|
||||
type ViewToken,
|
||||
} from 'react-native';
|
||||
import { format, type Locale } from 'date-fns';
|
||||
import { displayNow, isDisplayToday, isDisplayTomorrow } from '../../lib/calendar-timezone';
|
||||
import { endOfWeek, format, startOfWeek, type Locale } from 'date-fns';
|
||||
import { displayNow, isDisplayToday } from '../../lib/calendar-timezone';
|
||||
import type { Calendar, CalendarEvent } from '../../api/types';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
@@ -47,25 +47,28 @@ interface AgendaViewProps {
|
||||
/** Reports the first day in view as the user scrolls. */
|
||||
onVisibleDateChange?: (date: Date) => void;
|
||||
timeFormat?: TimeFormat;
|
||||
weekStartsOn?: 0 | 1 | 6;
|
||||
/** The user's addresses, to draw events they declined as inactive. */
|
||||
currentUserEmails?: string[];
|
||||
onSelectEvent?: (event: CalendarEvent) => void;
|
||||
/** Tapping an empty "today" row creates an event on that day. */
|
||||
onCreateAt?: (date: Date) => void;
|
||||
}
|
||||
|
||||
interface DaySection extends AgendaDay {
|
||||
key: string;
|
||||
title: string;
|
||||
/** Set on the first day of a month in the list: the month's name. */
|
||||
monthTitle: string | null;
|
||||
/** Set on the first day of a week in the list: the week's range. */
|
||||
weekTitle: string | null;
|
||||
}
|
||||
|
||||
function formatDayHeader(
|
||||
date: Date,
|
||||
t: (key: string, fallback?: string) => string,
|
||||
locale: Locale,
|
||||
): string {
|
||||
// Today and tomorrow on a clock in the calendar's time zone.
|
||||
if (isDisplayToday(date)) return t('calendar.events.today_header', 'Today');
|
||||
if (isDisplayTomorrow(date)) return t('calendar.events.tomorrow_header', 'Tomorrow');
|
||||
return format(date, 'EEEE, MMM d', { locale });
|
||||
// The week separators: "Oct 11 – 17", or "Sep 27 – Oct 3" across months.
|
||||
function formatWeekRange(date: Date, weekStartsOn: 0 | 1 | 6, locale: Locale): string {
|
||||
const start = startOfWeek(date, { weekStartsOn });
|
||||
const end = endOfWeek(date, { weekStartsOn });
|
||||
const sameMonth = start.getMonth() === end.getMonth();
|
||||
return `${format(start, 'MMM d', { locale })} – ${format(end, sameMonth ? 'd' : 'MMM d', { locale })}`;
|
||||
}
|
||||
|
||||
const VIEWABILITY_CONFIG = { itemVisiblePercentThreshold: 1 };
|
||||
@@ -92,8 +95,10 @@ export function AgendaView({
|
||||
isLoading = false,
|
||||
onVisibleDateChange,
|
||||
timeFormat,
|
||||
weekStartsOn = 1,
|
||||
currentUserEmails,
|
||||
onSelectEvent,
|
||||
onCreateAt,
|
||||
}: AgendaViewProps) {
|
||||
const c = useColors();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
@@ -104,15 +109,21 @@ export function AgendaView({
|
||||
[eventsByDay, events],
|
||||
);
|
||||
|
||||
const sections = React.useMemo<DaySection[]>(
|
||||
() =>
|
||||
buildAgendaDays(index, loaded, displayNow()).map((day) => ({
|
||||
...day,
|
||||
key: dayKey(day.date),
|
||||
title: formatDayHeader(day.date, t, locale),
|
||||
})),
|
||||
[index, loaded, t, locale],
|
||||
);
|
||||
// A heading where a month starts and a separator where a week starts,
|
||||
// then the days.
|
||||
const sections = React.useMemo<DaySection[]>(() => {
|
||||
let lastMonth = '';
|
||||
let lastWeek = '';
|
||||
return buildAgendaDays(index, loaded, displayNow()).map((day) => {
|
||||
const month = format(day.date, 'yyyy-MM');
|
||||
const week = dayKey(startOfWeek(day.date, { weekStartsOn }));
|
||||
const monthTitle = month !== lastMonth ? format(day.date, 'LLLL yyyy', { locale }) : null;
|
||||
const weekTitle = week !== lastWeek ? formatWeekRange(day.date, weekStartsOn, locale) : null;
|
||||
lastMonth = month;
|
||||
lastWeek = week;
|
||||
return { ...day, key: dayKey(day.date), monthTitle, weekTitle };
|
||||
});
|
||||
}, [index, loaded, weekStartsOn, locale]);
|
||||
|
||||
const listRef = React.useRef<SectionList<CalendarEvent, DaySection>>(null);
|
||||
const sectionsRef = React.useRef(sections);
|
||||
@@ -297,7 +308,7 @@ export function AgendaView({
|
||||
ref={listRef}
|
||||
sections={sections}
|
||||
keyExtractor={(item, i) => `${item.id}:${i}`}
|
||||
stickySectionHeadersEnabled
|
||||
stickySectionHeadersEnabled={false}
|
||||
// Earlier days are inserted above what the user is reading without
|
||||
// moving it.
|
||||
maintainVisibleContentPosition={{ minIndexForVisible: 0 }}
|
||||
@@ -314,32 +325,42 @@ export function AgendaView({
|
||||
initialNumToRender={12}
|
||||
maxToRenderPerBatch={12}
|
||||
windowSize={11}
|
||||
renderSectionHeader={({ section }) => {
|
||||
const today = isDisplayToday(section.date);
|
||||
return (
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={[styles.sectionTitle, today && styles.sectionTitleToday]}>
|
||||
{section.title}
|
||||
</Text>
|
||||
<Text style={styles.sectionSub}>{format(section.date, 'PP', { locale })}</Text>
|
||||
renderSectionHeader={({ section }) =>
|
||||
section.monthTitle || section.weekTitle ? (
|
||||
<View>
|
||||
{section.monthTitle && <Text style={styles.monthTitle}>{section.monthTitle}</Text>}
|
||||
{section.weekTitle && <Text style={styles.weekTitle}>{section.weekTitle}</Text>}
|
||||
</View>
|
||||
) : null
|
||||
}
|
||||
renderItem={({ item, index: i, section }) => (
|
||||
<View style={[styles.row, i === section.data.length - 1 && styles.rowLast]}>
|
||||
<DateColumn date={section.date} show={i === 0} locale={locale} styles={styles} />
|
||||
<View style={styles.eventCol}>
|
||||
<EventCard
|
||||
event={item}
|
||||
calendars={calendars}
|
||||
timeFormat={timeFormat}
|
||||
currentUserEmails={currentUserEmails}
|
||||
onPress={onSelectEvent}
|
||||
day={section.date}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}}
|
||||
renderItem={({ item }) => (
|
||||
<View style={styles.itemWrap}>
|
||||
<EventCard
|
||||
event={item}
|
||||
calendars={calendars}
|
||||
timeFormat={timeFormat}
|
||||
currentUserEmails={currentUserEmails}
|
||||
onPress={onSelectEvent}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
renderSectionFooter={({ section }) =>
|
||||
section.data.length === 0 ? (
|
||||
<View style={styles.emptyDayWrap}>
|
||||
<Text style={styles.emptyDayText}>{t('calendar.events.no_events', 'No events')}</Text>
|
||||
<View style={[styles.row, styles.rowLast]}>
|
||||
<DateColumn date={section.date} show locale={locale} styles={styles} />
|
||||
<Pressable
|
||||
style={styles.emptyDay}
|
||||
onPress={onCreateAt ? () => onCreateAt(section.date) : undefined}
|
||||
accessibilityRole={onCreateAt ? 'button' : undefined}
|
||||
>
|
||||
<Text style={styles.emptyDayText}>
|
||||
{t('calendar.events.nothing_planned', 'Nothing planned. Tap to create.')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
) : null
|
||||
}
|
||||
@@ -347,24 +368,79 @@ export function AgendaView({
|
||||
);
|
||||
}
|
||||
|
||||
type AgendaStyles = ReturnType<typeof makeStyles>;
|
||||
|
||||
// The day on the left of its first row: weekday over the day number, today
|
||||
// in a filled circle.
|
||||
function DateColumn({
|
||||
date,
|
||||
show,
|
||||
locale,
|
||||
styles,
|
||||
}: {
|
||||
date: Date;
|
||||
show: boolean;
|
||||
locale: Locale;
|
||||
styles: AgendaStyles;
|
||||
}) {
|
||||
if (!show) return <View style={styles.dateCol} />;
|
||||
const today = isDisplayToday(date);
|
||||
return (
|
||||
<View style={styles.dateCol}>
|
||||
<Text style={[styles.dateWeekday, today && styles.dateWeekdayToday]}>
|
||||
{format(date, 'EEE', { locale })}
|
||||
</Text>
|
||||
<View style={[styles.dateNumber, today && styles.dateNumberToday]}>
|
||||
<Text style={[styles.dateNumberText, today && styles.dateNumberTextToday]}>
|
||||
{format(date, 'd')}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const DATE_COL_WIDTH = 52;
|
||||
|
||||
function makeStyles(c: ThemePalette) {
|
||||
return StyleSheet.create({
|
||||
sectionHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'baseline',
|
||||
gap: spacing.sm,
|
||||
monthTitle: {
|
||||
...typography.h2,
|
||||
fontWeight: '500',
|
||||
color: c.text,
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingVertical: spacing.sm,
|
||||
backgroundColor: c.surface,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: c.border,
|
||||
paddingTop: spacing.xxl,
|
||||
paddingBottom: spacing.sm,
|
||||
},
|
||||
sectionTitle: { ...typography.bodyMedium, color: c.text },
|
||||
sectionTitleToday: { color: c.primary },
|
||||
sectionSub: { ...typography.caption, color: c.textMuted },
|
||||
itemWrap: { paddingHorizontal: spacing.lg, paddingTop: spacing.sm },
|
||||
emptyDayWrap: { paddingHorizontal: spacing.lg, paddingVertical: spacing.md },
|
||||
emptyDayText: { ...typography.caption, color: c.textMuted, textAlign: 'center' },
|
||||
weekTitle: {
|
||||
...typography.caption,
|
||||
color: c.textMuted,
|
||||
paddingLeft: DATE_COL_WIDTH + spacing.sm,
|
||||
paddingTop: spacing.md,
|
||||
paddingBottom: spacing.xs,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
paddingRight: spacing.lg,
|
||||
paddingLeft: spacing.sm,
|
||||
},
|
||||
rowLast: { paddingBottom: spacing.sm },
|
||||
dateCol: { width: DATE_COL_WIDTH, alignItems: 'center' },
|
||||
dateWeekday: { ...typography.captionMedium, color: c.textSecondary },
|
||||
dateWeekdayToday: { color: c.primary },
|
||||
dateNumber: {
|
||||
width: 34,
|
||||
height: 34,
|
||||
borderRadius: 17,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
dateNumberToday: { backgroundColor: c.primary },
|
||||
dateNumberText: { fontSize: 20, lineHeight: 26, fontWeight: '500', color: c.text },
|
||||
dateNumberTextToday: { color: c.primaryForeground },
|
||||
eventCol: { flex: 1, marginLeft: spacing.md },
|
||||
emptyDay: { flex: 1, marginLeft: spacing.md, paddingTop: 18 },
|
||||
emptyDayText: { ...typography.body, color: c.textSecondary },
|
||||
emptyWrap: { paddingVertical: spacing.xl, alignItems: 'center' },
|
||||
edge: {
|
||||
paddingHorizontal: spacing.lg,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useColors } from '../../theme/colors';
|
||||
import {
|
||||
eventTimeRange,
|
||||
getEventColor,
|
||||
isTimedEventFullDayOnDate,
|
||||
timePattern,
|
||||
type TimeFormat,
|
||||
} from '../../lib/calendar-utils';
|
||||
@@ -22,6 +23,8 @@ interface EventCardProps {
|
||||
currentUserEmails?: string[];
|
||||
onPress?: (event: CalendarEvent) => void;
|
||||
onLongPress?: (event: CalendarEvent) => void;
|
||||
/** The day the row is listed under: a timed event filling it reads as all day. */
|
||||
day?: Date;
|
||||
}
|
||||
|
||||
function participantCount(event: CalendarEvent): number {
|
||||
@@ -33,12 +36,14 @@ function participantCount(event: CalendarEvent): number {
|
||||
* colour before the title, no card and no bar. Declined and cancelled events
|
||||
* get a hollow dot and a struck-through, muted title.
|
||||
*/
|
||||
export function EventCard({ event, calendars, timeFormat, currentUserEmails, onPress, onLongPress }: EventCardProps) {
|
||||
export function EventCard({ event, calendars, timeFormat, currentUserEmails, onPress, onLongPress, day }: EventCardProps) {
|
||||
const c = useColors();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const { locale, t } = useCalendarLocale();
|
||||
const color = getEventColor(event, calendars);
|
||||
const { start, end, allDay } = eventTimeRange(event);
|
||||
const range = eventTimeRange(event);
|
||||
const { start, end } = range;
|
||||
const allDay = range.allDay || (!!day && isTimedEventFullDayOnDate(event, day));
|
||||
const fmt = timePattern(timeFormat);
|
||||
const time = allDay
|
||||
? t('calendar.events.all_day', 'All day')
|
||||
@@ -59,7 +64,7 @@ export function EventCard({ event, calendars, timeFormat, currentUserEmails, onP
|
||||
<Text style={[styles.title, inactive && styles.titleInactive]} numberOfLines={1}>
|
||||
{event.title || t('calendar.events.no_title', '(No title)')}
|
||||
</Text>
|
||||
{event.showWithoutTime && (
|
||||
{allDay && (
|
||||
<View style={styles.allDayBadge}>
|
||||
<Text style={styles.allDayText}>{t('calendar.events.all_day', 'All day')}</Text>
|
||||
</View>
|
||||
@@ -70,7 +75,7 @@ export function EventCard({ event, calendars, timeFormat, currentUserEmails, onP
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
{!event.showWithoutTime && (
|
||||
{!allDay && (
|
||||
<View style={styles.detailRow}>
|
||||
<Clock size={12} color={c.textMuted} />
|
||||
<Text style={styles.detailText}>{time}</Text>
|
||||
|
||||
@@ -1141,8 +1141,10 @@ export default function CalendarScreen() {
|
||||
eventsByDay={eventsByDay}
|
||||
calendars={calendars}
|
||||
timeFormat={calendarTimeFormat}
|
||||
weekStartsOn={calendarFirstDayOfWeek}
|
||||
currentUserEmails={currentUserEmails}
|
||||
onSelectEvent={handleSelectEvent}
|
||||
onCreateAt={openCreate}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user