201 lines
6.8 KiB
TypeScript
201 lines
6.8 KiB
TypeScript
import { Notification, NotificationStatus, getNotificationHtmlUrl, isNotificationBuiltFromTask } from "../notification";
|
|
import { Action, ActionPanel, Icon, getPreferenceValues, showToast, Toast } from "@raycast/api";
|
|
import { CreateTaskFromNotification } from "./CreateTaskFromNotification";
|
|
import { LinkNotificationToTask } from "./LinkNotificationToTask";
|
|
import { Page, UniversalInboxPreferences } from "../types";
|
|
import { MutatePromise } from "@raycast/utils";
|
|
import { useMemo, ReactElement } from "react";
|
|
import { handleErrors } from "../api";
|
|
import { TaskStatus } from "../task";
|
|
import utc from "dayjs/plugin/utc";
|
|
import fetch from "node-fetch";
|
|
import dayjs from "dayjs";
|
|
|
|
dayjs.extend(utc);
|
|
|
|
interface NotificationActionsProps {
|
|
notification: Notification;
|
|
detailsTarget: ReactElement;
|
|
mutate: MutatePromise<Page<Notification> | undefined>;
|
|
}
|
|
|
|
export function NotificationActions({ notification, detailsTarget, mutate }: NotificationActionsProps) {
|
|
const notification_html_url = useMemo(() => {
|
|
return getNotificationHtmlUrl(notification);
|
|
}, [notification]);
|
|
|
|
return (
|
|
<ActionPanel>
|
|
<Action.OpenInBrowser url={notification_html_url} />
|
|
<Action.Push title="Show Details" target={detailsTarget} />
|
|
<Action
|
|
title="Delete Notification"
|
|
icon={Icon.Trash}
|
|
shortcut={{ modifiers: ["ctrl"], key: "d" }}
|
|
onAction={() => deleteNotification(notification, mutate)}
|
|
/>
|
|
<Action
|
|
title="Unsubscribe From Notification"
|
|
icon={Icon.BellDisabled}
|
|
shortcut={{ modifiers: ["ctrl"], key: "u" }}
|
|
onAction={() => unsubscribeFromNotification(notification, mutate)}
|
|
/>
|
|
<Action
|
|
title="Snooze"
|
|
icon={Icon.Clock}
|
|
shortcut={{ modifiers: ["ctrl"], key: "s" }}
|
|
onAction={() => snoozeNotification(notification, mutate)}
|
|
/>
|
|
<Action.Push
|
|
title="Create Task"
|
|
icon={Icon.Calendar}
|
|
shortcut={{ modifiers: ["ctrl"], key: "p" }}
|
|
target={<CreateTaskFromNotification notification={notification} mutate={mutate} />}
|
|
/>
|
|
<Action.Push
|
|
title="Link to Task"
|
|
icon={Icon.Link}
|
|
shortcut={{ modifiers: ["ctrl"], key: "l" }}
|
|
target={<LinkNotificationToTask notification={notification} mutate={mutate} />}
|
|
/>
|
|
</ActionPanel>
|
|
);
|
|
}
|
|
|
|
async function deleteNotification(notification: Notification, mutate: MutatePromise<Page<Notification> | undefined>) {
|
|
const preferences = getPreferenceValues<UniversalInboxPreferences>();
|
|
const toast = await showToast({ style: Toast.Style.Animated, title: "Deleting notification" });
|
|
try {
|
|
if (isNotificationBuiltFromTask(notification) && notification.task) {
|
|
await mutate(
|
|
fetch(`${preferences.universalInboxBaseUrl}/api/tasks/${notification.task.id}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify({ status: TaskStatus.Deleted }),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${preferences.apiKey}`,
|
|
},
|
|
}),
|
|
{
|
|
optimisticUpdate(page) {
|
|
if (page) {
|
|
page.content = page.content.filter((n) => n.id !== notification.id);
|
|
}
|
|
return page;
|
|
},
|
|
},
|
|
);
|
|
} else {
|
|
await mutate(
|
|
handleErrors(
|
|
fetch(`${preferences.universalInboxBaseUrl}/api/notifications/${notification.id}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify({ status: NotificationStatus.Deleted }),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${preferences.apiKey}`,
|
|
},
|
|
}),
|
|
),
|
|
{
|
|
optimisticUpdate(page) {
|
|
if (page) {
|
|
page.content = page.content.filter((n) => n.id !== notification.id);
|
|
}
|
|
return page;
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
toast.style = Toast.Style.Success;
|
|
toast.title = "Notification successfully deleted";
|
|
} catch (error) {
|
|
toast.style = Toast.Style.Failure;
|
|
toast.title = "Failed to delete notification";
|
|
toast.message = (error as Error).message;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function unsubscribeFromNotification(
|
|
notification: Notification,
|
|
mutate: MutatePromise<Page<Notification> | undefined>,
|
|
) {
|
|
const preferences = getPreferenceValues<UniversalInboxPreferences>();
|
|
const toast = await showToast({ style: Toast.Style.Animated, title: "Unsubscribing from notification" });
|
|
try {
|
|
await mutate(
|
|
handleErrors(
|
|
fetch(`${preferences.universalInboxBaseUrl}/api/notifications/${notification.id}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify({ status: NotificationStatus.Unsubscribed }),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${preferences.apiKey}`,
|
|
},
|
|
}),
|
|
),
|
|
{
|
|
optimisticUpdate(page) {
|
|
if (page) {
|
|
page.content = page.content.filter((n) => n.id !== notification.id);
|
|
}
|
|
return page;
|
|
},
|
|
},
|
|
);
|
|
|
|
toast.style = Toast.Style.Success;
|
|
toast.title = "Notification successfully unsubscribed";
|
|
} catch (error) {
|
|
toast.style = Toast.Style.Failure;
|
|
toast.title = "Failed to unsubscribe from notification";
|
|
toast.message = (error as Error).message;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function snoozeNotification(notification: Notification, mutate: MutatePromise<Page<Notification> | undefined>) {
|
|
const preferences = getPreferenceValues<UniversalInboxPreferences>();
|
|
const toast = await showToast({ style: Toast.Style.Animated, title: "Snoozing notification" });
|
|
try {
|
|
const snoozeTime = computeSnoozedUntil(new Date(), 1, 6);
|
|
await mutate(
|
|
handleErrors(
|
|
fetch(`${preferences.universalInboxBaseUrl}/api/notifications/${notification.id}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify({ snoozed_until: snoozeTime }),
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${preferences.apiKey}`,
|
|
},
|
|
}),
|
|
),
|
|
{
|
|
optimisticUpdate(page) {
|
|
if (page) {
|
|
page.content = page.content.filter((n) => n.id !== notification.id);
|
|
}
|
|
return page;
|
|
},
|
|
},
|
|
);
|
|
|
|
toast.style = Toast.Style.Success;
|
|
toast.title = "Notification successfully snoozed";
|
|
} catch (error) {
|
|
toast.style = Toast.Style.Failure;
|
|
toast.title = "Failed to snooze notification";
|
|
toast.message = (error as Error).message;
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function computeSnoozedUntil(fromDate: Date, daysOffset: number, resetHour: number): Date {
|
|
const result = dayjs(fromDate)
|
|
.utc()
|
|
.add(fromDate.getHours() < resetHour ? daysOffset - 1 : daysOffset, "day");
|
|
return result.hour(resetHour).minute(0).second(0).millisecond(0).toDate();
|
|
}
|