From 166fd843ef3d778082726f2ac07a271649b2b74c Mon Sep 17 00:00:00 2001 From: Andrea Pang <98671035+andiedoescode@users.noreply.github.com> Date: Sun, 3 Mar 2024 14:20:28 -0500 Subject: [PATCH] Issue #9 - Marking item as purchased (#28) * add checkbox to ListItem * updated isPurchased handling * Passed through listPath, added updateDoc to firebase function, debugging list item * passed listPath to ListItem, added useEffect to update isPurchased, changed updateItem function in firebase * Refactored to remove try/catch in updateItem function * used setTimeout to uncheck boxes for items that were purchased more than a day ago * debugged checkbox * Changed to useMemo, updated timeout to use milliseconds in a day * Changed to useMemo, updated timeout to use milliseconds in a day * Decrement in the db when user unchecks before 24hrs is up, moved async purchaseItem function into changeHandler and removed useEffect * Revert "Decrement in the db when user unchecks before 24hrs is up, moved async purchaseItem function into changeHandler and removed useEffect" This reverts commit d5688d82d6b4e31b35eba0774f21bfcba2c682c1. * Decrement in the db when user unchecks before 24hrs is up, moved async purchaseItem function into changeHandler and removed useEffect * removed unnecessary comments, added in intentional comments * Switched changeHandler to function expression * Added updateTimer function to calculate remaining time, updated useEffeect to use remaining time in setTimeout * Updated setTimeout if statement * Update src/components/ListItem.jsx Co-authored-by: Raynaldo Sutisna * refactored to prevent timer running when purchasedOneDayAgo is false --------- Co-authored-by: Devina Gillis Co-authored-by: Devina Gillis <137969744+DevinaG007@users.noreply.github.com> Co-authored-by: Raynaldo Sutisna --- src/api/firebase.js | 14 ++++---- src/components/ListItem.jsx | 69 +++++++++++++++++++++++++++++++++++-- src/utils/dates.js | 2 +- src/views/List.jsx | 4 ++- 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/api/firebase.js b/src/api/firebase.js index 85fd092..360664d 100644 --- a/src/api/firebase.js +++ b/src/api/firebase.js @@ -7,6 +7,7 @@ import { doc, onSnapshot, updateDoc, + increment, } from 'firebase/firestore'; import { useEffect, useState } from 'react'; import { db } from './config'; @@ -186,12 +187,13 @@ export async function addItem(listPath, { itemName, daysUntilNextPurchase }) { return newItem; } -export async function updateItem() { - /** - * TODO: Fill this out so that it uses the correct Firestore function - * to update an existing item. You'll need to figure out what arguments - * this function must accept! - */ +export async function updateItem(listPath, itemID, isChecked) { + const listRef = doc(db, listPath, 'items', itemID); + + await updateDoc(listRef, { + dateLastPurchased: isChecked ? new Date() : null, + totalPurchases: isChecked ? increment(1) : increment(-1), + }); } export async function deleteItem() { diff --git a/src/components/ListItem.jsx b/src/components/ListItem.jsx index d047322..da96928 100644 --- a/src/components/ListItem.jsx +++ b/src/components/ListItem.jsx @@ -1,5 +1,70 @@ import './ListItem.css'; +import { updateItem } from '../api/firebase.js'; +import { useState, useEffect, useMemo } from 'react'; +import { ONE_DAY_IN_MILLISECONDS } from '../utils/dates.js'; -export function ListItem({ name }) { - return
  • {name}
  • ; +export function ListItem({ listPath, item }) { + /* Returns a boolean that is passed into isChecked useState + On render, box is checked if purchased less than a day ago */ + + const purchasedOneDayAgo = useMemo(() => { + if (item.dateLastPurchased === null) { + return false; + } + + const timeDiff = Date.now() - item.dateLastPurchased.seconds * 1000; + return timeDiff <= ONE_DAY_IN_MILLISECONDS; + }, [item.dateLastPurchased]); + + const [isChecked, setIsChecked] = useState(purchasedOneDayAgo); + const changeHandler = (e) => { + setIsChecked(!isChecked); + async function purchaseItem() { + try { + await updateItem(listPath, item.id, !isChecked); + } catch (error) { + alert(error.message); + } + } + purchaseItem(); + }; + + //Calculate time remaining if purchase was less than 24 hours ago + const updateTimer = () => { + if (item.dateLastPurchased) { + const timeElapsed = Date.now() - item.dateLastPurchased.seconds * 1000; + if (timeElapsed < ONE_DAY_IN_MILLISECONDS) { + return ONE_DAY_IN_MILLISECONDS - timeElapsed; + } else { + return 0; + } + } + }; + + //sets a timer to uncheck an item 24 hours after it's purchased + useEffect(() => { + if (purchasedOneDayAgo) { + let timeRemaining = updateTimer(); + + const timer = setTimeout(() => { + setIsChecked(false); + }, timeRemaining); + return () => clearTimeout(timer); + } + }, [isChecked, purchasedOneDayAgo]); + + return ( +
  • + +
  • + ); } diff --git a/src/utils/dates.js b/src/utils/dates.js index 9331fda..dc66d95 100644 --- a/src/utils/dates.js +++ b/src/utils/dates.js @@ -1,4 +1,4 @@ -const ONE_DAY_IN_MILLISECONDS = 86400000; +export const ONE_DAY_IN_MILLISECONDS = 86400000; /** * Get a new JavaScript Date that is `offset` days in the future. diff --git a/src/views/List.jsx b/src/views/List.jsx index a0abcc2..f14ff82 100644 --- a/src/views/List.jsx +++ b/src/views/List.jsx @@ -2,7 +2,9 @@ import { ListItem } from '../components'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; + export function List({ data, listPath }) { + const [searchTerm, setSearchTerm] = useState(''); const navigate = useNavigate(); @@ -62,7 +64,7 @@ export function List({ data, listPath }) { ) : null}
      {filteredData.map((item) => { - return ; + return ; })}