chore: fixed build errors 6c435a35
Steve · 2025-11-05 11:05 4 file(s) · +61 −21
src/App.tsx +15 −0
31 31
	const allFeeds = useQuery(allFeedsQuery);
32 32
	const hasFeeds = allFeeds.length > 0;
33 33
	const [isInitialLoading, setIsInitialLoading] = React.useState(true);
34 +
	const [isInitialized, setIsInitialized] = React.useState(() => {
35 +
		return localStorage.getItem("alcove_isInitialized") === "true";
36 +
	});
34 37
	const [urlInput, setUrlInput] = React.useState("");
35 38
	const [isAddingFeed, setIsAddingFeed] = React.useState(false);
36 39
	const [errorMessage, setErrorMessage] = React.useState("");
49 52
		}, 500);
50 53
		return () => clearTimeout(timer);
51 54
	}, []);
55 +
56 +
	// Mark as initialized when feeds are added
57 +
	React.useEffect(() => {
58 +
		if (allFeeds.length > 0 && !isInitialized) {
59 +
			localStorage.setItem("alcove_isInitialized", "true");
60 +
			setIsInitialized(true);
61 +
		}
62 +
	}, [allFeeds.length, isInitialized]);
52 63
53 64
	function handleRestoreDialogOpenChange(open: boolean) {
54 65
		setIsRestoreDialogOpen(open);
103 114
						category: feed.category || "Uncategorized",
104 115
						dateUpdated: new Date().toISOString(),
105 116
					});
117 +
118 +
					if (!result.ok) {
119 +
						continue;
120 +
					}
106 121
107 122
					for (const post of posts) {
108 123
						evolu.insert("rssPost", {
src/components/nav-user.tsx +5 −6
21 21
import { use, useState, useRef } from "react";
22 22
import { useEvolu, reset, allFeedsQuery } from "@/lib/evolu";
23 23
import { useQuery } from "@evolu/react";
24 -
import {
25 -
	generateOPML,
26 -
	parseOPML,
27 -
	downloadOPML,
28 -
	type OPMLFeed,
29 -
} from "@/lib/opml";
24 +
import { generateOPML, parseOPML, downloadOPML } from "@/lib/opml";
30 25
import {
31 26
	fetchFeedWithFallback,
32 27
	parseFeedXml,
127 122
						category: feed.category || "Uncategorized",
128 123
						dateUpdated: new Date().toISOString(),
129 124
					});
125 +
126 +
					if (!result.ok) {
127 +
						continue;
128 +
					}
130 129
131 130
					for (const post of posts) {
132 131
						evolu.insert("rssPost", {
src/lib/evolu.ts +33 −1
31 31
});
32 32
33 33
export const allFeedsQuery = evolu.createQuery((db) =>
34 -
	db.selectFrom("rssFeed").selectAll().where("isDeleted", "is not", sqliteTrue),
34 +
	db
35 +
		.selectFrom("rssFeed")
36 +
		.selectAll()
37 +
		.where("isDeleted", "is not", sqliteTrue)
38 +
		// Filter out null titles and feedUrls (required fields)
39 +
		.where("title", "is not", null)
40 +
		.where("feedUrl", "is not", null)
41 +
		.$narrowType<{
42 +
			title: import("@evolu/common").kysely.NotNull;
43 +
			feedUrl: import("@evolu/common").kysely.NotNull;
44 +
		}>()
45 +
		.orderBy("createdAt"),
35 46
);
36 47
37 48
export const postsByFeedQuery = (feedId: string) =>
41 52
			.selectAll()
42 53
			.where("feedId", "=", feedId as RSSFeedId)
43 54
			.where("isDeleted", "is not", sqliteTrue)
55 +
			// Filter out null required fields
56 +
			.where("title", "is not", null)
57 +
			.where("link", "is not", null)
58 +
			.$narrowType<{
59 +
				title: import("@evolu/common").kysely.NotNull;
60 +
				link: import("@evolu/common").kysely.NotNull;
61 +
			}>()
44 62
			.orderBy("id", "desc"),
45 63
	);
46 64
49 67
		.selectFrom("rssPost")
50 68
		.selectAll()
51 69
		.where("isDeleted", "is not", sqliteTrue)
70 +
		// Filter out null required fields
71 +
		.where("title", "is not", null)
72 +
		.where("link", "is not", null)
73 +
		.$narrowType<{
74 +
			title: import("@evolu/common").kysely.NotNull;
75 +
			link: import("@evolu/common").kysely.NotNull;
76 +
		}>()
52 77
		.orderBy("id", "desc"),
53 78
);
54 79
57 82
		.selectFrom("rssFeed")
58 83
		.selectAll()
59 84
		.where("isDeleted", "is not", sqliteTrue)
85 +
		// Filter out null required fields
86 +
		.where("title", "is not", null)
87 +
		.where("feedUrl", "is not", null)
88 +
		.$narrowType<{
89 +
			title: import("@evolu/common").kysely.NotNull;
90 +
			feedUrl: import("@evolu/common").kysely.NotNull;
91 +
		}>()
60 92
		.orderBy("category", "asc"),
61 93
);
62 94
src/lib/opml.ts +8 −14
1 -
import type { RSSFeedId } from "./scheme";
1 +
import { allFeedsQuery } from "./evolu";
2 +
3 +
type Feed = typeof allFeedsQuery.Row;
2 4
5 +
/**
6 +
 * Type for parsed OPML feed data
7 +
 */
3 8
export interface OPMLFeed {
4 9
	title: string;
5 10
	feedUrl: string;
7 12
	category?: string;
8 13
}
9 14
10 -
export interface RSSFeed {
11 -
	id: RSSFeedId;
12 -
	feedUrl: string;
13 -
	title: string;
14 -
	description: string | null;
15 -
	category: string | null;
16 -
	dateUpdated: string | null;
17 -
	isDeleted: number;
18 -
}
19 -
20 15
/**
21 16
 * Generate OPML XML from feeds
22 17
 */
23 -
export function generateOPML(feeds: readonly RSSFeed[]): string {
18 +
export function generateOPML(feeds: readonly Feed[]): string {
24 19
	const now = new Date().toUTCString();
25 -
	const categories = new Map<string, RSSFeed[]>();
20 +
	const categories = new Map<string, Feed[]>();
26 21
27 22
	// Group feeds by category
28 23
	for (const feed of feeds) {
75 70
	const outlines = xmlDoc.querySelectorAll("outline");
76 71
77 72
	for (const outline of outlines) {
78 -
		const type = outline.getAttribute("type");
79 73
		const xmlUrl = outline.getAttribute("xmlUrl");
80 74
81 75
		// If this outline has an xmlUrl, it's a feed