feat: Add support for Slack reaction and message notifications
This commit is contained in:
@@ -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 { NotificationActions } from "../../../action/NotificationActions";
|
||||
import { SlackStarPreview } from "../preview/SlackStarPreview";
|
||||
import { SlackBotInfo, SlackIcon, SlackUser } from "../types";
|
||||
/* import { NotificationActions } from "../../../action/NotificationActions"; */
|
||||
import { Icon, Image, List } from "@raycast/api";
|
||||
import { getAvatarIcon } from "@raycast/utils";
|
||||
import { match, P } from "ts-pattern";
|
||||
import { SlackReactionNotificationListItem } from "./SlackReactionNotificationListItem";
|
||||
import { SlackThreadNotificationListItem } from "./SlackThreadNotificationListItem";
|
||||
import { SlackStarNotificationListItem } from "./SlackStarNotificationListItem";
|
||||
import { NotificationListItemProps } from "../../../notification";
|
||||
|
||||
export function SlackNotificationListItem({ notification, mutate }: NotificationListItemProps) {
|
||||
const subtitle = getSlackNotificationSubtitle(notification.details);
|
||||
|
||||
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 (
|
||||
<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
|
||||
switch (notification.source_item.data.type) {
|
||||
case "SlackStar":
|
||||
return (
|
||||
<SlackStarNotificationListItem
|
||||
notification={notification}
|
||||
detailsTarget={<SlackStarPreview notification={notification} />}
|
||||
slack_star={notification.source_item.data.content}
|
||||
mutate={mutate}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function getSlackNotificationSubtitle(notificationDetails?: NotificationDetails): string {
|
||||
return match(notificationDetails)
|
||||
.with(
|
||||
{
|
||||
type: P.union("SlackMessage", "SlackFile", "SlackFileComment", "SlackChannel", "SlackIm", "SlackGroup"),
|
||||
content: P.select(),
|
||||
},
|
||||
(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 { 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;
|
||||
);
|
||||
case "SlackReaction":
|
||||
return (
|
||||
<SlackReactionNotificationListItem
|
||||
notification={notification}
|
||||
slack_reaction={notification.source_item.data.content}
|
||||
mutate={mutate}
|
||||
/>
|
||||
);
|
||||
case "SlackThread":
|
||||
return (
|
||||
<SlackThreadNotificationListItem
|
||||
notification={notification}
|
||||
slack_thread={notification.source_item.data.content}
|
||||
mutate={mutate}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
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 };
|
||||
}
|
||||
@@ -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 { SlackStar } from "../types";
|
||||
import { useMemo } from "react";
|
||||
|
||||
interface SlackStarPreviewProps {
|
||||
notification: Notification;
|
||||
slack_star: SlackStar;
|
||||
}
|
||||
|
||||
export function SlackStarPreview({ notification }: SlackStarPreviewProps) {
|
||||
|
||||
@@ -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 {
|
||||
team_id: string;
|
||||
api_app_id: string;
|
||||
@@ -51,16 +142,18 @@ export interface SlackHistoryMessage {
|
||||
channel_type?: string;
|
||||
thread_ts?: string;
|
||||
client_msg_id?: string;
|
||||
user?: string;
|
||||
bot_id?: string;
|
||||
|
||||
text?: string;
|
||||
blocks?: Array<SlackBlock>;
|
||||
attachments?: Array<SlackMessageAttachment>;
|
||||
upload?: boolean;
|
||||
files?: Array<SlackFile>;
|
||||
reactions?: Array<SlackReaction>;
|
||||
reactions?: Array<SlackReactionDetails>;
|
||||
}
|
||||
|
||||
export interface SlackReaction {
|
||||
export interface SlackReactionDetails {
|
||||
name: string;
|
||||
count: number;
|
||||
users: Array<string>;
|
||||
@@ -184,7 +277,7 @@ export interface SlackFile {
|
||||
url_private_download?: string;
|
||||
permalink?: string;
|
||||
permalink_public?: string;
|
||||
reactions?: Array<SlackReaction>;
|
||||
reactions?: Array<SlackReactionDetails>;
|
||||
editable?: boolean;
|
||||
is_external?: boolean;
|
||||
is_public?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user