mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 18:30:39 +09:00
Merge public-gh/master into pap-1239-ui-ux
This commit is contained in:
commit
b578bf1f51
56 changed files with 16126 additions and 397 deletions
|
|
@ -1,17 +1,19 @@
|
|||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { accessApi } from "../api/access";
|
||||
import { ApiError } from "../api/client";
|
||||
import { inboxDismissalsApi } from "../api/inboxDismissals";
|
||||
import { approvalsApi } from "../api/approvals";
|
||||
import { dashboardApi } from "../api/dashboard";
|
||||
import { heartbeatsApi } from "../api/heartbeats";
|
||||
import { issuesApi } from "../api/issues";
|
||||
import { queryKeys } from "../lib/queryKeys";
|
||||
import {
|
||||
buildInboxDismissedAtByKey,
|
||||
computeInboxBadgeData,
|
||||
getRecentTouchedIssues,
|
||||
loadDismissedInboxItems,
|
||||
saveDismissedInboxItems,
|
||||
loadDismissedInboxAlerts,
|
||||
saveDismissedInboxAlerts,
|
||||
loadReadInboxItems,
|
||||
saveReadInboxItems,
|
||||
READ_ITEMS_KEY,
|
||||
|
|
@ -19,13 +21,13 @@ import {
|
|||
|
||||
const INBOX_ISSUE_STATUSES = "backlog,todo,in_progress,in_review,blocked,done";
|
||||
|
||||
export function useDismissedInboxItems() {
|
||||
const [dismissed, setDismissed] = useState<Set<string>>(loadDismissedInboxItems);
|
||||
export function useDismissedInboxAlerts() {
|
||||
const [dismissed, setDismissed] = useState<Set<string>>(loadDismissedInboxAlerts);
|
||||
|
||||
useEffect(() => {
|
||||
const handleStorage = (event: StorageEvent) => {
|
||||
if (event.key !== "paperclip:inbox:dismissed") return;
|
||||
setDismissed(loadDismissedInboxItems());
|
||||
setDismissed(loadDismissedInboxAlerts());
|
||||
};
|
||||
window.addEventListener("storage", handleStorage);
|
||||
return () => window.removeEventListener("storage", handleStorage);
|
||||
|
|
@ -35,7 +37,7 @@ export function useDismissedInboxItems() {
|
|||
setDismissed((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(id);
|
||||
saveDismissedInboxItems(next);
|
||||
saveDismissedInboxAlerts(next);
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
|
@ -43,6 +45,63 @@ export function useDismissedInboxItems() {
|
|||
return { dismissed, dismiss };
|
||||
}
|
||||
|
||||
export function useInboxDismissals(companyId: string | null | undefined) {
|
||||
const queryClient = useQueryClient();
|
||||
const queryKey = companyId
|
||||
? queryKeys.inboxDismissals(companyId)
|
||||
: ["inbox-dismissals", "__disabled__"] as const;
|
||||
|
||||
const { data: dismissals = [] } = useQuery({
|
||||
queryKey,
|
||||
queryFn: () => inboxDismissalsApi.list(companyId!),
|
||||
enabled: !!companyId,
|
||||
});
|
||||
|
||||
const dismissMutation = useMutation({
|
||||
mutationFn: ({ itemKey }: { itemKey: string }) => inboxDismissalsApi.dismiss(companyId!, itemKey),
|
||||
onMutate: async ({ itemKey }) => {
|
||||
if (!companyId) return { previous: [] as typeof dismissals };
|
||||
await queryClient.cancelQueries({ queryKey });
|
||||
const previous = queryClient.getQueryData<typeof dismissals>(queryKey) ?? [];
|
||||
const now = new Date();
|
||||
queryClient.setQueryData(queryKey, [
|
||||
{
|
||||
id: `optimistic:${itemKey}`,
|
||||
companyId,
|
||||
userId: "me",
|
||||
itemKey,
|
||||
dismissedAt: now,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
...previous.filter((dismissal) => dismissal.itemKey !== itemKey),
|
||||
]);
|
||||
return { previous };
|
||||
},
|
||||
onError: (_error, _variables, context) => {
|
||||
if (!context) return;
|
||||
queryClient.setQueryData(queryKey, context.previous);
|
||||
},
|
||||
onSettled: () => {
|
||||
if (!companyId) return;
|
||||
queryClient.invalidateQueries({ queryKey });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.sidebarBadges(companyId) });
|
||||
},
|
||||
});
|
||||
|
||||
const dismissedAtByKey = useMemo(
|
||||
() => buildInboxDismissedAtByKey(dismissals),
|
||||
[dismissals],
|
||||
);
|
||||
|
||||
return {
|
||||
dismissals,
|
||||
dismissedAtByKey,
|
||||
dismiss: (itemKey: string) => dismissMutation.mutate({ itemKey }),
|
||||
isPending: dismissMutation.isPending,
|
||||
};
|
||||
}
|
||||
|
||||
export function useReadInboxItems() {
|
||||
const [readItems, setReadItems] = useState<Set<string>>(loadReadInboxItems);
|
||||
|
||||
|
|
@ -77,7 +136,8 @@ export function useReadInboxItems() {
|
|||
}
|
||||
|
||||
export function useInboxBadge(companyId: string | null | undefined) {
|
||||
const { dismissed } = useDismissedInboxItems();
|
||||
const { dismissed: dismissedAlerts } = useDismissedInboxAlerts();
|
||||
const { dismissedAtByKey } = useInboxDismissals(companyId);
|
||||
|
||||
const { data: approvals = [] } = useQuery({
|
||||
queryKey: queryKeys.approvals.list(companyId!),
|
||||
|
|
@ -134,8 +194,9 @@ export function useInboxBadge(companyId: string | null | undefined) {
|
|||
dashboard,
|
||||
heartbeatRuns,
|
||||
mineIssues,
|
||||
dismissed,
|
||||
dismissedAlerts,
|
||||
dismissedAtByKey,
|
||||
}),
|
||||
[approvals, joinRequests, dashboard, heartbeatRuns, mineIssues, dismissed],
|
||||
[approvals, joinRequests, dashboard, heartbeatRuns, mineIssues, dismissedAlerts, dismissedAtByKey],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue