added support for tasks

This commit is contained in:
dohsimpson
2025-01-22 17:59:59 -05:00
parent 3b33719e1a
commit d3502e284d
19 changed files with 223 additions and 105 deletions

View File

@@ -19,6 +19,15 @@ import {
getCompletionsForToday,
getISODate
} from "@/lib/utils";
import { atomWithStorage } from "jotai/utils";
export interface BrowserSettings {
viewType: ViewType
}
export const browserSettingsAtom = atomWithStorage('browserSettings', {
viewType: 'habits'
} as BrowserSettings)
export const settingsAtom = atom(getDefaultSettings());
export const habitsAtom = atom(getDefaultHabitsData());
@@ -120,11 +129,3 @@ export const pomodoroTodayCompletionsAtom = atom((get) => {
timezone: settings.system.timezone
})
})
export interface TransientSettings {
viewType: ViewType
}
export const transientSettingsAtom = atom<TransientSettings>({
viewType: 'habits'
})

View File

@@ -1,4 +1,7 @@
import { CheckSquare, Target } from "lucide-react"
export const INITIAL_RECURRENCE_RULE = 'daily'
export const INITIAL_DUE = 'today'
export const RECURRENCE_RULE_MAP: { [key: string]: string } = {
'daily': 'FREQ=DAILY',
@@ -8,3 +11,11 @@ export const RECURRENCE_RULE_MAP: { [key: string]: string } = {
'': 'invalid',
}
export const DUE_MAP: { [key: string]: string } = {
'tom': 'tomorrow',
'tod': 'today',
'yes': 'yesterday',
}
export const HabitIcon = Target
export const TaskIcon = CheckSquare;

View File

@@ -6,6 +6,7 @@ export type Habit = {
coinReward: number
targetCompletions?: number // Optional field, default to 1
completions: string[] // Array of UTC ISO date strings
isTask?: boolean // mark the habit as a task
}
@@ -18,7 +19,7 @@ export type WishlistItemType = {
coinCost: number
}
export type TransactionType = 'HABIT_COMPLETION' | 'HABIT_UNDO' | 'WISH_REDEMPTION' | 'MANUAL_ADJUSTMENT';
export type TransactionType = 'HABIT_COMPLETION' | 'HABIT_UNDO' | 'WISH_REDEMPTION' | 'MANUAL_ADJUSTMENT' | 'TASK_COMPLETION' | 'TASK_UNDO';
export interface CoinTransaction {
id: string;

View File

@@ -1,9 +1,10 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import { DateTime } from "luxon"
import { DateTime, DateTimeFormatOptions } from "luxon"
import { datetime, RRule } from 'rrule'
import { Freq, Habit, CoinTransaction } from '@/lib/types'
import { INITIAL_RECURRENCE_RULE, RECURRENCE_RULE_MAP } from "./constants"
import { DUE_MAP, INITIAL_RECURRENCE_RULE, RECURRENCE_RULE_MAP } from "./constants"
import * as chrono from 'chrono-node';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
@@ -41,9 +42,13 @@ export function d2t({ dateTime, timezone = 'utc' }: { dateTime: DateTime, timezo
}
// convert datetime object to string, mostly for display
export function d2s({ dateTime, format, timezone }: { dateTime: DateTime, format?: string, timezone: string }) {
export function d2s({ dateTime, format, timezone }: { dateTime: DateTime, format?: string | DateTimeFormatOptions, timezone: string }) {
if (format) {
return dateTime.setZone(timezone).toFormat(format);
if (typeof format === 'string') {
return dateTime.setZone(timezone).toFormat(format);
} else {
return dateTime.setZone(timezone).toLocaleString(format);
}
}
return dateTime.setZone(timezone).toLocaleString(DateTime.DATETIME_MED);
}
@@ -204,6 +209,17 @@ export function serializeRRule(rrule: RRule) {
return rrule.toString()
}
export function parseNaturalLanguageDate({ text, timezone }: { text: string, timezone: string }) {
if (DUE_MAP[text]) {
text = DUE_MAP[text]
}
const now = getNow({ timezone })
const due = chrono.parseDate(text, { instant: now.toJSDate(), timezone })
if (!due) throw Error('invalid rule')
// return d2s({ dateTime: DateTime.fromJSDate(due), timezone, format: DateTime.DATE_MED_WITH_WEEKDAY })
return DateTime.fromJSDate(due).setZone(timezone)
}
export function isHabitDue({
habit,
timezone,
@@ -213,6 +229,11 @@ export function isHabitDue({
timezone: string
date: DateTime
}): boolean {
if (habit.isTask) {
// For tasks, frequency is stored as a UTC ISO timestamp
const taskDueDate = t2d({ timestamp: habit.frequency, timezone })
return isSameDate(taskDueDate, date);
}
const startOfDay = date.setZone(timezone).startOf('day')
const endOfDay = date.setZone(timezone).endOf('day')
@@ -244,6 +265,10 @@ export function isHabitDueToday({
}
export function getHabitFreq(habit: Habit): Freq {
if (habit.isTask) {
// don't support recurring task yet
return 'daily'
}
const rrule = parseRRule(habit.frequency)
const freq = rrule.origOptions.freq
switch (freq) {