feat: Add Google Mail thread list item

This commit is contained in:
2024-01-26 22:50:13 +01:00
parent 3df827ebd7
commit e0f90b0c42
12 changed files with 185 additions and 72 deletions

View File

@@ -24,6 +24,7 @@ export function NotificationActions({ notification, detailsTarget, mutate }: Not
return getNotificationHtmlUrl(notification); return getNotificationHtmlUrl(notification);
}, [notification]); }, [notification]);
console.log("notification_html_url", notification_html_url, notification);
return ( return (
<ActionPanel> <ActionPanel>
<Action.OpenInBrowser url={notification_html_url} /> <Action.OpenInBrowser url={notification_html_url} />

View File

@@ -1,5 +1,5 @@
import { GoogleMailNotificationListItem } from "./integrations/google-mail/listitem/GoogleMailNotificationListItem";
import { Action, ActionPanel, Detail, List, getPreferenceValues, openExtensionPreferences } from "@raycast/api"; 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 { GithubNotificationListItem } from "./integrations/github/listitem/GithubNotificationListItem";
import { LinearNotificationListItem } from "./integrations/linear/listitem/LinearNotificationListItem"; import { LinearNotificationListItem } from "./integrations/linear/listitem/LinearNotificationListItem";
import { TodoistNotificationListItem } from "./integrations/todoist/TodoistNotificationListItem"; import { TodoistNotificationListItem } from "./integrations/todoist/TodoistNotificationListItem";

View File

@@ -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 (
<List.Item
key={notification.id}
title={notification.title}
icon={icon}
subtitle={`#${notification.source_id}`}
actions={
<NotificationActions
notification={notification}
detailsTarget={<Detail markdown="# To be implemented 👋" />}
mutate={mutate}
/>
}
/>
);
}

View File

@@ -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 (
<GoogleMailThreadListItem
icon={icon}
notification={notification}
googleMailThread={notification.metadata.content}
mutate={mutate}
/>
);
}

View File

@@ -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<Page<Notification> | 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 (
<List.Item
key={notification.id}
title={notification.title}
icon={icon}
accessories={accessories}
subtitle={subtitle}
actions={
<NotificationActions
notification={notification}
detailsTarget={<GoogleMailThreadPreview notification={notification} googleMailThread={googleMailThread} />}
mutate={mutate}
/>
}
/>
);
}

View File

@@ -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 (
<Detail
markdown={`# ${notification.title}`}
actions={
<ActionPanel>
<Action.OpenInBrowser url={notification_html_url} />
</ActionPanel>
}
/>
);
}

View File

@@ -0,0 +1,27 @@
export interface GoogleMailThread {
id: string;
user_email_address: string;
history_id: string;
messages: Array<GoogleMailMessage>;
}
export interface GoogleMailMessage {
id: string;
threadId: string;
labelIds?: Array<string>;
snippet: string;
payload: GoogleMailMessagePayload;
sizeEstimate: number;
historyId: string;
internalDate: Date;
}
export interface GoogleMailMessagePayload {
mimeType: string;
headers: Array<GoogleMailMessageHeader>;
}
export interface GoogleMailMessageHeader {
name: string;
value: string;
}

View File

@@ -1,11 +1,12 @@
import { LinearWorkflowStateType, LinearIssueNotification, LinearWorkflowState } from "../types";
import { NotificationActions } from "../../../action/NotificationActions"; import { NotificationActions } from "../../../action/NotificationActions";
import { LinearIssuePreview } from "../preview/LinearIssuePreview"; import { LinearIssuePreview } from "../preview/LinearIssuePreview";
import { getLinearUserAccessory } from "../accessories"; import { getLinearUserAccessory } from "../accessories";
import { Notification } from "../../../notification"; import { Notification } from "../../../notification";
import { LinearIssueNotification } from "../types";
import { MutatePromise } from "@raycast/utils"; import { MutatePromise } from "@raycast/utils";
import { Page } from "../../../types"; import { Page } from "../../../types";
import { List } from "@raycast/api"; import { List } from "@raycast/api";
import { match } from "ts-pattern";
interface LinearIssueNotificationListItemProps { interface LinearIssueNotificationListItemProps {
icon: string; icon: string;
@@ -53,36 +54,18 @@ export function LinearIssueNotificationListItem({
} }
export function getLinearIssueStateAccessory(state: LinearWorkflowState): List.Item.Accessory { export function getLinearIssueStateAccessory(state: LinearWorkflowState): List.Item.Accessory {
switch (state.type) { return {
case "Triage": icon: {
return { source: match(state)
icon: { source: "linear-issue-triage.svg", tintColor: state.color }, .with({ type: LinearWorkflowStateType.Triage }, () => "linear-issue-triage.svg")
tooltip: state.name, .with({ type: LinearWorkflowStateType.Backlog }, () => "linear-issue-backlog.svg")
}; .with({ type: LinearWorkflowStateType.Unstarted }, () => "linear-issue-unstarted.svg")
case "Backlog": .with({ type: LinearWorkflowStateType.Started }, () => "linear-issue-started.svg")
return { .with({ type: LinearWorkflowStateType.Completed }, () => "linear-issue-completed.svg")
icon: { source: "linear-issue-backlog.svg", tintColor: state.color }, .with({ type: LinearWorkflowStateType.Canceled }, () => "linear-issue-canceled.svg")
tooltip: state.name, .exhaustive(),
}; tintColor: state.color,
case "Unstarted": },
return { tooltip: state.name,
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,
};
}
} }

View File

@@ -29,7 +29,7 @@ export function LinearNotificationListItem({ notification, mutate }: Notificatio
<LinearProjectNotificationListItem <LinearProjectNotificationListItem
icon={icon} icon={icon}
notification={notification} notification={notification}
LinearProjectNotification={notification.metadata.content.content} linearProjectNotification={notification.metadata.content.content}
mutate={mutate} mutate={mutate}
/> />
); );

View File

@@ -43,7 +43,7 @@ export function LinearProjectNotificationListItem({
<NotificationActions <NotificationActions
notification={notification} notification={notification}
detailsTarget={ detailsTarget={
<LinearProjectPreview notification={notification} linearProject={linearProjectNotification.issue} /> <LinearProjectPreview notification={notification} linearProject={linearProjectNotification.project} />
} }
mutate={mutate} mutate={mutate}
/> />

View File

@@ -1,3 +1,7 @@
export type LinearNotification =
| { type: "IssueNotification"; content: LinearIssueNotification }
| { type: "ProjectNotification"; content: LinearProjectNotification };
export interface LinearIssueNotification { export interface LinearIssueNotification {
id: string; id: string;
type: string; type: string;

View File

@@ -1,4 +1,6 @@
import { GithubDiscussion, GithubPullRequest } from "./integrations/github/types"; 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 { MutatePromise } from "@raycast/utils";
import { match, P } from "ts-pattern"; import { match, P } from "ts-pattern";
import { Page } from "./types"; import { Page } from "./types";
@@ -18,11 +20,14 @@ export interface Notification {
details?: NotificationDetails; details?: NotificationDetails;
} }
export type NotificationMetadata = { export type NotificationMetadata =
type: "Github" | "Linear" | "GoogleMail" | "Todoist"; | {
// eslint-disable-next-line @typescript-eslint/no-explicit-any type: "Github" | "Todoist";
content: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any
}; content: any;
}
| { type: "Linear"; content: LinearNotification }
| { type: "GoogleMail"; content: GoogleMailThread };
export type NotificationDetails = export type NotificationDetails =
| { type: "GithubPullRequest"; content: GithubPullRequest } | { type: "GithubPullRequest"; content: GithubPullRequest }
@@ -43,8 +48,8 @@ export type NotificationListItemProps = {
export function getNotificationHtmlUrl(notification: Notification) { export function getNotificationHtmlUrl(notification: Notification) {
return match(notification) return match(notification)
.with( .with(
{ details: { type: P.union("GithubPullRequest", "GithubDiscussion") } }, { details: { type: P.union("GithubPullRequest", "GithubDiscussion"), content: P.select() } },
() => notification.details.content.url, (notificationDetails) => notificationDetails.url,
) )
.with( .with(
{ metadata: { type: "Linear", content: { type: "IssueNotification", content: P.select() } } }, { 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() } } }, { metadata: { type: "Linear", content: { type: "ProjectNotification", content: P.select() } } },
(linearProjectNotification) => linearProjectNotification.project.url, (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(); .exhaustive();
} }