Compare commits
2 Commits
contributi
...
33dc17483a
| Author | SHA1 | Date | |
|---|---|---|---|
|
33dc17483a
|
|||
|
735b2f5481
|
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add support for Slack reaction notifications
|
||||||
|
- Add support for Slack message notifications
|
||||||
|
|
||||||
## [0.1.4] - 2024-03-13
|
## [0.1.4] - 2024-03-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
},
|
},
|
||||||
"nodejs@latest": {
|
"nodejs@latest": {
|
||||||
"last_modified": "2024-01-14T03:55:27Z",
|
"last_modified": "2024-01-14T03:55:27Z",
|
||||||
|
"plugin_version": "0.0.2",
|
||||||
"resolved": "github:NixOS/nixpkgs/dd5621df6dcb90122b50da5ec31c411a0de3e538#nodejs_21",
|
"resolved": "github:NixOS/nixpkgs/dd5621df6dcb90122b50da5ec31c411a0de3e538#nodejs_21",
|
||||||
"source": "devbox-search",
|
"source": "devbox-search",
|
||||||
"version": "21.5.0",
|
"version": "21.5.0",
|
||||||
|
|||||||
2677
package-lock.json
generated
2677
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -41,7 +41,7 @@ export function CreateTaskFromNotification({ notification, mutate }: CreateTaskF
|
|||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
|
||||||
const { isLoading, data: projects } = useFetch<Array<ProjectSummary>>(
|
const { isLoading, data: projects } = useFetch<Array<ProjectSummary>>(
|
||||||
`${preferences.universalInboxBaseUrl}/api/tasks/projects/search?matches=${searchText}`,
|
`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/tasks/projects/search?matches=${searchText}`,
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -123,7 +123,7 @@ async function createTaskFromNotification(
|
|||||||
try {
|
try {
|
||||||
await mutate(
|
await mutate(
|
||||||
handleErrors(
|
handleErrors(
|
||||||
fetch(`${preferences.universalInboxBaseUrl}/api/notifications/${notification.id}/task`, {
|
fetch(`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/notifications/${notification.id}/task`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(taskCreation),
|
body: JSON.stringify(taskCreation),
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export function LinkNotificationToTask({ notification, mutate }: LinkNotificatio
|
|||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
|
||||||
const { isLoading, data: tasks } = useFetch<Array<Task>>(
|
const { isLoading, data: tasks } = useFetch<Array<Task>>(
|
||||||
`${preferences.universalInboxBaseUrl}/api/tasks/search?matches=${searchText}`,
|
`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/tasks/search?matches=${searchText}`,
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -78,7 +78,7 @@ async function linkNotificationToTask(
|
|||||||
try {
|
try {
|
||||||
await mutate(
|
await mutate(
|
||||||
handleErrors(
|
handleErrors(
|
||||||
fetch(`${preferences.universalInboxBaseUrl}/api/notifications/${notification.id}`, {
|
fetch(`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/notifications/${notification.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify({ status: TaskStatus.Deleted, task_id: taskId }),
|
body: JSON.stringify({ status: TaskStatus.Deleted, task_id: taskId }),
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function NotificationActions({ notification, detailsTarget, mutate }: Not
|
|||||||
onAction={() => deleteNotification(notification, mutate)}
|
onAction={() => deleteNotification(notification, mutate)}
|
||||||
/>
|
/>
|
||||||
<Action
|
<Action
|
||||||
title="Unsubscribe From Notification"
|
title="Unsubscribe from Notification"
|
||||||
icon={Icon.BellDisabled}
|
icon={Icon.BellDisabled}
|
||||||
shortcut={{ modifiers: ["ctrl"], key: "u" }}
|
shortcut={{ modifiers: ["ctrl"], key: "u" }}
|
||||||
onAction={() => unsubscribeFromNotification(notification, mutate)}
|
onAction={() => unsubscribeFromNotification(notification, mutate)}
|
||||||
@@ -47,13 +47,13 @@ export function NotificationActions({ notification, detailsTarget, mutate }: Not
|
|||||||
onAction={() => snoozeNotification(notification, mutate)}
|
onAction={() => snoozeNotification(notification, mutate)}
|
||||||
/>
|
/>
|
||||||
<Action.Push
|
<Action.Push
|
||||||
title="Create Task..."
|
title="Create Task…"
|
||||||
icon={Icon.Calendar}
|
icon={Icon.Calendar}
|
||||||
shortcut={{ modifiers: ["ctrl"], key: "t" }}
|
shortcut={{ modifiers: ["ctrl"], key: "t" }}
|
||||||
target={<CreateTaskFromNotification notification={notification} mutate={mutate} />}
|
target={<CreateTaskFromNotification notification={notification} mutate={mutate} />}
|
||||||
/>
|
/>
|
||||||
<Action.Push
|
<Action.Push
|
||||||
title="Link to Task..."
|
title="Link to Task…"
|
||||||
icon={Icon.Link}
|
icon={Icon.Link}
|
||||||
shortcut={{ modifiers: ["ctrl"], key: "l" }}
|
shortcut={{ modifiers: ["ctrl"], key: "l" }}
|
||||||
target={<LinkNotificationToTask notification={notification} mutate={mutate} />}
|
target={<LinkNotificationToTask notification={notification} mutate={mutate} />}
|
||||||
@@ -72,7 +72,7 @@ export async function deleteNotification(
|
|||||||
if (isNotificationBuiltFromTask(notification) && notification.task) {
|
if (isNotificationBuiltFromTask(notification) && notification.task) {
|
||||||
await mutate(
|
await mutate(
|
||||||
handleErrors(
|
handleErrors(
|
||||||
fetch(`${preferences.universalInboxBaseUrl}/api/tasks/${notification.task.id}`, {
|
fetch(`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/tasks/${notification.task.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify({ status: TaskStatus.Deleted }),
|
body: JSON.stringify({ status: TaskStatus.Deleted }),
|
||||||
headers: {
|
headers: {
|
||||||
@@ -93,7 +93,7 @@ export async function deleteNotification(
|
|||||||
} else {
|
} else {
|
||||||
await mutate(
|
await mutate(
|
||||||
handleErrors(
|
handleErrors(
|
||||||
fetch(`${preferences.universalInboxBaseUrl}/api/notifications/${notification.id}`, {
|
fetch(`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/notifications/${notification.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify({ status: NotificationStatus.Deleted }),
|
body: JSON.stringify({ status: NotificationStatus.Deleted }),
|
||||||
headers: {
|
headers: {
|
||||||
@@ -132,7 +132,7 @@ export async function unsubscribeFromNotification(
|
|||||||
try {
|
try {
|
||||||
await mutate(
|
await mutate(
|
||||||
handleErrors(
|
handleErrors(
|
||||||
fetch(`${preferences.universalInboxBaseUrl}/api/notifications/${notification.id}`, {
|
fetch(`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/notifications/${notification.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify({ status: NotificationStatus.Unsubscribed }),
|
body: JSON.stringify({ status: NotificationStatus.Unsubscribed }),
|
||||||
headers: {
|
headers: {
|
||||||
@@ -171,7 +171,7 @@ export async function snoozeNotification(
|
|||||||
const snoozeTime = computeSnoozedUntil(new Date(), 1, 6);
|
const snoozeTime = computeSnoozedUntil(new Date(), 1, 6);
|
||||||
await mutate(
|
await mutate(
|
||||||
handleErrors(
|
handleErrors(
|
||||||
fetch(`${preferences.universalInboxBaseUrl}/api/notifications/${notification.id}`, {
|
fetch(`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/notifications/${notification.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify({ snoozed_until: snoozeTime }),
|
body: JSON.stringify({ snoozed_until: snoozeTime }),
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function NotificationTaskActions({ notification, detailsTarget, mutate }:
|
|||||||
onAction={() => deleteNotification(notification, mutate)}
|
onAction={() => deleteNotification(notification, mutate)}
|
||||||
/>
|
/>
|
||||||
<Action
|
<Action
|
||||||
title="Unsubscribe From Notification"
|
title="Unsubscribe from Notification"
|
||||||
icon={Icon.BellDisabled}
|
icon={Icon.BellDisabled}
|
||||||
shortcut={{ modifiers: ["ctrl"], key: "u" }}
|
shortcut={{ modifiers: ["ctrl"], key: "u" }}
|
||||||
onAction={() => unsubscribeFromNotification(notification, mutate)}
|
onAction={() => unsubscribeFromNotification(notification, mutate)}
|
||||||
@@ -49,7 +49,7 @@ export function NotificationTaskActions({ notification, detailsTarget, mutate }:
|
|||||||
onAction={() => completeTask(notification, mutate)}
|
onAction={() => completeTask(notification, mutate)}
|
||||||
/>
|
/>
|
||||||
<Action.Push
|
<Action.Push
|
||||||
title="Plan Task..."
|
title="Plan Task…"
|
||||||
icon={Icon.Calendar}
|
icon={Icon.Calendar}
|
||||||
shortcut={{ modifiers: ["ctrl"], key: "t" }}
|
shortcut={{ modifiers: ["ctrl"], key: "t" }}
|
||||||
target={<PlanTask notification={notification} mutate={mutate} />}
|
target={<PlanTask notification={notification} mutate={mutate} />}
|
||||||
@@ -68,7 +68,7 @@ async function completeTask(notification: Notification, mutate: MutatePromise<Pa
|
|||||||
try {
|
try {
|
||||||
await mutate(
|
await mutate(
|
||||||
handleErrors(
|
handleErrors(
|
||||||
fetch(`${preferences.universalInboxBaseUrl}/api/tasks/${notification.task.id}`, {
|
fetch(`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/tasks/${notification.task.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify({ status: TaskStatus.Done }),
|
body: JSON.stringify({ status: TaskStatus.Done }),
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function PlanTask({ notification, mutate }: PlanTaskProps) {
|
|||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
|
||||||
const { isLoading, data: projects } = useFetch<Array<ProjectSummary>>(
|
const { isLoading, data: projects } = useFetch<Array<ProjectSummary>>(
|
||||||
`${preferences.universalInboxBaseUrl}/api/tasks/projects/search?matches=${searchText}`,
|
`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/tasks/projects/search?matches=${searchText}`,
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -122,7 +122,7 @@ async function planTask(
|
|||||||
try {
|
try {
|
||||||
await mutate(
|
await mutate(
|
||||||
handleErrors(
|
handleErrors(
|
||||||
fetch(`${preferences.universalInboxBaseUrl}/api/tasks/${notification.task.id}`, {
|
fetch(`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/tasks/${notification.task.id}`, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
project: taskPlanning.project.name,
|
project: taskPlanning.project.name,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { TodoistNotificationListItem } from "./integrations/todoist/listitem/Tod
|
|||||||
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 { SlackNotificationListItem } from "./integrations/slack/listitem/SlackNotificationListItem";
|
import { SlackNotificationListItem } from "./integrations/slack/listitem/SlackNotificationListItem";
|
||||||
import { Notification, NotificationListItemProps } from "./notification";
|
import { Notification, NotificationListItemProps, NotificationSourceKind } from "./notification";
|
||||||
import { NotificationActions } from "./action/NotificationActions";
|
import { NotificationActions } from "./action/NotificationActions";
|
||||||
import { Page, UniversalInboxPreferences } from "./types";
|
import { Page, UniversalInboxPreferences } from "./types";
|
||||||
import { useFetch } from "@raycast/utils";
|
import { useFetch } from "@raycast/utils";
|
||||||
@@ -33,7 +33,7 @@ export default function Command() {
|
|||||||
|
|
||||||
const [notificationKind, setNotificationKind] = useState("");
|
const [notificationKind, setNotificationKind] = useState("");
|
||||||
const { isLoading, data, mutate } = useFetch<Page<Notification>>(
|
const { isLoading, data, mutate } = useFetch<Page<Notification>>(
|
||||||
`${preferences.universalInboxBaseUrl}/api/notifications?status=Unread,Read&with_tasks=true${
|
`${preferences.universalInboxBaseUrl.replace(/\/$/, "")}/api/notifications?status=Unread,Read&with_tasks=true${
|
||||||
notificationKind ? "¬ification_kind=" + notificationKind : ""
|
notificationKind ? "¬ification_kind=" + notificationKind : ""
|
||||||
}`,
|
}`,
|
||||||
{
|
{
|
||||||
@@ -67,16 +67,16 @@ export default function Command() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function NotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
function NotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
||||||
switch (notification.metadata.type) {
|
switch (notification.kind) {
|
||||||
case "Github":
|
case NotificationSourceKind.Github:
|
||||||
return <GithubNotificationListItem notification={notification} mutate={mutate} />;
|
return <GithubNotificationListItem notification={notification} mutate={mutate} />;
|
||||||
case "Linear":
|
case NotificationSourceKind.Linear:
|
||||||
return <LinearNotificationListItem notification={notification} mutate={mutate} />;
|
return <LinearNotificationListItem notification={notification} mutate={mutate} />;
|
||||||
case "GoogleMail":
|
case NotificationSourceKind.GoogleMail:
|
||||||
return <GoogleMailNotificationListItem notification={notification} mutate={mutate} />;
|
return <GoogleMailNotificationListItem notification={notification} mutate={mutate} />;
|
||||||
case "Slack":
|
case NotificationSourceKind.Slack:
|
||||||
return <SlackNotificationListItem notification={notification} mutate={mutate} />;
|
return <SlackNotificationListItem notification={notification} mutate={mutate} />;
|
||||||
case "Todoist":
|
case NotificationSourceKind.Todoist:
|
||||||
return <TodoistNotificationListItem notification={notification} mutate={mutate} />;
|
return <TodoistNotificationListItem notification={notification} mutate={mutate} />;
|
||||||
default:
|
default:
|
||||||
return <DefaultNotificationListItem notification={notification} mutate={mutate} />;
|
return <DefaultNotificationListItem notification={notification} mutate={mutate} />;
|
||||||
@@ -88,7 +88,7 @@ function DefaultNotificationListItem({ notification, mutate }: NotificationListI
|
|||||||
<List.Item
|
<List.Item
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
title={notification.title}
|
title={notification.title}
|
||||||
subtitle={`#${notification.source_id}`}
|
subtitle={`#${notification.source_item.source_id}`}
|
||||||
actions={
|
actions={
|
||||||
<NotificationActions
|
<NotificationActions
|
||||||
notification={notification}
|
notification={notification}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ import { GithubDiscussionNotificationListItem } from "./GithubDiscussionNotifica
|
|||||||
import { NotificationListItemProps } from "../../../notification";
|
import { NotificationListItemProps } from "../../../notification";
|
||||||
|
|
||||||
export function GithubNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
export function GithubNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
||||||
switch (notification.details?.type) {
|
if (notification.source_item.data.type !== "GithubNotification") return null;
|
||||||
|
|
||||||
|
switch (notification.source_item.data.content.item?.type) {
|
||||||
case "GithubPullRequest":
|
case "GithubPullRequest":
|
||||||
return (
|
return (
|
||||||
<GithubPullRequestNotificationListItem
|
<GithubPullRequestNotificationListItem
|
||||||
notification={notification}
|
notification={notification}
|
||||||
githubPullRequest={notification.details.content}
|
githubPullRequest={notification.source_item.data.content.item.content}
|
||||||
mutate={mutate}
|
mutate={mutate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -16,7 +18,7 @@ export function GithubNotificationListItem({ notification, mutate }: Notificatio
|
|||||||
return (
|
return (
|
||||||
<GithubDiscussionNotificationListItem
|
<GithubDiscussionNotificationListItem
|
||||||
notification={notification}
|
notification={notification}
|
||||||
githubDiscussion={notification.details.content}
|
githubDiscussion={notification.source_item.data.content.item.content}
|
||||||
mutate={mutate}
|
mutate={mutate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
import { GithubDiscussion } from "../types";
|
import { GithubDiscussion } from "../types";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
import { GithubPullRequest } from "../types";
|
import { GithubPullRequest } from "../types";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|||||||
@@ -1,3 +1,60 @@
|
|||||||
|
import { match, P } from "ts-pattern";
|
||||||
|
|
||||||
|
export interface GithubNotification {
|
||||||
|
id: string;
|
||||||
|
repository: GithubRepositorySummary;
|
||||||
|
subject: GithubNotificationSubject;
|
||||||
|
reason: string;
|
||||||
|
unread: boolean;
|
||||||
|
updated_at: Date;
|
||||||
|
last_read_at?: Date;
|
||||||
|
url: string;
|
||||||
|
subscription_url: string;
|
||||||
|
item?: GithubNotificationItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGithubNotificationHtmlUrl(notification: GithubNotification): string {
|
||||||
|
return match(notification.item)
|
||||||
|
.with({ type: "GithubPullRequest", content: P.select() }, (pr) => pr.url)
|
||||||
|
.with({ type: "GithubDiscussion", content: P.select() }, (discussion) => discussion.url)
|
||||||
|
.otherwise(() => getHtmlUrlFromApiUrl(notification.subject.url) ?? getHtmlUrlFromMetadata(notification));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHtmlUrlFromApiUrl(apiUrl: string | undefined): string | undefined {
|
||||||
|
if (!apiUrl) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const url = new URL(apiUrl);
|
||||||
|
if (url.host === "api.github.com" && url.pathname.startsWith("/repos")) {
|
||||||
|
const result = new URL(apiUrl);
|
||||||
|
result.host = "github.com";
|
||||||
|
result.pathname = url.pathname.replace("/repos", "").replace("/pulls/", "/pull/");
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHtmlUrlFromMetadata(notification: GithubNotification): string {
|
||||||
|
return match(notification.subject.type)
|
||||||
|
.with("CheckSuite", () => {
|
||||||
|
const result = new URL(notification.repository.url);
|
||||||
|
result.pathname = `${result.pathname}/actions`;
|
||||||
|
return result.toString();
|
||||||
|
})
|
||||||
|
.otherwise(() => notification.repository.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GithubNotificationSubject {
|
||||||
|
title: string;
|
||||||
|
url?: string;
|
||||||
|
latest_comment_url?: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GithubNotificationItem =
|
||||||
|
| { type: "GithubPullRequest"; content: GithubPullRequest }
|
||||||
|
| { type: "GithubDiscussion"; content: GithubDiscussion };
|
||||||
|
|
||||||
export interface GithubPullRequest {
|
export interface GithubPullRequest {
|
||||||
id: string;
|
id: string;
|
||||||
number: number;
|
number: number;
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { GoogleMailThreadListItem } from "./GoogleMailThreadListItem";
|
|||||||
import { NotificationListItemProps } from "../../../notification";
|
import { NotificationListItemProps } from "../../../notification";
|
||||||
|
|
||||||
export function GoogleMailNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
export function GoogleMailNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
||||||
if (notification.metadata.type !== "GoogleMail") return null;
|
if (notification.source_item.data.type !== "GoogleMailThread") return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GoogleMailThreadListItem
|
<GoogleMailThreadListItem
|
||||||
notification={notification}
|
notification={notification}
|
||||||
googleMailThread={notification.metadata.content}
|
googleMailThread={notification.source_item.data.content}
|
||||||
mutate={mutate}
|
mutate={mutate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { GoogleMailThreadPreview } from "../preview/GoogleMailThreadPreview";
|
import { GoogleMailThreadPreview } from "../preview/GoogleMailThreadPreview";
|
||||||
import { NotificationActions } from "../../../action/NotificationActions";
|
import { NotificationActions } from "../../../action/NotificationActions";
|
||||||
//import { getLinearUserAccessory } from "../accessories";
|
|
||||||
import { Notification } from "../../../notification";
|
import { Notification } from "../../../notification";
|
||||||
import { Icon, Color, List } from "@raycast/api";
|
import { Icon, Color, List } from "@raycast/api";
|
||||||
import { MutatePromise } from "@raycast/utils";
|
import { MutatePromise } from "@raycast/utils";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
import { GoogleMailThread } from "../types";
|
import { GoogleMailThread } from "../types";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { LinearIssueNotificationListItem } from "./LinearIssueNotificationListIt
|
|||||||
import { NotificationListItemProps } from "../../../notification";
|
import { NotificationListItemProps } from "../../../notification";
|
||||||
|
|
||||||
export function LinearNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
export function LinearNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
||||||
if (notification.metadata.type !== "Linear") return null;
|
if (notification.source_item.data.type !== "LinearNotification") return null;
|
||||||
|
|
||||||
switch (notification.metadata.content.type) {
|
switch (notification.source_item.data.content.type) {
|
||||||
case "IssueNotification":
|
case "IssueNotification":
|
||||||
return (
|
return (
|
||||||
<LinearIssueNotificationListItem
|
<LinearIssueNotificationListItem
|
||||||
notification={notification}
|
notification={notification}
|
||||||
linearIssueNotification={notification.metadata.content.content}
|
linearIssueNotification={notification.source_item.data.content.content}
|
||||||
mutate={mutate}
|
mutate={mutate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -18,7 +18,7 @@ export function LinearNotificationListItem({ notification, mutate }: Notificatio
|
|||||||
return (
|
return (
|
||||||
<LinearProjectNotificationListItem
|
<LinearProjectNotificationListItem
|
||||||
notification={notification}
|
notification={notification}
|
||||||
linearProjectNotification={notification.metadata.content.content}
|
linearProjectNotification={notification.source_item.data.content.content}
|
||||||
mutate={mutate}
|
mutate={mutate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
import { LinearIssue } from "../types";
|
import { LinearIssue } from "../types";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
import { LinearProject } from "../types";
|
import { LinearProject } from "../types";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|||||||
@@ -2,6 +2,15 @@ export type LinearNotification =
|
|||||||
| { type: "IssueNotification"; content: LinearIssueNotification }
|
| { type: "IssueNotification"; content: LinearIssueNotification }
|
||||||
| { type: "ProjectNotification"; content: LinearProjectNotification };
|
| { type: "ProjectNotification"; content: LinearProjectNotification };
|
||||||
|
|
||||||
|
export function getLinearNotificationHtmlUrl(notification: LinearNotification): string {
|
||||||
|
switch (notification.type) {
|
||||||
|
case "IssueNotification":
|
||||||
|
return notification.content.issue.url;
|
||||||
|
case "ProjectNotification":
|
||||||
|
return notification.content.project.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface LinearIssueNotification {
|
export interface LinearIssueNotification {
|
||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
@@ -39,6 +48,10 @@ export interface LinearIssue {
|
|||||||
team: LinearTeam;
|
team: LinearTeam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getLinearIssueHtmlUrl(linearIssue: LinearIssue): string {
|
||||||
|
return linearIssue.url;
|
||||||
|
}
|
||||||
|
|
||||||
export enum LinearIssuePriority {
|
export enum LinearIssuePriority {
|
||||||
NoPriority = 0,
|
NoPriority = 0,
|
||||||
Urgent = 1,
|
Urgent = 1,
|
||||||
|
|||||||
42
src/integrations/slack/index.tsx
Normal file
42
src/integrations/slack/index.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { SlackIcon, SlackUser } from "./types";
|
||||||
|
|
||||||
|
export function getSlackUserAvatarUrl(slackUser: SlackUser): string | null {
|
||||||
|
if (!slackUser.profile) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (slackUser.profile.image_24) {
|
||||||
|
return slackUser.profile.image_24;
|
||||||
|
}
|
||||||
|
if (slackUser.profile.image_32) {
|
||||||
|
return slackUser.profile.image_32;
|
||||||
|
}
|
||||||
|
if (slackUser.profile.image_34) {
|
||||||
|
return slackUser.profile.image_34;
|
||||||
|
}
|
||||||
|
if (slackUser.profile.image_44) {
|
||||||
|
return slackUser.profile.image_44;
|
||||||
|
}
|
||||||
|
if (slackUser.profile.image_48) {
|
||||||
|
return slackUser.profile.image_48;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSlackIconUrl(slackIcon?: SlackIcon): string | null {
|
||||||
|
if (slackIcon?.image_24) {
|
||||||
|
return slackIcon.image_24;
|
||||||
|
}
|
||||||
|
if (slackIcon?.image_32) {
|
||||||
|
return slackIcon.image_32;
|
||||||
|
}
|
||||||
|
if (slackIcon?.image_34) {
|
||||||
|
return slackIcon.image_34;
|
||||||
|
}
|
||||||
|
if (slackIcon?.image_44) {
|
||||||
|
return slackIcon.image_44;
|
||||||
|
}
|
||||||
|
if (slackIcon?.image_48) {
|
||||||
|
return slackIcon.image_48;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,147 +1,35 @@
|
|||||||
import { NotificationDetails, NotificationListItemProps } from "../../../notification";
|
import { SlackReactionNotificationListItem } from "./SlackReactionNotificationListItem";
|
||||||
import { NotificationActions } from "../../../action/NotificationActions";
|
import { SlackThreadNotificationListItem } from "./SlackThreadNotificationListItem";
|
||||||
import { SlackStarPreview } from "../preview/SlackStarPreview";
|
import { SlackStarNotificationListItem } from "./SlackStarNotificationListItem";
|
||||||
import { SlackBotInfo, SlackIcon, SlackUser } from "../types";
|
import { NotificationListItemProps } from "../../../notification";
|
||||||
/* import { NotificationActions } from "../../../action/NotificationActions"; */
|
|
||||||
import { Icon, Image, List } from "@raycast/api";
|
|
||||||
import { getAvatarIcon } from "@raycast/utils";
|
|
||||||
import { match, P } from "ts-pattern";
|
|
||||||
|
|
||||||
export function SlackNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
export function SlackNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
||||||
const subtitle = getSlackNotificationSubtitle(notification.details);
|
switch (notification.source_item.data.type) {
|
||||||
|
case "SlackStar":
|
||||||
const author = getSlackAuthorAccessory(notification.details);
|
|
||||||
const team = getSlackTeamAccessory(notification.details);
|
|
||||||
const updated_at = "2023-01-01"; // TODO
|
|
||||||
|
|
||||||
const accessories: List.Item.Accessory[] = [{ date: new Date(updated_at), tooltip: `Updated at ${updated_at}` }];
|
|
||||||
|
|
||||||
if (author) {
|
|
||||||
accessories.unshift(author);
|
|
||||||
}
|
|
||||||
if (team) {
|
|
||||||
accessories.unshift(team);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<SlackStarNotificationListItem
|
||||||
key={notification.id}
|
|
||||||
title={notification.title}
|
|
||||||
icon={{ source: { light: "slack-logo-dark.svg", dark: "slack-logo-light.svg" } }}
|
|
||||||
subtitle={subtitle}
|
|
||||||
accessories={accessories}
|
|
||||||
actions={
|
|
||||||
<NotificationActions
|
|
||||||
notification={notification}
|
notification={notification}
|
||||||
detailsTarget={<SlackStarPreview notification={notification} />}
|
slack_star={notification.source_item.data.content}
|
||||||
mutate={mutate}
|
mutate={mutate}
|
||||||
/>
|
/>
|
||||||
}
|
);
|
||||||
|
case "SlackReaction":
|
||||||
|
return (
|
||||||
|
<SlackReactionNotificationListItem
|
||||||
|
notification={notification}
|
||||||
|
slack_reaction={notification.source_item.data.content}
|
||||||
|
mutate={mutate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
case "SlackThread":
|
||||||
|
return (
|
||||||
function getSlackNotificationSubtitle(notificationDetails?: NotificationDetails): string {
|
<SlackThreadNotificationListItem
|
||||||
return match(notificationDetails)
|
notification={notification}
|
||||||
.with(
|
slack_thread={notification.source_item.data.content}
|
||||||
{
|
mutate={mutate}
|
||||||
type: P.union("SlackMessage", "SlackFile", "SlackFileComment", "SlackChannel", "SlackIm", "SlackGroup"),
|
/>
|
||||||
content: P.select(),
|
);
|
||||||
},
|
default:
|
||||||
(slackNotificationDetails) => {
|
|
||||||
const channelName = slackNotificationDetails.channel?.name;
|
|
||||||
return channelName ? `#${channelName}` : "";
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.otherwise(() => "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSlackAuthorAccessory(notificationDetails?: NotificationDetails): List.Item.Accessory | null {
|
|
||||||
return match(notificationDetails)
|
|
||||||
.with(
|
|
||||||
{
|
|
||||||
type: "SlackMessage",
|
|
||||||
content: P.select(),
|
|
||||||
},
|
|
||||||
(slackNotificationDetails) => {
|
|
||||||
return match(slackNotificationDetails.sender)
|
|
||||||
.with({ type: "User", content: P.select() }, (slackUser: SlackUser) => {
|
|
||||||
const userAvatarUrl = getSlackUserAvatarUrl(slackUser);
|
|
||||||
const userName = slackUser.real_name || "Unknown";
|
|
||||||
return {
|
|
||||||
icon: userAvatarUrl ? { source: userAvatarUrl, mask: Image.Mask.Circle } : getAvatarIcon(userName),
|
|
||||||
tooltip: userName,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.with({ type: "Bot", content: P.select() }, (slackBot: SlackBotInfo) => {
|
|
||||||
const botAvatarUrl = getSlackIconUrl(slackBot.icons);
|
|
||||||
return {
|
|
||||||
icon: botAvatarUrl ? { source: botAvatarUrl, mask: Image.Mask.Circle } : getAvatarIcon(slackBot.name),
|
|
||||||
tooltip: slackBot.name,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.otherwise(() => ({ icon: Icon.Person, tooltip: "Unknown" }));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.otherwise(() => null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSlackTeamAccessory(notificationDetails?: NotificationDetails): List.Item.Accessory | null {
|
|
||||||
return match(notificationDetails)
|
|
||||||
.with(
|
|
||||||
{
|
|
||||||
type: P.union("SlackMessage", "SlackFile", "SlackFileComment", "SlackChannel", "SlackIm", "SlackGroup"),
|
|
||||||
content: P.select(),
|
|
||||||
},
|
|
||||||
(slackNotificationDetails) => {
|
|
||||||
const teamName = slackNotificationDetails.team.name;
|
|
||||||
const teamIconUrl = getSlackIconUrl(slackNotificationDetails.team.icon);
|
|
||||||
if (!teamName || !teamIconUrl) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return { icon: { source: teamIconUrl, mask: Image.Mask.Circle }, tooltip: teamName };
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.otherwise(() => null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSlackUserAvatarUrl(slackUser: SlackUser): string | null {
|
|
||||||
if (!slackUser.profile) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (slackUser.profile.image_24) {
|
|
||||||
return slackUser.profile.image_24;
|
|
||||||
}
|
|
||||||
if (slackUser.profile.image_32) {
|
|
||||||
return slackUser.profile.image_32;
|
|
||||||
}
|
|
||||||
if (slackUser.profile.image_34) {
|
|
||||||
return slackUser.profile.image_34;
|
|
||||||
}
|
|
||||||
if (slackUser.profile.image_44) {
|
|
||||||
return slackUser.profile.image_44;
|
|
||||||
}
|
|
||||||
if (slackUser.profile.image_48) {
|
|
||||||
return slackUser.profile.image_48;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSlackIconUrl(slackIcon?: SlackIcon): string | null {
|
|
||||||
if (slackIcon?.image_24) {
|
|
||||||
return slackIcon.image_24;
|
|
||||||
}
|
|
||||||
if (slackIcon?.image_32) {
|
|
||||||
return slackIcon.image_32;
|
|
||||||
}
|
|
||||||
if (slackIcon?.image_34) {
|
|
||||||
return slackIcon.image_34;
|
|
||||||
}
|
|
||||||
if (slackIcon?.image_44) {
|
|
||||||
return slackIcon.image_44;
|
|
||||||
}
|
|
||||||
if (slackIcon?.image_48) {
|
|
||||||
return slackIcon.image_48;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import { NotificationActions } from "../../../action/NotificationActions";
|
||||||
|
import { SlackReactionPreview } from "../preview/SlackReactionPreview";
|
||||||
|
import { SlackBotInfo, SlackReaction, SlackUser } from "../types";
|
||||||
|
import { getAvatarIcon, MutatePromise } from "@raycast/utils";
|
||||||
|
import { getSlackIconUrl, getSlackUserAvatarUrl } from "..";
|
||||||
|
import { Notification } from "../../../notification";
|
||||||
|
import { Icon, Image, List } from "@raycast/api";
|
||||||
|
import { Page } from "../../../types";
|
||||||
|
import { match, P } from "ts-pattern";
|
||||||
|
|
||||||
|
export type SlackReactionNotificationListItemProps = {
|
||||||
|
notification: Notification;
|
||||||
|
slack_reaction: SlackReaction;
|
||||||
|
mutate: MutatePromise<Page<Notification> | undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SlackReactionNotificationListItem({
|
||||||
|
notification,
|
||||||
|
slack_reaction,
|
||||||
|
mutate,
|
||||||
|
}: SlackReactionNotificationListItemProps) {
|
||||||
|
const subtitle = getSlackReactionNotificationSubtitle(slack_reaction);
|
||||||
|
|
||||||
|
const author = getSlackReactionAuthorAccessory(slack_reaction);
|
||||||
|
const team = getSlackReactionTeamAccessory(slack_reaction);
|
||||||
|
const updated_at = "2023-01-01"; // TODO
|
||||||
|
|
||||||
|
const accessories: List.Item.Accessory[] = [{ date: new Date(updated_at), tooltip: `Updated at ${updated_at}` }];
|
||||||
|
|
||||||
|
if (author) {
|
||||||
|
accessories.unshift(author);
|
||||||
|
}
|
||||||
|
if (team) {
|
||||||
|
accessories.unshift(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
icon={{ source: { light: "slack-logo-dark.svg", dark: "slack-logo-light.svg" } }}
|
||||||
|
subtitle={subtitle}
|
||||||
|
accessories={accessories}
|
||||||
|
actions={
|
||||||
|
<NotificationActions
|
||||||
|
notification={notification}
|
||||||
|
detailsTarget={<SlackReactionPreview notification={notification} slack_reaction={slack_reaction} />}
|
||||||
|
mutate={mutate}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackReactionNotificationSubtitle(slack_reaction: SlackReaction): string {
|
||||||
|
return match(slack_reaction.item)
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
type: P.union("Message", "File"),
|
||||||
|
content: P.select(),
|
||||||
|
},
|
||||||
|
(slack_reaction_item) => {
|
||||||
|
const channelName = slack_reaction_item.channel?.name;
|
||||||
|
return channelName ? `#${channelName}` : "";
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.otherwise(() => "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackReactionAuthorAccessory(slack_reaction: SlackReaction): List.Item.Accessory | null {
|
||||||
|
return match(slack_reaction.item)
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
type: "Message",
|
||||||
|
content: P.select(),
|
||||||
|
},
|
||||||
|
(slackMessageDetails) => {
|
||||||
|
return match(slackMessageDetails.sender)
|
||||||
|
.with({ type: "User", content: P.select() }, (slackUser: SlackUser) => {
|
||||||
|
const userAvatarUrl = getSlackUserAvatarUrl(slackUser);
|
||||||
|
const userName = slackUser.real_name || "Unknown";
|
||||||
|
return {
|
||||||
|
icon: userAvatarUrl ? { source: userAvatarUrl, mask: Image.Mask.Circle } : getAvatarIcon(userName),
|
||||||
|
tooltip: userName,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.with({ type: "Bot", content: P.select() }, (slackBot: SlackBotInfo) => {
|
||||||
|
const botAvatarUrl = getSlackIconUrl(slackBot.icons);
|
||||||
|
return {
|
||||||
|
icon: botAvatarUrl ? { source: botAvatarUrl, mask: Image.Mask.Circle } : getAvatarIcon(slackBot.name),
|
||||||
|
tooltip: slackBot.name,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.otherwise(() => ({ icon: Icon.Person, tooltip: "Unknown" }));
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.otherwise(() => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackReactionTeamAccessory(slack_reaction: SlackReaction): List.Item.Accessory | null {
|
||||||
|
return match(slack_reaction.item)
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
type: P.union("Message", "File"),
|
||||||
|
content: P.select(),
|
||||||
|
},
|
||||||
|
(slack_reaction_item) => {
|
||||||
|
const teamName = slack_reaction_item.team.name;
|
||||||
|
const teamIconUrl = getSlackIconUrl(slack_reaction_item.team.icon);
|
||||||
|
if (!teamName || !teamIconUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { icon: { source: teamIconUrl, mask: Image.Mask.Circle }, tooltip: teamName };
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.otherwise(() => null);
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import { NotificationActions } from "../../../action/NotificationActions";
|
||||||
|
import { SlackStarPreview } from "../preview/SlackStarPreview";
|
||||||
|
import { getAvatarIcon, MutatePromise } from "@raycast/utils";
|
||||||
|
import { SlackBotInfo, SlackStar, SlackUser } from "../types";
|
||||||
|
import { getSlackIconUrl, getSlackUserAvatarUrl } from "..";
|
||||||
|
import { Notification } from "../../../notification";
|
||||||
|
import { Icon, Image, List } from "@raycast/api";
|
||||||
|
import { Page } from "../../../types";
|
||||||
|
import { match, P } from "ts-pattern";
|
||||||
|
|
||||||
|
export type SlackStarNotificationListItemProps = {
|
||||||
|
notification: Notification;
|
||||||
|
slack_star: SlackStar;
|
||||||
|
mutate: MutatePromise<Page<Notification> | undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SlackStarNotificationListItem({
|
||||||
|
notification,
|
||||||
|
slack_star,
|
||||||
|
mutate,
|
||||||
|
}: SlackStarNotificationListItemProps) {
|
||||||
|
const subtitle = getSlackStarNotificationSubtitle(slack_star);
|
||||||
|
|
||||||
|
const author = getSlackStarAuthorAccessory(slack_star);
|
||||||
|
const team = getSlackStarTeamAccessory(slack_star);
|
||||||
|
const updated_at = "2023-01-01"; // TODO
|
||||||
|
|
||||||
|
const accessories: List.Item.Accessory[] = [{ date: new Date(updated_at), tooltip: `Updated at ${updated_at}` }];
|
||||||
|
|
||||||
|
if (author) {
|
||||||
|
accessories.unshift(author);
|
||||||
|
}
|
||||||
|
if (team) {
|
||||||
|
accessories.unshift(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
icon={{ source: { light: "slack-logo-dark.svg", dark: "slack-logo-light.svg" } }}
|
||||||
|
subtitle={subtitle}
|
||||||
|
accessories={accessories}
|
||||||
|
actions={
|
||||||
|
<NotificationActions
|
||||||
|
notification={notification}
|
||||||
|
detailsTarget={<SlackStarPreview notification={notification} slack_star={slack_star} />}
|
||||||
|
mutate={mutate}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackStarNotificationSubtitle(slack_star: SlackStar): string {
|
||||||
|
return match(slack_star.item)
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
type: P.union("Message", "File", "FileComment", "Channel", "Im", "Group"),
|
||||||
|
content: P.select(),
|
||||||
|
},
|
||||||
|
(slack_star_item) => {
|
||||||
|
const channelName = slack_star_item.channel?.name;
|
||||||
|
return channelName ? `#${channelName}` : "";
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.otherwise(() => "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackStarAuthorAccessory(slack_star: SlackStar): List.Item.Accessory | null {
|
||||||
|
return match(slack_star.item)
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
type: "Message",
|
||||||
|
content: P.select(),
|
||||||
|
},
|
||||||
|
(slackMessageDetails) => {
|
||||||
|
return match(slackMessageDetails.sender)
|
||||||
|
.with({ type: "User", content: P.select() }, (slackUser: SlackUser) => {
|
||||||
|
const userAvatarUrl = getSlackUserAvatarUrl(slackUser);
|
||||||
|
const userName = slackUser.real_name || "Unknown";
|
||||||
|
return {
|
||||||
|
icon: userAvatarUrl ? { source: userAvatarUrl, mask: Image.Mask.Circle } : getAvatarIcon(userName),
|
||||||
|
tooltip: userName,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.with({ type: "Bot", content: P.select() }, (slackBot: SlackBotInfo) => {
|
||||||
|
const botAvatarUrl = getSlackIconUrl(slackBot.icons);
|
||||||
|
return {
|
||||||
|
icon: botAvatarUrl ? { source: botAvatarUrl, mask: Image.Mask.Circle } : getAvatarIcon(slackBot.name),
|
||||||
|
tooltip: slackBot.name,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.otherwise(() => ({ icon: Icon.Person, tooltip: "Unknown" }));
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.otherwise(() => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackStarTeamAccessory(slack_star: SlackStar): List.Item.Accessory | null {
|
||||||
|
return match(slack_star.item)
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
type: P.union("Message", "File", "FileComment", "Channel", "Im", "Group"),
|
||||||
|
content: P.select(),
|
||||||
|
},
|
||||||
|
(slack_star_item) => {
|
||||||
|
const teamName = slack_star_item.team.name;
|
||||||
|
const teamIconUrl = getSlackIconUrl(slack_star_item.team.icon);
|
||||||
|
if (!teamName || !teamIconUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { icon: { source: teamIconUrl, mask: Image.Mask.Circle }, tooltip: teamName };
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.otherwise(() => null);
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { NotificationActions } from "../../../action/NotificationActions";
|
||||||
|
import { SlackThreadPreview } from "../preview/SlackThreadPreview";
|
||||||
|
import { getAvatarIcon, MutatePromise } from "@raycast/utils";
|
||||||
|
import { getSlackIconUrl, getSlackUserAvatarUrl } from "..";
|
||||||
|
import { Notification } from "../../../notification";
|
||||||
|
import { Icon, Image, List } from "@raycast/api";
|
||||||
|
import { SlackThread } from "../types";
|
||||||
|
import { Page } from "../../../types";
|
||||||
|
|
||||||
|
export type SlackThreadNotificationListItemProps = {
|
||||||
|
notification: Notification;
|
||||||
|
slack_thread: SlackThread;
|
||||||
|
mutate: MutatePromise<Page<Notification> | undefined>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SlackThreadNotificationListItem({
|
||||||
|
notification,
|
||||||
|
slack_thread,
|
||||||
|
mutate,
|
||||||
|
}: SlackThreadNotificationListItemProps) {
|
||||||
|
const subtitle = getSlackThreadNotificationSubtitle(slack_thread);
|
||||||
|
|
||||||
|
const author = getSlackThreadAuthorAccessory(slack_thread);
|
||||||
|
const team = getSlackThreadTeamAccessory(slack_thread);
|
||||||
|
const updated_at = "2023-01-01"; // TODO
|
||||||
|
|
||||||
|
const accessories: List.Item.Accessory[] = [{ date: new Date(updated_at), tooltip: `Updated at ${updated_at}` }];
|
||||||
|
|
||||||
|
if (author) {
|
||||||
|
accessories.unshift(author);
|
||||||
|
}
|
||||||
|
if (team) {
|
||||||
|
accessories.unshift(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item
|
||||||
|
key={notification.id}
|
||||||
|
title={notification.title}
|
||||||
|
icon={{ source: { light: "slack-logo-dark.svg", dark: "slack-logo-light.svg" } }}
|
||||||
|
subtitle={subtitle}
|
||||||
|
accessories={accessories}
|
||||||
|
actions={
|
||||||
|
<NotificationActions
|
||||||
|
notification={notification}
|
||||||
|
detailsTarget={<SlackThreadPreview notification={notification} slack_thread={slack_thread} />}
|
||||||
|
mutate={mutate}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackThreadNotificationSubtitle(slack_thread: SlackThread): string {
|
||||||
|
const channelName = slack_thread.channel?.name;
|
||||||
|
return channelName ? `in #${channelName}` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackThreadAuthorAccessory(slack_thread: SlackThread): List.Item.Accessory | null {
|
||||||
|
const firstUnreadMessage = slack_thread.messages[0];
|
||||||
|
|
||||||
|
if (firstUnreadMessage.user) {
|
||||||
|
const profile = slack_thread.sender_profiles[firstUnreadMessage.user];
|
||||||
|
|
||||||
|
if (profile && profile.type === "User") {
|
||||||
|
const slackUser = profile.content;
|
||||||
|
const userAvatarUrl = getSlackUserAvatarUrl(slackUser);
|
||||||
|
const userName = slackUser.real_name || "Unknown";
|
||||||
|
return {
|
||||||
|
icon: userAvatarUrl ? { source: userAvatarUrl, mask: Image.Mask.Circle } : getAvatarIcon(userName),
|
||||||
|
tooltip: userName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstUnreadMessage.bot_id) {
|
||||||
|
const profile = slack_thread.sender_profiles[firstUnreadMessage.bot_id];
|
||||||
|
|
||||||
|
if (profile && profile.type === "Bot") {
|
||||||
|
const slackBot = profile.content;
|
||||||
|
const botAvatarUrl = getSlackIconUrl(slackBot.icons);
|
||||||
|
return {
|
||||||
|
icon: botAvatarUrl ? { source: botAvatarUrl, mask: Image.Mask.Circle } : getAvatarIcon(slackBot.name),
|
||||||
|
tooltip: slackBot.name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { icon: Icon.Person, tooltip: "Unknown" };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlackThreadTeamAccessory(slack_thread: SlackThread): List.Item.Accessory | null {
|
||||||
|
const teamName = slack_thread.team.name;
|
||||||
|
const teamIconUrl = getSlackIconUrl(slack_thread.team.icon);
|
||||||
|
if (!teamName || !teamIconUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { icon: { source: teamIconUrl, mask: Image.Mask.Circle }, tooltip: teamName };
|
||||||
|
}
|
||||||
26
src/integrations/slack/preview/SlackReactionPreview.tsx
Normal file
26
src/integrations/slack/preview/SlackReactionPreview.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
|
import { SlackReaction } from "../types";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
interface SlackReactionPreviewProps {
|
||||||
|
notification: Notification;
|
||||||
|
slack_reaction: SlackReaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SlackReactionPreview({ notification }: SlackReactionPreviewProps) {
|
||||||
|
const notificationHtmlUrl = useMemo(() => {
|
||||||
|
return getNotificationHtmlUrl(notification);
|
||||||
|
}, [notification]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Detail
|
||||||
|
markdown={`# ${notification.title}`}
|
||||||
|
actions={
|
||||||
|
<ActionPanel>
|
||||||
|
<Action.OpenInBrowser url={notificationHtmlUrl} />
|
||||||
|
</ActionPanel>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
|
import { SlackStar } from "../types";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
interface SlackStarPreviewProps {
|
interface SlackStarPreviewProps {
|
||||||
notification: Notification;
|
notification: Notification;
|
||||||
|
slack_star: SlackStar;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SlackStarPreview({ notification }: SlackStarPreviewProps) {
|
export function SlackStarPreview({ notification }: SlackStarPreviewProps) {
|
||||||
|
|||||||
26
src/integrations/slack/preview/SlackThreadPreview.tsx
Normal file
26
src/integrations/slack/preview/SlackThreadPreview.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
|
import { SlackThread } from "../types";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
interface SlackThreadPreviewProps {
|
||||||
|
notification: Notification;
|
||||||
|
slack_thread: SlackThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SlackThreadPreview({ notification }: SlackThreadPreviewProps) {
|
||||||
|
const notificationHtmlUrl = useMemo(() => {
|
||||||
|
return getNotificationHtmlUrl(notification);
|
||||||
|
}, [notification]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Detail
|
||||||
|
markdown={`# ${notification.title}`}
|
||||||
|
actions={
|
||||||
|
<ActionPanel>
|
||||||
|
<Action.OpenInBrowser url={notificationHtmlUrl} />
|
||||||
|
</ActionPanel>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,3 +1,94 @@
|
|||||||
|
import { match, P } from "ts-pattern";
|
||||||
|
|
||||||
|
export interface SlackStar {
|
||||||
|
state: SlackStarState;
|
||||||
|
created_at: Date;
|
||||||
|
item: SlackStarItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSlackStarHtmlUrl(slack_star: SlackStar): string {
|
||||||
|
return match(slack_star.item)
|
||||||
|
.with({ type: "Message", content: P.select() }, (message) => message.url)
|
||||||
|
.with(
|
||||||
|
{ type: "File", content: P.select() },
|
||||||
|
(file) => `https://app.slack.com/client/${file.team.id}/${file.channel.id}`,
|
||||||
|
)
|
||||||
|
.with(
|
||||||
|
{ type: "FileComment", content: P.select() },
|
||||||
|
(fileComment) => `https://app.slack.com/client/${fileComment.team.id}/${fileComment.channel.id}`,
|
||||||
|
)
|
||||||
|
.with(
|
||||||
|
{ type: "Channel", content: P.select() },
|
||||||
|
(channel) => `https://app.slack.com/client/${channel.team.id}/${channel.channel.id}`,
|
||||||
|
)
|
||||||
|
.with({ type: "Im", content: P.select() }, (im) => `https://app.slack.com/client/${im.team.id}/${im.channel.id}`)
|
||||||
|
.with(
|
||||||
|
{ type: "Group", content: P.select() },
|
||||||
|
(group) => `https://app.slack.com/client/${group.team.id}/${group.channel.id}`,
|
||||||
|
)
|
||||||
|
.otherwise(() => "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SlackStarState {
|
||||||
|
StarAdded = "StarAdded",
|
||||||
|
StarRemoved = "StarRemoved",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SlackStarItem =
|
||||||
|
| { type: "Message"; content: SlackMessageDetails }
|
||||||
|
| { type: "File"; content: SlackFileDetails }
|
||||||
|
| { type: "FileComment"; content: SlackFileCommentDetails }
|
||||||
|
| { type: "Channel"; content: SlackChannelDetails }
|
||||||
|
| { type: "Im"; content: SlackImDetails }
|
||||||
|
| { type: "Group"; content: SlackGroupDetails };
|
||||||
|
|
||||||
|
export interface SlackReaction {
|
||||||
|
name: string;
|
||||||
|
state: SlackReactionState;
|
||||||
|
created_at: Date;
|
||||||
|
item: SlackReactionItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSlackReactionHtmlUrl(slack_reaction: SlackReaction): string {
|
||||||
|
return match(slack_reaction.item)
|
||||||
|
.with({ type: "Message", content: P.select() }, (message) => message.url)
|
||||||
|
.with(
|
||||||
|
{ type: "File", content: P.select() },
|
||||||
|
(file) => `https://app.slack.com/client/${file.team.id}/${file.channel.id}`,
|
||||||
|
)
|
||||||
|
.otherwise(() => "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SlackReactionState {
|
||||||
|
ReactionAdded = "ReactionAdded",
|
||||||
|
ReactionRemoved = "ReactionRemoved",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SlackReactionItem =
|
||||||
|
| { type: "Message"; content: SlackMessageDetails }
|
||||||
|
| { type: "File"; content: SlackFileDetails };
|
||||||
|
|
||||||
|
export interface SlackThread {
|
||||||
|
url: string;
|
||||||
|
messages: Array<SlackHistoryMessage>;
|
||||||
|
sender_profiles: Record<string, SlackMessageSenderDetails>;
|
||||||
|
subscribed: boolean;
|
||||||
|
last_read?: string;
|
||||||
|
channel: SlackChannelInfo;
|
||||||
|
team: SlackTeamInfo;
|
||||||
|
references?: SlackReferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSlackThreadHtmlUrl(slack_thread: SlackThread): string {
|
||||||
|
return slack_thread.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SlackReferences {
|
||||||
|
channels: Record<string, string | null>;
|
||||||
|
users: Record<string, string | null>;
|
||||||
|
usergroups: Record<string, string | null>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SlackPushEventCallback {
|
export interface SlackPushEventCallback {
|
||||||
team_id: string;
|
team_id: string;
|
||||||
api_app_id: string;
|
api_app_id: string;
|
||||||
@@ -51,16 +142,18 @@ export interface SlackHistoryMessage {
|
|||||||
channel_type?: string;
|
channel_type?: string;
|
||||||
thread_ts?: string;
|
thread_ts?: string;
|
||||||
client_msg_id?: string;
|
client_msg_id?: string;
|
||||||
|
user?: string;
|
||||||
|
bot_id?: string;
|
||||||
|
|
||||||
text?: string;
|
text?: string;
|
||||||
blocks?: Array<SlackBlock>;
|
blocks?: Array<SlackBlock>;
|
||||||
attachments?: Array<SlackMessageAttachment>;
|
attachments?: Array<SlackMessageAttachment>;
|
||||||
upload?: boolean;
|
upload?: boolean;
|
||||||
files?: Array<SlackFile>;
|
files?: Array<SlackFile>;
|
||||||
reactions?: Array<SlackReaction>;
|
reactions?: Array<SlackReactionDetails>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SlackReaction {
|
export interface SlackReactionDetails {
|
||||||
name: string;
|
name: string;
|
||||||
count: number;
|
count: number;
|
||||||
users: Array<string>;
|
users: Array<string>;
|
||||||
@@ -184,7 +277,7 @@ export interface SlackFile {
|
|||||||
url_private_download?: string;
|
url_private_download?: string;
|
||||||
permalink?: string;
|
permalink?: string;
|
||||||
permalink_public?: string;
|
permalink_public?: string;
|
||||||
reactions?: Array<SlackReaction>;
|
reactions?: Array<SlackReactionDetails>;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
is_external?: boolean;
|
is_external?: boolean;
|
||||||
is_public?: boolean;
|
is_public?: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
import { getNotificationHtmlUrl, Notification } from "../../../notification";
|
||||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
|||||||
3
src/integrations/todoist/types.ts
Normal file
3
src/integrations/todoist/types.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface TodoistItem {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
@@ -1,53 +1,29 @@
|
|||||||
import {
|
import { getThirdPartyItemHtmlUrl, ThirdPartyItem } from "./third_party_item";
|
||||||
SlackChannelDetails,
|
|
||||||
SlackFileCommentDetails,
|
|
||||||
SlackFileDetails,
|
|
||||||
SlackGroupDetails,
|
|
||||||
SlackImDetails,
|
|
||||||
SlackMessageDetails,
|
|
||||||
SlackPushEventCallback,
|
|
||||||
} from "./integrations/slack/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 { Page } from "./types";
|
import { Page } from "./types";
|
||||||
import { Task } from "./task";
|
import { Task } from "./task";
|
||||||
|
|
||||||
export interface Notification {
|
export interface Notification {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
source_id: string;
|
|
||||||
status: NotificationStatus;
|
status: NotificationStatus;
|
||||||
metadata: NotificationMetadata;
|
created_at: Date;
|
||||||
updated_at: Date;
|
updated_at: Date;
|
||||||
last_read_at?: Date;
|
last_read_at?: Date;
|
||||||
snoozed_until?: Date;
|
snoozed_until?: Date;
|
||||||
user_id: string;
|
user_id: string;
|
||||||
task?: Task;
|
task?: Task;
|
||||||
details?: NotificationDetails;
|
kind: NotificationSourceKind;
|
||||||
|
source_item: ThirdPartyItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NotificationMetadata =
|
export enum NotificationSourceKind {
|
||||||
| {
|
Github = "Github",
|
||||||
type: "Github" | "Todoist";
|
Todoist = "Todoist",
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
Linear = "Linear",
|
||||||
content: any;
|
GoogleMail = "GoogleMail",
|
||||||
|
Slack = "Slack",
|
||||||
}
|
}
|
||||||
| { type: "Linear"; content: LinearNotification }
|
|
||||||
| { type: "GoogleMail"; content: GoogleMailThread }
|
|
||||||
| { type: "Slack"; content: SlackPushEventCallback };
|
|
||||||
|
|
||||||
export type NotificationDetails =
|
|
||||||
| { type: "GithubPullRequest"; content: GithubPullRequest }
|
|
||||||
| { type: "GithubDiscussion"; content: GithubDiscussion }
|
|
||||||
| { type: "SlackMessage"; content: SlackMessageDetails }
|
|
||||||
| { type: "SlackFile"; content: SlackFileDetails }
|
|
||||||
| { type: "SlackFileComment"; content: SlackFileCommentDetails }
|
|
||||||
| { type: "SlackChannel"; content: SlackChannelDetails }
|
|
||||||
| { type: "SlackIm"; content: SlackImDetails }
|
|
||||||
| { type: "SlackGroup"; content: SlackGroupDetails };
|
|
||||||
|
|
||||||
export enum NotificationStatus {
|
export enum NotificationStatus {
|
||||||
Unread = "Unread",
|
Unread = "Unread",
|
||||||
@@ -61,42 +37,10 @@ export type NotificationListItemProps = {
|
|||||||
mutate: MutatePromise<Page<Notification> | undefined>;
|
mutate: MutatePromise<Page<Notification> | undefined>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getNotificationHtmlUrl(notification: Notification) {
|
export function getNotificationHtmlUrl(notification: Notification): string {
|
||||||
return match(notification)
|
return getThirdPartyItemHtmlUrl(notification.source_item);
|
||||||
.with({ details: { type: "SlackMessage", content: P.select() } }, (notificationDetails) => notificationDetails.url)
|
|
||||||
.with(
|
|
||||||
{
|
|
||||||
details: {
|
|
||||||
type: P.union("SlackChannel", "SlackFile", "SlackFileComment", "SlackGroup", "SlackIm"),
|
|
||||||
content: P.select(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
(notificationDetails) =>
|
|
||||||
`https://app.slack.com/client/${notificationDetails.team.id}/${notificationDetails.channel.id}`,
|
|
||||||
)
|
|
||||||
.with(
|
|
||||||
{ details: { type: P.union("GithubPullRequest", "GithubDiscussion"), content: P.select() } },
|
|
||||||
(notificationDetails) => notificationDetails.url,
|
|
||||||
)
|
|
||||||
.with(
|
|
||||||
{ metadata: { type: "Linear", content: { type: "IssueNotification", content: P.select() } } },
|
|
||||||
(linearIssueNotification) => linearIssueNotification.issue.url,
|
|
||||||
)
|
|
||||||
.with(
|
|
||||||
{ 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" } }, () => `https://todoist.com/showTask?id=${notification.source_id}`)
|
|
||||||
.with({ metadata: { type: "Github" } }, () => "https://github.com")
|
|
||||||
.with({ metadata: { type: "Slack" } }, () => "https://app.slack.com")
|
|
||||||
.exhaustive();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNotificationBuiltFromTask(notification: Notification) {
|
export function isNotificationBuiltFromTask(notification: Notification) {
|
||||||
return notification.metadata.type === "Todoist";
|
return notification.kind === NotificationSourceKind.Todoist;
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/third_party_item.ts
Normal file
58
src/third_party_item.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import {
|
||||||
|
getSlackReactionHtmlUrl,
|
||||||
|
getSlackStarHtmlUrl,
|
||||||
|
getSlackThreadHtmlUrl,
|
||||||
|
SlackReaction,
|
||||||
|
SlackStar,
|
||||||
|
SlackThread,
|
||||||
|
} from "./integrations/slack/types";
|
||||||
|
import {
|
||||||
|
getLinearIssueHtmlUrl,
|
||||||
|
getLinearNotificationHtmlUrl,
|
||||||
|
LinearIssue,
|
||||||
|
LinearNotification,
|
||||||
|
} from "./integrations/linear/types";
|
||||||
|
import { getGithubNotificationHtmlUrl, GithubNotification } from "./integrations/github/types";
|
||||||
|
import { GoogleMailThread } from "./integrations/google-mail/types";
|
||||||
|
import { TodoistItem } from "./integrations/todoist/types";
|
||||||
|
|
||||||
|
export interface ThirdPartyItem {
|
||||||
|
id: string;
|
||||||
|
source_id: string;
|
||||||
|
data: ThirdPartyItemData;
|
||||||
|
created_at: Date;
|
||||||
|
updated_at: Date;
|
||||||
|
user_id: string;
|
||||||
|
integration_connection_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ThirdPartyItemData =
|
||||||
|
| { type: "TodoistItem"; content: TodoistItem }
|
||||||
|
| { type: "SlackStar"; content: SlackStar }
|
||||||
|
| { type: "SlackReaction"; content: SlackReaction }
|
||||||
|
| { type: "SlackThread"; content: SlackThread }
|
||||||
|
| { type: "LinearIssue"; content: LinearIssue }
|
||||||
|
| { type: "LinearNotification"; content: LinearNotification }
|
||||||
|
| { type: "GithubNotification"; content: GithubNotification }
|
||||||
|
| { type: "GoogleMailThread"; content: GoogleMailThread };
|
||||||
|
|
||||||
|
export function getThirdPartyItemHtmlUrl(thirdPartyItem: ThirdPartyItem): string {
|
||||||
|
switch (thirdPartyItem.data.type) {
|
||||||
|
case "TodoistItem":
|
||||||
|
return `https://todoist.com/showTask?id=${thirdPartyItem.data.content.id}`;
|
||||||
|
case "SlackStar":
|
||||||
|
return getSlackStarHtmlUrl(thirdPartyItem.data.content);
|
||||||
|
case "SlackReaction":
|
||||||
|
return getSlackReactionHtmlUrl(thirdPartyItem.data.content);
|
||||||
|
case "SlackThread":
|
||||||
|
return getSlackThreadHtmlUrl(thirdPartyItem.data.content);
|
||||||
|
case "LinearIssue":
|
||||||
|
return getLinearIssueHtmlUrl(thirdPartyItem.data.content);
|
||||||
|
case "LinearNotification":
|
||||||
|
return getLinearNotificationHtmlUrl(thirdPartyItem.data.content);
|
||||||
|
case "GithubNotification":
|
||||||
|
return getGithubNotificationHtmlUrl(thirdPartyItem.data.content);
|
||||||
|
case "GoogleMailThread":
|
||||||
|
return `https://mail.google.com/mail/u/0/#inbox/${thirdPartyItem.data.content.id}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user