feat: favorites (#2103)

* feat: favorites and templates(ee)

* rename migrations

* fix sidebar

* cleanup tabs

* fix

* turn off templates

* cleanup

* uuid validation
This commit is contained in:
Philip Okugbe
2026-04-12 22:06:25 +01:00
committed by GitHub
parent 57efb91bd3
commit d42091ccb1
90 changed files with 4557 additions and 187 deletions
@@ -12,6 +12,8 @@ import {
IconMarkdown,
IconMessage,
IconPrinter,
IconStar,
IconStarFilled,
IconTrash,
IconWifiOff,
} from "@tabler/icons-react";
@@ -42,6 +44,11 @@ import { PageStateSegmentedControl } from "@/features/user/components/page-state
import MovePageModal from "@/features/page/components/move-page-modal.tsx";
import { useTimeAgo } from "@/hooks/use-time-ago.tsx";
import { PageShareModal } from "@/ee/page-permission";
import {
useFavoriteIds,
useAddFavoriteMutation,
useRemoveFavoriteMutation,
} from "@/features/favorite/queries/favorite-query";
import {
useWatchStatusQuery,
useWatchPageMutation,
@@ -130,6 +137,10 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) {
] = useDisclosure(false);
const [pageEditor] = useAtom(pageEditorAtom);
const pageUpdatedAt = useTimeAgo(page?.updatedAt);
const favoriteIds = useFavoriteIds("page");
const addFavoriteMutation = useAddFavoriteMutation();
const removeFavoriteMutation = useRemoveFavoriteMutation();
const isFavorited = page?.id ? favoriteIds.has(page.id) : false;
const { data: watchStatus } = useWatchStatusQuery(page?.id);
const watchPage = useWatchPageMutation();
const unwatchPage = useUnwatchPageMutation();
@@ -165,6 +176,16 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) {
openDeleteModal({ onConfirm: () => tree?.delete(page.id) });
};
const handleToggleFavorite = () => {
if (!page?.id) return;
const params = { type: "page" as const, pageId: page.id };
if (isFavorited) {
removeFavoriteMutation.mutate(params);
} else {
addFavoriteMutation.mutate(params);
}
};
return (
<>
<Menu
@@ -196,6 +217,19 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) {
{t("Copy as Markdown")}
</Menu.Item>
<Menu.Item
leftSection={
isFavorited ? (
<IconStarFilled size={16} color="var(--mantine-color-yellow-5)" />
) : (
<IconStar size={16} />
)
}
onClick={handleToggleFavorite}
>
{isFavorited ? t("Remove from favorites") : t("Add to favorites")}
</Menu.Item>
{watchStatus?.watching ? (
<Menu.Item
leftSection={<IconEyeOff size={16} />}