import { useCallback, useEffect, useRef, useState } from "react"; import { useAppSelector } from "../store"; import { locationService, userService } from "../services"; import { getMemoStats } from "../helpers/api"; import { DAILY_TIMESTAMP } from "../helpers/consts"; import * as utils from "../helpers/utils"; import "../less/usage-heat-map.less"; const tableConfig = { width: 12, height: 7, }; const getInitialUsageStat = (usedDaysAmount: number, beginDayTimestemp: number): DailyUsageStat[] => { const initialUsageStat: DailyUsageStat[] = []; for (let i = 1; i <= usedDaysAmount; i++) { initialUsageStat.push({ timestamp: beginDayTimestemp + DAILY_TIMESTAMP * i, count: 0, }); } return initialUsageStat; }; interface DailyUsageStat { timestamp: number; count: number; } const UsageHeatMap = () => { const { memos } = useAppSelector((state) => state.memo); const [allStat, setAllStat] = useState([]); const [currentStat, setCurrentStat] = useState(null); const containerElRef = useRef(null); const todayTimeStamp = utils.getDateStampByDate(Date.now()); const todayDay = new Date(todayTimeStamp).getDay() + 1; const nullCell = new Array(7 - todayDay).fill(0); const usedDaysAmount = (tableConfig.width - 1) * tableConfig.height + todayDay; const beginDayTimestemp = todayTimeStamp - usedDaysAmount * DAILY_TIMESTAMP; useEffect(() => { getMemoStats(userService.getCurrentUserId()) .then(({ data: { data } }) => { const newStat: DailyUsageStat[] = getInitialUsageStat(usedDaysAmount, beginDayTimestemp); for (const record of data) { const index = (utils.getDateStampByDate(record * 1000) - beginDayTimestemp) / (1000 * 3600 * 24) - 1; if (index >= 0) { newStat[index].count += 1; } } setAllStat([...newStat]); }) .catch((error) => { console.error(error); }); }, [memos.length]); const handleUsageStatItemMouseEnter = useCallback((event: React.MouseEvent, item: DailyUsageStat) => { const tempDiv = document.createElement("div"); tempDiv.className = "usage-detail-container pop-up"; const bounding = utils.getElementBounding(event.target as HTMLElement); tempDiv.style.left = bounding.left + "px"; tempDiv.style.top = bounding.top - 2 + "px"; tempDiv.innerHTML = `${item.count} memos on ${new Date(item.timestamp as number).toDateString()}`; document.body.appendChild(tempDiv); }, []); const handleUsageStatItemMouseLeave = useCallback(() => { document.body.querySelectorAll("div.usage-detail-container.pop-up").forEach((node) => node.remove()); }, []); const handleUsageStatItemClick = useCallback((item: DailyUsageStat) => { if (locationService.getState().query?.duration?.from === item.timestamp) { locationService.setFromAndToQuery(); setCurrentStat(null); } else if (item.count > 0) { locationService.setFromAndToQuery(item.timestamp, item.timestamp + DAILY_TIMESTAMP); setCurrentStat(item); } }, []); return (
Sun Tue Thu Sat
{allStat.map((v, i) => { const count = v.count; const colorLevel = count <= 0 ? "" : count <= 1 ? "stat-day-L1-bg" : count <= 2 ? "stat-day-L2-bg" : count <= 4 ? "stat-day-L3-bg" : "stat-day-L4-bg"; return (
handleUsageStatItemMouseEnter(e, v)} onMouseLeave={handleUsageStatItemMouseLeave} onClick={() => handleUsageStatItemClick(v)} >
); })} {nullCell.map((_, i) => (
))}
); }; export default UsageHeatMap;