smov/src/pages/parts/home/BookmarksPart.tsx
2024-09-28 21:10:40 -06:00

124 lines
3.9 KiB
TypeScript

import { useAutoAnimate } from "@formkit/auto-animate/react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { EditButton } from "@/components/buttons/EditButton";
import { Icons } from "@/components/Icon";
import { SectionHeading } from "@/components/layout/SectionHeading";
import { MediaGrid } from "@/components/media/MediaGrid";
import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
import { useBookmarkStore } from "@/stores/bookmarks";
import { useProgressStore } from "@/stores/progress";
import { MediaItem } from "@/utils/mediaTypes";
const LONG_PRESS_DURATION = 500; // 0.5 seconds
export function BookmarksPart({
onItemsChange,
}: {
onItemsChange: (hasItems: boolean) => void;
}) {
const { t } = useTranslation();
const progressItems = useProgressStore((s) => s.items);
const bookmarks = useBookmarkStore((s) => s.bookmarks);
const removeBookmark = useBookmarkStore((s) => s.removeBookmark);
const [editing, setEditing] = useState(false);
const [gridRef] = useAutoAnimate<HTMLDivElement>();
const pressTimerRef = useRef<NodeJS.Timeout | null>(null);
const items = useMemo(() => {
let output: MediaItem[] = [];
Object.entries(bookmarks).forEach((entry) => {
output.push({
id: entry[0],
...entry[1],
});
});
output = output.sort((a, b) => {
const bookmarkA = bookmarks[a.id];
const bookmarkB = bookmarks[b.id];
const progressA = progressItems[a.id];
const progressB = progressItems[b.id];
const dateA = Math.max(bookmarkA.updatedAt, progressA?.updatedAt ?? 0);
const dateB = Math.max(bookmarkB.updatedAt, progressB?.updatedAt ?? 0);
return dateB - dateA;
});
return output;
}, [bookmarks, progressItems]);
useEffect(() => {
onItemsChange(items.length > 0);
}, [items, onItemsChange]);
const handleLongPress = () => {
// Find the button by ID and simulate a click
const editButton = document.getElementById("edit-button-bookmark");
if (editButton) {
(editButton as HTMLButtonElement).click();
}
};
const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>) => {
e.preventDefault(); // Prevent default touch action
pressTimerRef.current = setTimeout(handleLongPress, LONG_PRESS_DURATION);
};
const handleTouchEnd = () => {
if (pressTimerRef.current) {
clearTimeout(pressTimerRef.current);
pressTimerRef.current = null;
}
};
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
e.preventDefault(); // Prevent default mouse action
pressTimerRef.current = setTimeout(handleLongPress, LONG_PRESS_DURATION);
};
const handleMouseUp = () => {
if (pressTimerRef.current) {
clearTimeout(pressTimerRef.current);
pressTimerRef.current = null;
}
};
if (items.length === 0) return null;
return (
<div
className="relative"
style={{ userSelect: "none" }} // Disable text selection
onContextMenu={(e: React.MouseEvent<HTMLDivElement>) =>
e.preventDefault()
} // Prevent right-click context menu
onTouchStart={handleTouchStart} // Handle touch start
onTouchEnd={handleTouchEnd} // Handle touch end
onMouseDown={handleMouseDown} // Handle mouse down
onMouseUp={handleMouseUp} // Handle mouse up
>
<SectionHeading
title={t("home.bookmarks.sectionTitle") || "Bookmarks"}
icon={Icons.BOOKMARK}
>
<EditButton
editing={editing}
onEdit={setEditing}
id="edit-button-bookmark"
/>
</SectionHeading>
<MediaGrid ref={gridRef}>
{items.map((v) => (
<WatchedMediaCard
key={v.id}
media={v}
closable={editing}
onClose={() => removeBookmark(v.id)}
/>
))}
</MediaGrid>
</div>
);
}