Extend native Discord command screens and same-message navigation

This commit is contained in:
syntaxbullet
2026-10-26 20:16:52 +01:00
parent 17b3714861
commit 5c7ac488b5
16 changed files with 449 additions and 202 deletions

View File

@@ -2,7 +2,7 @@ import { addDays } from '../habits/calendar';
import { dateSchema, type Schedule } from '../habits/contracts';
import { dailyAction, detail } from './daily';
import { fields, type Context, type ControlData, type Message, type Reply } from './types';
import { button, message, modal, pages, row, select, text } from './ui';
import { button, screen, modal, pages, row, text, componentMessage, display, panel, section, listSize, listPage, pageLabel } from './ui';
const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
function scheduleLabel(schedule: Schedule): string {
@@ -14,11 +14,15 @@ function scheduleLabel(schedule: Schedule): string {
export function history(c: Context, data: ControlData = {}): Reply {
const all = c.service.list(true);
const page = Math.max(0, Math.min(data.page ?? 0, Math.ceil(all.length / 10) - 1));
const state: ControlData = { date: data.date ?? c.service.today, page, mode: 'history', today: false };
const visible = all.slice(page * 10, page * 10 + 10);
return message(c, `**History** · ${text(c.service.user.timezone)}\nChoose a habit, then enter a calendar date. Archived habits are included.${all.length ? `\nPage ${page + 1} of ${Math.ceil(all.length / 10)}` : '\nNo habits yet. Use /habits to create one.'}`,
[...(visible.length ? [select(c, 'Choose a habit', 'history.select', state, visible.map(h => ({ id: h.id, name: h.name, description: `${h.archived ? 'Archived · ' : ''}Tracked since ${h.createdDate}` })))] : []), ...pages(c, 'history', state, all.length)]);
const page = listPage(data.page, all.length);
const state: ControlData = { date: data.date ?? c.service.today, mode: 'history', today: false };
return componentMessage(c, [
panel([display(`## History\n${text(c.service.user.timezone)} · ${pageLabel(page, all.length)}\n${all.length ? 'Open a habit to inspect or correct a date. Archived habits are included.' : 'No habits yet. Use /habits to create one.'}`)]),
...all.slice(page * listSize, (page + 1) * listSize).map(h => panel([
section(`**${text(h.name)}**\n${h.archived ? 'Archived · ' : ''}Tracked since ${h.createdDate}`, button(c, 'Open history', 'history.detail', { ...state, habitId: h.id })),
], c.service.settings(h.id).mainColor)),
...pages(c, 'history', { ...state, page }, all.length, listSize),
]);
}
function dateForm(c: Context, data: ControlData): Reply {
@@ -43,7 +47,13 @@ function withNavigation(c: Context, data: ControlData, reply: Reply): Reply {
: !d.due ? 'Read-only · Day off; nothing is scheduled for this date.' : '';
const body = reply.data as Message;
const requirements = d.requirements ? `${d.method === 'manual' ? 'Checkbox' : d.method === 'count' ? 'Count' : 'Tasks'} · ${scheduleLabel(d.requirements.schedule)}\n` : '';
return { ...reply, data: { ...body, content: `${requirements}${text(d.timezone)}\n${explanation ? explanation + '\n' : ''}${body.content ?? ''}`.slice(0, 2000), components: [...(body.components ?? []), row(...navigation)] } };
const [card, ...rest] = body.components ?? [];
if (card?.type === 17) {
const children = card.components as import('./types').DiscordComponent[];
const metadata = `${requirements}${explanation}`.trim();
return { ...reply, data: { ...body, components: [{ ...card, components: [children[0]!, ...(metadata ? [display(metadata)] : []), ...children.slice(1)] }, ...rest, row(...navigation)] } };
}
return reply;
}
export function historyAction(c: Context, action: string, data: ControlData): Reply | undefined {
@@ -52,7 +62,7 @@ export function historyAction(c: Context, action: string, data: ControlData): Re
if (action === 'history.form') return dateForm(c, data);
if (action === 'history.date') {
const parsed = dateSchema.safeParse(fields(c.interaction).date?.trim());
if (!parsed.success) return message(c, 'Enter a real calendar date as YYYY-MM-DD, from 1970 through 9998.', [row(button(c, 'Change date', 'history.form', data))]);
if (!parsed.success) return screen(c, 'Enter a real calendar date as YYYY-MM-DD, from 1970 through 9998.', [row(button(c, 'Change date', 'history.form', data))]);
data = { ...data, date: parsed.data, page: 0 };
action = 'history.detail';
}