Skip to content

Commit 97b128f

Browse files
authored
Add FeedGenerator view, update notifications (#52)
1 parent 9cc9c9e commit 97b128f

File tree

10 files changed

+178
-67
lines changed

10 files changed

+178
-67
lines changed

src-tauri/Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ tauri-build = { version = "2.0.0-beta", features = [] }
1818
async-trait = "0.1.77"
1919
atrium-api = "0.20.0"
2020
atrium-xrpc-client = "0.5.0"
21+
itertools = "0.12.1"
2122
log = "0.4.21"
2223
serde = { version = "1", features = ["derive"] }
2324
serde_json = "1"

src-tauri/src/command.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::task::{poll_feed, poll_notifications, poll_unread_notifications};
88
use atrium_api::agent::store::SessionStore;
99
use atrium_api::agent::AtpAgent;
1010
use atrium_api::records::Record;
11-
use atrium_api::types::string::{Datetime, Did, Language};
11+
use atrium_api::types::string::{AtIdentifier, Datetime, Did, Language};
1212
use atrium_api::types::{Collection, Union};
1313
use atrium_xrpc_client::reqwest::ReqwestClient;
1414
use serde::Deserialize;
@@ -67,9 +67,10 @@ pub async fn get_preferences<R: Runtime>(
6767

6868
#[tauri::command]
6969
pub async fn get_profile<R: Runtime>(
70+
actor: AtIdentifier,
7071
app: AppHandle<R>,
7172
) -> Result<atrium_api::app::bsky::actor::get_profile::Output> {
72-
let actor = did(app.clone())?.parse().expect("failed to parse DID");
73+
log::info!("get_profile: {actor:?}");
7374
Ok(app
7475
.state::<State<R>>()
7576
.agent
@@ -84,12 +85,11 @@ pub async fn get_profile<R: Runtime>(
8485
}
8586

8687
#[tauri::command]
87-
pub async fn get_feed_generators<R: Runtime>(
88+
pub async fn get_pinned_feed_generators<R: Runtime>(
8889
app: AppHandle<R>,
8990
) -> Result<atrium_api::app::bsky::feed::get_feed_generators::Output> {
90-
log::info!("get_feed_generators");
91-
let agent = app.state::<State<R>>().agent.lock().await.clone();
92-
let preferences = get_preferences(app).await?;
91+
log::info!("get_pinned_feed_generators");
92+
let preferences = get_preferences(app.clone()).await?;
9393
let feeds = preferences
9494
.preferences
9595
.iter()
@@ -104,7 +104,20 @@ pub async fn get_feed_generators<R: Runtime>(
104104
}
105105
})
106106
.unwrap_or_default();
107-
Ok(agent
107+
get_feed_generators(app, feeds).await
108+
}
109+
110+
#[tauri::command]
111+
pub async fn get_feed_generators<R: Runtime>(
112+
app: AppHandle<R>,
113+
feeds: Vec<String>,
114+
) -> Result<atrium_api::app::bsky::feed::get_feed_generators::Output> {
115+
log::info!("get_feed_generators: {feeds:?}");
116+
Ok(app
117+
.state::<State<R>>()
118+
.agent
119+
.lock()
120+
.await
108121
.api
109122
.app
110123
.bsky

src-tauri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ pub fn run() {
9292
command::logout,
9393
command::me,
9494
command::get_profile,
95+
command::get_pinned_feed_generators,
9596
command::get_feed_generators,
9697
command::get_posts,
9798
command::subscribe,
Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { ProfileView } from "@/atproto/types/app/bsky/actor/defs";
22
import { isView } from "@/atproto/types/app/bsky/embed/images";
3-
import { PostView } from "@/atproto/types/app/bsky/feed/defs";
3+
import {
4+
GeneratorView,
5+
PostView,
6+
isGeneratorView,
7+
isPostView,
8+
} from "@/atproto/types/app/bsky/feed/defs";
49
import { isRecord } from "@/atproto/types/app/bsky/feed/post";
510
import Avatar from "@/components/Avatar";
611
import DistanceToNow from "@/components/DistanceToNow";
7-
import PostEmbed from "@/components/PostEmbed";
12+
import PostEmbed, { Generator } from "@/components/PostEmbed";
813
import Post from "@/components/PostView";
914
import { NotificationReason } from "@/constants";
1015
import { useAutoAnimate } from "@formkit/auto-animate/react";
@@ -23,12 +28,27 @@ export interface NotificationGroup {
2328
authors: ProfileView[];
2429
}
2530

31+
const PostContent: FC<{ post: PostView }> = ({ post }) => {
32+
return (
33+
<>
34+
{post.record && isRecord(post.record) && (
35+
<div className="text-muted whitespace-pre-wrap">{post.record.text}</div>
36+
)}
37+
{isView(post.embed) && (
38+
<div className="w-1/4">
39+
<PostEmbed embed={post.embed} />
40+
</div>
41+
)}
42+
</>
43+
);
44+
};
45+
2646
const NotificationView: FC<
2747
PropsWithChildren<{
2848
group: NotificationGroup;
29-
post?: PostView | null;
49+
content?: PostView | GeneratorView | null;
3050
}>
31-
> = ({ group, post, children }) => {
51+
> = ({ group, content, children }) => {
3252
const { reason, indexedAt, authors } = group;
3353
const avatars = (
3454
<div className="flex items-center mb-1">
@@ -89,57 +109,55 @@ const NotificationView: FC<
89109
<div>
90110
{subject} {children}
91111
</div>
92-
{post?.record && isRecord(post.record) && (
93-
<div className="text-muted whitespace-pre-wrap">
94-
{post.record.text}
95-
</div>
96-
)}
97-
{post && isView(post.embed) && (
98-
<div className="w-1/4">
99-
<PostEmbed embed={post.embed} />
100-
</div>
101-
)}
112+
{isPostView(content) && <PostContent post={content} />}
113+
{isGeneratorView(content) && <Generator generator={content} />}
102114
</div>
103115
</div>
104116
);
105117
};
106118

107119
const NotificationItem: FC<{
108120
group: NotificationGroup;
109-
post?: PostView | null;
110-
}> = ({ group, post }) => {
121+
content?: PostView | GeneratorView | null;
122+
}> = ({ group, content }) => {
123+
// TODO: other content?
124+
const contentName = (() => {
125+
if (isPostView(content)) return "your post";
126+
if (isGeneratorView(content)) return "your custom feed";
127+
return null;
128+
})();
111129
switch (group.reason) {
112130
case NotificationReason.Like:
113131
return (
114-
<NotificationView group={group} post={post}>
115-
liked your post
132+
<NotificationView group={group} content={content}>
133+
liked {contentName}
116134
</NotificationView>
117135
);
118136
case NotificationReason.Repost:
119137
return (
120-
<NotificationView group={group} post={post}>
138+
<NotificationView group={group} content={content}>
121139
reposted your post
122140
</NotificationView>
123141
);
124142
case NotificationReason.Follow:
125143
return <NotificationView group={group}>followed you</NotificationView>;
126144
default:
127-
return post && <Post post={post} />;
145+
return content && isPostView(content) && <Post post={content} />;
128146
}
129147
};
130148

131149
const NotificationList: FC<{
132150
groups: NotificationGroup[];
133-
posts: Map<string, PostView | null>;
134-
}> = ({ groups, posts }) => {
151+
contents: Map<string, PostView | GeneratorView | null>;
152+
}> = ({ groups, contents }) => {
135153
const [parent, _] = useAutoAnimate();
136154
return (
137155
<div ref={parent}>
138156
{groups.map((group) => (
139157
<div key={group.key} className="border-b border-slate-500 px-3 pt-3">
140158
<NotificationItem
141159
group={group}
142-
post={group.uri ? posts.get(group.uri) : undefined}
160+
content={group.uri ? contents.get(group.uri) : undefined}
143161
/>
144162
</div>
145163
))}

src/components/PostEmbed.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ import {
1818
View as RecordWithMediaView,
1919
isView as isRecordWithMediaView,
2020
} from "@/atproto/types/app/bsky/embed/recordWithMedia";
21+
import {
22+
GeneratorView,
23+
isGeneratorView,
24+
} from "@/atproto/types/app/bsky/feed/defs";
2125
import { isRecord } from "@/atproto/types/app/bsky/feed/post";
2226
import Avatar from "@/components/Avatar";
2327
import DistanceToNow from "@/components/DistanceToNow";
28+
import { RssIcon } from "@heroicons/react/24/solid";
2429
import { FC } from "react";
2530

2631
export type EmbedView =
@@ -100,6 +105,35 @@ const Record: FC<{ record: ViewRecord }> = ({ record }) => {
100105
);
101106
};
102107

108+
export const Generator: FC<{ generator: GeneratorView }> = ({ generator }) => {
109+
return (
110+
<div className="border border-slate-500 rounded-md w-full mt-2">
111+
<div className="break-all m-2">
112+
<div className="flex">
113+
<div className="h-9 w-9 rounded-md overflow-hidden mt-0.5">
114+
{generator.avatar ? (
115+
<img src={generator.avatar} />
116+
) : (
117+
<div className="bg-blue-500 p-0.5">
118+
<RssIcon className="text-white" />
119+
</div>
120+
)}
121+
</div>
122+
<div className="text-sm ml-2">
123+
<span className="font-semibold">{generator.displayName}</span>
124+
<div className="text-muted">
125+
Feed by @{generator.creator.handle}
126+
</div>
127+
</div>
128+
</div>
129+
<div className="text-sm text-muted mt-1">
130+
Liked by {generator.likeCount || 0} users
131+
</div>
132+
</div>
133+
</div>
134+
);
135+
};
136+
103137
const PostEmbed: FC<{ embed?: EmbedView }> = ({ embed }) => {
104138
if (isImagesView(embed)) {
105139
return (
@@ -124,6 +158,13 @@ const PostEmbed: FC<{ embed?: EmbedView }> = ({ embed }) => {
124158
</div>
125159
);
126160
}
161+
if (isGeneratorView(record)) {
162+
return (
163+
<div className="pb-2">
164+
<Generator generator={record} />
165+
</div>
166+
);
167+
}
127168
}
128169
if (isRecordWithMediaView(embed)) {
129170
return (

src/components/Sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function useFeedGenerators() {
2323
useEffect(() => {
2424
(async () => {
2525
const result = await invoke<{ feeds: GeneratorView[] }>(
26-
Command.GetFeedGenerators
26+
Command.GetPinnedFeedGenerators
2727
);
2828
setFeedGenerators(result.feeds);
2929
})();

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const Command = {
99
Logout: "logout",
1010
Me: "me",
1111
GetProfile: "get_profile",
12+
GetPinnedFeedGenerators: "get_pinned_feed_generators",
1213
GetFeedGenerators: "get_feed_generators",
1314
GetPosts: "get_posts",
1415
Subscribe: "subscribe",

0 commit comments

Comments
 (0)