diff --git a/assets/linear-project-backlog.svg b/assets/linear-project-backlog.svg
new file mode 100644
index 0000000..6729424
--- /dev/null
+++ b/assets/linear-project-backlog.svg
@@ -0,0 +1 @@
+
diff --git a/assets/linear-project-canceled.svg b/assets/linear-project-canceled.svg
new file mode 100644
index 0000000..0410953
--- /dev/null
+++ b/assets/linear-project-canceled.svg
@@ -0,0 +1 @@
+
diff --git a/assets/linear-project-completed.svg b/assets/linear-project-completed.svg
new file mode 100644
index 0000000..5a939e7
--- /dev/null
+++ b/assets/linear-project-completed.svg
@@ -0,0 +1 @@
+
diff --git a/assets/linear-project-paused.svg b/assets/linear-project-paused.svg
new file mode 100644
index 0000000..099dac5
--- /dev/null
+++ b/assets/linear-project-paused.svg
@@ -0,0 +1 @@
+
diff --git a/assets/linear-project-planned.svg b/assets/linear-project-planned.svg
new file mode 100644
index 0000000..f401046
--- /dev/null
+++ b/assets/linear-project-planned.svg
@@ -0,0 +1 @@
+
diff --git a/assets/linear-project-started.svg b/assets/linear-project-started.svg
new file mode 100644
index 0000000..ccf1843
--- /dev/null
+++ b/assets/linear-project-started.svg
@@ -0,0 +1 @@
+
diff --git a/src/action/CreateTaskFromNotification.tsx b/src/action/CreateTaskFromNotification.tsx
index cc9892d..bbf6d4e 100644
--- a/src/action/CreateTaskFromNotification.tsx
+++ b/src/action/CreateTaskFromNotification.tsx
@@ -1,15 +1,15 @@
import { Action, useNavigation, ActionPanel, Form, Icon, getPreferenceValues, showToast, Toast } from "@raycast/api";
import { MutatePromise, useForm, FormValidation, useFetch } from "@raycast/utils";
import { Page, UniversalInboxPreferences } from "../types";
+import { default as dayjs, extend } from "dayjs";
import { Notification } from "../notification";
import { TaskPriority } from "../task";
import { handleErrors } from "../api";
import utc from "dayjs/plugin/utc";
import { useState } from "react";
import fetch from "node-fetch";
-import dayjs from "dayjs";
-dayjs.extend(utc);
+extend(utc);
interface CreateTaskFromNotificationProps {
notification: Notification;
diff --git a/src/action/NotificationActions.tsx b/src/action/NotificationActions.tsx
index 1f4385e..9fa4cd2 100644
--- a/src/action/NotificationActions.tsx
+++ b/src/action/NotificationActions.tsx
@@ -3,15 +3,15 @@ import { Action, ActionPanel, Icon, getPreferenceValues, showToast, Toast } from
import { CreateTaskFromNotification } from "./CreateTaskFromNotification";
import { LinkNotificationToTask } from "./LinkNotificationToTask";
import { Page, UniversalInboxPreferences } from "../types";
+import { default as dayjs, extend } from "dayjs";
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);
+extend(utc);
interface NotificationActionsProps {
notification: Notification;
@@ -20,14 +20,13 @@ interface NotificationActionsProps {
}
export function NotificationActions({ notification, detailsTarget, mutate }: NotificationActionsProps) {
- const notification_html_url = useMemo(() => {
+ const notificationHtmlUrl = useMemo(() => {
return getNotificationHtmlUrl(notification);
}, [notification]);
- console.log("notification_html_url", notification_html_url, notification);
return (
-
+
| undefined>;
}
+
export function NotificationTaskActions({ notification, detailsTarget }: NotificationTaskActionsProps) {
- const notification_html_url = useMemo(() => {
+ const notificationHtmlUrl = useMemo(() => {
return getNotificationHtmlUrl(notification);
}, [notification]);
return (
-
+
| undefined>;
}
export function GithubDiscussionNotificationListItem({
- icon,
notification,
githubDiscussion,
mutate,
@@ -45,7 +43,7 @@ export function GithubDiscussionNotificationListItem({
{
- if (environment.appearance === "dark") {
- return "github-logo-light.svg";
- }
- return "github-logo-dark.svg";
- }, [environment]);
-
switch (notification.details?.type) {
case "GithubPullRequest":
return (
| undefined>;
}
export function GithubPullRequestNotificationListItem({
- icon,
notification,
githubPullRequest,
mutate,
@@ -64,7 +62,7 @@ export function GithubPullRequestNotificationListItem({
{
+ const notificationHtmlUrl = useMemo(() => {
return getNotificationHtmlUrl(notification);
}, [notification]);
@@ -18,7 +18,7 @@ export function GithubDiscussionPreview({ notification, githubDiscussion }: Gith
markdown={`# ${githubDiscussion.title}`}
actions={
-
+
}
/>
diff --git a/src/integrations/github/preview/GithubPullRequestPreview.tsx b/src/integrations/github/preview/GithubPullRequestPreview.tsx
index 8c332ae..0b0ece6 100644
--- a/src/integrations/github/preview/GithubPullRequestPreview.tsx
+++ b/src/integrations/github/preview/GithubPullRequestPreview.tsx
@@ -9,7 +9,7 @@ interface GithubPullRequestPreviewProps {
}
export function GithubPullRequestPreview({ notification, githubPullRequest }: GithubPullRequestPreviewProps) {
- const notification_html_url = useMemo(() => {
+ const notificationHtmlUrl = useMemo(() => {
return getNotificationHtmlUrl(notification);
}, [notification]);
@@ -18,7 +18,7 @@ export function GithubPullRequestPreview({ notification, githubPullRequest }: Gi
markdown={`# ${githubPullRequest.title}`}
actions={
-
+
}
/>
diff --git a/src/integrations/google-mail/listitem/GoogleMailNotificationListItem.tsx b/src/integrations/google-mail/listitem/GoogleMailNotificationListItem.tsx
index 208fc7e..b4161ef 100644
--- a/src/integrations/google-mail/listitem/GoogleMailNotificationListItem.tsx
+++ b/src/integrations/google-mail/listitem/GoogleMailNotificationListItem.tsx
@@ -1,21 +1,11 @@
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 (
| undefined>;
}
-export function GoogleMailThreadListItem({
- icon,
- notification,
- googleMailThread,
- mutate,
-}: GoogleMailThreadListItemProps) {
+export function GoogleMailThreadListItem({ 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;
@@ -50,7 +44,7 @@ export function GoogleMailThreadListItem({
{
+ const notificationHtmlUrl = useMemo(() => {
return getNotificationHtmlUrl(notification);
}, [notification]);
@@ -18,7 +18,7 @@ export function GoogleMailThreadPreview({ notification }: GoogleMailThreadPrevie
markdown={`# ${notification.title}`}
actions={
-
+
}
/>
diff --git a/src/integrations/linear/accessories.ts b/src/integrations/linear/accessories.ts
index 9bdffbf..aad852f 100644
--- a/src/integrations/linear/accessories.ts
+++ b/src/integrations/linear/accessories.ts
@@ -1,10 +1,11 @@
import { Icon, Image, List } from "@raycast/api";
+import { getAvatarIcon } from "@raycast/utils";
import { LinearUser } from "./types";
export function getLinearUserAccessory(user?: LinearUser): List.Item.Accessory {
if (user) {
return {
- icon: user.avatar_url ? { source: user.avatar_url, mask: Image.Mask.Circle } : Icon.Person,
+ icon: user.avatar_url ? { source: user.avatar_url, mask: Image.Mask.Circle } : getAvatarIcon(user.name),
tooltip: user.name,
};
}
diff --git a/src/integrations/linear/listitem/LinearIssueNotificationListItem.tsx b/src/integrations/linear/listitem/LinearIssueNotificationListItem.tsx
index c2302ea..f819c25 100644
--- a/src/integrations/linear/listitem/LinearIssueNotificationListItem.tsx
+++ b/src/integrations/linear/listitem/LinearIssueNotificationListItem.tsx
@@ -9,14 +9,12 @@ import { List } from "@raycast/api";
import { match } from "ts-pattern";
interface LinearIssueNotificationListItemProps {
- icon: string;
notification: Notification;
linearIssueNotification: LinearIssueNotification;
mutate: MutatePromise | undefined>;
}
export function LinearIssueNotificationListItem({
- icon,
notification,
linearIssueNotification,
mutate,
@@ -39,7 +37,7 @@ export function LinearIssueNotificationListItem({
{
- if (environment.appearance === "dark") {
- return "linear-logo-light.svg";
- }
- return "linear-logo-dark.svg";
- }, [environment]);
-
if (notification.metadata.type !== "Linear") return null;
switch (notification.metadata.content.type) {
case "IssueNotification":
return (
| undefined>;
}
export function LinearProjectNotificationListItem({
- icon,
notification,
linearProjectNotification,
mutate,
}: LinearProjectNotificationListItemProps) {
const subtitle = linearProjectNotification.project.name;
+ const state = getLinearProjectStateAccessory(linearProjectNotification.project);
const lead = getLinearUserAccessory(linearProjectNotification.project.lead);
const accessories: List.Item.Accessory[] = [
+ state,
lead,
{
date: new Date(linearProjectNotification.updated_at),
@@ -36,7 +37,7 @@ export function LinearProjectNotificationListItem({
);
}
+
+export function getLinearProjectStateAccessory(project: LinearProject): List.Item.Accessory {
+ return {
+ icon: match(project)
+ .with({ state: LinearProjectState.Planned }, () => {
+ return { source: "linear-project-planned.svg", tintColor: Color.SecondaryText };
+ })
+ .with({ state: LinearProjectState.Backlog }, () => {
+ return { source: "linear-project-backlog.svg", tintColor: Color.PrimaryText };
+ })
+ .with({ state: LinearProjectState.Started }, () => {
+ return { source: "linear-project-started.svg", tintColor: Color.Blue };
+ })
+ .with({ state: LinearProjectState.Paused }, () => {
+ return { source: "linear-project-paused.svg", tintColor: Color.PrimaryText };
+ })
+ .with({ state: LinearProjectState.Completed }, () => {
+ return { source: "linear-project-completed.svg", tintColor: Color.Magenta };
+ })
+ .with({ state: LinearProjectState.Canceled }, () => {
+ return { source: "linear-project-canceled.svg", tintColor: Color.SecondaryText };
+ })
+ .exhaustive(),
+ tooltip: project.state,
+ };
+}
diff --git a/src/integrations/linear/preview/LinearIssuePreview.tsx b/src/integrations/linear/preview/LinearIssuePreview.tsx
index 136ea92..5aeb74a 100644
--- a/src/integrations/linear/preview/LinearIssuePreview.tsx
+++ b/src/integrations/linear/preview/LinearIssuePreview.tsx
@@ -9,7 +9,7 @@ interface LinearIssuePreviewProps {
}
export function LinearIssuePreview({ notification, linearIssue }: LinearIssuePreviewProps) {
- const notification_html_url = useMemo(() => {
+ const notificationHtmlUrl = useMemo(() => {
return getNotificationHtmlUrl(notification);
}, [notification]);
@@ -18,7 +18,7 @@ export function LinearIssuePreview({ notification, linearIssue }: LinearIssuePre
markdown={`# ${linearIssue.title}`}
actions={
-
+
}
/>
diff --git a/src/integrations/linear/preview/LinearProjectPreview.tsx b/src/integrations/linear/preview/LinearProjectPreview.tsx
index 9be85e1..dbe453a 100644
--- a/src/integrations/linear/preview/LinearProjectPreview.tsx
+++ b/src/integrations/linear/preview/LinearProjectPreview.tsx
@@ -9,7 +9,7 @@ interface LinearProjectPreviewProps {
}
export function LinearProjectPreview({ notification, linearProject }: LinearProjectPreviewProps) {
- const notification_html_url = useMemo(() => {
+ const notificationHtmlUrl = useMemo(() => {
return getNotificationHtmlUrl(notification);
}, [notification]);
@@ -18,7 +18,7 @@ export function LinearProjectPreview({ notification, linearProject }: LinearProj
markdown={`# ${linearProject.name}`}
actions={
-
+
}
/>
diff --git a/src/integrations/todoist/TodoistNotificationListItem.tsx b/src/integrations/todoist/TodoistNotificationListItem.tsx
deleted file mode 100644
index c8a2840..0000000
--- a/src/integrations/todoist/TodoistNotificationListItem.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { NotificationTaskActions } from "../../action/NotificationTaskActions";
-import { NotificationListItemProps } from "../../notification";
-import { Detail, List, environment } from "@raycast/api";
-import { useMemo } from "react";
-
-export function TodoistNotificationListItem({ notification, mutate }: NotificationListItemProps) {
- const icon = useMemo(() => {
- if (environment.appearance === "dark") {
- return "todoist-icon-light.svg";
- }
- return "todoist-icon-dark.svg";
- }, [environment]);
-
- return (
- }
- mutate={mutate}
- />
- }
- />
- );
-}
diff --git a/src/integrations/todoist/listitem/TodoistNotificationListItem.tsx b/src/integrations/todoist/listitem/TodoistNotificationListItem.tsx
new file mode 100644
index 0000000..a143a3d
--- /dev/null
+++ b/src/integrations/todoist/listitem/TodoistNotificationListItem.tsx
@@ -0,0 +1,52 @@
+import { NotificationTaskActions } from "../../../action/NotificationTaskActions";
+import { TodoistTaskPreview } from "../preview/TodoistTaskPreview";
+import { NotificationListItemProps } from "../../../notification";
+import { Icon, List, Color } from "@raycast/api";
+import { TaskPriority } from "../../../task";
+import { match } from "ts-pattern";
+import dayjs from "dayjs";
+
+export function TodoistNotificationListItem({ notification, mutate }: NotificationListItemProps) {
+ const dueAt = notification.task?.due_at?.content;
+ const subtitle = dueAt ? dayjs(dueAt).format("YYYY-MM-DD") : undefined;
+
+ const color = match(notification)
+ .with({ task: { priority: TaskPriority.P1 } }, () => Color.Red)
+ .with({ task: { priority: TaskPriority.P2 } }, () => Color.Orange)
+ .with({ task: { priority: TaskPriority.P3 } }, () => Color.Yellow)
+ .otherwise(() => null);
+
+ const accessories: List.Item.Accessory[] = [
+ {
+ icon: { source: Icon.Circle, tintColor: color },
+ },
+ {
+ date: new Date(notification.updated_at),
+ tooltip: `Updated at ${notification.updated_at}`,
+ },
+ ];
+
+ const task = notification.task;
+ if (task) {
+ for (const tag of task.tags) {
+ accessories.unshift({ tag: { value: tag } });
+ }
+ }
+
+ return (
+ }
+ mutate={mutate}
+ />
+ }
+ />
+ );
+}
diff --git a/src/integrations/todoist/preview/TodoistTaskPreview.tsx b/src/integrations/todoist/preview/TodoistTaskPreview.tsx
new file mode 100644
index 0000000..180ae8f
--- /dev/null
+++ b/src/integrations/todoist/preview/TodoistTaskPreview.tsx
@@ -0,0 +1,24 @@
+import { Notification, getNotificationHtmlUrl } from "../../../notification";
+import { Detail, ActionPanel, Action } from "@raycast/api";
+import { useMemo } from "react";
+
+interface TodoistTaskPreviewProps {
+ notification: Notification;
+}
+
+export function TodoistTaskPreview({ notification }: TodoistTaskPreviewProps) {
+ const notificationHtmlUrl = useMemo(() => {
+ return getNotificationHtmlUrl(notification);
+ }, [notification]);
+
+ return (
+
+
+
+ }
+ />
+ );
+}
diff --git a/src/notification.ts b/src/notification.ts
index 5e7fc11..7f4ba87 100644
--- a/src/notification.ts
+++ b/src/notification.ts
@@ -64,7 +64,7 @@ export function getNotificationHtmlUrl(notification: Notification) {
(googleMailThread) =>
`https://mail.google.com/mail/u/${googleMailThread.user_email_address}/#inbox/${googleMailThread.id}`,
)
- .with({ metadata: { type: "Todoist", content: P.select() } }, () => "")
+ .with({ metadata: { type: "Todoist" } }, () => `https://todoist.com/showTask?id=${notification.source_id}`)
.with({ metadata: { type: "Github" } }, () => "https://github.com")
.exhaustive();
}