feat: bookmark the site directory on a first start
This commit is contained in:
parent
95602ac623
commit
dcb70a93d1
14 changed files with 506 additions and 1 deletions
|
|
@ -127,6 +127,16 @@ class ClientCertificates extends Table {
|
|||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||
}
|
||||
|
||||
/// What a fresh install has already been given, so it is given once: what
|
||||
/// the reader removes afterwards stays removed.
|
||||
class FirstStart extends Table {
|
||||
IntColumn get id => integer().withDefault(const Constant(0))();
|
||||
BoolColumn get directorySeeded => boolean().withDefault(const Constant(false))();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
/// Which certificate the reader presents to one host: a binding the reader
|
||||
/// made on a certificate request, worn only there.
|
||||
class SiteCertificates extends Table {
|
||||
|
|
@ -149,6 +159,7 @@ class SiteCertificates extends Table {
|
|||
KnownHosts,
|
||||
ClientCertificates,
|
||||
SiteCertificates,
|
||||
FirstStart,
|
||||
])
|
||||
class MewsDatabase extends _$MewsDatabase {
|
||||
MewsDatabase() : super(_open());
|
||||
|
|
@ -157,7 +168,7 @@ class MewsDatabase extends _$MewsDatabase {
|
|||
MewsDatabase.test(super.executor);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 5;
|
||||
int get schemaVersion => 6;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
|
|
@ -183,6 +194,9 @@ class MewsDatabase extends _$MewsDatabase {
|
|||
await m.createTable(clientCertificates);
|
||||
await m.createTable(siteCertificates);
|
||||
}
|
||||
if (from < 6) {
|
||||
await m.createTable(firstStart);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -309,6 +323,28 @@ class MewsDatabase extends _$MewsDatabase {
|
|||
await _removeTagsOf(TagKind.bookmark, url);
|
||||
});
|
||||
|
||||
/// Gives a fresh install the project's directory of sites as a bookmark,
|
||||
/// so the reader starts somewhere. It happens once: removing it is the
|
||||
/// reader's, and later starts never bring it back.
|
||||
Future<void> seedDirectoryBookmark(String url, String title) async {
|
||||
await transaction(() async {
|
||||
if (await directorySeeded()) return;
|
||||
await addBookmark(url, title);
|
||||
await into(firstStart).insert(
|
||||
FirstStartCompanion.insert(
|
||||
id: const Value(0),
|
||||
directorySeeded: const Value(true),
|
||||
),
|
||||
mode: InsertMode.insertOrReplace);
|
||||
});
|
||||
}
|
||||
|
||||
/// Whether the directory bookmark has been given to this install.
|
||||
Future<bool> directorySeeded() async {
|
||||
final row = await (select(firstStart)..where((r) => r.id.equals(0))).getSingleOrNull();
|
||||
return row?.directorySeeded ?? false;
|
||||
}
|
||||
|
||||
// Subscriptions ---------------------------------------------------------
|
||||
|
||||
Future<void> addSubscription(String url, String? title, String? atomHref) =>
|
||||
|
|
|
|||
|
|
@ -3376,6 +3376,209 @@ class SiteCertificatesCompanion extends UpdateCompanion<SiteCertificate> {
|
|||
}
|
||||
}
|
||||
|
||||
class $FirstStartTable extends FirstStart
|
||||
with TableInfo<$FirstStartTable, FirstStartData> {
|
||||
@override
|
||||
final GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$FirstStartTable(this.attachedDatabase, [this._alias]);
|
||||
static const VerificationMeta _idMeta = const VerificationMeta('id');
|
||||
@override
|
||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: const Constant(0),
|
||||
);
|
||||
static const VerificationMeta _directorySeededMeta = const VerificationMeta(
|
||||
'directorySeeded',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<bool> directorySeeded = GeneratedColumn<bool>(
|
||||
'directory_seeded',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("directory_seeded" IN (0, 1))',
|
||||
),
|
||||
defaultValue: const Constant(false),
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [id, directorySeeded];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'first_start';
|
||||
@override
|
||||
VerificationContext validateIntegrity(
|
||||
Insertable<FirstStartData> instance, {
|
||||
bool isInserting = false,
|
||||
}) {
|
||||
final context = VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
}
|
||||
if (data.containsKey('directory_seeded')) {
|
||||
context.handle(
|
||||
_directorySeededMeta,
|
||||
directorySeeded.isAcceptableOrUnknown(
|
||||
data['directory_seeded']!,
|
||||
_directorySeededMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
FirstStartData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return FirstStartData(
|
||||
id: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}id'],
|
||||
)!,
|
||||
directorySeeded: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.bool,
|
||||
data['${effectivePrefix}directory_seeded'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$FirstStartTable createAlias(String alias) {
|
||||
return $FirstStartTable(attachedDatabase, alias);
|
||||
}
|
||||
}
|
||||
|
||||
class FirstStartData extends DataClass implements Insertable<FirstStartData> {
|
||||
final int id;
|
||||
final bool directorySeeded;
|
||||
const FirstStartData({required this.id, required this.directorySeeded});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
map['id'] = Variable<int>(id);
|
||||
map['directory_seeded'] = Variable<bool>(directorySeeded);
|
||||
return map;
|
||||
}
|
||||
|
||||
FirstStartCompanion toCompanion(bool nullToAbsent) {
|
||||
return FirstStartCompanion(
|
||||
id: Value(id),
|
||||
directorySeeded: Value(directorySeeded),
|
||||
);
|
||||
}
|
||||
|
||||
factory FirstStartData.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return FirstStartData(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
directorySeeded: serializer.fromJson<bool>(json['directorySeeded']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<int>(id),
|
||||
'directorySeeded': serializer.toJson<bool>(directorySeeded),
|
||||
};
|
||||
}
|
||||
|
||||
FirstStartData copyWith({int? id, bool? directorySeeded}) => FirstStartData(
|
||||
id: id ?? this.id,
|
||||
directorySeeded: directorySeeded ?? this.directorySeeded,
|
||||
);
|
||||
FirstStartData copyWithCompanion(FirstStartCompanion data) {
|
||||
return FirstStartData(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
directorySeeded: data.directorySeeded.present
|
||||
? data.directorySeeded.value
|
||||
: this.directorySeeded,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('FirstStartData(')
|
||||
..write('id: $id, ')
|
||||
..write('directorySeeded: $directorySeeded')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, directorySeeded);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is FirstStartData &&
|
||||
other.id == this.id &&
|
||||
other.directorySeeded == this.directorySeeded);
|
||||
}
|
||||
|
||||
class FirstStartCompanion extends UpdateCompanion<FirstStartData> {
|
||||
final Value<int> id;
|
||||
final Value<bool> directorySeeded;
|
||||
const FirstStartCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.directorySeeded = const Value.absent(),
|
||||
});
|
||||
FirstStartCompanion.insert({
|
||||
this.id = const Value.absent(),
|
||||
this.directorySeeded = const Value.absent(),
|
||||
});
|
||||
static Insertable<FirstStartData> custom({
|
||||
Expression<int>? id,
|
||||
Expression<bool>? directorySeeded,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (directorySeeded != null) 'directory_seeded': directorySeeded,
|
||||
});
|
||||
}
|
||||
|
||||
FirstStartCompanion copyWith({Value<int>? id, Value<bool>? directorySeeded}) {
|
||||
return FirstStartCompanion(
|
||||
id: id ?? this.id,
|
||||
directorySeeded: directorySeeded ?? this.directorySeeded,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = Variable<int>(id.value);
|
||||
}
|
||||
if (directorySeeded.present) {
|
||||
map['directory_seeded'] = Variable<bool>(directorySeeded.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('FirstStartCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('directorySeeded: $directorySeeded')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$MewsDatabase extends GeneratedDatabase {
|
||||
_$MewsDatabase(QueryExecutor e) : super(e);
|
||||
$MewsDatabaseManager get managers => $MewsDatabaseManager(this);
|
||||
|
|
@ -3392,6 +3595,7 @@ abstract class _$MewsDatabase extends GeneratedDatabase {
|
|||
late final $SiteCertificatesTable siteCertificates = $SiteCertificatesTable(
|
||||
this,
|
||||
);
|
||||
late final $FirstStartTable firstStart = $FirstStartTable(this);
|
||||
late final Index historyItemsUrl = Index(
|
||||
'history_items_url',
|
||||
'CREATE INDEX history_items_url ON history_items (url)',
|
||||
|
|
@ -3411,6 +3615,7 @@ abstract class _$MewsDatabase extends GeneratedDatabase {
|
|||
knownHosts,
|
||||
clientCertificates,
|
||||
siteCertificates,
|
||||
firstStart,
|
||||
historyItemsUrl,
|
||||
];
|
||||
}
|
||||
|
|
@ -5333,6 +5538,137 @@ typedef $$SiteCertificatesTableProcessedTableManager =
|
|||
SiteCertificate,
|
||||
PrefetchHooks Function()
|
||||
>;
|
||||
typedef $$FirstStartTableCreateCompanionBuilder =
|
||||
FirstStartCompanion Function({Value<int> id, Value<bool> directorySeeded});
|
||||
typedef $$FirstStartTableUpdateCompanionBuilder =
|
||||
FirstStartCompanion Function({Value<int> id, Value<bool> directorySeeded});
|
||||
|
||||
class $$FirstStartTableFilterComposer
|
||||
extends Composer<_$MewsDatabase, $FirstStartTable> {
|
||||
$$FirstStartTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
ColumnFilters<int> get id => $composableBuilder(
|
||||
column: $table.id,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<bool> get directorySeeded => $composableBuilder(
|
||||
column: $table.directorySeeded,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$FirstStartTableOrderingComposer
|
||||
extends Composer<_$MewsDatabase, $FirstStartTable> {
|
||||
$$FirstStartTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
ColumnOrderings<int> get id => $composableBuilder(
|
||||
column: $table.id,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<bool> get directorySeeded => $composableBuilder(
|
||||
column: $table.directorySeeded,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$FirstStartTableAnnotationComposer
|
||||
extends Composer<_$MewsDatabase, $FirstStartTable> {
|
||||
$$FirstStartTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
GeneratedColumn<int> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<bool> get directorySeeded => $composableBuilder(
|
||||
column: $table.directorySeeded,
|
||||
builder: (column) => column,
|
||||
);
|
||||
}
|
||||
|
||||
class $$FirstStartTableTableManager
|
||||
extends
|
||||
RootTableManager<
|
||||
_$MewsDatabase,
|
||||
$FirstStartTable,
|
||||
FirstStartData,
|
||||
$$FirstStartTableFilterComposer,
|
||||
$$FirstStartTableOrderingComposer,
|
||||
$$FirstStartTableAnnotationComposer,
|
||||
$$FirstStartTableCreateCompanionBuilder,
|
||||
$$FirstStartTableUpdateCompanionBuilder,
|
||||
(
|
||||
FirstStartData,
|
||||
BaseReferences<_$MewsDatabase, $FirstStartTable, FirstStartData>,
|
||||
),
|
||||
FirstStartData,
|
||||
PrefetchHooks Function()
|
||||
> {
|
||||
$$FirstStartTableTableManager(_$MewsDatabase db, $FirstStartTable table)
|
||||
: super(
|
||||
TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
$$FirstStartTableFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
$$FirstStartTableOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
$$FirstStartTableAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback:
|
||||
({
|
||||
Value<int> id = const Value.absent(),
|
||||
Value<bool> directorySeeded = const Value.absent(),
|
||||
}) =>
|
||||
FirstStartCompanion(id: id, directorySeeded: directorySeeded),
|
||||
createCompanionCallback:
|
||||
({
|
||||
Value<int> id = const Value.absent(),
|
||||
Value<bool> directorySeeded = const Value.absent(),
|
||||
}) => FirstStartCompanion.insert(
|
||||
id: id,
|
||||
directorySeeded: directorySeeded,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
typedef $$FirstStartTableProcessedTableManager =
|
||||
ProcessedTableManager<
|
||||
_$MewsDatabase,
|
||||
$FirstStartTable,
|
||||
FirstStartData,
|
||||
$$FirstStartTableFilterComposer,
|
||||
$$FirstStartTableOrderingComposer,
|
||||
$$FirstStartTableAnnotationComposer,
|
||||
$$FirstStartTableCreateCompanionBuilder,
|
||||
$$FirstStartTableUpdateCompanionBuilder,
|
||||
(
|
||||
FirstStartData,
|
||||
BaseReferences<_$MewsDatabase, $FirstStartTable, FirstStartData>,
|
||||
),
|
||||
FirstStartData,
|
||||
PrefetchHooks Function()
|
||||
>;
|
||||
|
||||
class $MewsDatabaseManager {
|
||||
final _$MewsDatabase _db;
|
||||
|
|
@ -5357,4 +5693,6 @@ class $MewsDatabaseManager {
|
|||
$$ClientCertificatesTableTableManager(_db, _db.clientCertificates);
|
||||
$$SiteCertificatesTableTableManager get siteCertificates =>
|
||||
$$SiteCertificatesTableTableManager(_db, _db.siteCertificates);
|
||||
$$FirstStartTableTableManager get firstStart =>
|
||||
$$FirstStartTableTableManager(_db, _db.firstStart);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,6 +101,11 @@ class AppDependencies {
|
|||
Future<void> start() async {
|
||||
await settings.load();
|
||||
await applyLanguage();
|
||||
// A fresh install starts with the project's directory of sites
|
||||
// bookmarked, so the reader has somewhere to go; it is given once, so
|
||||
// removing it is the reader's own and it never comes back.
|
||||
await db.seedDirectoryBookmark(
|
||||
'https://mews.page/directory', strings.t('bookmarks-directory-title'));
|
||||
await bookmarks.load();
|
||||
await subscriptions.load();
|
||||
await browser.restoreLastSession();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue