mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-21 06:34:30 +01:00
use jotai for all states (#19)
This commit is contained in:
@@ -1,28 +1,12 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { loadCoinsData, addCoins, removeCoins } from '@/app/actions/data'
|
||||
import { useAtom } from 'jotai'
|
||||
import { coinsAtom } from '@/lib/atoms'
|
||||
import { addCoins, removeCoins } from '@/app/actions/data'
|
||||
import { toast } from '@/hooks/use-toast'
|
||||
import { CoinTransaction, TransactionType } from '@/lib/types'
|
||||
|
||||
export function useCoins() {
|
||||
const [balance, setBalance] = useState(0)
|
||||
const [transactions, setTransactions] = useState<CoinTransaction[]>([])
|
||||
const [coins, setCoins] = useAtom(coinsAtom)
|
||||
|
||||
useEffect(() => {
|
||||
fetchCoins()
|
||||
}, [])
|
||||
|
||||
const fetchCoins = async () => {
|
||||
const data = await loadCoinsData()
|
||||
setBalance(data.balance)
|
||||
setTransactions(data.transactions || [])
|
||||
}
|
||||
|
||||
const addAmount = async (
|
||||
amount: number,
|
||||
description: string,
|
||||
type: TransactionType = 'MANUAL_ADJUSTMENT',
|
||||
relatedItemId?: string
|
||||
) => {
|
||||
const add = async (amount: number, description: string) => {
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
toast({
|
||||
title: "Invalid amount",
|
||||
@@ -31,19 +15,15 @@ export function useCoins() {
|
||||
return null
|
||||
}
|
||||
|
||||
const data = await addCoins(amount, description, type, relatedItemId)
|
||||
setBalance(data.balance)
|
||||
setTransactions(data.transactions)
|
||||
const data = await addCoins(amount, description)
|
||||
setCoins(data)
|
||||
toast({ title: "Success", description: `Added ${amount} coins` })
|
||||
return data
|
||||
}
|
||||
|
||||
const removeAmount = async (
|
||||
amount: number,
|
||||
description: string,
|
||||
type: TransactionType = 'MANUAL_ADJUSTMENT',
|
||||
relatedItemId?: string
|
||||
) => {
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
const remove = async (amount: number, description: string) => {
|
||||
const numAmount = Math.abs(amount)
|
||||
if (isNaN(numAmount) || numAmount <= 0) {
|
||||
toast({
|
||||
title: "Invalid amount",
|
||||
description: "Please enter a valid positive number"
|
||||
@@ -51,17 +31,16 @@ export function useCoins() {
|
||||
return null
|
||||
}
|
||||
|
||||
const data = await removeCoins(amount, description, type, relatedItemId)
|
||||
setBalance(data.balance)
|
||||
setTransactions(data.transactions)
|
||||
const data = await removeCoins(numAmount, description)
|
||||
setCoins(data)
|
||||
toast({ title: "Success", description: `Removed ${numAmount} coins` })
|
||||
return data
|
||||
}
|
||||
|
||||
return {
|
||||
balance,
|
||||
transactions,
|
||||
addAmount,
|
||||
removeAmount,
|
||||
fetchCoins
|
||||
add,
|
||||
remove,
|
||||
balance: coins.balance,
|
||||
transactions: coins.transactions
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,47 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { loadHabitsData, saveHabitsData, addCoins, removeCoins } from '@/app/actions/data'
|
||||
import { useAtom } from 'jotai'
|
||||
import { habitsAtom, coinsAtom, settingsAtom } from '@/lib/atoms'
|
||||
import { addCoins, removeCoins, saveHabitsData } from '@/app/actions/data'
|
||||
import { Habit } from '@/lib/types'
|
||||
import { getNowInMilliseconds, getTodayInTimezone } from '@/lib/utils'
|
||||
import { toast } from '@/hooks/use-toast'
|
||||
import { ToastAction } from '@/components/ui/toast'
|
||||
import { Undo2 } from 'lucide-react'
|
||||
import { Habit } from '@/lib/types'
|
||||
import { getNowInMilliseconds, getTodayInTimezone } from '@/lib/utils'
|
||||
import { useAtom } from 'jotai'
|
||||
import { settingsAtom } from '@/lib/atoms'
|
||||
|
||||
export function useHabits() {
|
||||
const [habits, setHabits] = useState<Habit[]>([])
|
||||
const [habitsData, setHabitsData] = useAtom(habitsAtom)
|
||||
const [coins, setCoins] = useAtom(coinsAtom)
|
||||
const [settings] = useAtom(settingsAtom)
|
||||
|
||||
useEffect(() => {
|
||||
fetchHabits()
|
||||
}, [])
|
||||
|
||||
const fetchHabits = async () => {
|
||||
const data = await loadHabitsData()
|
||||
setHabits(data.habits)
|
||||
}
|
||||
|
||||
const addHabit = async (habit: Omit<Habit, 'id'>) => {
|
||||
const newHabit = { ...habit, id: getNowInMilliseconds() }
|
||||
const newHabits = [...habits, newHabit]
|
||||
setHabits(newHabits)
|
||||
await saveHabitsData({ habits: newHabits })
|
||||
}
|
||||
|
||||
const editHabit = async (updatedHabit: Habit) => {
|
||||
const newHabits = habits.map(habit =>
|
||||
habit.id === updatedHabit.id ? updatedHabit : habit
|
||||
)
|
||||
setHabits(newHabits)
|
||||
await saveHabitsData({ habits: newHabits })
|
||||
}
|
||||
|
||||
const deleteHabit = async (id: string) => {
|
||||
const newHabits = habits.filter(habit => habit.id !== id)
|
||||
setHabits(newHabits)
|
||||
await saveHabitsData({ habits: newHabits })
|
||||
}
|
||||
|
||||
const completeHabit = async (habit: Habit) => {
|
||||
const today = getTodayInTimezone(settings.system.timezone)
|
||||
const timezone = settings.system.timezone
|
||||
const today = getTodayInTimezone(timezone)
|
||||
if (!habit.completions.includes(today)) {
|
||||
const updatedHabit = {
|
||||
...habit,
|
||||
completions: [...habit.completions, today]
|
||||
}
|
||||
const updatedHabits = habits.map(h =>
|
||||
const updatedHabits = habitsData.habits.map(h =>
|
||||
h.id === habit.id ? updatedHabit : h
|
||||
)
|
||||
await saveHabitsData({ habits: updatedHabits })
|
||||
const coinsData = await addCoins(habit.coinReward, `Completed habit: ${habit.name}`, 'HABIT_COMPLETION', habit.id)
|
||||
setHabitsData({ habits: updatedHabits })
|
||||
|
||||
setHabits(updatedHabits)
|
||||
const coinsData = await addCoins(habit.coinReward, `Completed habit: ${habit.name}`, 'HABIT_COMPLETION', habit.id)
|
||||
setCoins(coinsData)
|
||||
|
||||
toast({
|
||||
title: "Habit completed!",
|
||||
description: `You earned ${habit.coinReward} coins.`,
|
||||
action: <ToastAction altText="Undo" className="gap-2" onClick={() => undoComplete(habit)}><Undo2 className="h-4 w-4" />Undo</ToastAction>
|
||||
action: <ToastAction altText="Undo" className="gap-2" onClick={() => undoComplete(habit)}>
|
||||
<Undo2 className="h-4 w-4" />Undo
|
||||
</ToastAction>
|
||||
})
|
||||
|
||||
return coinsData.balance
|
||||
return {
|
||||
updatedHabits,
|
||||
newBalance: coinsData.balance,
|
||||
newTransactions: coinsData.transactions
|
||||
}
|
||||
} else {
|
||||
toast({
|
||||
title: "Habit already completed",
|
||||
@@ -75,34 +53,61 @@ export function useHabits() {
|
||||
}
|
||||
|
||||
const undoComplete = async (habit: Habit) => {
|
||||
const today = getTodayInTimezone(settings.system.timezone)
|
||||
const timezone = settings.system.timezone
|
||||
const today = getTodayInTimezone(timezone)
|
||||
const updatedHabit = {
|
||||
...habit,
|
||||
completions: habit.completions.filter(date => date !== today)
|
||||
}
|
||||
const updatedHabits = habits.map(h =>
|
||||
const updatedHabits = habitsData.habits.map(h =>
|
||||
h.id === habit.id ? updatedHabit : h
|
||||
)
|
||||
await saveHabitsData({ habits: updatedHabits })
|
||||
const coinsData = await removeCoins(habit.coinReward, `Undid habit completion: ${habit.name}`, 'HABIT_UNDO', habit.id)
|
||||
setHabitsData({ habits: updatedHabits })
|
||||
|
||||
setHabits(updatedHabits)
|
||||
const coinsData = await removeCoins(habit.coinReward, `Undid habit completion: ${habit.name}`, 'HABIT_UNDO', habit.id)
|
||||
setCoins(coinsData)
|
||||
|
||||
toast({
|
||||
title: "Completion undone",
|
||||
description: `${habit.coinReward} coins have been deducted.`,
|
||||
action: <ToastAction altText="Redo" onClick={() => completeHabit(habit)}><Undo2 className="h-4 w-4" />Undo</ToastAction>
|
||||
action: <ToastAction altText="Redo" onClick={() => completeHabit(habit)}>
|
||||
<Undo2 className="h-4 w-4" />Undo
|
||||
</ToastAction>
|
||||
})
|
||||
|
||||
return coinsData.balance
|
||||
return {
|
||||
updatedHabits,
|
||||
newBalance: coinsData.balance,
|
||||
newTransactions: coinsData.transactions
|
||||
}
|
||||
}
|
||||
|
||||
const saveHabit = async (habit: Omit<Habit, 'id'> & { id?: string }) => {
|
||||
const newHabit = {
|
||||
...habit,
|
||||
id: habit.id || getNowInMilliseconds().toString()
|
||||
}
|
||||
const updatedHabits = habit.id
|
||||
? habitsData.habits.map(h => h.id === habit.id ? newHabit : h)
|
||||
: [...habitsData.habits, newHabit]
|
||||
|
||||
await saveHabitsData({ habits: updatedHabits })
|
||||
setHabitsData({ habits: updatedHabits })
|
||||
return updatedHabits
|
||||
}
|
||||
|
||||
const deleteHabit = async (id: string) => {
|
||||
const updatedHabits = habitsData.habits.filter(h => h.id !== id)
|
||||
await saveHabitsData({ habits: updatedHabits })
|
||||
setHabitsData({ habits: updatedHabits })
|
||||
return updatedHabits
|
||||
}
|
||||
|
||||
return {
|
||||
habits,
|
||||
addHabit,
|
||||
editHabit,
|
||||
deleteHabit,
|
||||
completeHabit,
|
||||
undoComplete
|
||||
undoComplete,
|
||||
saveHabit,
|
||||
deleteHabit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,45 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { loadWishlistItems, saveWishlistItems } from '@/app/actions/data'
|
||||
import { useAtom } from 'jotai'
|
||||
import { wishlistAtom, coinsAtom } from '@/lib/atoms'
|
||||
import { saveWishlistItems, removeCoins } from '@/app/actions/data'
|
||||
import { toast } from '@/hooks/use-toast'
|
||||
import { WishlistItemType } from '@/lib/types'
|
||||
import { useCoins } from '@/hooks/useCoins'
|
||||
import { celebrations } from '@/utils/celebrations'
|
||||
|
||||
export function useWishlist() {
|
||||
const [wishlistItems, setWishlistItems] = useState<WishlistItemType[]>([])
|
||||
const { balance, removeAmount } = useCoins()
|
||||
|
||||
useEffect(() => {
|
||||
fetchWishlistItems()
|
||||
}, [])
|
||||
|
||||
const fetchWishlistItems = async () => {
|
||||
const items = await loadWishlistItems()
|
||||
setWishlistItems(items)
|
||||
}
|
||||
const [wishlist, setWishlist] = useAtom(wishlistAtom)
|
||||
const [coins, setCoins] = useAtom(coinsAtom)
|
||||
const balance = coins.balance
|
||||
|
||||
const addWishlistItem = async (item: Omit<WishlistItemType, 'id'>) => {
|
||||
const newItem = { ...item, id: Date.now().toString() }
|
||||
const newItems = [...wishlistItems, newItem]
|
||||
setWishlistItems(newItems)
|
||||
const newItems = [...wishlist.items, newItem]
|
||||
setWishlist({ items: newItems })
|
||||
await saveWishlistItems(newItems)
|
||||
}
|
||||
|
||||
const editWishlistItem = async (updatedItem: WishlistItemType) => {
|
||||
const newItems = wishlistItems.map(item =>
|
||||
const newItems = wishlist.items.map(item =>
|
||||
item.id === updatedItem.id ? updatedItem : item
|
||||
)
|
||||
setWishlistItems(newItems)
|
||||
setWishlist({ items: newItems })
|
||||
await saveWishlistItems(newItems)
|
||||
}
|
||||
|
||||
const deleteWishlistItem = async (id: string) => {
|
||||
const newItems = wishlistItems.filter(item => item.id !== id)
|
||||
setWishlistItems(newItems)
|
||||
const newItems = wishlist.items.filter(item => item.id !== id)
|
||||
setWishlist({ items: newItems })
|
||||
await saveWishlistItems(newItems)
|
||||
}
|
||||
|
||||
const redeemWishlistItem = async (item: WishlistItemType) => {
|
||||
if (balance >= item.coinCost) {
|
||||
await removeAmount(
|
||||
const data = await removeCoins(
|
||||
item.coinCost,
|
||||
`Redeemed reward: ${item.name}`,
|
||||
'WISH_REDEMPTION',
|
||||
item.id
|
||||
)
|
||||
setCoins(data)
|
||||
|
||||
// Randomly choose a celebration effect
|
||||
const celebrationEffects = [
|
||||
@@ -71,12 +64,14 @@ export function useWishlist() {
|
||||
}
|
||||
}
|
||||
|
||||
const canRedeem = (cost: number) => balance >= cost
|
||||
|
||||
return {
|
||||
wishlistItems,
|
||||
addWishlistItem,
|
||||
editWishlistItem,
|
||||
deleteWishlistItem,
|
||||
redeemWishlistItem,
|
||||
canRedeem: (cost: number) => balance >= cost
|
||||
canRedeem,
|
||||
wishlistItems: wishlist.items
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user