feat: Add Linear notification list item
This commit is contained in:
@@ -87,7 +87,7 @@ function getGithubPullRequestChecksAccessory(latestCommit: GithubCommitChecks):
|
||||
case GithubCheckStatusState.Pending:
|
||||
return { icon: Icon.Pause, tooltip: "Pending" };
|
||||
case GithubCheckStatusState.InProgress:
|
||||
return { icon: Icon.Pause, tooltip: "In progress" }; // TODO Spinner
|
||||
return { icon: Icon.CircleProgress, tooltip: "In progress" };
|
||||
case GithubCheckStatusState.Completed:
|
||||
switch (progress.conclusion()) {
|
||||
case GithubCheckConclusionState.Success:
|
||||
|
||||
@@ -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 LinearNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
||||
const icon = useMemo(() => {
|
||||
if (environment.appearance === "dark") {
|
||||
return "linear-logo-light.svg";
|
||||
}
|
||||
return "linear-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}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
12
src/integrations/linear/accessories.ts
Normal file
12
src/integrations/linear/accessories.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Icon, Image, List } from "@raycast/api";
|
||||
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,
|
||||
tooltip: user.name,
|
||||
};
|
||||
}
|
||||
return { icon: Icon.Person, tooltip: "Unknown" };
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { NotificationActions } from "../../../action/NotificationActions";
|
||||
import { LinearIssuePreview } from "../preview/LinearIssuePreview";
|
||||
import { getLinearUserAccessory } from "../accessories";
|
||||
import { Notification } from "../../../notification";
|
||||
import { LinearIssueNotification } from "../types";
|
||||
import { MutatePromise } from "@raycast/utils";
|
||||
import { Page } from "../../../types";
|
||||
import { List } from "@raycast/api";
|
||||
|
||||
interface LinearIssueNotificationListItemProps {
|
||||
icon: string;
|
||||
notification: Notification;
|
||||
linearIssueNotification: LinearIssueNotification;
|
||||
mutate: MutatePromise<Page<Notification> | undefined>;
|
||||
}
|
||||
|
||||
export function LinearIssueNotificationListItem({
|
||||
icon,
|
||||
notification,
|
||||
linearIssueNotification,
|
||||
mutate,
|
||||
}: LinearIssueNotificationListItemProps) {
|
||||
const subtitle = `${linearIssueNotification.issue.team.name} #${linearIssueNotification.issue.identifier}`;
|
||||
|
||||
const state = getLinearIssueStateAccessory(linearIssueNotification.issue.state);
|
||||
const assignee = getLinearUserAccessory(linearIssueNotification.issue.assignee);
|
||||
|
||||
const accessories: List.Item.Accessory[] = [
|
||||
state,
|
||||
assignee,
|
||||
{
|
||||
date: new Date(linearIssueNotification.updated_at),
|
||||
tooltip: `Updated at ${linearIssueNotification.updated_at}`,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<List.Item
|
||||
key={notification.id}
|
||||
title={notification.title}
|
||||
icon={icon}
|
||||
accessories={accessories}
|
||||
subtitle={subtitle}
|
||||
actions={
|
||||
<NotificationActions
|
||||
notification={notification}
|
||||
detailsTarget={<LinearIssuePreview notification={notification} linearIssue={linearIssueNotification.issue} />}
|
||||
mutate={mutate}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function getLinearIssueStateAccessory(state: LinearWorkflowState): List.Item.Accessory {
|
||||
switch (state.type) {
|
||||
case "Triage":
|
||||
return {
|
||||
icon: { source: "linear-issue-triage.svg", tintColor: state.color },
|
||||
tooltip: state.name,
|
||||
};
|
||||
case "Backlog":
|
||||
return {
|
||||
icon: { source: "linear-issue-backlog.svg", tintColor: state.color },
|
||||
tooltip: state.name,
|
||||
};
|
||||
case "Unstarted":
|
||||
return {
|
||||
icon: { source: "linear-issue-unstarted.svg", tintColor: state.color },
|
||||
tooltip: state.name,
|
||||
};
|
||||
case "Started":
|
||||
return {
|
||||
icon: { source: "linear-issue-started.svg", tintColor: state.color },
|
||||
tooltip: state.name,
|
||||
};
|
||||
case "Completed":
|
||||
return {
|
||||
icon: { source: "linear-issue-completed.svg", tintColor: state.color },
|
||||
tooltip: state.name,
|
||||
};
|
||||
case "Canceled":
|
||||
return {
|
||||
icon: { source: "linear-issue-canceled.svg", tintColor: state.color },
|
||||
tooltip: state.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { LinearProjectNotificationListItem } from "./LinearProjectNotificationListItem";
|
||||
import { LinearIssueNotificationListItem } from "./LinearIssueNotificationListItem";
|
||||
import { NotificationListItemProps } from "../../../notification";
|
||||
import { environment } from "@raycast/api";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export function LinearNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
||||
const icon = useMemo(() => {
|
||||
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 (
|
||||
<LinearIssueNotificationListItem
|
||||
icon={icon}
|
||||
notification={notification}
|
||||
linearIssueNotification={notification.metadata.content.content}
|
||||
mutate={mutate}
|
||||
/>
|
||||
);
|
||||
case "ProjectNotification":
|
||||
return (
|
||||
<LinearProjectNotificationListItem
|
||||
icon={icon}
|
||||
notification={notification}
|
||||
LinearProjectNotification={notification.metadata.content.content}
|
||||
mutate={mutate}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { NotificationActions } from "../../../action/NotificationActions";
|
||||
import { LinearProjectPreview } from "../preview/LinearProjectPreview";
|
||||
import { getLinearUserAccessory } from "../accessories";
|
||||
import { Notification } from "../../../notification";
|
||||
import { LinearProjectNotification } from "../types";
|
||||
import { MutatePromise } from "@raycast/utils";
|
||||
import { Page } from "../../../types";
|
||||
import { List } from "@raycast/api";
|
||||
|
||||
interface LinearProjectNotificationListItemProps {
|
||||
icon: string;
|
||||
notification: Notification;
|
||||
linearProjectNotification: LinearProjectNotification;
|
||||
mutate: MutatePromise<Page<Notification> | undefined>;
|
||||
}
|
||||
|
||||
export function LinearProjectNotificationListItem({
|
||||
icon,
|
||||
notification,
|
||||
linearProjectNotification,
|
||||
mutate,
|
||||
}: LinearProjectNotificationListItemProps) {
|
||||
const subtitle = linearProjectNotification.project.name;
|
||||
|
||||
const lead = getLinearUserAccessory(linearProjectNotification.project.lead);
|
||||
|
||||
const accessories: List.Item.Accessory[] = [
|
||||
lead,
|
||||
{
|
||||
date: new Date(linearProjectNotification.updated_at),
|
||||
tooltip: `Updated at ${linearProjectNotification.updated_at}`,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<List.Item
|
||||
key={notification.id}
|
||||
title={notification.title}
|
||||
icon={icon}
|
||||
accessories={accessories}
|
||||
subtitle={subtitle}
|
||||
actions={
|
||||
<NotificationActions
|
||||
notification={notification}
|
||||
detailsTarget={
|
||||
<LinearProjectPreview notification={notification} linearProject={linearProjectNotification.issue} />
|
||||
}
|
||||
mutate={mutate}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
26
src/integrations/linear/preview/LinearIssuePreview.tsx
Normal file
26
src/integrations/linear/preview/LinearIssuePreview.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||
import { LinearIssue } from "../types";
|
||||
import { useMemo } from "react";
|
||||
|
||||
interface LinearIssuePreviewProps {
|
||||
notification: Notification;
|
||||
linearIssue: LinearIssue;
|
||||
}
|
||||
|
||||
export function LinearIssuePreview({ notification, linearIssue }: LinearIssuePreviewProps) {
|
||||
const notification_html_url = useMemo(() => {
|
||||
return getNotificationHtmlUrl(notification);
|
||||
}, [notification]);
|
||||
|
||||
return (
|
||||
<Detail
|
||||
markdown={`# ${linearIssue.title}`}
|
||||
actions={
|
||||
<ActionPanel>
|
||||
<Action.OpenInBrowser url={notification_html_url} />
|
||||
</ActionPanel>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
26
src/integrations/linear/preview/LinearProjectPreview.tsx
Normal file
26
src/integrations/linear/preview/LinearProjectPreview.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Notification, getNotificationHtmlUrl } from "../../../notification";
|
||||
import { Detail, ActionPanel, Action } from "@raycast/api";
|
||||
import { LinearProject } from "../types";
|
||||
import { useMemo } from "react";
|
||||
|
||||
interface LinearProjectPreviewProps {
|
||||
notification: Notification;
|
||||
linearProject: LinearProject;
|
||||
}
|
||||
|
||||
export function LinearProjectPreview({ notification, linearProject }: LinearProjectPreviewProps) {
|
||||
const notification_html_url = useMemo(() => {
|
||||
return getNotificationHtmlUrl(notification);
|
||||
}, [notification]);
|
||||
|
||||
return (
|
||||
<Detail
|
||||
markdown={`# ${linearProject.name}`}
|
||||
actions={
|
||||
<ActionPanel>
|
||||
<Action.OpenInBrowser url={notification_html_url} />
|
||||
</ActionPanel>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
116
src/integrations/linear/types.ts
Normal file
116
src/integrations/linear/types.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
export interface LinearIssueNotification {
|
||||
id: string;
|
||||
type: string;
|
||||
read_at?: Date;
|
||||
updated_at: Date;
|
||||
snoozed_until_at?: Date;
|
||||
organization: LinearOrganization;
|
||||
issue: LinearIssue;
|
||||
}
|
||||
|
||||
export interface LinearOrganization {
|
||||
name: string;
|
||||
key: string;
|
||||
logo_url?: string;
|
||||
}
|
||||
|
||||
export interface LinearIssue {
|
||||
id: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
completed_at?: Date;
|
||||
canceled_at?: Date;
|
||||
due_date?: Date;
|
||||
identifier: string;
|
||||
title: string;
|
||||
url: string;
|
||||
priority: LinearIssuePriority;
|
||||
project?: LinearProject;
|
||||
project_milestone?: LinearProjectMilestone;
|
||||
creator?: LinearUser;
|
||||
assignee?: LinearUser;
|
||||
state: LinearWorkflowState;
|
||||
labels: Array<LinearLabel>;
|
||||
description: string;
|
||||
team: LinearTeam;
|
||||
}
|
||||
|
||||
export enum LinearIssuePriority {
|
||||
NoPriority = 0,
|
||||
Urgent = 1,
|
||||
High = 2,
|
||||
Normal = 3,
|
||||
Low = 4,
|
||||
}
|
||||
|
||||
export interface LinearProject {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
description: string;
|
||||
icon?: string;
|
||||
color: string;
|
||||
state: LinearProjectState;
|
||||
progress: number; // percentage between 0 and 100
|
||||
start_date?: Date;
|
||||
target_date?: Date;
|
||||
lead?: LinearUser;
|
||||
}
|
||||
|
||||
export enum LinearProjectState {
|
||||
Planned = "Planned",
|
||||
Backlog = "Backlog",
|
||||
Started = "Started",
|
||||
Paused = "Paused",
|
||||
Completed = "Completed",
|
||||
Canceled = "Canceled",
|
||||
}
|
||||
|
||||
export interface LinearProjectMilestone {
|
||||
name: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface LinearUser {
|
||||
name: string;
|
||||
avatar_url?: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface LinearWorkflowState {
|
||||
name: string;
|
||||
description?: string;
|
||||
color: string;
|
||||
type: LinearWorkflowStateType;
|
||||
}
|
||||
|
||||
export enum LinearWorkflowStateType {
|
||||
Triage = "Triage",
|
||||
Backlog = "Backlog",
|
||||
Unstarted = "Unstarted",
|
||||
Started = "Started",
|
||||
Completed = "Completed",
|
||||
Canceled = "Canceled",
|
||||
}
|
||||
|
||||
export interface LinearLabel {
|
||||
name: string;
|
||||
description?: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface LinearTeam {
|
||||
id: string;
|
||||
key: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface LinearProjectNotification {
|
||||
id: string;
|
||||
type: string;
|
||||
read_at?: Date;
|
||||
updated_at: Date;
|
||||
snoozed_until_at?: Date;
|
||||
organization: LinearOrganization;
|
||||
project: LinearProject;
|
||||
}
|
||||
Reference in New Issue
Block a user