feat: change the colour of the birthdays calendar

This commit is contained in:
Linus Rath
2026-10-31 04:31:14 +01:00
parent b6995bb638
commit 574db42652
5 changed files with 30 additions and 6 deletions
@@ -322,8 +322,9 @@ function Section({
const isSubscription = !!isSubscriptionCalendar?.(cal.id);
const isOwn = !cal.isShared && !isBirthday && !isSubscription;
const canSetDefault = !!onSetDefault && isOwn && !cal.isDefault;
// Own calendars recolour server-side; shared ones locally (#345).
const canRecolor = !!onSetColor && (!!cal.isShared || isOwn);
// Own calendars recolour server-side; shared ones and the birthdays
// locally (#345).
const canRecolor = !!onSetColor && (!!cal.isShared || isOwn || isBirthday);
const canRename = !!onRename && isOwn;
const canShare = !!onShare && isOwn && (!cal.myRights || cal.myRights.mayShare !== false);
const canClear = !!onClear && isOwn && isWritableCalendar(cal);
@@ -0,0 +1,11 @@
import { describe, expect, it } from 'vitest';
import { BIRTHDAY_CALENDAR_COLOR, createBirthdayCalendar } from '../birthday-calendar';
import { useSettingsStore } from '../../stores/settings-store';
describe('birthday calendar colour', () => {
it('takes the colour the user picked and keeps yellow until then', () => {
expect(createBirthdayCalendar('Birthdays', '#ef4444').color).toBe('#ef4444');
expect(createBirthdayCalendar('Birthdays').color).toBe(BIRTHDAY_CALENDAR_COLOR);
expect(useSettingsStore.getState().birthdayCalendarColor).toBe(BIRTHDAY_CALENDAR_COLOR);
});
});
+2 -2
View File
@@ -6,11 +6,11 @@ export const BIRTHDAY_CALENDAR_ID = '__birthday-calendar__';
export const BIRTHDAY_CALENDAR_COLOR = '#eab308'; // yellow
// A read-only virtual calendar holding generated birthday events.
export function createBirthdayCalendar(name = 'Birthdays'): Calendar {
export function createBirthdayCalendar(name = 'Birthdays', color?: string): Calendar {
return {
id: BIRTHDAY_CALENDAR_ID,
name,
color: BIRTHDAY_CALENDAR_COLOR,
color: color || BIRTHDAY_CALENDAR_COLOR,
sortOrder: 999,
isSubscribed: true,
isVisible: true,
+7 -2
View File
@@ -165,6 +165,7 @@ export default function CalendarScreen() {
const calendarFreeScroll = useSettingsStore((s) => s.calendarFreeScroll);
const calendarTimeFormat = useSettingsStore((s) => s.calendarTimeFormat);
const showBirthdayCalendar = useSettingsStore((s) => s.showBirthdayCalendar);
const birthdayCalendarColor = useSettingsStore((s) => s.birthdayCalendarColor);
const enableCalendarTasks = useSettingsStore((s) => s.enableCalendarTasks);
const sharedCalendarColors = useSettingsStore((s) => s.sharedCalendarColors);
const setSharedCalendarColor = useSettingsStore((s) => s.setSharedCalendarColor);
@@ -318,8 +319,8 @@ export default function CalendarScreen() {
}, [storeCalendars, sharedCalendarColors, setSharedCalendarColor]);
const allCalendars = React.useMemo(
() => (showBirthdayCalendar ? [...displayCalendars, createBirthdayCalendar()] : displayCalendars),
[displayCalendars, showBirthdayCalendar],
() => (showBirthdayCalendar ? [...displayCalendars, createBirthdayCalendar(undefined, birthdayCalendarColor)] : displayCalendars),
[displayCalendars, showBirthdayCalendar, birthdayCalendarColor],
);
// Tasks with a due date are overlaid on the grid as chips (webmail's
// showTasksOnCalendar); tapping one opens the tasks sheet on that task.
@@ -884,6 +885,10 @@ export default function CalendarScreen() {
const handleSetCalendarColor = React.useCallback(
(cal: Calendar, color: string) => {
if (cal.id === BIRTHDAY_CALENDAR_ID) {
useSettingsStore.getState().updateSetting('birthdayCalendarColor', color);
return;
}
if (cal.isShared) {
// Per-viewer recolor (#345): the owner's colour is left alone.
setSharedCalendarColor(sharedCalendarColorKey(cal), color);
+7
View File
@@ -309,6 +309,10 @@ interface PersistedSettings {
// Hard cap on the on-disk body cache, in megabytes. When a sync pushes the
// cache past this, the oldest messages are evicted to fit.
offlineCacheMaxMB: number;
// calendar: the colour of the virtual Birthdays calendar (webmail
// birthdayCalendarColor).
birthdayCalendarColor: string;
}
const DEFAULT_PERSISTED: PersistedSettings = {
@@ -463,6 +467,9 @@ const DEFAULT_PERSISTED: PersistedSettings = {
offlineCacheEnabled: false,
offlineCacheDays: 7,
offlineCacheMaxMB: 50,
// calendar:
birthdayCalendarColor: '#eab308',
};
export interface SettingsState extends PersistedSettings {