performance optimization via atoms (#108)

This commit is contained in:
Doh
2025-04-20 12:14:51 -04:00
committed by GitHub
parent dda8b522e3
commit 2408ed84bd
7 changed files with 94 additions and 76 deletions

View File

@@ -1,5 +1,11 @@
# Changelog
## Version 0.2.10
### Improved
* performance optimization: faster load time for large data set
## Version 0.2.9
### Added

View File

@@ -6,7 +6,7 @@ import {
ContextMenuItem,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
import { cn, getHabitFreq } from '@/lib/utils'
import { cn } from '@/lib/utils'
import Link from 'next/link'
import { useState } from 'react'
import { useAtom } from 'jotai'
@@ -57,7 +57,7 @@ const ItemSection = ({
settings,
setBrowserSettings,
}: ItemSectionProps) => {
const { completeHabit, undoComplete, saveHabit } = useHabits();
const { completeHabit, undoComplete, saveHabit, habitFreqMap } = useHabits();
const [_, setPomo] = useAtom(pomodoroAtom);
if (items.length === 0) {
@@ -117,8 +117,8 @@ const ItemSection = ({
}
// Then by frequency (daily first)
const aFreq = getHabitFreq(a);
const bFreq = getHabitFreq(b);
const aFreq = habitFreqMap.get(a.id) || 'daily';
const bFreq = habitFreqMap.get(b.id) || 'daily';
const freqOrder = ['daily', 'weekly', 'monthly', 'yearly'];
if (freqOrder.indexOf(aFreq) !== freqOrder.indexOf(bFreq)) {
return freqOrder.indexOf(aFreq) - freqOrder.indexOf(bFreq);
@@ -243,9 +243,9 @@ const ItemSection = ({
{completionsToday}/{target}
</span>
)}
{getHabitFreq(habit) !== 'daily' && (
{habitFreqMap.get(habit.id) !== 'daily' && (
<Badge variant="outline" className="text-xs">
{getHabitFreq(habit)}
{habitFreqMap.get(habit.id)}
</Badge>
)}
<span className="flex items-center">

View File

@@ -2,10 +2,10 @@
import { Habit } from '@/lib/types'
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { d2s, getNow, t2d, getCompletedHabitsForDate } from '@/lib/utils'
import { d2s, getNow, t2d } from '@/lib/utils' // Removed getCompletedHabitsForDate
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'
import { useAtom } from 'jotai'
import { settingsAtom, hasTasksAtom } from '@/lib/atoms'
import { settingsAtom, hasTasksAtom, completedHabitsMapAtom } from '@/lib/atoms' // Added completedHabitsMapAtom
interface HabitStreakProps {
habits: Habit[]
@@ -14,6 +14,8 @@ interface HabitStreakProps {
export default function HabitStreak({ habits }: HabitStreakProps) {
const [settings] = useAtom(settingsAtom)
const [hasTasks] = useAtom(hasTasksAtom)
const [completedHabitsMap] = useAtom(completedHabitsMapAtom) // Use the atom
// Get the last 7 days of data
const dates = Array.from({ length: 7 }, (_, i) => {
const d = getNow({ timezone: settings.system.timezone });
@@ -21,20 +23,17 @@ export default function HabitStreak({ habits }: HabitStreakProps) {
}).reverse()
const completions = dates.map(date => {
const completedHabits = getCompletedHabitsForDate({
habits: habits.filter(h => !h.isTask),
date: t2d({ timestamp: date, timezone: settings.system.timezone }),
timezone: settings.system.timezone
});
const completedTasks = getCompletedHabitsForDate({
habits: habits.filter(h => h.isTask),
date: t2d({ timestamp: date, timezone: settings.system.timezone }),
timezone: settings.system.timezone
});
// Get completed habits for the date from the map
const completedOnDate = completedHabitsMap.get(date) || [];
// Filter the completed list to count habits and tasks
const completedHabitsCount = completedOnDate.filter(h => !h.isTask).length;
const completedTasksCount = completedOnDate.filter(h => h.isTask).length;
return {
date,
habits: completedHabits.length,
tasks: completedTasks.length
habits: completedHabitsCount,
tasks: completedTasksCount
};
});

View File

@@ -2,14 +2,14 @@ import { useAtom } from 'jotai'
import { calculateCoinsEarnedToday, calculateCoinsSpentToday, calculateTotalEarned, calculateTotalSpent, calculateTransactionsToday, checkPermission } from '@/lib/utils'
import {
coinsAtom,
// coinsEarnedTodayAtom,
// totalEarnedAtom,
// totalSpentAtom,
// coinsSpentTodayAtom,
// transactionsTodayAtom,
// coinsBalanceAtom,
coinsEarnedTodayAtom,
totalEarnedAtom,
totalSpentAtom,
coinsSpentTodayAtom,
transactionsTodayAtom,
coinsBalanceAtom,
settingsAtom,
usersAtom
usersAtom,
} from '@/lib/atoms'
import { addCoins, removeCoins, saveCoinsData } from '@/app/actions/data'
import { CoinsData, User } from '@/lib/types'
@@ -57,12 +57,12 @@ export function useCoins(options?: { selectedUser?: string }) {
// Filter transactions for the selectd user
const transactions = coins.transactions.filter(t => t.userId === user?.id)
const balance = transactions.reduce((sum, t) => sum + t.amount, 0)
const coinsEarnedToday = calculateCoinsEarnedToday(transactions, settings.system.timezone)
const totalEarned = calculateTotalEarned(transactions)
const totalSpent = calculateTotalSpent(transactions)
const coinsSpentToday = calculateCoinsSpentToday(transactions, settings.system.timezone)
const transactionsToday = calculateTransactionsToday(transactions, settings.system.timezone)
const [balance] = useAtom(coinsBalanceAtom)
const [coinsEarnedToday] = useAtom(coinsEarnedTodayAtom)
const [totalEarned] = useAtom(totalEarnedAtom)
const [totalSpent] = useAtom(totalSpentAtom)
const [coinsSpentToday] = useAtom(coinsSpentTodayAtom)
const [transactionsToday] = useAtom(transactionsTodayAtom)
const add = async (amount: number, description: string, note?: string) => {
if (!handlePermissionCheck(currentUser, 'coins', 'write')) return null

View File

@@ -1,5 +1,5 @@
import { useAtom } from 'jotai'
import { habitsAtom, coinsAtom, settingsAtom, usersAtom } from '@/lib/atoms'
import { useAtom, atom } from 'jotai'
import { habitsAtom, coinsAtom, settingsAtom, usersAtom, habitFreqMapAtom } from '@/lib/atoms'
import { addCoins, removeCoins, saveHabitsData } from '@/app/actions/data'
import { Habit, Permission, SafeUser, User } from '@/lib/types'
import { toast } from '@/hooks/use-toast'
@@ -54,6 +54,7 @@ export function useHabits() {
const [habitsData, setHabitsData] = useAtom(habitsAtom)
const [coins, setCoins] = useAtom(coinsAtom)
const [settings] = useAtom(settingsAtom)
const [habitFreqMap] = useAtom(habitFreqMapAtom)
const completeHabit = async (habit: Habit) => {
if (!handlePermissionCheck(currentUser, 'habit', 'interact')) return
@@ -313,6 +314,7 @@ export function useHabits() {
deleteHabit,
completePastHabit,
archiveHabit,
unarchiveHabit
unarchiveHabit,
habitFreqMap,
}
}

View File

@@ -24,10 +24,12 @@ import {
getISODate,
isHabitDueToday,
getNow,
isHabitDue
isHabitDue,
getHabitFreq
} from "@/lib/utils";
import { atomFamily, atomWithStorage } from "jotai/utils";
import { DateTime } from "luxon";
import { Freq } from "./types";
export interface BrowserSettings {
viewType: ViewType
@@ -50,44 +52,44 @@ export const coinsAtom = atom(getDefaultCoinsData());
export const wishlistAtom = atom(getDefaultWishlistData());
export const serverSettingsAtom = atom(getDefaultServerSettings());
// // Derived atom for coins earned today
// export const coinsEarnedTodayAtom = atom((get) => {
// const coins = get(coinsAtom);
// const settings = get(settingsAtom);
// return calculateCoinsEarnedToday(coins.transactions, settings.system.timezone);
// });
// Derived atom for coins earned today
export const coinsEarnedTodayAtom = atom((get) => {
const coins = get(coinsAtom);
const settings = get(settingsAtom);
return calculateCoinsEarnedToday(coins.transactions, settings.system.timezone);
});
// // Derived atom for total earned
// export const totalEarnedAtom = atom((get) => {
// const coins = get(coinsAtom);
// return calculateTotalEarned(coins.transactions);
// });
// Derived atom for total earned
export const totalEarnedAtom = atom((get) => {
const coins = get(coinsAtom);
return calculateTotalEarned(coins.transactions);
});
// // Derived atom for total spent
// export const totalSpentAtom = atom((get) => {
// const coins = get(coinsAtom);
// return calculateTotalSpent(coins.transactions);
// });
// Derived atom for total spent
export const totalSpentAtom = atom((get) => {
const coins = get(coinsAtom);
return calculateTotalSpent(coins.transactions);
});
// // Derived atom for coins spent today
// export const coinsSpentTodayAtom = atom((get) => {
// const coins = get(coinsAtom);
// const settings = get(settingsAtom);
// return calculateCoinsSpentToday(coins.transactions, settings.system.timezone);
// });
// Derived atom for coins spent today
export const coinsSpentTodayAtom = atom((get) => {
const coins = get(coinsAtom);
const settings = get(settingsAtom);
return calculateCoinsSpentToday(coins.transactions, settings.system.timezone);
});
// // Derived atom for transactions today
// export const transactionsTodayAtom = atom((get) => {
// const coins = get(coinsAtom);
// const settings = get(settingsAtom);
// return calculateTransactionsToday(coins.transactions, settings.system.timezone);
// });
// Derived atom for transactions today
export const transactionsTodayAtom = atom((get) => {
const coins = get(coinsAtom);
const settings = get(settingsAtom);
return calculateTransactionsToday(coins.transactions, settings.system.timezone);
});
// // Derived atom for current balance from all transactions
// export const coinsBalanceAtom = atom((get) => {
// const coins = get(coinsAtom);
// return coins.transactions.reduce((sum, transaction) => sum + transaction.amount, 0);
// });
// Derived atom for current balance from all transactions
export const coinsBalanceAtom = atom((get) => {
const coins = get(coinsAtom);
return coins.transactions.reduce((sum, transaction) => sum + transaction.amount, 0);
});
/* transient atoms */
interface PomodoroAtom {
@@ -150,6 +152,15 @@ export const completedHabitsMapAtom = atom((get) => {
return map;
});
// Derived atom for habit frequency map
export const habitFreqMapAtom = atom((get) => {
const habits = get(habitsAtom).habits;
const map = new Map<string, Freq>();
habits.forEach(habit => {
map.set(habit.id, getHabitFreq(habit));
});
return map;
});
export const pomodoroTodayCompletionsAtom = atom((get) => {
const pomo = get(pomodoroAtom)

View File

@@ -1,6 +1,6 @@
{
"name": "habittrove",
"version": "0.2.9",
"version": "0.2.10",
"private": true,
"scripts": {
"dev": "next dev --turbopack",