web feed intermediate

This commit is contained in:
Fabian Freund
2025-02-28 14:31:07 +01:00
parent 3c4d479bd9
commit 2a2311253b
51 changed files with 5503 additions and 0 deletions
@@ -0,0 +1,119 @@
import 'package:drift/drift.dart';
import 'package:lensai/features/web_feed/data/database/database.dart';
import 'package:lensai/features/web_feed/data/models/feed_article.dart';
import 'package:lensai/features/web_feed/data/models/feed_article_query_result.dart';
part 'article.g.dart';
@DriftAccessor()
class ArticleDao extends DatabaseAccessor<FeedDatabase> with _$ArticleDaoMixin {
ArticleDao(super.attachedDatabase);
Selectable<FeedArticle> getFeedArticles(Uri? url) {
final select = db.article.select();
if (url != null) {
select.where((article) => article.feedId.equalsValue(url));
}
return select..orderBy([
(row) => OrderingTerm(
expression: coalesce([row.updated, row.created]),
mode: OrderingMode.desc,
),
]);
}
SingleOrNullSelectable<FeedArticle> getArticleById(String articleId) {
return db.article.select()..where((row) => row.id.equals(articleId));
}
Future<void> upsertArticles(List<FeedArticle> articles) {
return db.transaction(() async {
await Future.wait(
articles
.map(
(article) => db.article.insertOne(
article,
onConflict: DoUpdate(
(old) {
return ArticleCompanion(
authors: Value(article.authors),
contentMarkdown: Value(article.contentMarkdown),
contentPlain: Value(article.contentPlain),
links: Value(article.links),
summaryMarkdown: Value(article.summaryMarkdown),
summaryPlain: Value(article.summaryPlain),
tags: Value(article.tags),
title: Value(article.title),
updated: Value(article.updated),
);
},
where:
(old) =>
old.updated.isNotNull() &
old.updated.isSmallerThanValue(
article.updated ?? DateTime(0),
),
),
),
)
.toList(),
);
});
}
Future<int> updateArticleRead(String articleId, DateTime? read) {
final statement =
db.article.update()..where((article) => article.id.equals(articleId));
return statement.write(ArticleCompanion(lastRead: Value(read)));
}
Selectable<(String, int)> getUnreadArticleCount() {
final count = countAll();
final countByFeed =
db.article.selectOnly()
..addColumns([db.article.feedId, count])
..where(
db.article.lastRead.isNull() |
(db.article.updated.isNotNull() &
db.article.lastRead.isSmallerThan(db.article.lastRead)),
)
..groupBy([db.article.feedId]);
return countByFeed.map(
(result) => (result.read(db.article.feedId)!, result.read(count)!),
);
}
Selectable<FeedArticleQueryResult> queryArticles({
required String matchPrefix,
required String matchSuffix,
required String ellipsis,
required int snippetLength,
required String searchString,
required Uri? feedId,
}) {
final ftsQuery = db.buildFtsQuery(searchString);
if (ftsQuery.isNotEmpty) {
return db.queryArticlesFullContent(
feedId: feedId?.toString(),
query: ftsQuery,
snippetLength: snippetLength,
beforeMatch: matchPrefix,
afterMatch: matchSuffix,
ellipsis: ellipsis,
);
} else {
return db.queryArticlesBasic(
feedId: feedId?.toString(),
query: db.buildLikeQuery(searchString),
beforeMatch: matchPrefix,
afterMatch: matchSuffix,
);
}
}
}
@@ -0,0 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'article.dart';
// ignore_for_file: type=lint
mixin _$ArticleDaoMixin on DatabaseAccessor<FeedDatabase> {}
@@ -0,0 +1,38 @@
import 'package:drift/drift.dart';
import 'package:lensai/features/web_feed/data/database/database.dart';
part 'feed.g.dart';
@DriftAccessor()
class FeedDao extends DatabaseAccessor<FeedDatabase> with _$FeedDaoMixin {
FeedDao(super.attachedDatabase);
Selectable<FeedData> getFeeds() {
return db.feed.select();
}
Future<int> updateFeedFetched(Uri url, DateTime fetched) {
final statement =
db.feed.update()..where((feed) => feed.url.equalsValue(url));
return statement.write(FeedCompanion(lastFetched: Value(fetched)));
}
Future<int> deleteFeed(Uri url) {
return db.feed.deleteWhere((feed) => feed.url.equals(url.toString()));
}
Future<int> upsertFeed(FeedData feedData) {
return db.feed.insertOne(
feedData,
onConflict: DoUpdate((old) {
return FeedCompanion(
authors: Value(feedData.authors),
title: Value(feedData.title),
description: Value(feedData.description),
tags: Value(feedData.tags),
);
}),
);
}
}
@@ -0,0 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'feed.dart';
// ignore_for_file: type=lint
mixin _$FeedDaoMixin on DatabaseAccessor<FeedDatabase> {}