mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-20 22:24:28 +01:00
added recurrence (#35)
This commit is contained in:
82
lib/utils.ts
82
lib/utils.ts
@@ -1,7 +1,9 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { DateTime } from "luxon"
|
||||
import { Habit, CoinTransaction } from '@/lib/types'
|
||||
import { datetime, RRule } from 'rrule'
|
||||
import { Freq, Habit, CoinTransaction } from '@/lib/types'
|
||||
import { INITIAL_RECURRENCE_RULE, RECURRENCE_RULE_MAP } from "./constants"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
@@ -14,8 +16,8 @@ export function getTodayInTimezone(timezone: string): string {
|
||||
}
|
||||
|
||||
// get datetime object of now
|
||||
export function getNow({ timezone = 'utc' }: { timezone?: string }) {
|
||||
return DateTime.now().setZone(timezone);
|
||||
export function getNow({ timezone = 'utc', keepLocalTime }: { timezone?: string, keepLocalTime?: boolean }) {
|
||||
return DateTime.now().setZone(timezone, { keepLocalTime });
|
||||
}
|
||||
|
||||
// get current time in epoch milliseconds
|
||||
@@ -97,18 +99,6 @@ export function getCompletedHabitsForDate({
|
||||
})
|
||||
}
|
||||
|
||||
export function isHabitCompletedToday({
|
||||
habit,
|
||||
timezone
|
||||
}: {
|
||||
habit: Habit,
|
||||
timezone: string
|
||||
}): boolean {
|
||||
const today = getTodayInTimezone(timezone)
|
||||
const completionsToday = getCompletionsForDate({ habit, date: today, timezone })
|
||||
return completionsToday >= (habit.targetCompletions || 1)
|
||||
}
|
||||
|
||||
export function getHabitProgress({
|
||||
habit,
|
||||
timezone
|
||||
@@ -135,7 +125,7 @@ export function calculateCoinsEarnedToday(transactions: CoinTransaction[], timez
|
||||
|
||||
export function calculateTotalEarned(transactions: CoinTransaction[]): number {
|
||||
return transactions
|
||||
.filter(transaction =>
|
||||
.filter(transaction =>
|
||||
transaction.amount > 0 || transaction.type === 'HABIT_UNDO'
|
||||
)
|
||||
.reduce((sum, transaction) => sum + transaction.amount, 0);
|
||||
@@ -144,7 +134,7 @@ export function calculateTotalEarned(transactions: CoinTransaction[]): number {
|
||||
export function calculateTotalSpent(transactions: CoinTransaction[]): number {
|
||||
return Math.abs(
|
||||
transactions
|
||||
.filter(transaction =>
|
||||
.filter(transaction =>
|
||||
transaction.amount < 0 &&
|
||||
transaction.type !== 'HABIT_UNDO'
|
||||
)
|
||||
@@ -173,3 +163,61 @@ export function calculateTransactionsToday(transactions: CoinTransaction[], time
|
||||
t2d({ timestamp: today, timezone }))
|
||||
).length;
|
||||
}
|
||||
|
||||
export function getRRuleUTC(recurrenceRule: string) {
|
||||
return RRule.fromString(recurrenceRule); // this returns UTC
|
||||
}
|
||||
|
||||
export function parseNaturalLanguageRRule(ruleText: string) {
|
||||
ruleText = ruleText.trim()
|
||||
if (RECURRENCE_RULE_MAP[ruleText]) {
|
||||
return RRule.fromString(RECURRENCE_RULE_MAP[ruleText])
|
||||
}
|
||||
|
||||
return RRule.fromText(ruleText)
|
||||
}
|
||||
|
||||
export function parseRRule(ruleText: string) {
|
||||
ruleText = ruleText.trim()
|
||||
if (RECURRENCE_RULE_MAP[ruleText]) {
|
||||
return RRule.fromString(RECURRENCE_RULE_MAP[ruleText])
|
||||
}
|
||||
|
||||
return RRule.fromString(ruleText)
|
||||
}
|
||||
|
||||
export function serializeRRule(rrule: RRule) {
|
||||
return rrule.toString()
|
||||
}
|
||||
|
||||
export function isHabitDueToday(habit: Habit, timezone: string): boolean {
|
||||
const startOfDay = DateTime.now().setZone(timezone).startOf('day')
|
||||
const endOfDay = DateTime.now().setZone(timezone).endOf('day')
|
||||
|
||||
const ruleText = habit.frequency
|
||||
const rrule = parseRRule(ruleText)
|
||||
|
||||
rrule.origOptions.tzid = timezone // set the target timezone, rrule will do calculation in this timezone
|
||||
rrule.options.tzid = rrule.origOptions.tzid
|
||||
rrule.origOptions.dtstart = datetime(startOfDay.year, startOfDay.month, startOfDay.day, startOfDay.hour, startOfDay.minute, startOfDay.second) // set the start time to 00:00:00 of timezone's today
|
||||
rrule.options.dtstart = rrule.origOptions.dtstart
|
||||
rrule.origOptions.count = 1
|
||||
rrule.options.count = rrule.origOptions.count
|
||||
|
||||
const matches = rrule.all() // this is given as local time, we need to convert back to timezone time
|
||||
if (!matches.length) return false
|
||||
const t = DateTime.fromJSDate(matches[0]).toUTC().setZone('local', { keepLocalTime: true }).setZone(timezone) // this is the formula to convert local time matches[0] to tz time
|
||||
return startOfDay <= t && t <= endOfDay
|
||||
}
|
||||
|
||||
export function getHabitFreq(habit: Habit): Freq {
|
||||
const rrule = parseRRule(habit.frequency)
|
||||
const freq = rrule.origOptions.freq
|
||||
switch (freq) {
|
||||
case RRule.DAILY: return 'daily'
|
||||
case RRule.WEEKLY: return 'weekly'
|
||||
case RRule.MONTHLY: return 'monthly'
|
||||
case RRule.YEARLY: return 'yearly'
|
||||
default: throw new Error(`Invalid frequency: ${freq}`)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user