chore: added feed delete 0c6d7cf4
Steve · 2025-11-02 15:31 3 file(s) · +65 −4
src/components/app-sidebar.tsx +36 −0
25 25
	allReadStatusesWithUnreadQuery,
26 26
	useEvolu,
27 27
	reset,
28 +
	evolu as evoluInstance,
28 29
} from "@/lib/evolu";
30 +
import * as Evolu from "@evolu/common";
29 31
import { useQuery } from "@evolu/react";
30 32
import {
31 33
	fetchFeedWithFallback,
224 226
		);
225 227
	}, [filteredPosts, allReadStatusesWithUnread, evolu]);
226 228
229 +
	// Delete feed (soft delete using isDeleted flag)
230 +
	const handleDeleteFeed = React.useCallback(() => {
231 +
		if (!selectedFeedId) return;
232 +
233 +
		const feedToDelete = allFeeds.find((f) => f.id === selectedFeedId);
234 +
		if (!feedToDelete) return;
235 +
236 +
		// Soft delete the feed (CRDT-friendly, preserves sync history)
237 +
		evoluInstance.update("rssFeed", {
238 +
			id: selectedFeedId as any,
239 +
			isDeleted: Evolu.sqliteTrue,
240 +
		});
241 +
242 +
		// Also soft delete all posts associated with this feed
243 +
		const postsToDelete = allPosts.filter((p) => p.feedId === selectedFeedId);
244 +
		postsToDelete.forEach((post) => {
245 +
			evoluInstance.update("rssPost", {
246 +
				id: post.id as any,
247 +
				isDeleted: Evolu.sqliteTrue,
248 +
			});
249 +
		});
250 +
251 +
		toast.success(
252 +
			`Deleted feed "${feedToDelete.title}" and ${postsToDelete.length} post${postsToDelete.length !== 1 ? "s" : ""}`,
253 +
		);
254 +
255 +
		// Navigate back to all feeds
256 +
		onFeedSelect(null);
257 +
	}, [selectedFeedId, allFeeds, allPosts, onFeedSelect]);
258 +
227 259
	const refreshFeeds = React.useCallback(async () => {
228 260
		if (allFeeds.length === 0) {
229 261
			toast.error("No feeds to refresh");
344 376
								isPostRead={isPostRead}
345 377
								onMarkAllAsRead={handleMarkAllAsRead}
346 378
								onMarkAllAsUnread={handleMarkAllAsUnread}
379 +
								onDeleteFeed={handleDeleteFeed}
380 +
								selectedFeedId={selectedFeedId}
347 381
								className="border-0"
348 382
							/>
349 383
						</div>
381 415
				isPostRead={isPostRead}
382 416
				onMarkAllAsRead={handleMarkAllAsRead}
383 417
				onMarkAllAsUnread={handleMarkAllAsUnread}
418 +
				onDeleteFeed={handleDeleteFeed}
419 +
				selectedFeedId={selectedFeedId}
384 420
				className={`bg-sidebar text-sidebar-foreground hidden md:flex ${hidden ? "w-0 min-w-0 border-0 overflow-hidden" : "w-[320px] overflow-y-auto"}`}
385 421
			/>
386 422
		</>
src/components/posts-list.tsx +14 −1
1 -
import { MoreVertical, Check, X } from "lucide-react";
1 +
import { MoreVertical, Check, X, Trash2 } from "lucide-react";
2 2
import { Button } from "@/components/ui/button";
3 3
import {
4 4
	DropdownMenu,
28 28
	isPostRead: (postId: string) => boolean;
29 29
	onMarkAllAsRead: () => void;
30 30
	onMarkAllAsUnread: () => void;
31 +
	onDeleteFeed?: () => void;
32 +
	selectedFeedId?: string | null;
31 33
	className?: string;
32 34
}
33 35
41 43
	isPostRead,
42 44
	onMarkAllAsRead,
43 45
	onMarkAllAsUnread,
46 +
	onDeleteFeed,
47 +
	selectedFeedId,
44 48
	className = "",
45 49
}: PostsListProps) {
46 50
	return (
69 73
									<X className="h-4 w-4 mr-2" />
70 74
									Mark all as unread
71 75
								</DropdownMenuItem>
76 +
								{selectedFeedId && onDeleteFeed && (
77 +
									<DropdownMenuItem
78 +
										onClick={onDeleteFeed}
79 +
										className="text-destructive focus:text-destructive"
80 +
									>
81 +
										<Trash2 className="h-4 w-4 mr-2" />
82 +
										Delete feed
83 +
									</DropdownMenuItem>
84 +
								)}
72 85
							</DropdownMenuContent>
73 86
						</DropdownMenu>
74 87
					</div>
src/lib/evolu.ts +15 −3
33 33
});
34 34
35 35
export const allFeedsQuery = evolu.createQuery((db) =>
36 -
	db.selectFrom("rssFeed").selectAll(),
36 +
	db
37 +
		.selectFrom("rssFeed")
38 +
		.selectAll()
39 +
		.where("isDeleted", "is not", Evolu.sqliteTrue),
37 40
);
38 41
39 42
export const postsByFeedQuery = (feedId: string) =>
42 45
			.selectFrom("rssPost")
43 46
			.selectAll()
44 47
			.where("feedId", "=", feedId as RSSFeedId)
48 +
			.where("isDeleted", "is not", Evolu.sqliteTrue)
45 49
			.orderBy("id", "desc"),
46 50
	);
47 51
48 52
export const allPostsQuery = evolu.createQuery((db) =>
49 -
	db.selectFrom("rssPost").selectAll().orderBy("id", "desc"),
53 +
	db
54 +
		.selectFrom("rssPost")
55 +
		.selectAll()
56 +
		.where("isDeleted", "is not", Evolu.sqliteTrue)
57 +
		.orderBy("id", "desc"),
50 58
);
51 59
52 60
export const feedsByCategoryQuery = evolu.createQuery((db) =>
53 -
	db.selectFrom("rssFeed").selectAll().orderBy("category", "asc"),
61 +
	db
62 +
		.selectFrom("rssFeed")
63 +
		.selectAll()
64 +
		.where("isDeleted", "is not", Evolu.sqliteTrue)
65 +
		.orderBy("category", "asc"),
54 66
);
55 67
56 68
export const allReadStatusesQuery = evolu.createQuery((db) =>