this repo has no description

feat: Update session model to include PDS and refactor session storage logic

+67 -48
+15 -29
lib/api.dart
··· 300 300 return null; 301 301 } 302 302 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 303 - final issuer = session.session.issuer; 304 303 final did = session.session.subject; 305 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.createRecord'); 304 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.createRecord'); 306 305 final record = { 307 306 'collection': 'social.grain.gallery', 308 307 'repo': did, ··· 391 390 return null; 392 391 } 393 392 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 394 - final issuer = session.session.issuer; 395 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.uploadBlob'); 393 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.uploadBlob'); 396 394 397 395 // Detect MIME type, fallback to application/octet-stream if unknown 398 396 String? mimeType = lookupMimeType(file.path); ··· 439 437 return null; 440 438 } 441 439 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 442 - final issuer = session.session.issuer; 443 440 final did = session.session.subject; 444 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.createRecord'); 441 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.createRecord'); 445 442 final record = { 446 443 'collection': 'social.grain.photo', 447 444 'repo': did, ··· 480 477 return null; 481 478 } 482 479 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 483 - final issuer = session.session.issuer; 484 480 final did = session.session.subject; 485 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.createRecord'); 481 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.createRecord'); 486 482 final record = { 487 483 'collection': 'social.grain.gallery.item', 488 484 'repo': did, ··· 523 519 return null; 524 520 } 525 521 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 526 - final issuer = session.session.issuer; 527 522 final did = session.session.subject; 528 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.createRecord'); 523 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.createRecord'); 529 524 final record = { 530 525 'collection': 'social.grain.comment', 531 526 'repo': did, ··· 562 557 return null; 563 558 } 564 559 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 565 - final issuer = session.session.issuer; 566 560 final did = session.session.subject; 567 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.createRecord'); 561 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.createRecord'); 568 562 final record = { 569 563 'collection': 'social.grain.favorite', 570 564 'repo': did, ··· 594 588 return null; 595 589 } 596 590 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 597 - final issuer = session.session.issuer; 598 591 final did = session.session.subject; 599 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.createRecord'); 592 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.createRecord'); 600 593 final record = { 601 594 'collection': 'social.grain.graph.follow', 602 595 'repo': did, ··· 628 621 return false; 629 622 } 630 623 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 631 - final issuer = session.session.issuer; 632 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.deleteRecord'); 624 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.deleteRecord'); 633 625 final repo = session.session.subject; 634 626 if (repo.isEmpty) { 635 627 appLogger.w('No repo (DID) available from session for deleteRecord'); ··· 679 671 return false; 680 672 } 681 673 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 682 - final issuer = session.session.issuer; 683 674 final did = session.session.subject; 684 675 // Fetch the raw profile record from atproto getRecord endpoint 685 676 final getUrl = Uri.parse( 686 - '$issuer/xrpc/com.atproto.repo.getRecord?repo=$did&collection=social.grain.actor.profile&rkey=self', 677 + '${session.pds}/xrpc/com.atproto.repo.getRecord?repo=$did&collection=social.grain.actor.profile&rkey=self', 687 678 ); 688 679 final getResp = await dpopClient.send( 689 680 method: 'GET', ··· 713 704 } 714 705 } 715 706 // Update the profile record 716 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.putRecord'); 707 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.putRecord'); 717 708 final record = { 718 709 'collection': 'social.grain.actor.profile', 719 710 'repo': did, ··· 781 772 return false; 782 773 } 783 774 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 784 - final issuer = session.session.issuer; 785 775 final did = session.session.subject; 786 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.applyWrites'); 776 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.applyWrites'); 787 777 788 778 final updates = <Map<String, dynamic>>[]; 789 779 int position = 0; ··· 843 833 return false; 844 834 } 845 835 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 846 - final issuer = session.session.issuer; 847 836 final did = session.session.subject; 848 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.putRecord'); 837 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.putRecord'); 849 838 // Extract rkey from galleryUri 850 839 String rkey = ''; 851 840 try { ··· 905 894 return null; 906 895 } 907 896 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 908 - final issuer = session.session.issuer; 909 897 final did = session.session.subject; 910 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.createRecord'); 898 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.createRecord'); 911 899 final record = { 912 900 'collection': 'social.grain.photo.exif', 913 901 'repo': did, ··· 955 943 return false; 956 944 } 957 945 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 958 - final issuer = session.session.issuer; 959 946 final did = session.session.subject; 960 - final url = Uri.parse('$issuer/xrpc/com.atproto.repo.applyWrites'); 947 + final url = Uri.parse('${session.pds}/xrpc/com.atproto.repo.applyWrites'); 961 948 962 949 // Fetch current photo records for all photos 963 950 final photoRecords = await fetchPhotoRecords(); ··· 1032 1019 return {}; 1033 1020 } 1034 1021 final dpopClient = DpopHttpClient(dpopKey: session.session.dpopJwk); 1035 - final issuer = session.session.issuer; 1036 1022 final did = session.session.subject; 1037 1023 final url = Uri.parse( 1038 - '$issuer/xrpc/com.atproto.repo.listRecords?repo=$did&collection=social.grain.photo', 1024 + '${session.pds}/xrpc/com.atproto.repo.listRecords?repo=$did&collection=social.grain.photo', 1039 1025 ); 1040 1026 1041 1027 final response = await dpopClient.send(
+6 -9
lib/auth.dart
··· 37 37 } 38 38 39 39 Future<void> _saveSession(Session session) async { 40 - final atprotoSessionJson = jsonEncode(session.session.toJson()); 41 - await _storage.write(key: 'atproto_session', value: atprotoSessionJson); 42 - await _storage.write(key: 'api_token', value: session.token); 40 + final sessionJson = jsonEncode(session.toJson()); 41 + await _storage.write(key: 'session', value: sessionJson); 43 42 } 44 43 45 44 Future<Session?> _loadSession() async { 46 - final sessionJsonString = await _storage.read(key: 'atproto_session'); 47 - final token = await _storage.read(key: 'api_token'); 48 - if (sessionJsonString == null || token == null) return null; 45 + final sessionJsonString = await _storage.read(key: 'session'); 46 + if (sessionJsonString == null) return null; 49 47 50 48 try { 51 49 final sessionJson = jsonDecode(sessionJsonString); 52 - return Session(session: AtprotoSession.fromJson(sessionJson), token: token); 50 + return Session.fromJson(sessionJson); 53 51 } catch (e) { 54 52 // Optionally log or clear storage if corrupted 55 53 return null; ··· 96 94 // Revoke session on the server 97 95 await apiService.revokeSession(); 98 96 // Remove session from secure storage 99 - await _storage.delete(key: 'atproto_session'); 100 - await _storage.delete(key: 'api_token'); 97 + await _storage.delete(key: 'session'); 101 98 // Clear any in-memory session/user data 102 99 apiService.currentUser = null; 103 100 }
+5 -1
lib/models/session.dart
··· 7 7 8 8 @freezed 9 9 class Session with _$Session { 10 - const factory Session({required AtprotoSession session, required String token}) = _Session; 10 + const factory Session({ 11 + required AtprotoSession session, 12 + required String token, 13 + required String pds, 14 + }) = _Session; 11 15 12 16 factory Session.fromJson(Map<String, dynamic> json) => _$SessionFromJson(json); 13 17 }
+35 -8
lib/models/session.freezed.dart
··· 23 23 mixin _$Session { 24 24 AtprotoSession get session => throw _privateConstructorUsedError; 25 25 String get token => throw _privateConstructorUsedError; 26 + String get pds => throw _privateConstructorUsedError; 26 27 27 28 /// Serializes this Session to a JSON map. 28 29 Map<String, dynamic> toJson() => throw _privateConstructorUsedError; ··· 38 39 factory $SessionCopyWith(Session value, $Res Function(Session) then) = 39 40 _$SessionCopyWithImpl<$Res, Session>; 40 41 @useResult 41 - $Res call({AtprotoSession session, String token}); 42 + $Res call({AtprotoSession session, String token, String pds}); 42 43 43 44 $AtprotoSessionCopyWith<$Res> get session; 44 45 } ··· 57 58 /// with the given fields replaced by the non-null parameter values. 58 59 @pragma('vm:prefer-inline') 59 60 @override 60 - $Res call({Object? session = null, Object? token = null}) { 61 + $Res call({ 62 + Object? session = null, 63 + Object? token = null, 64 + Object? pds = null, 65 + }) { 61 66 return _then( 62 67 _value.copyWith( 63 68 session: null == session ··· 68 73 ? _value.token 69 74 : token // ignore: cast_nullable_to_non_nullable 70 75 as String, 76 + pds: null == pds 77 + ? _value.pds 78 + : pds // ignore: cast_nullable_to_non_nullable 79 + as String, 71 80 ) 72 81 as $Val, 73 82 ); ··· 92 101 ) = __$$SessionImplCopyWithImpl<$Res>; 93 102 @override 94 103 @useResult 95 - $Res call({AtprotoSession session, String token}); 104 + $Res call({AtprotoSession session, String token, String pds}); 96 105 97 106 @override 98 107 $AtprotoSessionCopyWith<$Res> get session; ··· 111 120 /// with the given fields replaced by the non-null parameter values. 112 121 @pragma('vm:prefer-inline') 113 122 @override 114 - $Res call({Object? session = null, Object? token = null}) { 123 + $Res call({ 124 + Object? session = null, 125 + Object? token = null, 126 + Object? pds = null, 127 + }) { 115 128 return _then( 116 129 _$SessionImpl( 117 130 session: null == session ··· 122 135 ? _value.token 123 136 : token // ignore: cast_nullable_to_non_nullable 124 137 as String, 138 + pds: null == pds 139 + ? _value.pds 140 + : pds // ignore: cast_nullable_to_non_nullable 141 + as String, 125 142 ), 126 143 ); 127 144 } ··· 130 147 /// @nodoc 131 148 @JsonSerializable() 132 149 class _$SessionImpl implements _Session { 133 - const _$SessionImpl({required this.session, required this.token}); 150 + const _$SessionImpl({ 151 + required this.session, 152 + required this.token, 153 + required this.pds, 154 + }); 134 155 135 156 factory _$SessionImpl.fromJson(Map<String, dynamic> json) => 136 157 _$$SessionImplFromJson(json); ··· 139 160 final AtprotoSession session; 140 161 @override 141 162 final String token; 163 + @override 164 + final String pds; 142 165 143 166 @override 144 167 String toString() { 145 - return 'Session(session: $session, token: $token)'; 168 + return 'Session(session: $session, token: $token, pds: $pds)'; 146 169 } 147 170 148 171 @override ··· 151 174 (other.runtimeType == runtimeType && 152 175 other is _$SessionImpl && 153 176 (identical(other.session, session) || other.session == session) && 154 - (identical(other.token, token) || other.token == token)); 177 + (identical(other.token, token) || other.token == token) && 178 + (identical(other.pds, pds) || other.pds == pds)); 155 179 } 156 180 157 181 @JsonKey(includeFromJson: false, includeToJson: false) 158 182 @override 159 - int get hashCode => Object.hash(runtimeType, session, token); 183 + int get hashCode => Object.hash(runtimeType, session, token, pds); 160 184 161 185 /// Create a copy of Session 162 186 /// with the given fields replaced by the non-null parameter values. ··· 176 200 const factory _Session({ 177 201 required final AtprotoSession session, 178 202 required final String token, 203 + required final String pds, 179 204 }) = _$SessionImpl; 180 205 181 206 factory _Session.fromJson(Map<String, dynamic> json) = _$SessionImpl.fromJson; ··· 184 209 AtprotoSession get session; 185 210 @override 186 211 String get token; 212 + @override 213 + String get pds; 187 214 188 215 /// Create a copy of Session 189 216 /// with the given fields replaced by the non-null parameter values.
+6 -1
lib/models/session.g.dart
··· 10 10 _$SessionImpl( 11 11 session: AtprotoSession.fromJson(json['session'] as Map<String, dynamic>), 12 12 token: json['token'] as String, 13 + pds: json['pds'] as String, 13 14 ); 14 15 15 16 Map<String, dynamic> _$$SessionImplToJson(_$SessionImpl instance) => 16 - <String, dynamic>{'session': instance.session, 'token': instance.token}; 17 + <String, dynamic>{ 18 + 'session': instance.session, 19 + 'token': instance.token, 20 + 'pds': instance.pds, 21 + };