mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-03-08 03:29:49 +01:00
fix: migrate atoms to normal functions
This commit is contained in:
@@ -13,22 +13,22 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip"
|
} from "@/components/ui/tooltip"
|
||||||
import { useHabits } from '@/hooks/useHabits'
|
import { useHabits } from '@/hooks/useHabits'
|
||||||
import { browserSettingsAtom, completedHabitsMapAtom, hasTasksAtom, pomodoroAtom, settingsAtom } from '@/lib/atoms'
|
import { browserSettingsAtom, completedHabitsMapAtom, settingsAtom } from '@/lib/atoms'
|
||||||
import { DESKTOP_DISPLAY_ITEM_COUNT } from '@/lib/constants'
|
import { DESKTOP_DISPLAY_ITEM_COUNT } from '@/lib/constants'
|
||||||
import { Habit, WishlistItemType } from '@/lib/types'
|
import { Habit, WishlistItemType } from '@/lib/types'
|
||||||
import { cn, d2t, getNow, getTodayInTimezone, isHabitDue, isSameDate, isTaskOverdue, t2d } from '@/lib/utils'
|
import { cn, d2t, getNow, getTodayInTimezone, isHabitDue, isSameDate, isTaskOverdue, t2d } from '@/lib/utils'
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { AlertTriangle, ArrowRight, ChevronDown, ChevronUp, Circle, CircleCheck, Coins, Pin, Plus } from 'lucide-react'; // Removed unused icons
|
import { AlertTriangle, ArrowRight, ChevronDown, ChevronUp, Circle, CircleCheck, Coins, Pin, Plus } from 'lucide-react';
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import AddEditHabitModal from './AddEditHabitModal'
|
import AddEditHabitModal from './AddEditHabitModal'
|
||||||
import CompletionCountBadge from './CompletionCountBadge'
|
import CompletionCountBadge from './CompletionCountBadge'
|
||||||
import ConfirmDialog from './ConfirmDialog'
|
import ConfirmDialog from './ConfirmDialog'
|
||||||
|
import DrawingDisplay from './DrawingDisplay'
|
||||||
import { HabitContextMenuItems } from './HabitContextMenuItems'
|
import { HabitContextMenuItems } from './HabitContextMenuItems'
|
||||||
import Linkify from './linkify'
|
import Linkify from './linkify'
|
||||||
import { Button } from './ui/button'
|
import { Button } from './ui/button'
|
||||||
import DrawingDisplay from './DrawingDisplay'
|
|
||||||
|
|
||||||
interface UpcomingItemsProps {
|
interface UpcomingItemsProps {
|
||||||
habits: Habit[]
|
habits: Habit[]
|
||||||
@@ -54,8 +54,7 @@ const ItemSection = ({
|
|||||||
addNewItem,
|
addNewItem,
|
||||||
}: ItemSectionProps) => {
|
}: ItemSectionProps) => {
|
||||||
const t = useTranslations('DailyOverview');
|
const t = useTranslations('DailyOverview');
|
||||||
const { completeHabit, undoComplete, saveHabit, deleteHabit, archiveHabit, habitFreqMap } = useHabits();
|
const { completeHabit, undoComplete, saveHabit, deleteHabit, habitFreqMap } = useHabits();
|
||||||
const [_, setPomo] = useAtom(pomodoroAtom);
|
|
||||||
const [browserSettings, setBrowserSettings] = useAtom(browserSettingsAtom);
|
const [browserSettings, setBrowserSettings] = useAtom(browserSettingsAtom);
|
||||||
const [settings] = useAtom(settingsAtom);
|
const [settings] = useAtom(settingsAtom);
|
||||||
const [completedHabitsMap] = useAtom(completedHabitsMapAtom);
|
const [completedHabitsMap] = useAtom(completedHabitsMapAtom);
|
||||||
@@ -397,8 +396,6 @@ export default function DailyOverview({
|
|||||||
return a.coinCost - b.coinCost
|
return a.coinCost - b.coinCost
|
||||||
})
|
})
|
||||||
|
|
||||||
const [hasTasks] = useAtom(hasTasksAtom)
|
|
||||||
const [, setPomo] = useAtom(pomodoroAtom)
|
|
||||||
const [modalConfig, setModalConfig] = useState<{
|
const [modalConfig, setModalConfig] = useState<{
|
||||||
isOpen: boolean,
|
isOpen: boolean,
|
||||||
isTask: boolean
|
isTask: boolean
|
||||||
@@ -416,7 +413,7 @@ export default function DailyOverview({
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Tasks Section */}
|
{/* Tasks Section */}
|
||||||
{hasTasks && (
|
{habits.some(habit => habit.isTask === true) && (
|
||||||
<ItemSection
|
<ItemSection
|
||||||
title={t('dailyTasksTitle')}
|
title={t('dailyTasksTitle')}
|
||||||
items={dailyTasks}
|
items={dailyTasks}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import CompletionCountBadge from '@/components/CompletionCountBadge'
|
|||||||
import { Calendar } from '@/components/ui/calendar'
|
import { Calendar } from '@/components/ui/calendar'
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { useHabits } from '@/hooks/useHabits'
|
import { useHabits } from '@/hooks/useHabits'
|
||||||
import { completedHabitsMapAtom, habitsAtom, hasTasksAtom, settingsAtom } from '@/lib/atoms'
|
import { completedHabitsMapAtom, habitsAtom, settingsAtom } from '@/lib/atoms'
|
||||||
import { Habit } from '@/lib/types'
|
import { Habit } from '@/lib/types'
|
||||||
import { d2s, getCompletionsForDate, getISODate, getNow, isHabitDue } from '@/lib/utils'
|
import { d2s, getCompletionsForDate, getISODate, getNow, isHabitDue } from '@/lib/utils'
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
@@ -29,7 +29,6 @@ export default function HabitCalendar() {
|
|||||||
const [selectedDateTime, setSelectedDateTime] = useState<DateTime>(getNow({ timezone: settings.system.timezone }))
|
const [selectedDateTime, setSelectedDateTime] = useState<DateTime>(getNow({ timezone: settings.system.timezone }))
|
||||||
const selectedDate = selectedDateTime.toFormat("yyyy-MM-dd")
|
const selectedDate = selectedDateTime.toFormat("yyyy-MM-dd")
|
||||||
const [habitsData] = useAtom(habitsAtom)
|
const [habitsData] = useAtom(habitsAtom)
|
||||||
const [hasTasks] = useAtom(hasTasksAtom)
|
|
||||||
const habits = habitsData.habits
|
const habits = habitsData.habits
|
||||||
|
|
||||||
const [completedHabitsMap] = useAtom(completedHabitsMapAtom)
|
const [completedHabitsMap] = useAtom(completedHabitsMapAtom)
|
||||||
@@ -83,7 +82,7 @@ export default function HabitCalendar() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{selectedDateTime && (
|
{selectedDateTime && (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
{hasTasks && (
|
{habits.some(habit => habit.isTask === true) && (
|
||||||
<div className="pt-2 border-t">
|
<div className="pt-2 border-t">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<h3 className="font-medium text-sm text-muted-foreground uppercase tracking-wide">{t('tasksSectionTitle')}</h3>
|
<h3 className="font-medium text-sm text-muted-foreground uppercase tracking-wide">{t('tasksSectionTitle')}</h3>
|
||||||
|
|||||||
@@ -1,22 +1,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { completedHabitsMapAtom, hasTasksAtom, settingsAtom } from '@/lib/atoms'; // Added completedHabitsMapAtom
|
import { completedHabitsMapAtom, settingsAtom } from '@/lib/atoms';
|
||||||
import { Habit } from '@/lib/types';
|
import { Habit } from '@/lib/types';
|
||||||
import { d2s, getNow } from '@/lib/utils'; // Removed getCompletedHabitsForDate
|
import { d2s, getNow } from '@/lib/utils';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
|
import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
|
||||||
|
|
||||||
|
export default function HabitStreak({ habits }: { habits: Habit[] }) {
|
||||||
interface HabitStreakProps {
|
|
||||||
habits: Habit[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function HabitStreak({ habits }: HabitStreakProps) {
|
|
||||||
const t = useTranslations('HabitStreak');
|
const t = useTranslations('HabitStreak');
|
||||||
const [settings] = useAtom(settingsAtom)
|
const [settings] = useAtom(settingsAtom)
|
||||||
const [hasTasks] = useAtom(hasTasksAtom)
|
|
||||||
const [completedHabitsMap] = useAtom(completedHabitsMapAtom) // Use the atom
|
const [completedHabitsMap] = useAtom(completedHabitsMapAtom) // Use the atom
|
||||||
|
|
||||||
// Get the last 7 days of data
|
// Get the last 7 days of data
|
||||||
@@ -72,7 +66,7 @@ export default function HabitStreak({ habits }: HabitStreakProps) {
|
|||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
dot={false}
|
dot={false}
|
||||||
/>
|
/>
|
||||||
{hasTasks && (
|
{habits.some(habit => habit.isTask === true) && (
|
||||||
<Line
|
<Line
|
||||||
type="monotone"
|
type="monotone"
|
||||||
name={t('tooltipTasksLabel')}
|
name={t('tooltipTasksLabel')}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Progress } from '@/components/ui/progress'
|
import { Progress } from '@/components/ui/progress'
|
||||||
import { useHabits } from '@/hooks/useHabits'
|
import { useHabits } from '@/hooks/useHabits'
|
||||||
import { habitsAtom, pomodoroAtom, pomodoroTodayCompletionsAtom } from '@/lib/atoms'
|
import { habitsAtom, pomodoroAtom, settingsAtom } from '@/lib/atoms'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn, getTodayCompletions } from '@/lib/utils'
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { Clock, Minus, Pause, Play, RotateCw, SkipForward, X } from 'lucide-react'
|
import { Clock, Minus, Pause, Play, RotateCw, SkipForward, X } from 'lucide-react'
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
@@ -41,12 +41,13 @@ export default function PomodoroTimer() {
|
|||||||
const [pomo, setPomo] = useAtom(pomodoroAtom)
|
const [pomo, setPomo] = useAtom(pomodoroAtom)
|
||||||
const { show, selectedHabitId, autoStart, minimized } = pomo
|
const { show, selectedHabitId, autoStart, minimized } = pomo
|
||||||
const [habitsData] = useAtom(habitsAtom)
|
const [habitsData] = useAtom(habitsAtom)
|
||||||
|
const [settingsData] = useAtom(settingsAtom)
|
||||||
const { completeHabit } = useHabits()
|
const { completeHabit } = useHabits()
|
||||||
const selectedHabit = selectedHabitId ? habitsData.habits.find(habit => habit.id === selectedHabitId) : null
|
const selectedHabit = selectedHabitId ? habitsData.habits.find(habit => habit.id === selectedHabitId) : null
|
||||||
const [timeLeft, setTimeLeft] = useState(PomoConfigs.focus.duration)
|
const [timeLeft, setTimeLeft] = useState(PomoConfigs.focus.duration)
|
||||||
const [state, setState] = useState<'started' | 'stopped' | 'paused'>(autoStart ? 'started' : 'stopped')
|
const [state, setState] = useState<'started' | 'stopped' | 'paused'>(autoStart ? 'started' : 'stopped')
|
||||||
const wakeLock = useRef<WakeLockSentinel | null>(null)
|
const wakeLock = useRef<WakeLockSentinel | null>(null)
|
||||||
const [todayCompletions] = useAtom(pomodoroTodayCompletionsAtom)
|
const todayCompletions = getTodayCompletions(pomo, habitsData, settingsData);
|
||||||
const currentTimerRef = useRef<PomoConfig>(PomoConfigs.focus)
|
const currentTimerRef = useRef<PomoConfig>(PomoConfigs.focus)
|
||||||
const [currentLabel, setCurrentLabel] = useState(() => {
|
const [currentLabel, setCurrentLabel] = useState(() => {
|
||||||
const labels = currentTimerRef.current.getLabels();
|
const labels = currentTimerRef.current.getLabels();
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { addCoins, removeCoins, saveHabitsData } from '@/app/actions/data'
|
import { addCoins, removeCoins, saveHabitsData } from '@/app/actions/data'
|
||||||
import { ToastAction } from '@/components/ui/toast'
|
import { ToastAction } from '@/components/ui/toast'
|
||||||
import { toast } from '@/hooks/use-toast'
|
import { toast } from '@/hooks/use-toast'
|
||||||
import { coinsAtom, currentUserAtom, habitFreqMapAtom, habitsAtom, settingsAtom, usersAtom } from '@/lib/atoms'
|
import { coinsAtom, currentUserAtom, habitsAtom, settingsAtom } from '@/lib/atoms'
|
||||||
import { Habit } from '@/lib/types'
|
import { Freq, Habit } from '@/lib/types'
|
||||||
import {
|
import {
|
||||||
d2s,
|
d2s,
|
||||||
d2t,
|
d2t,
|
||||||
getCompletionsForDate,
|
getCompletionsForDate,
|
||||||
|
getHabitFreq,
|
||||||
getISODate,
|
getISODate,
|
||||||
getNow,
|
getNow,
|
||||||
getTodayInTimezone,
|
getTodayInTimezone,
|
||||||
@@ -23,12 +24,15 @@ import { useTranslations } from 'next-intl'
|
|||||||
export function useHabits() {
|
export function useHabits() {
|
||||||
const t = useTranslations('useHabits');
|
const t = useTranslations('useHabits');
|
||||||
const tCommon = useTranslations('Common');
|
const tCommon = useTranslations('Common');
|
||||||
const [usersData] = useAtom(usersAtom)
|
|
||||||
const [currentUser] = useAtom(currentUserAtom)
|
const [currentUser] = useAtom(currentUserAtom)
|
||||||
const [habitsData, setHabitsData] = useAtom(habitsAtom)
|
const [habitsData, setHabitsData] = useAtom(habitsAtom)
|
||||||
const [coins, setCoins] = useAtom(coinsAtom)
|
const [coins, setCoins] = useAtom(coinsAtom)
|
||||||
const [settings] = useAtom(settingsAtom)
|
const [settings] = useAtom(settingsAtom)
|
||||||
const [habitFreqMap] = useAtom(habitFreqMapAtom)
|
// const [habitFreqMap] = useAtom(habitFreqMapAtom)
|
||||||
|
const habitFreqMap = new Map<string, Freq>();
|
||||||
|
habitsData.habits.forEach(habit => {
|
||||||
|
habitFreqMap.set(habit.id, getHabitFreq(habit));
|
||||||
|
})
|
||||||
|
|
||||||
const completeHabit = async (habit: Habit) => {
|
const completeHabit = async (habit: Habit) => {
|
||||||
if (!handlePermissionCheck(currentUser, 'habit', 'interact', tCommon)) return
|
if (!handlePermissionCheck(currentUser, 'habit', 'interact', tCommon)) return
|
||||||
|
|||||||
34
lib/atoms.ts
34
lib/atoms.ts
@@ -114,7 +114,7 @@ export const coinsBalanceAtom = atom((get) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* transient atoms */
|
/* transient atoms */
|
||||||
interface PomodoroAtom {
|
export interface PomodoroAtom {
|
||||||
show: boolean
|
show: boolean
|
||||||
selectedHabitId: string | null
|
selectedHabitId: string | null
|
||||||
autoStart: boolean
|
autoStart: boolean
|
||||||
@@ -191,38 +191,6 @@ export const completedHabitsMapAtom = atom((get) => {
|
|||||||
return map;
|
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)
|
|
||||||
const habits = get(habitsAtom)
|
|
||||||
const settings = get(settingsAtom)
|
|
||||||
|
|
||||||
if (!pomo.selectedHabitId) return 0
|
|
||||||
|
|
||||||
const selectedHabit = habits.habits.find(h => h.id === pomo.selectedHabitId!)
|
|
||||||
if (!selectedHabit) return 0
|
|
||||||
|
|
||||||
return getCompletionsForToday({
|
|
||||||
habit: selectedHabit,
|
|
||||||
timezone: settings.system.timezone
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Derived atom to check if any habits are tasks
|
|
||||||
export const hasTasksAtom = atom((get) => {
|
|
||||||
const habits = get(habitsAtom)
|
|
||||||
return habits.habits.some(habit => habit.isTask === true)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Atom family for habits by specific date
|
// Atom family for habits by specific date
|
||||||
export const habitsByDateFamily = atomFamily((dateString: string) =>
|
export const habitsByDateFamily = atomFamily((dateString: string) =>
|
||||||
atom((get) => {
|
atom((get) => {
|
||||||
|
|||||||
15
lib/utils.ts
15
lib/utils.ts
@@ -6,6 +6,7 @@ import { DateTime, DateTimeFormatOptions } from "luxon"
|
|||||||
import { Formats } from "next-intl"
|
import { Formats } from "next-intl"
|
||||||
import { datetime, RRule } from 'rrule'
|
import { datetime, RRule } from 'rrule'
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
|
import { PomodoroAtom } from "./atoms"
|
||||||
import { DUE_MAP, INITIAL_DUE, RECURRENCE_RULE_MAP } from "./constants"
|
import { DUE_MAP, INITIAL_DUE, RECURRENCE_RULE_MAP } from "./constants"
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
@@ -84,6 +85,20 @@ export function getCompletionsForToday({
|
|||||||
return getCompletionsForDate({ habit, date: getTodayInTimezone(timezone), timezone })
|
return getCompletionsForDate({ habit, date: getTodayInTimezone(timezone), timezone })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTodayCompletions({ selectedHabitId }: PomodoroAtom, { habits }: HabitsData, { system: { timezone } }: Settings): number {
|
||||||
|
if (!selectedHabitId)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const selectedHabit = habits.find(h => h.id === selectedHabitId!);
|
||||||
|
if (!selectedHabit)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return getCompletionsForToday({
|
||||||
|
habit: selectedHabit,
|
||||||
|
timezone: timezone
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getCompletedHabitsForDate({
|
export function getCompletedHabitsForDate({
|
||||||
habits,
|
habits,
|
||||||
date,
|
date,
|
||||||
|
|||||||
Reference in New Issue
Block a user