From e0f90b0c4280026c9e4fe56f628d35d8f4cf8881 Mon Sep 17 00:00:00 2001 From: David Rousselie Date: Fri, 26 Jan 2024 22:50:13 +0100 Subject: [PATCH] feat: Add Google Mail thread list item --- src/action/NotificationActions.tsx | 1 + src/index.tsx | 2 +- .../GoogleMailNotificationListItem.tsx | 29 --------- .../GoogleMailNotificationListItem.tsx | 24 +++++++ .../listitem/GoogleMailThreadListItem.tsx | 65 +++++++++++++++++++ .../preview/GoogleMailThreadPreview.tsx | 26 ++++++++ src/integrations/google-mail/types.ts | 27 ++++++++ .../LinearIssueNotificationListItem.tsx | 49 +++++--------- .../listitem/LinearNotificationListItem.tsx | 2 +- .../LinearProjectNotificationListItem.tsx | 2 +- src/integrations/linear/types.ts | 4 ++ src/notification.ts | 26 ++++++-- 12 files changed, 185 insertions(+), 72 deletions(-) delete mode 100644 src/integrations/google-mail/GoogleMailNotificationListItem.tsx create mode 100644 src/integrations/google-mail/listitem/GoogleMailNotificationListItem.tsx create mode 100644 src/integrations/google-mail/listitem/GoogleMailThreadListItem.tsx create mode 100644 src/integrations/google-mail/preview/GoogleMailThreadPreview.tsx create mode 100644 src/integrations/google-mail/types.ts diff --git a/src/action/NotificationActions.tsx b/src/action/NotificationActions.tsx index ab97c97..1f4385e 100644 --- a/src/action/NotificationActions.tsx +++ b/src/action/NotificationActions.tsx @@ -24,6 +24,7 @@ export function NotificationActions({ notification, detailsTarget, mutate }: Not return getNotificationHtmlUrl(notification); }, [notification]); + console.log("notification_html_url", notification_html_url, notification); return ( diff --git a/src/index.tsx b/src/index.tsx index e4df547..12e2392 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,5 @@ +import { GoogleMailNotificationListItem } from "./integrations/google-mail/listitem/GoogleMailNotificationListItem"; import { Action, ActionPanel, Detail, List, getPreferenceValues, openExtensionPreferences } from "@raycast/api"; -import { GoogleMailNotificationListItem } from "./integrations/google-mail/GoogleMailNotificationListItem"; import { GithubNotificationListItem } from "./integrations/github/listitem/GithubNotificationListItem"; import { LinearNotificationListItem } from "./integrations/linear/listitem/LinearNotificationListItem"; import { TodoistNotificationListItem } from "./integrations/todoist/TodoistNotificationListItem"; diff --git a/src/integrations/google-mail/GoogleMailNotificationListItem.tsx b/src/integrations/google-mail/GoogleMailNotificationListItem.tsx deleted file mode 100644 index d039c1b..0000000 --- a/src/integrations/google-mail/GoogleMailNotificationListItem.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { NotificationActions } from "../../action/NotificationActions"; -import { NotificationListItemProps } from "../../notification"; -import { Detail, List, environment } from "@raycast/api"; -import { useMemo } from "react"; - -export function GoogleMailNotificationListItem({ notification, mutate }: NotificationListItemProps) { - const icon = useMemo(() => { - if (environment.appearance === "dark") { - return "google-mail-logo-light.svg"; - } - return "google-mail-logo-dark.svg"; - }, [environment]); - - return ( - } - mutate={mutate} - /> - } - /> - ); -} diff --git a/src/integrations/google-mail/listitem/GoogleMailNotificationListItem.tsx b/src/integrations/google-mail/listitem/GoogleMailNotificationListItem.tsx new file mode 100644 index 0000000..208fc7e --- /dev/null +++ b/src/integrations/google-mail/listitem/GoogleMailNotificationListItem.tsx @@ -0,0 +1,24 @@ +import { GoogleMailThreadListItem } from "./GoogleMailThreadListItem"; +import { NotificationListItemProps } from "../../../notification"; +import { environment } from "@raycast/api"; +import { useMemo } from "react"; + +export function GoogleMailNotificationListItem({ notification, mutate }: NotificationListItemProps) { + const icon = useMemo(() => { + if (environment.appearance === "dark") { + return "google-mail-logo-light.svg"; + } + return "google-mail-logo-dark.svg"; + }, [environment]); + + if (notification.metadata.type !== "GoogleMail") return null; + + return ( + + ); +} diff --git a/src/integrations/google-mail/listitem/GoogleMailThreadListItem.tsx b/src/integrations/google-mail/listitem/GoogleMailThreadListItem.tsx new file mode 100644 index 0000000..392526f --- /dev/null +++ b/src/integrations/google-mail/listitem/GoogleMailThreadListItem.tsx @@ -0,0 +1,65 @@ +import { GoogleMailThreadPreview } from "../preview/GoogleMailThreadPreview"; +import { NotificationActions } from "../../../action/NotificationActions"; +//import { getLinearUserAccessory } from "../accessories"; +import { Notification } from "../../../notification"; +import { Icon, Color, List } from "@raycast/api"; +import { MutatePromise } from "@raycast/utils"; +import { GoogleMailThread } from "../types"; +import { Page } from "../../../types"; + +interface GoogleMailThreadListItemProps { + icon: string; + notification: Notification; + googleMailThread: GoogleMailThread; + mutate: MutatePromise | undefined>; +} + +export function GoogleMailThreadListItem({ + icon, + notification, + googleMailThread, + mutate, +}: GoogleMailThreadListItemProps) { + const isStarred = googleMailThread.messages.some((message) => message.labelIds?.includes("STARRED")); + const isImportant = googleMailThread.messages.some((message) => message.labelIds?.includes("IMPORTANT")); + const fromAddress = googleMailThread.messages[0].payload.headers.find((header) => header.name === "From")?.value; + const subtitle = fromAddress; + + const accessories: List.Item.Accessory[] = [ + { + date: new Date(notification.updated_at), + tooltip: `Updated at ${notification.updated_at}`, + }, + ]; + + if (isStarred) { + accessories.unshift({ + icon: { source: Icon.Star, tintColor: Color.Yellow }, + tooltip: "Starred", + }); + } + + if (isImportant) { + accessories.unshift({ + icon: { source: Icon.Exclamationmark, tintColor: Color.Red }, + tooltip: "Important", + }); + } + + return ( + } + mutate={mutate} + /> + } + /> + ); +} diff --git a/src/integrations/google-mail/preview/GoogleMailThreadPreview.tsx b/src/integrations/google-mail/preview/GoogleMailThreadPreview.tsx new file mode 100644 index 0000000..f0edec9 --- /dev/null +++ b/src/integrations/google-mail/preview/GoogleMailThreadPreview.tsx @@ -0,0 +1,26 @@ +import { Notification, getNotificationHtmlUrl } from "../../../notification"; +import { Detail, ActionPanel, Action } from "@raycast/api"; +import { GoogleMailThread } from "../types"; +import { useMemo } from "react"; + +interface GoogleMailThreadPreviewProps { + notification: Notification; + googleMailThread: GoogleMailThread; +} + +export function GoogleMailThreadPreview({ notification }: GoogleMailThreadPreviewProps) { + const notification_html_url = useMemo(() => { + return getNotificationHtmlUrl(notification); + }, [notification]); + + return ( + + + + } + /> + ); +} diff --git a/src/integrations/google-mail/types.ts b/src/integrations/google-mail/types.ts new file mode 100644 index 0000000..5b319b2 --- /dev/null +++ b/src/integrations/google-mail/types.ts @@ -0,0 +1,27 @@ +export interface GoogleMailThread { + id: string; + user_email_address: string; + history_id: string; + messages: Array; +} + +export interface GoogleMailMessage { + id: string; + threadId: string; + labelIds?: Array; + snippet: string; + payload: GoogleMailMessagePayload; + sizeEstimate: number; + historyId: string; + internalDate: Date; +} + +export interface GoogleMailMessagePayload { + mimeType: string; + headers: Array; +} + +export interface GoogleMailMessageHeader { + name: string; + value: string; +} diff --git a/src/integrations/linear/listitem/LinearIssueNotificationListItem.tsx b/src/integrations/linear/listitem/LinearIssueNotificationListItem.tsx index 5fbc3b9..c2302ea 100644 --- a/src/integrations/linear/listitem/LinearIssueNotificationListItem.tsx +++ b/src/integrations/linear/listitem/LinearIssueNotificationListItem.tsx @@ -1,11 +1,12 @@ +import { LinearWorkflowStateType, LinearIssueNotification, LinearWorkflowState } from "../types"; import { NotificationActions } from "../../../action/NotificationActions"; import { LinearIssuePreview } from "../preview/LinearIssuePreview"; import { getLinearUserAccessory } from "../accessories"; import { Notification } from "../../../notification"; -import { LinearIssueNotification } from "../types"; import { MutatePromise } from "@raycast/utils"; import { Page } from "../../../types"; import { List } from "@raycast/api"; +import { match } from "ts-pattern"; interface LinearIssueNotificationListItemProps { icon: string; @@ -53,36 +54,18 @@ export function LinearIssueNotificationListItem({ } export function getLinearIssueStateAccessory(state: LinearWorkflowState): List.Item.Accessory { - switch (state.type) { - case "Triage": - return { - icon: { source: "linear-issue-triage.svg", tintColor: state.color }, - tooltip: state.name, - }; - case "Backlog": - return { - icon: { source: "linear-issue-backlog.svg", tintColor: state.color }, - tooltip: state.name, - }; - case "Unstarted": - return { - icon: { source: "linear-issue-unstarted.svg", tintColor: state.color }, - tooltip: state.name, - }; - case "Started": - return { - icon: { source: "linear-issue-started.svg", tintColor: state.color }, - tooltip: state.name, - }; - case "Completed": - return { - icon: { source: "linear-issue-completed.svg", tintColor: state.color }, - tooltip: state.name, - }; - case "Canceled": - return { - icon: { source: "linear-issue-canceled.svg", tintColor: state.color }, - tooltip: state.name, - }; - } + return { + icon: { + source: match(state) + .with({ type: LinearWorkflowStateType.Triage }, () => "linear-issue-triage.svg") + .with({ type: LinearWorkflowStateType.Backlog }, () => "linear-issue-backlog.svg") + .with({ type: LinearWorkflowStateType.Unstarted }, () => "linear-issue-unstarted.svg") + .with({ type: LinearWorkflowStateType.Started }, () => "linear-issue-started.svg") + .with({ type: LinearWorkflowStateType.Completed }, () => "linear-issue-completed.svg") + .with({ type: LinearWorkflowStateType.Canceled }, () => "linear-issue-canceled.svg") + .exhaustive(), + tintColor: state.color, + }, + tooltip: state.name, + }; } diff --git a/src/integrations/linear/listitem/LinearNotificationListItem.tsx b/src/integrations/linear/listitem/LinearNotificationListItem.tsx index 6a1aebb..2a01f19 100644 --- a/src/integrations/linear/listitem/LinearNotificationListItem.tsx +++ b/src/integrations/linear/listitem/LinearNotificationListItem.tsx @@ -29,7 +29,7 @@ export function LinearNotificationListItem({ notification, mutate }: Notificatio ); diff --git a/src/integrations/linear/listitem/LinearProjectNotificationListItem.tsx b/src/integrations/linear/listitem/LinearProjectNotificationListItem.tsx index 304871b..a78d0c0 100644 --- a/src/integrations/linear/listitem/LinearProjectNotificationListItem.tsx +++ b/src/integrations/linear/listitem/LinearProjectNotificationListItem.tsx @@ -43,7 +43,7 @@ export function LinearProjectNotificationListItem({ + } mutate={mutate} /> diff --git a/src/integrations/linear/types.ts b/src/integrations/linear/types.ts index 14f1591..ed242c5 100644 --- a/src/integrations/linear/types.ts +++ b/src/integrations/linear/types.ts @@ -1,3 +1,7 @@ +export type LinearNotification = + | { type: "IssueNotification"; content: LinearIssueNotification } + | { type: "ProjectNotification"; content: LinearProjectNotification }; + export interface LinearIssueNotification { id: string; type: string; diff --git a/src/notification.ts b/src/notification.ts index 05e219a..5e7fc11 100644 --- a/src/notification.ts +++ b/src/notification.ts @@ -1,4 +1,6 @@ import { GithubDiscussion, GithubPullRequest } from "./integrations/github/types"; +import { GoogleMailThread } from "./integrations/google-mail/types"; +import { LinearNotification } from "./integrations/linear/types"; import { MutatePromise } from "@raycast/utils"; import { match, P } from "ts-pattern"; import { Page } from "./types"; @@ -18,11 +20,14 @@ export interface Notification { details?: NotificationDetails; } -export type NotificationMetadata = { - type: "Github" | "Linear" | "GoogleMail" | "Todoist"; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - content: any; -}; +export type NotificationMetadata = + | { + type: "Github" | "Todoist"; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + content: any; + } + | { type: "Linear"; content: LinearNotification } + | { type: "GoogleMail"; content: GoogleMailThread }; export type NotificationDetails = | { type: "GithubPullRequest"; content: GithubPullRequest } @@ -43,8 +48,8 @@ export type NotificationListItemProps = { export function getNotificationHtmlUrl(notification: Notification) { return match(notification) .with( - { details: { type: P.union("GithubPullRequest", "GithubDiscussion") } }, - () => notification.details.content.url, + { details: { type: P.union("GithubPullRequest", "GithubDiscussion"), content: P.select() } }, + (notificationDetails) => notificationDetails.url, ) .with( { metadata: { type: "Linear", content: { type: "IssueNotification", content: P.select() } } }, @@ -54,6 +59,13 @@ export function getNotificationHtmlUrl(notification: Notification) { { metadata: { type: "Linear", content: { type: "ProjectNotification", content: P.select() } } }, (linearProjectNotification) => linearProjectNotification.project.url, ) + .with( + { metadata: { type: "GoogleMail", content: P.select() } }, + (googleMailThread) => + `https://mail.google.com/mail/u/${googleMailThread.user_email_address}/#inbox/${googleMailThread.id}`, + ) + .with({ metadata: { type: "Todoist", content: P.select() } }, () => "") + .with({ metadata: { type: "Github" } }, () => "https://github.com") .exhaustive(); }