mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-02 00:06:23 +00:00
feat: draw calendar colours in the colour-blind-safe palette when that setting is on
This commit is contained in:
@@ -14,7 +14,7 @@ import { format } from 'date-fns';
|
||||
import { ArrowLeft, MapPin, Repeat, Search, X } from '../icons';
|
||||
import type { Calendar, CalendarEvent } from '../../api/types';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { useCalendarStore, searchCalendarEvents } from '../../stores/calendar-store';
|
||||
import { useCalendarLocale } from '../../lib/calendar-locale';
|
||||
import { formatCalendarDay } from '../../lib/calendar-system';
|
||||
@@ -62,6 +62,7 @@ export function CalendarSearchSheet({
|
||||
onClose,
|
||||
}: CalendarSearchSheetProps) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const { locale, t, system } = useCalendarLocale();
|
||||
const loaded = useCalendarStore((s) => s.events);
|
||||
@@ -134,7 +135,7 @@ export function CalendarSearchSheet({
|
||||
const start = getEventStartDate(event);
|
||||
const end = getEventEndDate(event);
|
||||
const calendar = calendars.find((cal) => cal.id === getPrimaryCalendarId(event));
|
||||
const color = getEventColor(event, calendars);
|
||||
const color = paint(getEventColor(event, calendars));
|
||||
const inactive = isInactiveEvent(event, currentUserEmails);
|
||||
const place = event.locations ? Object.values(event.locations)[0]?.name : undefined;
|
||||
const meta = [place, calendar?.name].filter(Boolean).join(' · ');
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from '../icons';
|
||||
import type { Calendar } from '../../api/types';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { useAnimDuration } from '../../theme/dynamic';
|
||||
import { CALENDAR_COLOR_PALETTE, calendarColorName, getCalendarColor } from '../../lib/calendar-utils';
|
||||
import { BIRTHDAY_CALENDAR_ID } from '../../lib/birthday-calendar';
|
||||
@@ -309,6 +309,7 @@ function Section({
|
||||
isSubscriptionCalendar?: (calendarId: string) => boolean;
|
||||
}) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const t = useLocaleStore((s) => s.t);
|
||||
// Neighbours a calendar can move past: the server calendars of this section.
|
||||
@@ -352,8 +353,8 @@ function Section({
|
||||
style={[
|
||||
styles.swatch,
|
||||
{
|
||||
backgroundColor: visible ? getCalendarColor(cal) : 'transparent',
|
||||
borderColor: getCalendarColor(cal),
|
||||
backgroundColor: visible ? paint(getCalendarColor(cal)) : 'transparent',
|
||||
borderColor: paint(getCalendarColor(cal)),
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Clock, MapPin, Users } from '../icons';
|
||||
import { format } from 'date-fns';
|
||||
import type { Calendar, CalendarEvent } from '../../api/types';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import {
|
||||
eventTimeRange,
|
||||
getEventColor,
|
||||
@@ -38,9 +38,10 @@ function participantCount(event: CalendarEvent): number {
|
||||
*/
|
||||
export function EventCard({ event, calendars, timeFormat, currentUserEmails, onPress, onLongPress, day }: EventCardProps) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const { locale, t } = useCalendarLocale();
|
||||
const color = getEventColor(event, calendars);
|
||||
const color = paint(getEventColor(event, calendars));
|
||||
const range = eventTimeRange(event);
|
||||
const { start, end } = range;
|
||||
const allDay = range.allDay || (!!day && isTimedEventFullDayOnDate(event, day));
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
import { format, type Locale } from 'date-fns';
|
||||
import type { Calendar, CalendarEvent } from '../../api/types';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import {
|
||||
eventTimeRange,
|
||||
getEventColor,
|
||||
@@ -140,6 +140,7 @@ export function EventDetailSheet({
|
||||
onSaveNote,
|
||||
}: EventDetailSheetProps) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const { locale, t } = useCalendarLocale();
|
||||
const [rsvpBusy, setRsvpBusy] = React.useState(false);
|
||||
const [noteOpen, setNoteOpen] = React.useState(false);
|
||||
@@ -200,7 +201,7 @@ export function EventDetailSheet({
|
||||
|
||||
if (!event) return null;
|
||||
|
||||
const color = getEventColor(event, calendars);
|
||||
const color = paint(getEventColor(event, calendars));
|
||||
const calendarId = getPrimaryCalendarId(event);
|
||||
const calendar = calendars.find((c) => c.id === calendarId);
|
||||
const range = formatRange(event, timeFormat, locale);
|
||||
|
||||
@@ -36,7 +36,7 @@ import type {
|
||||
VirtualLocation,
|
||||
} from '../../api/types';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import {
|
||||
getCalendarColor,
|
||||
getPrimaryCalendarId,
|
||||
@@ -189,6 +189,7 @@ export function EventModal({
|
||||
onClose,
|
||||
}: EventModalProps) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const { locale, t } = useCalendarLocale();
|
||||
const timeFormat = useSettingsStore((s) => s.calendarTimeFormat);
|
||||
@@ -588,7 +589,7 @@ export function EventModal({
|
||||
<View
|
||||
style={[
|
||||
styles.calendarSwatch,
|
||||
{ backgroundColor: getCalendarColor(selectedCalendar) },
|
||||
{ backgroundColor: paint(getCalendarColor(selectedCalendar)) },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
@@ -614,7 +615,7 @@ export function EventModal({
|
||||
<View
|
||||
style={[
|
||||
styles.calendarSwatch,
|
||||
{ backgroundColor: getCalendarColor(c) },
|
||||
{ backgroundColor: paint(getCalendarColor(c)) },
|
||||
]}
|
||||
/>
|
||||
<Text style={styles.popoverRowText}>{c.name}</Text>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { uploadBlob } from '../../api/blob';
|
||||
import { parseCalendarBlob } from '../../api/calendar';
|
||||
import Button from '../Button';
|
||||
import { spacing, radius, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { getCalendarColor } from '../../lib/calendar-utils';
|
||||
import { isWritableCalendar } from '../../lib/calendar-editability';
|
||||
import { ImportRefusedError, type ImportResult } from '../../stores/calendar-store';
|
||||
@@ -58,6 +58,7 @@ function eventDateLabel(event: Partial<CalendarEvent>, locale: Locale): string |
|
||||
|
||||
export function ICalImportSheet({ visible, onClose, calendars, onImport, onImported }: Props) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const insets = useSafeAreaInsets();
|
||||
const t = useLocaleStore((s) => s.t);
|
||||
@@ -308,7 +309,7 @@ export function ICalImportSheet({ visible, onClose, calendars, onImport, onImpor
|
||||
onPress={() => setCalendarId(cal.id)}
|
||||
style={[styles.calChip, cal.id === calendarId && styles.calChipActive]}
|
||||
>
|
||||
<View style={[styles.calSwatch, { backgroundColor: getCalendarColor(cal) }]} />
|
||||
<View style={[styles.calSwatch, { backgroundColor: paint(getCalendarColor(cal)) }]} />
|
||||
<Text style={styles.calChipText} numberOfLines={1}>{cal.name}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { X, Plus, RefreshCw, Trash2, Rss, AlertTriangle } from '../icons';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import Button from '../Button';
|
||||
import { spacing, radius, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { CALENDAR_COLOR_PALETTE, calendarColorName } from '../../lib/calendar-utils';
|
||||
import { useCalendarLocale } from '../../lib/calendar-locale';
|
||||
import {
|
||||
@@ -37,6 +37,7 @@ interface Props {
|
||||
|
||||
export function ICalSubscriptionSheet({ visible, onClose }: Props) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const insets = useSafeAreaInsets();
|
||||
const { locale, t } = useCalendarLocale();
|
||||
@@ -257,7 +258,7 @@ export function ICalSubscriptionSheet({ visible, onClose }: Props) {
|
||||
const busy = !!syncing[sub.id];
|
||||
return (
|
||||
<View key={sub.id} style={styles.subRow}>
|
||||
<View style={[styles.subSwatch, { backgroundColor: sub.color || c.primary }]} />
|
||||
<View style={[styles.subSwatch, { backgroundColor: sub.color ? paint(sub.color) : c.primary }]} />
|
||||
<Pressable style={{ flex: 1, minWidth: 0 }} onPress={() => startEdit(sub)}>
|
||||
<Text style={styles.subName} numberOfLines={1}>{sub.name}</Text>
|
||||
<Text style={styles.subMeta} numberOfLines={1}>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from 'date-fns';
|
||||
import type { Calendar, CalendarEvent } from '../../api/types';
|
||||
import { componentSizes, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { useSettingsStore } from '../../stores/settings-store';
|
||||
import {
|
||||
buildEventDayIndex,
|
||||
@@ -157,6 +157,7 @@ function MonthWeekRowInner({
|
||||
const { width } = useWindowDimensions();
|
||||
// "Light event colors" (webmail calendarTintedEvents).
|
||||
const tinted = useSettingsStore((s) => s.calendarTintedEvents);
|
||||
const paint = useCvdColor();
|
||||
const chipTime = monthChipShowsTime(true, width / 7);
|
||||
if (variant === 'full') {
|
||||
return (
|
||||
@@ -241,7 +242,7 @@ function MonthWeekRowInner({
|
||||
{visible.map((event, idx) => (
|
||||
<View
|
||||
key={`${event.id}-${idx}`}
|
||||
style={[styles.dot, { backgroundColor: getEventColor(event, calendars) }]}
|
||||
style={[styles.dot, { backgroundColor: paint(getEventColor(event, calendars)) }]}
|
||||
/>
|
||||
))}
|
||||
{overflow > 0 && (
|
||||
@@ -255,7 +256,7 @@ function MonthWeekRowInner({
|
||||
// Solid bars in the calendar colour; declined and
|
||||
// cancelled events outlined (repos/branding/APP.md).
|
||||
const inactive = isInactiveEvent(event, currentUserEmails);
|
||||
const colors = eventBlockColors(getEventColor(event, calendars), inactive, c, tinted);
|
||||
const colors = eventBlockColors(paint(getEventColor(event, calendars)), inactive, c, tinted);
|
||||
return (
|
||||
<View
|
||||
key={`${event.id}-${idx}`}
|
||||
@@ -331,6 +332,7 @@ function FullWeekRow({
|
||||
const { t, system } = useCalendarLocale();
|
||||
// "Light event colors" (webmail calendarTintedEvents).
|
||||
const tinted = useSettingsStore((s) => s.calendarTintedEvents);
|
||||
const paint = useCvdColor();
|
||||
const showTime = monthChipShowsTime(showTimeInMonthView, width / 7);
|
||||
return (
|
||||
<View style={[styles.fullWeekRow, { height }]}>
|
||||
@@ -371,7 +373,7 @@ function FullWeekRow({
|
||||
</View>
|
||||
{dayEvents.slice(0, shown).map((event, idx) => {
|
||||
const inactive = isInactiveEvent(event, currentUserEmails);
|
||||
const colors = eventBlockColors(getEventColor(event, calendars), inactive, c, tinted);
|
||||
const colors = eventBlockColors(paint(getEventColor(event, calendars)), inactive, c, tinted);
|
||||
// No time on events that fill the whole day.
|
||||
const time = showTime && !event.showWithoutTime && !isTimedEventFullDayOnDate(event, d)
|
||||
? `${format(getEventStartDate(event), timePattern(timeFormat), { locale })} `
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import { format, isBefore, startOfDay } from 'date-fns';
|
||||
import type { Calendar, CalendarEvent } from '../../api/types';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { getCalendarColor, getTaskDueDisplayDate, timePattern, type TimeFormat } from '../../lib/calendar-utils';
|
||||
import { displayNow, isDisplayToday } from '../../lib/calendar-timezone';
|
||||
import {
|
||||
@@ -109,6 +109,7 @@ export function TasksSheet({
|
||||
onDelete,
|
||||
}: TasksSheetProps) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const { locale, t } = useCalendarLocale();
|
||||
const slideY = React.useRef(new Animated.Value(Dimensions.get('window').height)).current;
|
||||
@@ -373,7 +374,7 @@ export function TasksSheet({
|
||||
onPress={() => setEditor((e) => ({ ...e, calendarId: cal.id }))}
|
||||
style={[styles.calChip, cal.id === editor.calendarId && styles.calChipActive]}
|
||||
>
|
||||
<View style={[styles.calSwatch, { backgroundColor: getCalendarColor(cal) }]} />
|
||||
<View style={[styles.calSwatch, { backgroundColor: paint(getCalendarColor(cal)) }]} />
|
||||
<Text style={styles.calChipText} numberOfLines={1}>{cal.name}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { differenceInCalendarDays, format, type Locale } from 'date-fns';
|
||||
import type { Calendar, CalendarEvent } from '../../api/types';
|
||||
import { spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { useSettingsStore } from '../../stores/settings-store';
|
||||
import { useCalendarLocale } from '../../lib/calendar-locale';
|
||||
import { dayNumberLabel } from '../../lib/calendar-system';
|
||||
@@ -125,6 +125,7 @@ function TimeGridScrollViewInner({
|
||||
draftResetKey = 0,
|
||||
}: TimeGridScrollViewProps) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
// "Light event colors" (webmail calendarTintedEvents).
|
||||
const tinted = useSettingsStore((s) => s.calendarTintedEvents);
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
@@ -455,7 +456,7 @@ function TimeGridScrollViewInner({
|
||||
key={`${segment.event.id}:${segment.startIndex}:${segment.row}`}
|
||||
title={segment.event.title || t('calendar.events.no_title', '(No title)')}
|
||||
colors={eventBlockColors(
|
||||
getEventColor(segment.event, calendars),
|
||||
paint(getEventColor(segment.event, calendars)),
|
||||
isInactiveEvent(segment.event, currentUserEmails),
|
||||
c,
|
||||
tinted,
|
||||
@@ -620,6 +621,7 @@ const DayColumn = React.memo(function DayColumn({
|
||||
const { locale, t } = useCalendarLocale();
|
||||
// "Light event colors" (webmail calendarTintedEvents).
|
||||
const tinted = useSettingsStore((s) => s.calendarTintedEvents);
|
||||
const paint = useCvdColor();
|
||||
const handleLongPress = React.useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
onLongPressAt?.(day, hourAtOffset(e.nativeEvent.locationY, HOUR_HEIGHT));
|
||||
@@ -650,7 +652,7 @@ const DayColumn = React.memo(function DayColumn({
|
||||
? minutesToTimeLabel(startMinutes, timeFormat)
|
||||
: null}
|
||||
colors={eventBlockColors(
|
||||
getEventColor(event, calendars),
|
||||
paint(getEventColor(event, calendars)),
|
||||
isInactiveEvent(event, currentUserEmails),
|
||||
colors,
|
||||
tinted,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { addDays, format, isSameDay, startOfWeek } from 'date-fns';
|
||||
import { displayNow, displayNowMinutes, isDisplayToday } from '../../lib/calendar-timezone';
|
||||
import type { Calendar, CalendarEvent } from '../../api/types';
|
||||
import { spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { useSettingsStore } from '../../stores/settings-store';
|
||||
import { useCalendarLocale } from '../../lib/calendar-locale';
|
||||
import { dayNumberLabel } from '../../lib/calendar-system';
|
||||
@@ -68,6 +68,7 @@ function WeekViewInner({
|
||||
currentUserEmails,
|
||||
}: WeekViewProps) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
// "Light event colors" (webmail calendarTintedEvents).
|
||||
const tinted = useSettingsStore((s) => s.calendarTintedEvents);
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
@@ -213,7 +214,7 @@ function WeekViewInner({
|
||||
key={`${segment.event.id}:${segment.startIndex}:${segment.row}`}
|
||||
title={segment.event.title || t('calendar.events.no_title', '(No title)')}
|
||||
colors={eventBlockColors(
|
||||
getEventColor(segment.event, calendars),
|
||||
paint(getEventColor(segment.event, calendars)),
|
||||
isInactiveEvent(segment.event, currentUserEmails),
|
||||
c,
|
||||
tinted,
|
||||
@@ -289,7 +290,7 @@ function WeekViewInner({
|
||||
? minutesToTimeLabel(startMinutes, timeFormat)
|
||||
: null}
|
||||
colors={eventBlockColors(
|
||||
getEventColor(event, calendars),
|
||||
paint(getEventColor(event, calendars)),
|
||||
isInactiveEvent(event, currentUserEmails),
|
||||
c,
|
||||
tinted,
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { cvdColor } from '../../../lib/cvd-palette';
|
||||
|
||||
// Checked on the source: the suite has no React Native renderer. Every place
|
||||
// that draws a calendar's colour runs it through useCvdColor() first, so the
|
||||
// colour-blind-safe setting recolours calendars as its description promises
|
||||
// (webmail cf4d02b49). The colour pickers keep the raw colours.
|
||||
const src = path.resolve(__dirname, '../../..');
|
||||
const DRAWERS = [
|
||||
'components/calendar/EventCard.tsx',
|
||||
'components/calendar/EventDetailSheet.tsx',
|
||||
'components/calendar/MonthView.tsx',
|
||||
'components/calendar/WeekView.tsx',
|
||||
'components/calendar/TimeGridScrollView.tsx',
|
||||
'components/calendar/CalendarSearchSheet.tsx',
|
||||
'components/calendar/CalendarSidebarDrawer.tsx',
|
||||
'components/calendar/EventModal.tsx',
|
||||
'components/calendar/TasksSheet.tsx',
|
||||
'components/calendar/ICalImportSheet.tsx',
|
||||
'components/contacts/ContactActivity.tsx',
|
||||
'components/email/CalendarInvitationBanner.tsx',
|
||||
'components/email/structured-mail/booking-calendar-actions.tsx',
|
||||
];
|
||||
|
||||
function colourCalls(source: string): string[] {
|
||||
return source
|
||||
.split(/\r?\n/)
|
||||
.filter((line) => !/^\s*import\b/.test(line) && /\bget(Event|Calendar)Color\(/.test(line))
|
||||
.map((line) => line.trim());
|
||||
}
|
||||
|
||||
describe('calendar colours in the colour-blind-safe palette', () => {
|
||||
it.each(DRAWERS)('%s paints every calendar colour it draws', (file) => {
|
||||
const source = fs.readFileSync(path.join(src, file), 'utf8');
|
||||
expect(source).toMatch(/const paint = useCvdColor\(\);/);
|
||||
const calls = colourCalls(source);
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
for (const line of calls) {
|
||||
expect(line).toMatch(/paint\(get(Event|Calendar)Color\(/);
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps the raw colour in the calendar editor, which is a colour picker', () => {
|
||||
const source = fs.readFileSync(path.join(src, 'components/calendar/CalendarEditSheet.tsx'), 'utf8');
|
||||
expect(source).not.toMatch(/useCvdColor/);
|
||||
});
|
||||
|
||||
it('paints a subscription swatch through the hook', () => {
|
||||
const source = fs.readFileSync(path.join(src, 'components/calendar/ICalSubscriptionSheet.tsx'), 'utf8');
|
||||
expect(source).toMatch(/paint\(sub\.color\)/);
|
||||
});
|
||||
|
||||
it('maps a calendar green to the palette green only when the setting is on', () => {
|
||||
expect(cvdColor('#22c55e', false)).toBe('#22c55e');
|
||||
expect(cvdColor('#22c55e', true).toLowerCase()).not.toBe('#22c55e');
|
||||
});
|
||||
});
|
||||
@@ -17,7 +17,7 @@ import { singleLine } from '../../lib/single-line';
|
||||
import { prefetchMessage } from '../../lib/email-detail-cache';
|
||||
import SenderAvatar from '../SenderAvatar';
|
||||
import { radius, spacing, typography, componentSizes, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
|
||||
const EMAIL_LIMIT = 5;
|
||||
const EVENT_LIMIT = 5;
|
||||
@@ -92,6 +92,7 @@ interface Props {
|
||||
|
||||
export function ContactActivity({ contact }: Props) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const navigation = useNavigation<Nav>();
|
||||
const t = useLocaleStore((s) => s.t);
|
||||
@@ -254,7 +255,7 @@ export function ContactActivity({ contact }: Props) {
|
||||
upcomingEvents.map((event) => (
|
||||
<View key={event.id} style={styles.eventRow}>
|
||||
<View
|
||||
style={[styles.eventDot, { backgroundColor: getEventColor(event, calendars) }]}
|
||||
style={[styles.eventDot, { backgroundColor: paint(getEventColor(event, calendars)) }]}
|
||||
/>
|
||||
<Text style={styles.eventTime}>{formatEventTime(event, t)}</Text>
|
||||
<View style={styles.eventInfo}>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { format, isSameDay } from 'date-fns';
|
||||
import type { Calendar, Email, CalendarEvent } from '../../api/types';
|
||||
import { spacing, radius, typography, type ThemePalette } from '../../theme/tokens';
|
||||
import { useColors } from '../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../theme/colors';
|
||||
import { fetchCalendarBlobText, findEventsByUid, parseCalendarBlob } from '../../api/calendar';
|
||||
import { useCalendarStore } from '../../stores/calendar-store';
|
||||
import { useSettingsStore } from '../../stores/settings-store';
|
||||
@@ -72,6 +72,7 @@ function trustReasonText(
|
||||
|
||||
export function CalendarInvitationBanner({ email, jmapAccountId }: Props) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const t = useLocaleStore((s) => s.t);
|
||||
const locale = useLocaleStore((s) => s.locale);
|
||||
@@ -390,7 +391,7 @@ export function CalendarInvitationBanner({ email, jmapAccountId }: Props) {
|
||||
{state !== 'done' && !existing && targetCalendar && candidates.length > 1 && (
|
||||
<View>
|
||||
<Pressable style={styles.calendarPicker} onPress={() => setPickerOpen((v) => !v)}>
|
||||
<View style={[styles.calendarSwatch, { backgroundColor: getCalendarColor(targetCalendar) }]} />
|
||||
<View style={[styles.calendarSwatch, { backgroundColor: paint(getCalendarColor(targetCalendar)) }]} />
|
||||
<Text style={styles.calendarPickerText} numberOfLines={1}>{targetCalendar.name}</Text>
|
||||
<ChevronDown size={14} color={c.textMuted} />
|
||||
</Pressable>
|
||||
@@ -402,7 +403,7 @@ export function CalendarInvitationBanner({ email, jmapAccountId }: Props) {
|
||||
style={[styles.calendarRow, cal.id === targetCalendar.id && styles.calendarRowActive]}
|
||||
onPress={() => { setCalendarId(cal.id); setPickerOpen(false); }}
|
||||
>
|
||||
<View style={[styles.calendarSwatch, { backgroundColor: getCalendarColor(cal) }]} />
|
||||
<View style={[styles.calendarSwatch, { backgroundColor: paint(getCalendarColor(cal)) }]} />
|
||||
<Text style={styles.calendarPickerText} numberOfLines={1}>{cal.name}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getCalendarColor } from '../../../lib/calendar-utils';
|
||||
import type { StructuredCard } from '../../../lib/structured-mail';
|
||||
import type { RootStackParamList } from '../../../navigation/types';
|
||||
import { useLocaleStore } from '../../../stores/locale-store';
|
||||
import { useColors } from '../../../theme/colors';
|
||||
import { useColors, useCvdColor } from '../../../theme/colors';
|
||||
import { radius, spacing, typography, type ThemePalette } from '../../../theme/tokens';
|
||||
import { ArrowRight, CalendarCheck, CalendarDays, CalendarPlus, CalendarX, ChevronDown } from '../../icons';
|
||||
import { useBookingCalendar } from './use-booking-calendar';
|
||||
@@ -27,6 +27,7 @@ type Nav = NativeStackNavigationProp<RootStackParamList>;
|
||||
*/
|
||||
export function BookingCalendarActions({ booking, email }: { booking: StructuredCard; email: Pick<Email, 'from' | 'subject'> }) {
|
||||
const c = useColors();
|
||||
const paint = useCvdColor();
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const t = useLocaleStore((s) => s.t);
|
||||
const navigation = useNavigation<Nav>();
|
||||
@@ -144,7 +145,7 @@ export function BookingCalendarActions({ booking, email }: { booking: Structured
|
||||
accessibilityLabel={cal.name}
|
||||
testID="booking-calendar-choice"
|
||||
>
|
||||
<View style={[styles.swatch, { backgroundColor: getCalendarColor(cal) }]} />
|
||||
<View style={[styles.swatch, { backgroundColor: paint(getCalendarColor(cal)) }]} />
|
||||
<Text style={styles.pickerText} numberOfLines={1}>{cal.name}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user