mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter

feat: add dev tool related tables

Changed files
+1141 -6
doc
lib
test
src
infrastructure
+3 -3
doc/roadmap.txt
··· 29 - [x] Add dev_network_logs table with LRU eviction (1000 in-memory, 24h in DB) 30 31 Phase 1: User-Facing "DevTools" 32 - - [ ] Add dev_pins table for pinned collections/records 33 - - [ ] Add dev_recent_records table for LRU cache 34 - [ ] Create /devtools route group (always available, not kDebugMode gated) 35 - [ ] DevToolsHomePage: show DID, PDS host, quick actions 36 - - [ ] "Copy my DID" action 37 - [ ] Add Settings → "Developer Tools" toggle (works in release builds) 38 - [ ] Link debug overlay to full DevTools (/devtools routes) 39
··· 29 - [x] Add dev_network_logs table with LRU eviction (1000 in-memory, 24h in DB) 30 31 Phase 1: User-Facing "DevTools" 32 + - [x] Add dev_pins table for pinned collections/records 33 + - [x] Add dev_recent_records table for LRU cache 34 - [ ] Create /devtools route group (always available, not kDebugMode gated) 35 - [ ] DevToolsHomePage: show DID, PDS host, quick actions 36 + - [ ] "Copy my DID" action 37 - [ ] Add Settings → "Developer Tools" toggle (works in release builds) 38 - [ ] Link debug overlay to full DevTools (/devtools routes) 39
+2
lib/src/infrastructure/db/app_database.dart
··· 58 DmOutbox, 59 DevSettings, 60 DevNetworkLogs, 61 ], 62 daos: [ 63 FeedContentDao,
··· 58 DmOutbox, 59 DevSettings, 60 DevNetworkLogs, 61 + DevPins, 62 + DevRecentRecords, 63 ], 64 daos: [ 65 FeedContentDao,
+912
lib/src/infrastructure/db/app_database.g.dart
··· 12570 } 12571 } 12572 12573 abstract class _$AppDatabase extends GeneratedDatabase { 12574 _$AppDatabase(QueryExecutor e) : super(e); 12575 $AppDatabaseManager get managers => $AppDatabaseManager(this); ··· 12603 late final $DmOutboxTable dmOutbox = $DmOutboxTable(this); 12604 late final $DevSettingsTable devSettings = $DevSettingsTable(this); 12605 late final $DevNetworkLogsTable devNetworkLogs = $DevNetworkLogsTable(this); 12606 late final Index feedContentSortIdx = Index( 12607 'feed_content_sort_idx', 12608 'CREATE INDEX feed_content_sort_idx ON feed_content_items (feed_key, sort_key)', ··· 12685 dmOutbox, 12686 devSettings, 12687 devNetworkLogs, 12688 feedContentSortIdx, 12689 recentSearchesUniqueIdx, 12690 searchCacheSortIdx, ··· 19890 DevNetworkLog, 19891 PrefetchHooks Function() 19892 >; 19893 19894 class $AppDatabaseManager { 19895 final _$AppDatabase _db; ··· 19941 $$DevSettingsTableTableManager(_db, _db.devSettings); 19942 $$DevNetworkLogsTableTableManager get devNetworkLogs => 19943 $$DevNetworkLogsTableTableManager(_db, _db.devNetworkLogs); 19944 }
··· 12570 } 12571 } 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 + 13170 abstract class _$AppDatabase extends GeneratedDatabase { 13171 _$AppDatabase(QueryExecutor e) : super(e); 13172 $AppDatabaseManager get managers => $AppDatabaseManager(this); ··· 13200 late final $DmOutboxTable dmOutbox = $DmOutboxTable(this); 13201 late final $DevSettingsTable devSettings = $DevSettingsTable(this); 13202 late final $DevNetworkLogsTable devNetworkLogs = $DevNetworkLogsTable(this); 13203 + late final $DevPinsTable devPins = $DevPinsTable(this); 13204 + late final $DevRecentRecordsTable devRecentRecords = $DevRecentRecordsTable(this); 13205 late final Index feedContentSortIdx = Index( 13206 'feed_content_sort_idx', 13207 'CREATE INDEX feed_content_sort_idx ON feed_content_items (feed_key, sort_key)', ··· 13284 dmOutbox, 13285 devSettings, 13286 devNetworkLogs, 13287 + devPins, 13288 + devRecentRecords, 13289 feedContentSortIdx, 13290 recentSearchesUniqueIdx, 13291 searchCacheSortIdx, ··· 20491 DevNetworkLog, 20492 PrefetchHooks Function() 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 + >; 20802 20803 class $AppDatabaseManager { 20804 final _$AppDatabase _db; ··· 20850 $$DevSettingsTableTableManager(_db, _db.devSettings); 20851 $$DevNetworkLogsTableTableManager get devNetworkLogs => 20852 $$DevNetworkLogsTableTableManager(_db, _db.devNetworkLogs); 20853 + $$DevPinsTableTableManager get devPins => $$DevPinsTableTableManager(_db, _db.devPins); 20854 + $$DevRecentRecordsTableTableManager get devRecentRecords => 20855 + $$DevRecentRecordsTableTableManager(_db, _db.devRecentRecords); 20856 }
+83 -1
lib/src/infrastructure/db/daos/dev_tools_dao.dart
··· 5 6 part 'dev_tools_dao.g.dart'; 7 8 - @DriftAccessor(tables: [DevSettings, DevNetworkLogs]) 9 class DevToolsDao extends DatabaseAccessor<AppDatabase> with _$DevToolsDaoMixin { 10 DevToolsDao(super.db); 11 ··· 79 final deleteQuery = delete(devNetworkLogs)..where((t) => t.id.isNotIn(idsToKeep)); 80 81 await deleteQuery.go(); 82 } 83 }
··· 5 6 part 'dev_tools_dao.g.dart'; 7 8 + @DriftAccessor(tables: [DevSettings, DevNetworkLogs, DevPins, DevRecentRecords]) 9 class DevToolsDao extends DatabaseAccessor<AppDatabase> with _$DevToolsDaoMixin { 10 DevToolsDao(super.db); 11 ··· 79 final deleteQuery = delete(devNetworkLogs)..where((t) => t.id.isNotIn(idsToKeep)); 80 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 + } 164 } 165 }
+2
lib/src/infrastructure/db/daos/dev_tools_dao.g.dart
··· 6 mixin _$DevToolsDaoMixin on DatabaseAccessor<AppDatabase> { 7 $DevSettingsTable get devSettings => attachedDatabase.devSettings; 8 $DevNetworkLogsTable get devNetworkLogs => attachedDatabase.devNetworkLogs; 9 }
··· 6 mixin _$DevToolsDaoMixin on DatabaseAccessor<AppDatabase> { 7 $DevSettingsTable get devSettings => attachedDatabase.devSettings; 8 $DevNetworkLogsTable get devNetworkLogs => attachedDatabase.devNetworkLogs; 9 + $DevPinsTable get devPins => attachedDatabase.devPins; 10 + $DevRecentRecordsTable get devRecentRecords => attachedDatabase.devRecentRecords; 11 }
+31
lib/src/infrastructure/db/tables.dart
··· 659 IntColumn get durationMs => integer()(); 660 TextColumn get error => text().nullable()(); 661 }
··· 659 IntColumn get durationMs => integer()(); 660 TextColumn get error => text().nullable()(); 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
··· 52 } 53 } 54 55 - String _$dioPublicHash() => r'b7ad12dfcf7bebb3ca8f836e21c9c41e9a698edc'; 56 57 /// Provides the PDS Dio client for authenticated API access. 58 /// ··· 101 } 102 } 103 104 - String _$dioPdsHash() => r'58b56b8b3713905fbe532dff9f7e5e80f3c47288'; 105 106 /// Provides the XRPC client for making API requests. 107 ///
··· 52 } 53 } 54 55 + String _$dioPublicHash() => r'32be6de7d8ba289bb1cf4af536dbb9263494bbae'; 56 57 /// Provides the PDS Dio client for authenticated API access. 58 /// ··· 101 } 102 } 103 104 + String _$dioPdsHash() => r'10c062ffcf5d155b80cf9b090b1d3dc1137ab469'; 105 106 /// Provides the XRPC client for making API requests. 107 ///
+106
test/src/infrastructure/db/daos/dev_tools_dao_test.dart
··· 82 final logs = await dao.watchLogs().first; 83 expect(logs, isEmpty); 84 }); 85 }); 86 }
··· 82 final logs = await dao.watchLogs().first; 83 expect(logs, isEmpty); 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 + }); 191 }); 192 }