implementing favorite apps feature (#1410)

This PR implements favorite apps feature and addresses issue #827 
    
<!-- This is an auto-generated description by cubic. -->
---

## Summary by cubic
Adds a favorite apps feature with a star toggle in the sidebar.
Favorites are grouped separately and persisted, with optimistic UI
updates and e2e tests.

- **New Features**
- Added isFavorite to the apps schema and an IPC handler
(add-to-favorite) to toggle and persist the state.
  - Updated AppList to show “Favorite apps” and “Other apps” sections.
- Introduced AppItem component with a star button; uses
useAddAppToFavorite for optimistic updates and toasts.
  - Added Playwright tests to verify favoriting and unfavoriting.

- **Migration**
- Run DB migrations to add the apps.is_favorite column (defaults to 0).

<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Mohamed Aziz Mejri
2025-10-06 20:44:18 +01:00
committed by GitHub
parent e8b93e3298
commit 423a95ed81
12 changed files with 1030 additions and 25 deletions

View File

@@ -0,0 +1,36 @@
import { useMutation } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import { showError, showSuccess } from "@/lib/toast";
import { useAtom } from "jotai";
import { appsListAtom } from "@/atoms/appAtoms";
export function useAddAppToFavorite() {
const [_, setApps] = useAtom(appsListAtom);
const mutation = useMutation<boolean, Error, number>({
mutationFn: async (appId: number): Promise<boolean> => {
const result = await IpcClient.getInstance().addAppToFavorite(appId);
return result.isFavorite;
},
onSuccess: (newIsFavorite, appId) => {
setApps((currentApps) =>
currentApps.map((app) =>
app.id === appId ? { ...app, isFavorite: newIsFavorite } : app,
),
);
showSuccess("App favorite status updated");
},
onError: (error) => {
showError(error.message || "Failed to update favorite status");
},
});
return {
toggleFavorite: mutation.mutate,
toggleFavoriteAsync: mutation.mutateAsync,
isLoading: mutation.isPending,
error: mutation.error,
isError: mutation.isError,
isSuccess: mutation.isSuccess,
};
}