+3
-3
doc/roadmap.txt
+3
-3
doc/roadmap.txt
···
29
29
- [x] Add dev_network_logs table with LRU eviction (1000 in-memory, 24h in DB)
30
30
31
31
Phase 1: User-Facing "DevTools"
32
-
- [ ] Add dev_pins table for pinned collections/records
33
-
- [ ] Add dev_recent_records table for LRU cache
32
+
- [x] Add dev_pins table for pinned collections/records
33
+
- [x] Add dev_recent_records table for LRU cache
34
34
- [ ] Create /devtools route group (always available, not kDebugMode gated)
35
35
- [ ] DevToolsHomePage: show DID, PDS host, quick actions
36
-
- [ ] "Copy my DID" action
36
+
- [ ] "Copy my DID" action
37
37
- [ ] Add Settings → "Developer Tools" toggle (works in release builds)
38
38
- [ ] Link debug overlay to full DevTools (/devtools routes)
39
39
+2
lib/src/infrastructure/db/app_database.dart
+2
lib/src/infrastructure/db/app_database.dart
+912
lib/src/infrastructure/db/app_database.g.dart
+912
lib/src/infrastructure/db/app_database.g.dart
···
12570
12570
}
12571
12571
}
12572
12572
12573
+
class $DevPinsTable extends DevPins with TableInfo<$DevPinsTable, DevPin> {
12574
+
@override
12575
+
final GeneratedDatabase attachedDatabase;
12576
+
final String? _alias;
12577
+
$DevPinsTable(this.attachedDatabase, [this._alias]);
12578
+
static const VerificationMeta _idMeta = const VerificationMeta('id');
12579
+
@override
12580
+
late final GeneratedColumn<int> id = GeneratedColumn<int>(
12581
+
'id',
12582
+
aliasedName,
12583
+
false,
12584
+
hasAutoIncrement: true,
12585
+
type: DriftSqlType.int,
12586
+
requiredDuringInsert: false,
12587
+
defaultConstraints: GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'),
12588
+
);
12589
+
static const VerificationMeta _uriMeta = const VerificationMeta('uri');
12590
+
@override
12591
+
late final GeneratedColumn<String> uri = GeneratedColumn<String>(
12592
+
'uri',
12593
+
aliasedName,
12594
+
false,
12595
+
type: DriftSqlType.string,
12596
+
requiredDuringInsert: true,
12597
+
defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'),
12598
+
);
12599
+
static const VerificationMeta _labelMeta = const VerificationMeta('label');
12600
+
@override
12601
+
late final GeneratedColumn<String> label = GeneratedColumn<String>(
12602
+
'label',
12603
+
aliasedName,
12604
+
true,
12605
+
type: DriftSqlType.string,
12606
+
requiredDuringInsert: false,
12607
+
);
12608
+
static const VerificationMeta _typeMeta = const VerificationMeta('type');
12609
+
@override
12610
+
late final GeneratedColumn<String> type = GeneratedColumn<String>(
12611
+
'type',
12612
+
aliasedName,
12613
+
false,
12614
+
type: DriftSqlType.string,
12615
+
requiredDuringInsert: true,
12616
+
);
12617
+
static const VerificationMeta _createdAtMeta = const VerificationMeta('createdAt');
12618
+
@override
12619
+
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
12620
+
'created_at',
12621
+
aliasedName,
12622
+
false,
12623
+
type: DriftSqlType.dateTime,
12624
+
requiredDuringInsert: true,
12625
+
);
12626
+
@override
12627
+
List<GeneratedColumn> get $columns => [id, uri, label, type, createdAt];
12628
+
@override
12629
+
String get aliasedName => _alias ?? actualTableName;
12630
+
@override
12631
+
String get actualTableName => $name;
12632
+
static const String $name = 'dev_pins';
12633
+
@override
12634
+
VerificationContext validateIntegrity(Insertable<DevPin> instance, {bool isInserting = false}) {
12635
+
final context = VerificationContext();
12636
+
final data = instance.toColumns(true);
12637
+
if (data.containsKey('id')) {
12638
+
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
12639
+
}
12640
+
if (data.containsKey('uri')) {
12641
+
context.handle(_uriMeta, uri.isAcceptableOrUnknown(data['uri']!, _uriMeta));
12642
+
} else if (isInserting) {
12643
+
context.missing(_uriMeta);
12644
+
}
12645
+
if (data.containsKey('label')) {
12646
+
context.handle(_labelMeta, label.isAcceptableOrUnknown(data['label']!, _labelMeta));
12647
+
}
12648
+
if (data.containsKey('type')) {
12649
+
context.handle(_typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta));
12650
+
} else if (isInserting) {
12651
+
context.missing(_typeMeta);
12652
+
}
12653
+
if (data.containsKey('created_at')) {
12654
+
context.handle(
12655
+
_createdAtMeta,
12656
+
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
12657
+
);
12658
+
} else if (isInserting) {
12659
+
context.missing(_createdAtMeta);
12660
+
}
12661
+
return context;
12662
+
}
12663
+
12664
+
@override
12665
+
Set<GeneratedColumn> get $primaryKey => {id};
12666
+
@override
12667
+
DevPin map(Map<String, dynamic> data, {String? tablePrefix}) {
12668
+
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
12669
+
return DevPin(
12670
+
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
12671
+
uri: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}uri'])!,
12672
+
label: attachedDatabase.typeMapping.read(
12673
+
DriftSqlType.string,
12674
+
data['${effectivePrefix}label'],
12675
+
),
12676
+
type: attachedDatabase.typeMapping.read(
12677
+
DriftSqlType.string,
12678
+
data['${effectivePrefix}type'],
12679
+
)!,
12680
+
createdAt: attachedDatabase.typeMapping.read(
12681
+
DriftSqlType.dateTime,
12682
+
data['${effectivePrefix}created_at'],
12683
+
)!,
12684
+
);
12685
+
}
12686
+
12687
+
@override
12688
+
$DevPinsTable createAlias(String alias) {
12689
+
return $DevPinsTable(attachedDatabase, alias);
12690
+
}
12691
+
}
12692
+
12693
+
class DevPin extends DataClass implements Insertable<DevPin> {
12694
+
final int id;
12695
+
12696
+
/// The AT URI of the collection or record being pinned.
12697
+
final String uri;
12698
+
12699
+
/// Optional custom label for the pin.
12700
+
final String? label;
12701
+
12702
+
/// Type of item: 'collection' or 'record'.
12703
+
final String type;
12704
+
12705
+
/// When this pin was created.
12706
+
final DateTime createdAt;
12707
+
const DevPin({
12708
+
required this.id,
12709
+
required this.uri,
12710
+
this.label,
12711
+
required this.type,
12712
+
required this.createdAt,
12713
+
});
12714
+
@override
12715
+
Map<String, Expression> toColumns(bool nullToAbsent) {
12716
+
final map = <String, Expression>{};
12717
+
map['id'] = Variable<int>(id);
12718
+
map['uri'] = Variable<String>(uri);
12719
+
if (!nullToAbsent || label != null) {
12720
+
map['label'] = Variable<String>(label);
12721
+
}
12722
+
map['type'] = Variable<String>(type);
12723
+
map['created_at'] = Variable<DateTime>(createdAt);
12724
+
return map;
12725
+
}
12726
+
12727
+
DevPinsCompanion toCompanion(bool nullToAbsent) {
12728
+
return DevPinsCompanion(
12729
+
id: Value(id),
12730
+
uri: Value(uri),
12731
+
label: label == null && nullToAbsent ? const Value.absent() : Value(label),
12732
+
type: Value(type),
12733
+
createdAt: Value(createdAt),
12734
+
);
12735
+
}
12736
+
12737
+
factory DevPin.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
12738
+
serializer ??= driftRuntimeOptions.defaultSerializer;
12739
+
return DevPin(
12740
+
id: serializer.fromJson<int>(json['id']),
12741
+
uri: serializer.fromJson<String>(json['uri']),
12742
+
label: serializer.fromJson<String?>(json['label']),
12743
+
type: serializer.fromJson<String>(json['type']),
12744
+
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
12745
+
);
12746
+
}
12747
+
@override
12748
+
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
12749
+
serializer ??= driftRuntimeOptions.defaultSerializer;
12750
+
return <String, dynamic>{
12751
+
'id': serializer.toJson<int>(id),
12752
+
'uri': serializer.toJson<String>(uri),
12753
+
'label': serializer.toJson<String?>(label),
12754
+
'type': serializer.toJson<String>(type),
12755
+
'createdAt': serializer.toJson<DateTime>(createdAt),
12756
+
};
12757
+
}
12758
+
12759
+
DevPin copyWith({
12760
+
int? id,
12761
+
String? uri,
12762
+
Value<String?> label = const Value.absent(),
12763
+
String? type,
12764
+
DateTime? createdAt,
12765
+
}) => DevPin(
12766
+
id: id ?? this.id,
12767
+
uri: uri ?? this.uri,
12768
+
label: label.present ? label.value : this.label,
12769
+
type: type ?? this.type,
12770
+
createdAt: createdAt ?? this.createdAt,
12771
+
);
12772
+
DevPin copyWithCompanion(DevPinsCompanion data) {
12773
+
return DevPin(
12774
+
id: data.id.present ? data.id.value : this.id,
12775
+
uri: data.uri.present ? data.uri.value : this.uri,
12776
+
label: data.label.present ? data.label.value : this.label,
12777
+
type: data.type.present ? data.type.value : this.type,
12778
+
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
12779
+
);
12780
+
}
12781
+
12782
+
@override
12783
+
String toString() {
12784
+
return (StringBuffer('DevPin(')
12785
+
..write('id: $id, ')
12786
+
..write('uri: $uri, ')
12787
+
..write('label: $label, ')
12788
+
..write('type: $type, ')
12789
+
..write('createdAt: $createdAt')
12790
+
..write(')'))
12791
+
.toString();
12792
+
}
12793
+
12794
+
@override
12795
+
int get hashCode => Object.hash(id, uri, label, type, createdAt);
12796
+
@override
12797
+
bool operator ==(Object other) =>
12798
+
identical(this, other) ||
12799
+
(other is DevPin &&
12800
+
other.id == this.id &&
12801
+
other.uri == this.uri &&
12802
+
other.label == this.label &&
12803
+
other.type == this.type &&
12804
+
other.createdAt == this.createdAt);
12805
+
}
12806
+
12807
+
class DevPinsCompanion extends UpdateCompanion<DevPin> {
12808
+
final Value<int> id;
12809
+
final Value<String> uri;
12810
+
final Value<String?> label;
12811
+
final Value<String> type;
12812
+
final Value<DateTime> createdAt;
12813
+
const DevPinsCompanion({
12814
+
this.id = const Value.absent(),
12815
+
this.uri = const Value.absent(),
12816
+
this.label = const Value.absent(),
12817
+
this.type = const Value.absent(),
12818
+
this.createdAt = const Value.absent(),
12819
+
});
12820
+
DevPinsCompanion.insert({
12821
+
this.id = const Value.absent(),
12822
+
required String uri,
12823
+
this.label = const Value.absent(),
12824
+
required String type,
12825
+
required DateTime createdAt,
12826
+
}) : uri = Value(uri),
12827
+
type = Value(type),
12828
+
createdAt = Value(createdAt);
12829
+
static Insertable<DevPin> custom({
12830
+
Expression<int>? id,
12831
+
Expression<String>? uri,
12832
+
Expression<String>? label,
12833
+
Expression<String>? type,
12834
+
Expression<DateTime>? createdAt,
12835
+
}) {
12836
+
return RawValuesInsertable({
12837
+
if (id != null) 'id': id,
12838
+
if (uri != null) 'uri': uri,
12839
+
if (label != null) 'label': label,
12840
+
if (type != null) 'type': type,
12841
+
if (createdAt != null) 'created_at': createdAt,
12842
+
});
12843
+
}
12844
+
12845
+
DevPinsCompanion copyWith({
12846
+
Value<int>? id,
12847
+
Value<String>? uri,
12848
+
Value<String?>? label,
12849
+
Value<String>? type,
12850
+
Value<DateTime>? createdAt,
12851
+
}) {
12852
+
return DevPinsCompanion(
12853
+
id: id ?? this.id,
12854
+
uri: uri ?? this.uri,
12855
+
label: label ?? this.label,
12856
+
type: type ?? this.type,
12857
+
createdAt: createdAt ?? this.createdAt,
12858
+
);
12859
+
}
12860
+
12861
+
@override
12862
+
Map<String, Expression> toColumns(bool nullToAbsent) {
12863
+
final map = <String, Expression>{};
12864
+
if (id.present) {
12865
+
map['id'] = Variable<int>(id.value);
12866
+
}
12867
+
if (uri.present) {
12868
+
map['uri'] = Variable<String>(uri.value);
12869
+
}
12870
+
if (label.present) {
12871
+
map['label'] = Variable<String>(label.value);
12872
+
}
12873
+
if (type.present) {
12874
+
map['type'] = Variable<String>(type.value);
12875
+
}
12876
+
if (createdAt.present) {
12877
+
map['created_at'] = Variable<DateTime>(createdAt.value);
12878
+
}
12879
+
return map;
12880
+
}
12881
+
12882
+
@override
12883
+
String toString() {
12884
+
return (StringBuffer('DevPinsCompanion(')
12885
+
..write('id: $id, ')
12886
+
..write('uri: $uri, ')
12887
+
..write('label: $label, ')
12888
+
..write('type: $type, ')
12889
+
..write('createdAt: $createdAt')
12890
+
..write(')'))
12891
+
.toString();
12892
+
}
12893
+
}
12894
+
12895
+
class $DevRecentRecordsTable extends DevRecentRecords
12896
+
with TableInfo<$DevRecentRecordsTable, DevRecentRecord> {
12897
+
@override
12898
+
final GeneratedDatabase attachedDatabase;
12899
+
final String? _alias;
12900
+
$DevRecentRecordsTable(this.attachedDatabase, [this._alias]);
12901
+
static const VerificationMeta _idMeta = const VerificationMeta('id');
12902
+
@override
12903
+
late final GeneratedColumn<int> id = GeneratedColumn<int>(
12904
+
'id',
12905
+
aliasedName,
12906
+
false,
12907
+
hasAutoIncrement: true,
12908
+
type: DriftSqlType.int,
12909
+
requiredDuringInsert: false,
12910
+
defaultConstraints: GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'),
12911
+
);
12912
+
static const VerificationMeta _uriMeta = const VerificationMeta('uri');
12913
+
@override
12914
+
late final GeneratedColumn<String> uri = GeneratedColumn<String>(
12915
+
'uri',
12916
+
aliasedName,
12917
+
false,
12918
+
type: DriftSqlType.string,
12919
+
requiredDuringInsert: true,
12920
+
defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'),
12921
+
);
12922
+
static const VerificationMeta _cidMeta = const VerificationMeta('cid');
12923
+
@override
12924
+
late final GeneratedColumn<String> cid = GeneratedColumn<String>(
12925
+
'cid',
12926
+
aliasedName,
12927
+
true,
12928
+
type: DriftSqlType.string,
12929
+
requiredDuringInsert: false,
12930
+
);
12931
+
static const VerificationMeta _viewedAtMeta = const VerificationMeta('viewedAt');
12932
+
@override
12933
+
late final GeneratedColumn<DateTime> viewedAt = GeneratedColumn<DateTime>(
12934
+
'viewed_at',
12935
+
aliasedName,
12936
+
false,
12937
+
type: DriftSqlType.dateTime,
12938
+
requiredDuringInsert: true,
12939
+
);
12940
+
@override
12941
+
List<GeneratedColumn> get $columns => [id, uri, cid, viewedAt];
12942
+
@override
12943
+
String get aliasedName => _alias ?? actualTableName;
12944
+
@override
12945
+
String get actualTableName => $name;
12946
+
static const String $name = 'dev_recent_records';
12947
+
@override
12948
+
VerificationContext validateIntegrity(
12949
+
Insertable<DevRecentRecord> instance, {
12950
+
bool isInserting = false,
12951
+
}) {
12952
+
final context = VerificationContext();
12953
+
final data = instance.toColumns(true);
12954
+
if (data.containsKey('id')) {
12955
+
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
12956
+
}
12957
+
if (data.containsKey('uri')) {
12958
+
context.handle(_uriMeta, uri.isAcceptableOrUnknown(data['uri']!, _uriMeta));
12959
+
} else if (isInserting) {
12960
+
context.missing(_uriMeta);
12961
+
}
12962
+
if (data.containsKey('cid')) {
12963
+
context.handle(_cidMeta, cid.isAcceptableOrUnknown(data['cid']!, _cidMeta));
12964
+
}
12965
+
if (data.containsKey('viewed_at')) {
12966
+
context.handle(
12967
+
_viewedAtMeta,
12968
+
viewedAt.isAcceptableOrUnknown(data['viewed_at']!, _viewedAtMeta),
12969
+
);
12970
+
} else if (isInserting) {
12971
+
context.missing(_viewedAtMeta);
12972
+
}
12973
+
return context;
12974
+
}
12975
+
12976
+
@override
12977
+
Set<GeneratedColumn> get $primaryKey => {id};
12978
+
@override
12979
+
DevRecentRecord map(Map<String, dynamic> data, {String? tablePrefix}) {
12980
+
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
12981
+
return DevRecentRecord(
12982
+
id: attachedDatabase.typeMapping.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
12983
+
uri: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}uri'])!,
12984
+
cid: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}cid']),
12985
+
viewedAt: attachedDatabase.typeMapping.read(
12986
+
DriftSqlType.dateTime,
12987
+
data['${effectivePrefix}viewed_at'],
12988
+
)!,
12989
+
);
12990
+
}
12991
+
12992
+
@override
12993
+
$DevRecentRecordsTable createAlias(String alias) {
12994
+
return $DevRecentRecordsTable(attachedDatabase, alias);
12995
+
}
12996
+
}
12997
+
12998
+
class DevRecentRecord extends DataClass implements Insertable<DevRecentRecord> {
12999
+
final int id;
13000
+
13001
+
/// The AT URI of the record.
13002
+
final String uri;
13003
+
13004
+
/// The CID of the record at the time it was viewed.
13005
+
final String? cid;
13006
+
13007
+
/// When this record was last viewed.
13008
+
final DateTime viewedAt;
13009
+
const DevRecentRecord({required this.id, required this.uri, this.cid, required this.viewedAt});
13010
+
@override
13011
+
Map<String, Expression> toColumns(bool nullToAbsent) {
13012
+
final map = <String, Expression>{};
13013
+
map['id'] = Variable<int>(id);
13014
+
map['uri'] = Variable<String>(uri);
13015
+
if (!nullToAbsent || cid != null) {
13016
+
map['cid'] = Variable<String>(cid);
13017
+
}
13018
+
map['viewed_at'] = Variable<DateTime>(viewedAt);
13019
+
return map;
13020
+
}
13021
+
13022
+
DevRecentRecordsCompanion toCompanion(bool nullToAbsent) {
13023
+
return DevRecentRecordsCompanion(
13024
+
id: Value(id),
13025
+
uri: Value(uri),
13026
+
cid: cid == null && nullToAbsent ? const Value.absent() : Value(cid),
13027
+
viewedAt: Value(viewedAt),
13028
+
);
13029
+
}
13030
+
13031
+
factory DevRecentRecord.fromJson(Map<String, dynamic> json, {ValueSerializer? serializer}) {
13032
+
serializer ??= driftRuntimeOptions.defaultSerializer;
13033
+
return DevRecentRecord(
13034
+
id: serializer.fromJson<int>(json['id']),
13035
+
uri: serializer.fromJson<String>(json['uri']),
13036
+
cid: serializer.fromJson<String?>(json['cid']),
13037
+
viewedAt: serializer.fromJson<DateTime>(json['viewedAt']),
13038
+
);
13039
+
}
13040
+
@override
13041
+
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
13042
+
serializer ??= driftRuntimeOptions.defaultSerializer;
13043
+
return <String, dynamic>{
13044
+
'id': serializer.toJson<int>(id),
13045
+
'uri': serializer.toJson<String>(uri),
13046
+
'cid': serializer.toJson<String?>(cid),
13047
+
'viewedAt': serializer.toJson<DateTime>(viewedAt),
13048
+
};
13049
+
}
13050
+
13051
+
DevRecentRecord copyWith({
13052
+
int? id,
13053
+
String? uri,
13054
+
Value<String?> cid = const Value.absent(),
13055
+
DateTime? viewedAt,
13056
+
}) => DevRecentRecord(
13057
+
id: id ?? this.id,
13058
+
uri: uri ?? this.uri,
13059
+
cid: cid.present ? cid.value : this.cid,
13060
+
viewedAt: viewedAt ?? this.viewedAt,
13061
+
);
13062
+
DevRecentRecord copyWithCompanion(DevRecentRecordsCompanion data) {
13063
+
return DevRecentRecord(
13064
+
id: data.id.present ? data.id.value : this.id,
13065
+
uri: data.uri.present ? data.uri.value : this.uri,
13066
+
cid: data.cid.present ? data.cid.value : this.cid,
13067
+
viewedAt: data.viewedAt.present ? data.viewedAt.value : this.viewedAt,
13068
+
);
13069
+
}
13070
+
13071
+
@override
13072
+
String toString() {
13073
+
return (StringBuffer('DevRecentRecord(')
13074
+
..write('id: $id, ')
13075
+
..write('uri: $uri, ')
13076
+
..write('cid: $cid, ')
13077
+
..write('viewedAt: $viewedAt')
13078
+
..write(')'))
13079
+
.toString();
13080
+
}
13081
+
13082
+
@override
13083
+
int get hashCode => Object.hash(id, uri, cid, viewedAt);
13084
+
@override
13085
+
bool operator ==(Object other) =>
13086
+
identical(this, other) ||
13087
+
(other is DevRecentRecord &&
13088
+
other.id == this.id &&
13089
+
other.uri == this.uri &&
13090
+
other.cid == this.cid &&
13091
+
other.viewedAt == this.viewedAt);
13092
+
}
13093
+
13094
+
class DevRecentRecordsCompanion extends UpdateCompanion<DevRecentRecord> {
13095
+
final Value<int> id;
13096
+
final Value<String> uri;
13097
+
final Value<String?> cid;
13098
+
final Value<DateTime> viewedAt;
13099
+
const DevRecentRecordsCompanion({
13100
+
this.id = const Value.absent(),
13101
+
this.uri = const Value.absent(),
13102
+
this.cid = const Value.absent(),
13103
+
this.viewedAt = const Value.absent(),
13104
+
});
13105
+
DevRecentRecordsCompanion.insert({
13106
+
this.id = const Value.absent(),
13107
+
required String uri,
13108
+
this.cid = const Value.absent(),
13109
+
required DateTime viewedAt,
13110
+
}) : uri = Value(uri),
13111
+
viewedAt = Value(viewedAt);
13112
+
static Insertable<DevRecentRecord> custom({
13113
+
Expression<int>? id,
13114
+
Expression<String>? uri,
13115
+
Expression<String>? cid,
13116
+
Expression<DateTime>? viewedAt,
13117
+
}) {
13118
+
return RawValuesInsertable({
13119
+
if (id != null) 'id': id,
13120
+
if (uri != null) 'uri': uri,
13121
+
if (cid != null) 'cid': cid,
13122
+
if (viewedAt != null) 'viewed_at': viewedAt,
13123
+
});
13124
+
}
13125
+
13126
+
DevRecentRecordsCompanion copyWith({
13127
+
Value<int>? id,
13128
+
Value<String>? uri,
13129
+
Value<String?>? cid,
13130
+
Value<DateTime>? viewedAt,
13131
+
}) {
13132
+
return DevRecentRecordsCompanion(
13133
+
id: id ?? this.id,
13134
+
uri: uri ?? this.uri,
13135
+
cid: cid ?? this.cid,
13136
+
viewedAt: viewedAt ?? this.viewedAt,
13137
+
);
13138
+
}
13139
+
13140
+
@override
13141
+
Map<String, Expression> toColumns(bool nullToAbsent) {
13142
+
final map = <String, Expression>{};
13143
+
if (id.present) {
13144
+
map['id'] = Variable<int>(id.value);
13145
+
}
13146
+
if (uri.present) {
13147
+
map['uri'] = Variable<String>(uri.value);
13148
+
}
13149
+
if (cid.present) {
13150
+
map['cid'] = Variable<String>(cid.value);
13151
+
}
13152
+
if (viewedAt.present) {
13153
+
map['viewed_at'] = Variable<DateTime>(viewedAt.value);
13154
+
}
13155
+
return map;
13156
+
}
13157
+
13158
+
@override
13159
+
String toString() {
13160
+
return (StringBuffer('DevRecentRecordsCompanion(')
13161
+
..write('id: $id, ')
13162
+
..write('uri: $uri, ')
13163
+
..write('cid: $cid, ')
13164
+
..write('viewedAt: $viewedAt')
13165
+
..write(')'))
13166
+
.toString();
13167
+
}
13168
+
}
13169
+
12573
13170
abstract class _$AppDatabase extends GeneratedDatabase {
12574
13171
_$AppDatabase(QueryExecutor e) : super(e);
12575
13172
$AppDatabaseManager get managers => $AppDatabaseManager(this);
···
12603
13200
late final $DmOutboxTable dmOutbox = $DmOutboxTable(this);
12604
13201
late final $DevSettingsTable devSettings = $DevSettingsTable(this);
12605
13202
late final $DevNetworkLogsTable devNetworkLogs = $DevNetworkLogsTable(this);
13203
+
late final $DevPinsTable devPins = $DevPinsTable(this);
13204
+
late final $DevRecentRecordsTable devRecentRecords = $DevRecentRecordsTable(this);
12606
13205
late final Index feedContentSortIdx = Index(
12607
13206
'feed_content_sort_idx',
12608
13207
'CREATE INDEX feed_content_sort_idx ON feed_content_items (feed_key, sort_key)',
···
12685
13284
dmOutbox,
12686
13285
devSettings,
12687
13286
devNetworkLogs,
13287
+
devPins,
13288
+
devRecentRecords,
12688
13289
feedContentSortIdx,
12689
13290
recentSearchesUniqueIdx,
12690
13291
searchCacheSortIdx,
···
19890
20491
DevNetworkLog,
19891
20492
PrefetchHooks Function()
19892
20493
>;
20494
+
typedef $$DevPinsTableCreateCompanionBuilder =
20495
+
DevPinsCompanion Function({
20496
+
Value<int> id,
20497
+
required String uri,
20498
+
Value<String?> label,
20499
+
required String type,
20500
+
required DateTime createdAt,
20501
+
});
20502
+
typedef $$DevPinsTableUpdateCompanionBuilder =
20503
+
DevPinsCompanion Function({
20504
+
Value<int> id,
20505
+
Value<String> uri,
20506
+
Value<String?> label,
20507
+
Value<String> type,
20508
+
Value<DateTime> createdAt,
20509
+
});
20510
+
20511
+
class $$DevPinsTableFilterComposer extends Composer<_$AppDatabase, $DevPinsTable> {
20512
+
$$DevPinsTableFilterComposer({
20513
+
required super.$db,
20514
+
required super.$table,
20515
+
super.joinBuilder,
20516
+
super.$addJoinBuilderToRootComposer,
20517
+
super.$removeJoinBuilderFromRootComposer,
20518
+
});
20519
+
ColumnFilters<int> get id =>
20520
+
$composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column));
20521
+
20522
+
ColumnFilters<String> get uri =>
20523
+
$composableBuilder(column: $table.uri, builder: (column) => ColumnFilters(column));
20524
+
20525
+
ColumnFilters<String> get label =>
20526
+
$composableBuilder(column: $table.label, builder: (column) => ColumnFilters(column));
20527
+
20528
+
ColumnFilters<String> get type =>
20529
+
$composableBuilder(column: $table.type, builder: (column) => ColumnFilters(column));
20530
+
20531
+
ColumnFilters<DateTime> get createdAt =>
20532
+
$composableBuilder(column: $table.createdAt, builder: (column) => ColumnFilters(column));
20533
+
}
20534
+
20535
+
class $$DevPinsTableOrderingComposer extends Composer<_$AppDatabase, $DevPinsTable> {
20536
+
$$DevPinsTableOrderingComposer({
20537
+
required super.$db,
20538
+
required super.$table,
20539
+
super.joinBuilder,
20540
+
super.$addJoinBuilderToRootComposer,
20541
+
super.$removeJoinBuilderFromRootComposer,
20542
+
});
20543
+
ColumnOrderings<int> get id =>
20544
+
$composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column));
20545
+
20546
+
ColumnOrderings<String> get uri =>
20547
+
$composableBuilder(column: $table.uri, builder: (column) => ColumnOrderings(column));
20548
+
20549
+
ColumnOrderings<String> get label =>
20550
+
$composableBuilder(column: $table.label, builder: (column) => ColumnOrderings(column));
20551
+
20552
+
ColumnOrderings<String> get type =>
20553
+
$composableBuilder(column: $table.type, builder: (column) => ColumnOrderings(column));
20554
+
20555
+
ColumnOrderings<DateTime> get createdAt =>
20556
+
$composableBuilder(column: $table.createdAt, builder: (column) => ColumnOrderings(column));
20557
+
}
20558
+
20559
+
class $$DevPinsTableAnnotationComposer extends Composer<_$AppDatabase, $DevPinsTable> {
20560
+
$$DevPinsTableAnnotationComposer({
20561
+
required super.$db,
20562
+
required super.$table,
20563
+
super.joinBuilder,
20564
+
super.$addJoinBuilderToRootComposer,
20565
+
super.$removeJoinBuilderFromRootComposer,
20566
+
});
20567
+
GeneratedColumn<int> get id =>
20568
+
$composableBuilder(column: $table.id, builder: (column) => column);
20569
+
20570
+
GeneratedColumn<String> get uri =>
20571
+
$composableBuilder(column: $table.uri, builder: (column) => column);
20572
+
20573
+
GeneratedColumn<String> get label =>
20574
+
$composableBuilder(column: $table.label, builder: (column) => column);
20575
+
20576
+
GeneratedColumn<String> get type =>
20577
+
$composableBuilder(column: $table.type, builder: (column) => column);
20578
+
20579
+
GeneratedColumn<DateTime> get createdAt =>
20580
+
$composableBuilder(column: $table.createdAt, builder: (column) => column);
20581
+
}
20582
+
20583
+
class $$DevPinsTableTableManager
20584
+
extends
20585
+
RootTableManager<
20586
+
_$AppDatabase,
20587
+
$DevPinsTable,
20588
+
DevPin,
20589
+
$$DevPinsTableFilterComposer,
20590
+
$$DevPinsTableOrderingComposer,
20591
+
$$DevPinsTableAnnotationComposer,
20592
+
$$DevPinsTableCreateCompanionBuilder,
20593
+
$$DevPinsTableUpdateCompanionBuilder,
20594
+
(DevPin, BaseReferences<_$AppDatabase, $DevPinsTable, DevPin>),
20595
+
DevPin,
20596
+
PrefetchHooks Function()
20597
+
> {
20598
+
$$DevPinsTableTableManager(_$AppDatabase db, $DevPinsTable table)
20599
+
: super(
20600
+
TableManagerState(
20601
+
db: db,
20602
+
table: table,
20603
+
createFilteringComposer: () => $$DevPinsTableFilterComposer($db: db, $table: table),
20604
+
createOrderingComposer: () => $$DevPinsTableOrderingComposer($db: db, $table: table),
20605
+
createComputedFieldComposer: () =>
20606
+
$$DevPinsTableAnnotationComposer($db: db, $table: table),
20607
+
updateCompanionCallback:
20608
+
({
20609
+
Value<int> id = const Value.absent(),
20610
+
Value<String> uri = const Value.absent(),
20611
+
Value<String?> label = const Value.absent(),
20612
+
Value<String> type = const Value.absent(),
20613
+
Value<DateTime> createdAt = const Value.absent(),
20614
+
}) => DevPinsCompanion(
20615
+
id: id,
20616
+
uri: uri,
20617
+
label: label,
20618
+
type: type,
20619
+
createdAt: createdAt,
20620
+
),
20621
+
createCompanionCallback:
20622
+
({
20623
+
Value<int> id = const Value.absent(),
20624
+
required String uri,
20625
+
Value<String?> label = const Value.absent(),
20626
+
required String type,
20627
+
required DateTime createdAt,
20628
+
}) => DevPinsCompanion.insert(
20629
+
id: id,
20630
+
uri: uri,
20631
+
label: label,
20632
+
type: type,
20633
+
createdAt: createdAt,
20634
+
),
20635
+
withReferenceMapper: (p0) =>
20636
+
p0.map((e) => (e.readTable(table), BaseReferences(db, table, e))).toList(),
20637
+
prefetchHooksCallback: null,
20638
+
),
20639
+
);
20640
+
}
20641
+
20642
+
typedef $$DevPinsTableProcessedTableManager =
20643
+
ProcessedTableManager<
20644
+
_$AppDatabase,
20645
+
$DevPinsTable,
20646
+
DevPin,
20647
+
$$DevPinsTableFilterComposer,
20648
+
$$DevPinsTableOrderingComposer,
20649
+
$$DevPinsTableAnnotationComposer,
20650
+
$$DevPinsTableCreateCompanionBuilder,
20651
+
$$DevPinsTableUpdateCompanionBuilder,
20652
+
(DevPin, BaseReferences<_$AppDatabase, $DevPinsTable, DevPin>),
20653
+
DevPin,
20654
+
PrefetchHooks Function()
20655
+
>;
20656
+
typedef $$DevRecentRecordsTableCreateCompanionBuilder =
20657
+
DevRecentRecordsCompanion Function({
20658
+
Value<int> id,
20659
+
required String uri,
20660
+
Value<String?> cid,
20661
+
required DateTime viewedAt,
20662
+
});
20663
+
typedef $$DevRecentRecordsTableUpdateCompanionBuilder =
20664
+
DevRecentRecordsCompanion Function({
20665
+
Value<int> id,
20666
+
Value<String> uri,
20667
+
Value<String?> cid,
20668
+
Value<DateTime> viewedAt,
20669
+
});
20670
+
20671
+
class $$DevRecentRecordsTableFilterComposer
20672
+
extends Composer<_$AppDatabase, $DevRecentRecordsTable> {
20673
+
$$DevRecentRecordsTableFilterComposer({
20674
+
required super.$db,
20675
+
required super.$table,
20676
+
super.joinBuilder,
20677
+
super.$addJoinBuilderToRootComposer,
20678
+
super.$removeJoinBuilderFromRootComposer,
20679
+
});
20680
+
ColumnFilters<int> get id =>
20681
+
$composableBuilder(column: $table.id, builder: (column) => ColumnFilters(column));
20682
+
20683
+
ColumnFilters<String> get uri =>
20684
+
$composableBuilder(column: $table.uri, builder: (column) => ColumnFilters(column));
20685
+
20686
+
ColumnFilters<String> get cid =>
20687
+
$composableBuilder(column: $table.cid, builder: (column) => ColumnFilters(column));
20688
+
20689
+
ColumnFilters<DateTime> get viewedAt =>
20690
+
$composableBuilder(column: $table.viewedAt, builder: (column) => ColumnFilters(column));
20691
+
}
20692
+
20693
+
class $$DevRecentRecordsTableOrderingComposer
20694
+
extends Composer<_$AppDatabase, $DevRecentRecordsTable> {
20695
+
$$DevRecentRecordsTableOrderingComposer({
20696
+
required super.$db,
20697
+
required super.$table,
20698
+
super.joinBuilder,
20699
+
super.$addJoinBuilderToRootComposer,
20700
+
super.$removeJoinBuilderFromRootComposer,
20701
+
});
20702
+
ColumnOrderings<int> get id =>
20703
+
$composableBuilder(column: $table.id, builder: (column) => ColumnOrderings(column));
20704
+
20705
+
ColumnOrderings<String> get uri =>
20706
+
$composableBuilder(column: $table.uri, builder: (column) => ColumnOrderings(column));
20707
+
20708
+
ColumnOrderings<String> get cid =>
20709
+
$composableBuilder(column: $table.cid, builder: (column) => ColumnOrderings(column));
20710
+
20711
+
ColumnOrderings<DateTime> get viewedAt =>
20712
+
$composableBuilder(column: $table.viewedAt, builder: (column) => ColumnOrderings(column));
20713
+
}
20714
+
20715
+
class $$DevRecentRecordsTableAnnotationComposer
20716
+
extends Composer<_$AppDatabase, $DevRecentRecordsTable> {
20717
+
$$DevRecentRecordsTableAnnotationComposer({
20718
+
required super.$db,
20719
+
required super.$table,
20720
+
super.joinBuilder,
20721
+
super.$addJoinBuilderToRootComposer,
20722
+
super.$removeJoinBuilderFromRootComposer,
20723
+
});
20724
+
GeneratedColumn<int> get id =>
20725
+
$composableBuilder(column: $table.id, builder: (column) => column);
20726
+
20727
+
GeneratedColumn<String> get uri =>
20728
+
$composableBuilder(column: $table.uri, builder: (column) => column);
20729
+
20730
+
GeneratedColumn<String> get cid =>
20731
+
$composableBuilder(column: $table.cid, builder: (column) => column);
20732
+
20733
+
GeneratedColumn<DateTime> get viewedAt =>
20734
+
$composableBuilder(column: $table.viewedAt, builder: (column) => column);
20735
+
}
20736
+
20737
+
class $$DevRecentRecordsTableTableManager
20738
+
extends
20739
+
RootTableManager<
20740
+
_$AppDatabase,
20741
+
$DevRecentRecordsTable,
20742
+
DevRecentRecord,
20743
+
$$DevRecentRecordsTableFilterComposer,
20744
+
$$DevRecentRecordsTableOrderingComposer,
20745
+
$$DevRecentRecordsTableAnnotationComposer,
20746
+
$$DevRecentRecordsTableCreateCompanionBuilder,
20747
+
$$DevRecentRecordsTableUpdateCompanionBuilder,
20748
+
(
20749
+
DevRecentRecord,
20750
+
BaseReferences<_$AppDatabase, $DevRecentRecordsTable, DevRecentRecord>,
20751
+
),
20752
+
DevRecentRecord,
20753
+
PrefetchHooks Function()
20754
+
> {
20755
+
$$DevRecentRecordsTableTableManager(_$AppDatabase db, $DevRecentRecordsTable table)
20756
+
: super(
20757
+
TableManagerState(
20758
+
db: db,
20759
+
table: table,
20760
+
createFilteringComposer: () =>
20761
+
$$DevRecentRecordsTableFilterComposer($db: db, $table: table),
20762
+
createOrderingComposer: () =>
20763
+
$$DevRecentRecordsTableOrderingComposer($db: db, $table: table),
20764
+
createComputedFieldComposer: () =>
20765
+
$$DevRecentRecordsTableAnnotationComposer($db: db, $table: table),
20766
+
updateCompanionCallback:
20767
+
({
20768
+
Value<int> id = const Value.absent(),
20769
+
Value<String> uri = const Value.absent(),
20770
+
Value<String?> cid = const Value.absent(),
20771
+
Value<DateTime> viewedAt = const Value.absent(),
20772
+
}) => DevRecentRecordsCompanion(id: id, uri: uri, cid: cid, viewedAt: viewedAt),
20773
+
createCompanionCallback:
20774
+
({
20775
+
Value<int> id = const Value.absent(),
20776
+
required String uri,
20777
+
Value<String?> cid = const Value.absent(),
20778
+
required DateTime viewedAt,
20779
+
}) =>
20780
+
DevRecentRecordsCompanion.insert(id: id, uri: uri, cid: cid, viewedAt: viewedAt),
20781
+
withReferenceMapper: (p0) =>
20782
+
p0.map((e) => (e.readTable(table), BaseReferences(db, table, e))).toList(),
20783
+
prefetchHooksCallback: null,
20784
+
),
20785
+
);
20786
+
}
20787
+
20788
+
typedef $$DevRecentRecordsTableProcessedTableManager =
20789
+
ProcessedTableManager<
20790
+
_$AppDatabase,
20791
+
$DevRecentRecordsTable,
20792
+
DevRecentRecord,
20793
+
$$DevRecentRecordsTableFilterComposer,
20794
+
$$DevRecentRecordsTableOrderingComposer,
20795
+
$$DevRecentRecordsTableAnnotationComposer,
20796
+
$$DevRecentRecordsTableCreateCompanionBuilder,
20797
+
$$DevRecentRecordsTableUpdateCompanionBuilder,
20798
+
(DevRecentRecord, BaseReferences<_$AppDatabase, $DevRecentRecordsTable, DevRecentRecord>),
20799
+
DevRecentRecord,
20800
+
PrefetchHooks Function()
20801
+
>;
19893
20802
19894
20803
class $AppDatabaseManager {
19895
20804
final _$AppDatabase _db;
···
19941
20850
$$DevSettingsTableTableManager(_db, _db.devSettings);
19942
20851
$$DevNetworkLogsTableTableManager get devNetworkLogs =>
19943
20852
$$DevNetworkLogsTableTableManager(_db, _db.devNetworkLogs);
20853
+
$$DevPinsTableTableManager get devPins => $$DevPinsTableTableManager(_db, _db.devPins);
20854
+
$$DevRecentRecordsTableTableManager get devRecentRecords =>
20855
+
$$DevRecentRecordsTableTableManager(_db, _db.devRecentRecords);
19944
20856
}
+83
-1
lib/src/infrastructure/db/daos/dev_tools_dao.dart
+83
-1
lib/src/infrastructure/db/daos/dev_tools_dao.dart
···
5
5
6
6
part 'dev_tools_dao.g.dart';
7
7
8
-
@DriftAccessor(tables: [DevSettings, DevNetworkLogs])
8
+
@DriftAccessor(tables: [DevSettings, DevNetworkLogs, DevPins, DevRecentRecords])
9
9
class DevToolsDao extends DatabaseAccessor<AppDatabase> with _$DevToolsDaoMixin {
10
10
DevToolsDao(super.db);
11
11
···
79
79
final deleteQuery = delete(devNetworkLogs)..where((t) => t.id.isNotIn(idsToKeep));
80
80
81
81
await deleteQuery.go();
82
+
}
83
+
84
+
Future<void> savePin({
85
+
required String uri,
86
+
required String type,
87
+
String? label,
88
+
DateTime? createdAt,
89
+
}) async {
90
+
await into(devPins).insert(
91
+
DevPinsCompanion(
92
+
uri: Value(uri),
93
+
type: Value(type),
94
+
label: Value(label),
95
+
createdAt: Value(createdAt ?? DateTime.now()),
96
+
),
97
+
mode: InsertMode.insertOrReplace,
98
+
);
99
+
}
100
+
101
+
Future<void> deletePin(String uri) async {
102
+
await (delete(devPins)..where((t) => t.uri.equals(uri))).go();
103
+
}
104
+
105
+
Future<DevPin?> getPin(String uri) async {
106
+
return (select(devPins)..where((t) => t.uri.equals(uri))).getSingleOrNull();
107
+
}
108
+
109
+
Stream<List<DevPin>> watchPins() {
110
+
return (select(
111
+
devPins,
112
+
)..orderBy([(t) => OrderingTerm(expression: t.createdAt, mode: OrderingMode.desc)])).watch();
113
+
}
114
+
115
+
Future<List<DevPin>> getAllPins() async {
116
+
return (select(
117
+
devPins,
118
+
)..orderBy([(t) => OrderingTerm(expression: t.createdAt, mode: OrderingMode.desc)])).get();
119
+
}
120
+
121
+
Future<void> addRecentRecord({
122
+
required String uri,
123
+
String? cid,
124
+
DateTime? viewedAt,
125
+
int limit = 50,
126
+
}) async {
127
+
await transaction(() async {
128
+
await into(devRecentRecords).insert(
129
+
DevRecentRecordsCompanion(
130
+
uri: Value(uri),
131
+
cid: Value(cid),
132
+
viewedAt: Value(viewedAt ?? DateTime.now()),
133
+
),
134
+
mode: InsertMode.insertOrReplace,
135
+
);
136
+
137
+
await pruneRecentRecords(limit);
138
+
});
139
+
}
140
+
141
+
Stream<List<DevRecentRecord>> watchRecentRecords() {
142
+
return (select(
143
+
devRecentRecords,
144
+
)..orderBy([(t) => OrderingTerm(expression: t.viewedAt, mode: OrderingMode.desc)])).watch();
145
+
}
146
+
147
+
Future<void> clearRecentRecords() async {
148
+
await delete(devRecentRecords).go();
149
+
}
150
+
151
+
Future<void> pruneRecentRecords(int limit) async {
152
+
final idsToKeepQuery = select(devRecentRecords, distinct: false)
153
+
..orderBy([
154
+
(t) => OrderingTerm(expression: t.viewedAt, mode: OrderingMode.desc),
155
+
(t) => OrderingTerm(expression: t.id, mode: OrderingMode.desc),
156
+
])
157
+
..limit(limit);
158
+
159
+
final idsToKeep = await idsToKeepQuery.map((row) => row.id).get();
160
+
161
+
if (idsToKeep.isNotEmpty) {
162
+
await (delete(devRecentRecords)..where((t) => t.id.isNotIn(idsToKeep))).go();
163
+
}
82
164
}
83
165
}
+2
lib/src/infrastructure/db/daos/dev_tools_dao.g.dart
+2
lib/src/infrastructure/db/daos/dev_tools_dao.g.dart
···
6
6
mixin _$DevToolsDaoMixin on DatabaseAccessor<AppDatabase> {
7
7
$DevSettingsTable get devSettings => attachedDatabase.devSettings;
8
8
$DevNetworkLogsTable get devNetworkLogs => attachedDatabase.devNetworkLogs;
9
+
$DevPinsTable get devPins => attachedDatabase.devPins;
10
+
$DevRecentRecordsTable get devRecentRecords => attachedDatabase.devRecentRecords;
9
11
}
+31
lib/src/infrastructure/db/tables.dart
+31
lib/src/infrastructure/db/tables.dart
···
659
659
IntColumn get durationMs => integer()();
660
660
TextColumn get error => text().nullable()();
661
661
}
662
+
663
+
/// Stores pinned collections and records for easy access in DevTools.
664
+
class DevPins extends Table {
665
+
IntColumn get id => integer().autoIncrement()();
666
+
667
+
/// The AT URI of the collection or record being pinned.
668
+
TextColumn get uri => text().unique()();
669
+
670
+
/// Optional custom label for the pin.
671
+
TextColumn get label => text().nullable()();
672
+
673
+
/// Type of item: 'collection' or 'record'.
674
+
TextColumn get type => text()();
675
+
676
+
/// When this pin was created.
677
+
DateTimeColumn get createdAt => dateTime()();
678
+
}
679
+
680
+
/// LRU cache of recently viewed records in the Repository inspector.
681
+
class DevRecentRecords extends Table {
682
+
IntColumn get id => integer().autoIncrement()();
683
+
684
+
/// The AT URI of the record.
685
+
TextColumn get uri => text().unique()();
686
+
687
+
/// The CID of the record at the time it was viewed.
688
+
TextColumn get cid => text().nullable()();
689
+
690
+
/// When this record was last viewed.
691
+
DateTimeColumn get viewedAt => dateTime()();
692
+
}
+2
-2
lib/src/infrastructure/network/providers.g.dart
+2
-2
lib/src/infrastructure/network/providers.g.dart
···
52
52
}
53
53
}
54
54
55
-
String _$dioPublicHash() => r'b7ad12dfcf7bebb3ca8f836e21c9c41e9a698edc';
55
+
String _$dioPublicHash() => r'32be6de7d8ba289bb1cf4af536dbb9263494bbae';
56
56
57
57
/// Provides the PDS Dio client for authenticated API access.
58
58
///
···
101
101
}
102
102
}
103
103
104
-
String _$dioPdsHash() => r'58b56b8b3713905fbe532dff9f7e5e80f3c47288';
104
+
String _$dioPdsHash() => r'10c062ffcf5d155b80cf9b090b1d3dc1137ab469';
105
105
106
106
/// Provides the XRPC client for making API requests.
107
107
///
+106
test/src/infrastructure/db/daos/dev_tools_dao_test.dart
+106
test/src/infrastructure/db/daos/dev_tools_dao_test.dart
···
82
82
final logs = await dao.watchLogs().first;
83
83
expect(logs, isEmpty);
84
84
});
85
+
86
+
group('Pins', () {
87
+
test('savePin adds a pin', () async {
88
+
await dao.savePin(
89
+
uri: 'at://did:plc:123/app.bsky.feed.post/123',
90
+
type: 'record',
91
+
label: 'Test Pin',
92
+
);
93
+
94
+
final pin = await dao.getPin('at://did:plc:123/app.bsky.feed.post/123');
95
+
expect(pin, isNotNull);
96
+
expect(pin!.uri, 'at://did:plc:123/app.bsky.feed.post/123');
97
+
expect(pin.type, 'record');
98
+
expect(pin.label, 'Test Pin');
99
+
});
100
+
101
+
test('savePin updates existing pin', () async {
102
+
await dao.savePin(
103
+
uri: 'at://did:plc:123/app.bsky.feed.post/123',
104
+
type: 'record',
105
+
label: 'Original',
106
+
);
107
+
108
+
await dao.savePin(
109
+
uri: 'at://did:plc:123/app.bsky.feed.post/123',
110
+
type: 'record',
111
+
label: 'Updated',
112
+
);
113
+
114
+
final pin = await dao.getPin('at://did:plc:123/app.bsky.feed.post/123');
115
+
expect(pin?.label, 'Updated');
116
+
});
117
+
118
+
test('deletePin removes a pin', () async {
119
+
await dao.savePin(uri: 'at://did:plc:123/app.bsky.feed.post/123', type: 'record');
120
+
121
+
await dao.deletePin('at://did:plc:123/app.bsky.feed.post/123');
122
+
123
+
final pin = await dao.getPin('at://did:plc:123/app.bsky.feed.post/123');
124
+
expect(pin, isNull);
125
+
});
126
+
127
+
test('getAllPins and watchPins return sorted pins', () async {
128
+
final now = DateTime.now();
129
+
await dao.savePin(
130
+
uri: '1',
131
+
type: 'record',
132
+
label: '1',
133
+
createdAt: now.subtract(const Duration(seconds: 10)),
134
+
);
135
+
await dao.savePin(uri: '2', type: 'record', label: '2', createdAt: now);
136
+
137
+
final pins = await dao.getAllPins();
138
+
expect(pins.length, 2);
139
+
expect(pins[0].uri, '2'); // Most recent first
140
+
expect(pins[1].uri, '1');
141
+
142
+
expect(
143
+
dao.watchPins(),
144
+
emits(
145
+
isA<List<DevPin>>()
146
+
.having((l) => l[0].uri, 'first uri', '2')
147
+
.having((l) => l[1].uri, 'second uri', '1'),
148
+
),
149
+
);
150
+
});
151
+
});
152
+
153
+
group('Recent Records', () {
154
+
test('addRecentRecord adds a record', () async {
155
+
await dao.addRecentRecord(
156
+
uri: 'at://did:plc:123/app.bsky.feed.post/123',
157
+
cid: 'bafyre...',
158
+
);
159
+
160
+
final records = await dao.watchRecentRecords().first;
161
+
expect(records.length, 1);
162
+
expect(records.first.uri, 'at://did:plc:123/app.bsky.feed.post/123');
163
+
expect(records.first.cid, 'bafyre...');
164
+
});
165
+
166
+
test('addRecentRecord maintains LRU order', () async {
167
+
final now = DateTime.now();
168
+
await dao.addRecentRecord(uri: '1', viewedAt: now.subtract(const Duration(seconds: 10)));
169
+
await dao.addRecentRecord(uri: '2', viewedAt: now);
170
+
171
+
final records = await dao.watchRecentRecords().first;
172
+
expect(records.length, 2);
173
+
expect(records[0].uri, '2');
174
+
expect(records[1].uri, '1');
175
+
176
+
await dao.addRecentRecord(uri: '1', viewedAt: now.add(const Duration(seconds: 10)));
177
+
final recordsAfter = await dao.watchRecentRecords().first;
178
+
expect(recordsAfter.length, 2);
179
+
expect(recordsAfter[0].uri, '1');
180
+
expect(recordsAfter[1].uri, '2');
181
+
});
182
+
183
+
test('clearRecentRecords removes all', () async {
184
+
await dao.addRecentRecord(uri: '1');
185
+
await dao.clearRecentRecords();
186
+
187
+
final records = await dao.watchRecentRecords().first;
188
+
expect(records, isEmpty);
189
+
});
190
+
});
85
191
});
86
192
}