improved tab & topic handling
This commit is contained in:
@@ -8,11 +8,42 @@ part 'tab.g.dart';
|
||||
class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
|
||||
TabDao(super.db);
|
||||
|
||||
Future<void> upsertTab(ITab tab) {
|
||||
Stream<List<TabData>> watchTabs() {
|
||||
return (db.tab.select()..orderBy([(u) => OrderingTerm.asc(u.id)])).watch();
|
||||
}
|
||||
|
||||
Stream<TabData?> watchTab(String tabId) {
|
||||
return (db.tab.select()..where((t) => t.id.equals(tabId)))
|
||||
.watchSingleOrNull();
|
||||
}
|
||||
|
||||
Stream<bool> watchTabExisiting(String tabId) {
|
||||
final existsStatement =
|
||||
existsQuery(db.tab.select()..where((t) => t.id.equals(tabId)));
|
||||
|
||||
return selectExpressions([existsStatement])
|
||||
.map((row) => row.read(existsStatement)!)
|
||||
.watchSingle();
|
||||
}
|
||||
|
||||
Stream<List<String>> watchTopicTabs(String? topicId) {
|
||||
final query = (selectOnly(db.tab)
|
||||
..addColumns([db.tab.id])
|
||||
..where(
|
||||
(topicId == null)
|
||||
? db.tab.topicId.isNull()
|
||||
: db.tab.topicId.equals(topicId),
|
||||
)
|
||||
..orderBy([OrderingTerm.asc(db.tab.id)]));
|
||||
|
||||
return query.map((row) => row.read(db.tab.id)!).watch();
|
||||
}
|
||||
|
||||
Future<void> upsertTab(ITab tab, DateTime timestamp) {
|
||||
return db.tab.insertOne(
|
||||
TabCompanion.insert(
|
||||
id: tab.id,
|
||||
timestamp: DateTime.now(),
|
||||
timestamp: timestamp,
|
||||
url: tab.url,
|
||||
topicId: Value(tab.topicId),
|
||||
title: Value(tab.title),
|
||||
@@ -24,6 +55,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
|
||||
|
||||
Future<void> updateTab(
|
||||
String id, {
|
||||
required DateTime timestamp,
|
||||
Value<Uri> url = const Value.absent(),
|
||||
Value<String?> title = const Value.absent(),
|
||||
Value<String?> topicId = const Value.absent(),
|
||||
@@ -33,7 +65,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
|
||||
|
||||
return statement.write(
|
||||
TabCompanion(
|
||||
timestamp: Value(DateTime.now()),
|
||||
timestamp: Value(timestamp),
|
||||
url: url,
|
||||
title: title,
|
||||
topicId: topicId,
|
||||
@@ -41,4 +73,18 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteTab(String id) {
|
||||
return db.tab.deleteOne(TabCompanion.custom(id: Variable(id)));
|
||||
}
|
||||
|
||||
Future<void> deleteTopicTabs(String? topicId) {
|
||||
return (db.tab.delete()
|
||||
..where(
|
||||
(t) => (topicId == null)
|
||||
? t.topicId.isNull()
|
||||
: t.topicId.equals(topicId),
|
||||
))
|
||||
.go();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:ui';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:lensai/core/uuid.dart';
|
||||
import 'package:lensai/features/topics/data/database/database.dart';
|
||||
import 'package:lensai/features/topics/data/models/topic_data.dart';
|
||||
|
||||
part 'topic.g.dart';
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:lensai/features/topics/data/database/daos/tab.dart';
|
||||
import 'package:lensai/features/topics/data/database/daos/topic.dart';
|
||||
import 'package:lensai/features/topics/data/database/drift/converters/color.dart';
|
||||
import 'package:lensai/features/topics/data/database/drift/converters/uri.dart';
|
||||
import 'package:lensai/features/topics/data/models/topic_data.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import 'package:lensai/features/topics/data/database/drift/converters/color.dart';
|
||||
import 'package:lensai/features/topics/data/database/drift/converters/uri.dart';
|
||||
import 'package:lensai/features/topics/data/models/topic_data.dart';
|
||||
|
||||
CREATE TABLE topic (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
name TEXT,
|
||||
color INTEGER NOT NULL MAPPED BY `const ColorConverter()`
|
||||
);
|
||||
) WITH TopicData;
|
||||
|
||||
CREATE TABLE tab (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
@@ -16,12 +17,18 @@ CREATE TABLE tab (
|
||||
screenshot BLOB
|
||||
);
|
||||
|
||||
topics:
|
||||
SELECT topic.*
|
||||
topicsWithCount WITH TopicDataWithCount:
|
||||
SELECT
|
||||
topic.*,
|
||||
tab_agg.tab_count
|
||||
FROM topic
|
||||
LEFT JOIN (
|
||||
SELECT topic_id, MAX(timestamp) AS last_updated
|
||||
SELECT
|
||||
topic_id,
|
||||
COUNT(*) AS tab_count,
|
||||
MAX(timestamp) AS last_updated
|
||||
FROM tab
|
||||
GROUP BY topic_id
|
||||
) AS tab_max ON topic.id = tab_max.topic_id
|
||||
ORDER BY tab_max.last_updated DESC NULLS FIRST;
|
||||
) AS tab_agg ON topic.id = tab_agg.topic_id
|
||||
ORDER BY tab_agg.last_updated DESC NULLS FIRST;
|
||||
|
||||
|
||||
@@ -56,81 +56,6 @@ class Topic extends Table with TableInfo<Topic, TopicData> {
|
||||
bool get dontWriteConstraints => true;
|
||||
}
|
||||
|
||||
class TopicData extends DataClass implements Insertable<TopicData> {
|
||||
final String id;
|
||||
final String? name;
|
||||
final Color color;
|
||||
const TopicData({required this.id, this.name, required this.color});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
map['id'] = Variable<String>(id);
|
||||
if (!nullToAbsent || name != null) {
|
||||
map['name'] = Variable<String>(name);
|
||||
}
|
||||
{
|
||||
map['color'] = Variable<int>(Topic.$convertercolor.toSql(color));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
factory TopicData.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer? serializer}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return TopicData(
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
name: serializer.fromJson<String?>(json['name']),
|
||||
color: serializer.fromJson<Color>(json['color']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<String>(id),
|
||||
'name': serializer.toJson<String?>(name),
|
||||
'color': serializer.toJson<Color>(color),
|
||||
};
|
||||
}
|
||||
|
||||
TopicData copyWith(
|
||||
{String? id,
|
||||
Value<String?> name = const Value.absent(),
|
||||
Color? color}) =>
|
||||
TopicData(
|
||||
id: id ?? this.id,
|
||||
name: name.present ? name.value : this.name,
|
||||
color: color ?? this.color,
|
||||
);
|
||||
TopicData copyWithCompanion(TopicCompanion data) {
|
||||
return TopicData(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
name: data.name.present ? data.name.value : this.name,
|
||||
color: data.color.present ? data.color.value : this.color,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('TopicData(')
|
||||
..write('id: $id, ')
|
||||
..write('name: $name, ')
|
||||
..write('color: $color')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, name, color);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is TopicData &&
|
||||
other.id == this.id &&
|
||||
other.name == this.name &&
|
||||
other.color == this.color);
|
||||
}
|
||||
|
||||
class TopicCompanion extends UpdateCompanion<TopicData> {
|
||||
final Value<String> id;
|
||||
final Value<String?> name;
|
||||
@@ -511,14 +436,19 @@ abstract class _$TabDatabase extends GeneratedDatabase {
|
||||
late final Tab tab = Tab(this);
|
||||
late final TopicDao topicDao = TopicDao(this as TabDatabase);
|
||||
late final TabDao tabDao = TabDao(this as TabDatabase);
|
||||
Selectable<TopicData> topics() {
|
||||
Selectable<TopicDataWithCount> topicsWithCount() {
|
||||
return customSelect(
|
||||
'SELECT topic.* FROM topic LEFT JOIN (SELECT topic_id, MAX(timestamp) AS last_updated FROM tab GROUP BY topic_id) AS tab_max ON topic.id = tab_max.topic_id ORDER BY tab_max.last_updated DESC NULLS FIRST',
|
||||
'SELECT topic.*, tab_agg.tab_count FROM topic LEFT JOIN (SELECT topic_id, COUNT(*) AS tab_count, MAX(timestamp) AS last_updated FROM tab GROUP BY topic_id) AS tab_agg ON topic.id = tab_agg.topic_id ORDER BY tab_agg.last_updated DESC NULLS FIRST',
|
||||
variables: [],
|
||||
readsFrom: {
|
||||
topic,
|
||||
tab,
|
||||
}).asyncMap(topic.mapFromRow);
|
||||
}).map((QueryRow row) => TopicDataWithCount(
|
||||
id: row.read<String>('id'),
|
||||
name: row.readNullable<String>('name'),
|
||||
color: Topic.$convertercolor.fromSql(row.read<int>('color')),
|
||||
tabCount: row.readNullable<int>('tab_count'),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'package:drift/drift.dart';
|
||||
import 'package:lensai/utils/uri_parser.dart' as uri_parser;
|
||||
|
||||
class UriConverter extends TypeConverter<Uri, String> {
|
||||
const UriConverter();
|
||||
|
||||
@override
|
||||
Uri fromSql(String fromDb) {
|
||||
return uri_parser.tryParseUrl(fromDb, eagerParsing: true)!;
|
||||
|
||||
Reference in New Issue
Block a user