1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
import { useCallback, useEffect, useState } from "react";
import type { DeckAccount, DeckColumn, ChatState } from "../types/app";
import { twitterClient } from "../lib/client/twitterClient";
import { ChatCard } from "./ChatCard";
interface ChatColumnProps {
column: DeckColumn & { kind: "chat" };
account: DeckAccount;
onRemove: () => void;
}
export function ChatColumn({ column, account, onRemove }: ChatColumnProps) {
const [state, setState] = useState<ChatState>({ entries: [], isLoading: false });
const [error, setError] = useState<string | undefined>();
const refresh = useCallback(async () => {
setState(prev => ({ ...prev, isLoading: true }));
setError(undefined);
try {
const notifications = await twitterClient.notifications({ cookie: account.cookie });
setState({ entries: notifications, isLoading: false });
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to refresh chat");
setState(prev => ({ ...prev, isLoading: false }));
}
}, [account.cookie]);
useEffect(() => {
refresh();
}, [refresh, account.id]);
return (
<article className="column">
<header>
<div>
<p className="eyebrow">Signal</p>
<h3>{column.title || "Chat"}</h3>
<p className="muted tiny">Mentions, follows and notifications for {account.label}</p>
</div>
<div className="column-actions">
<button className="ghost" onClick={refresh} aria-label="Refresh chat">
↻
</button>
<button className="ghost" onClick={onRemove} aria-label="Remove column">
×
</button>
</div>
</header>
{error && <p className="error">{error}</p>}
{state.isLoading && !state.entries.length ? (
<div className="column-loading">Loading…</div>
) : (
<div className="chat-stack">
{state.entries.map(entry => (
<ChatCard key={entry.id} notification={entry} accent={account.accent} />
))}
{!state.entries.length && <p className="muted">No recent notifications.</p>}
</div>
)}
</article>
);
}
|