import { Action, ActionPanel, List, showToast, Toast } from "@raycast/api";
import { useEffect, useState } from "react";
import Parser from "rss-parser";
const parser = new Parser();
interface State {
items?: Parser.Item[];
error?: Error;
}
export default function Command() {
const [state, setState] = useState<State>({});
useEffect(() => {
async function fetchStories() {
try {
const feed = await parser.parseURL("https://hnrss.org/frontpage?description=0&count=25");
setState({ items: feed.items });
} catch (error) {
setState({
error: error instanceof Error ? error : new Error("Something went wrong"),
});
}
}
fetchStories();
}, []);
console.log(state.items); // Prints stories
return <List isLoading={!state.items && !state.error} />;
}
function StoryListItem(props: { item: Parser.Item; index: number }) {
const icon = getIcon(props.index + 1);
const points = getPoints(props.item);
const comments = getComments(props.item);
return (
<List.Item
icon={icon}
title={props.item.title ?? "No title"}
subtitle={props.item.creator}
accessories={[{ text: `👍 ${points}` }, { text: `💬 ${comments}` }]}
/>
);
}
const iconToEmojiMap = new Map<number, string>([
[1, "1️⃣"],
[2, "2️⃣"],
[3, "3️⃣"],
[4, "4️⃣"],
[5, "5️⃣"],
[6, "6️⃣"],
[7, "7️⃣"],
[8, "8️⃣"],
[9, "9️⃣"],
[10, "🔟"],
]);
function getIcon(index: number) {
return iconToEmojiMap.get(index) ?? "⏺";
}
function getPoints(item: Parser.Item) {
const matches = item.contentSnippet?.match(/(?<=Points:\s*)(\d+)/g);
return matches?.[0];
}
function getComments(item: Parser.Item) {
const matches = item.contentSnippet?.match(/(?<=Comments:\s*)(\d+)/g);
return matches?.[0];
}
export default function Command() {
const [state, setState] = useState<State>({});
// ...
return (
<List isLoading={!state.items && !state.error}>
{state.items?.map((item, index) => (
<StoryListItem key={item.guid} item={item} index={index} />
))}
</List>
);
}
当我们在列表中选择一个故事时,我们希望能够在浏览器中打开它并复制它的链接,以便我们可以在 Watercooler Slack 频道中共享它。为此,我们创建一个新的 React 组件:
function Actions(props: { item: Parser.Item }) {
return (
<ActionPanel title={props.item.title}>
<ActionPanel.Section>
{props.item.link && <Action.OpenInBrowser url={props.item.link} />}
{props.item.guid && <Action.OpenInBrowser url={props.item.guid} title="Open Comments in Browser" />}
</ActionPanel.Section>
<ActionPanel.Section>
{props.item.link && (
<Action.CopyToClipboard
content={props.item.link}
title="Copy Link"
shortcut={{ modifiers: ["cmd"], key: "." }}
/>
)}
</ActionPanel.Section>
</ActionPanel>
);
}
function StoryListItem(props: { item: Parser.Item; index: number }) {
// ...
return (
<List.Item
icon={icon}
title={props.item.title ?? "No title"}
subtitle={props.item.creator}
accessories={[{ text: `👍 ${points}` }, { text: `💬 ${comments}` }]}
// Wire up actions
actions={<Actions item={props.item} />}
/>
);
}
export default function Command() {
const [state, setState] = useState<State>({});
// ...
if (state.error) {
showToast({
style: Toast.Style.Failure,
title: "Failed loading stories",
message: state.error.message,
});
}
// ...
}
就是这样,您就有了一个可以阅读 Hacker News 首页的扩展。作为后续步骤,您可以添加另一个命令来显示作业源或添加一个操作来复制 Markdown 格式的链接。