Compare commits
	
		
			5 Commits
		
	
	
		
			e367fc3f5c
			...
			3.0.0+110
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 007acedf29 | |||
| 8e903ec6c1 | |||
| b55e56c3c4 | |||
| 6f9de431b1 | |||
| a8efd26262 | 
@@ -676,5 +676,7 @@
 | 
			
		||||
  "publisherFeatureDevelopDescription": "Unlock development abilities for your publisher, including custom apps, API keys, and more.",
 | 
			
		||||
  "publisherFeatureDevelopHint": "Currently, this feature is under active development, you need send a request to unlock this feature.",
 | 
			
		||||
  "learnMore": "Learn More",
 | 
			
		||||
  "discoverWebArticles": "Articles from external sites"
 | 
			
		||||
  "discoverWebArticles": "Articles from external sites",
 | 
			
		||||
  "webArticlesStand": "Article Stand",
 | 
			
		||||
  "about": "About"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,31 +40,31 @@ PODS:
 | 
			
		||||
  - file_picker (0.0.1):
 | 
			
		||||
    - DKImagePickerController/PhotoGallery
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - Firebase/CoreOnly (11.13.0):
 | 
			
		||||
    - FirebaseCore (~> 11.13.0)
 | 
			
		||||
  - Firebase/Messaging (11.13.0):
 | 
			
		||||
  - Firebase/CoreOnly (11.15.0):
 | 
			
		||||
    - FirebaseCore (~> 11.15.0)
 | 
			
		||||
  - Firebase/Messaging (11.15.0):
 | 
			
		||||
    - Firebase/CoreOnly
 | 
			
		||||
    - FirebaseMessaging (~> 11.13.0)
 | 
			
		||||
  - firebase_core (3.14.0):
 | 
			
		||||
    - Firebase/CoreOnly (= 11.13.0)
 | 
			
		||||
    - FirebaseMessaging (~> 11.15.0)
 | 
			
		||||
  - firebase_core (3.15.0):
 | 
			
		||||
    - Firebase/CoreOnly (= 11.15.0)
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - firebase_messaging (15.2.7):
 | 
			
		||||
    - Firebase/Messaging (= 11.13.0)
 | 
			
		||||
  - firebase_messaging (15.2.8):
 | 
			
		||||
    - Firebase/Messaging (= 11.15.0)
 | 
			
		||||
    - firebase_core
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - FirebaseCore (11.13.0):
 | 
			
		||||
    - FirebaseCoreInternal (~> 11.13.0)
 | 
			
		||||
  - FirebaseCore (11.15.0):
 | 
			
		||||
    - FirebaseCoreInternal (~> 11.15.0)
 | 
			
		||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
			
		||||
    - GoogleUtilities/Logger (~> 8.1)
 | 
			
		||||
  - FirebaseCoreInternal (11.13.0):
 | 
			
		||||
  - FirebaseCoreInternal (11.15.0):
 | 
			
		||||
    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
			
		||||
  - FirebaseInstallations (11.13.0):
 | 
			
		||||
    - FirebaseCore (~> 11.13.0)
 | 
			
		||||
  - FirebaseInstallations (11.15.0):
 | 
			
		||||
    - FirebaseCore (~> 11.15.0)
 | 
			
		||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
			
		||||
    - GoogleUtilities/UserDefaults (~> 8.1)
 | 
			
		||||
    - PromisesObjC (~> 2.4)
 | 
			
		||||
  - FirebaseMessaging (11.13.0):
 | 
			
		||||
    - FirebaseCore (~> 11.13.0)
 | 
			
		||||
  - FirebaseMessaging (11.15.0):
 | 
			
		||||
    - FirebaseCore (~> 11.15.0)
 | 
			
		||||
    - FirebaseInstallations (~> 11.0)
 | 
			
		||||
    - GoogleDataTransport (~> 10.0)
 | 
			
		||||
    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
			
		||||
@@ -80,6 +80,8 @@ PODS:
 | 
			
		||||
  - flutter_inappwebview_ios/Core (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - OrderedSet (~> 6.0.3)
 | 
			
		||||
  - flutter_keyboard_visibility (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - flutter_native_splash (2.4.3):
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - flutter_platform_alert (0.0.1):
 | 
			
		||||
@@ -128,8 +130,8 @@ PODS:
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - irondash_engine_context (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - Kingfisher (8.3.2)
 | 
			
		||||
  - livekit_client (2.4.8):
 | 
			
		||||
  - Kingfisher (8.3.3)
 | 
			
		||||
  - livekit_client (2.4.9):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - flutter_webrtc
 | 
			
		||||
    - WebRTC-SDK (= 125.6422.07)
 | 
			
		||||
@@ -155,6 +157,8 @@ PODS:
 | 
			
		||||
  - path_provider_foundation (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - pointer_interceptor_ios (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - PromisesObjC (2.4.0)
 | 
			
		||||
  - receive_sharing_intent (1.8.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
@@ -217,6 +221,7 @@ DEPENDENCIES:
 | 
			
		||||
  - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
 | 
			
		||||
  - Flutter (from `Flutter`)
 | 
			
		||||
  - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
 | 
			
		||||
  - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
 | 
			
		||||
  - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
 | 
			
		||||
  - flutter_platform_alert (from `.symlinks/plugins/flutter_platform_alert/ios`)
 | 
			
		||||
  - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
 | 
			
		||||
@@ -235,6 +240,7 @@ DEPENDENCIES:
 | 
			
		||||
  - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
 | 
			
		||||
  - pasteboard (from `.symlinks/plugins/pasteboard/ios`)
 | 
			
		||||
  - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
 | 
			
		||||
  - pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`)
 | 
			
		||||
  - receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
 | 
			
		||||
  - record_ios (from `.symlinks/plugins/record_ios/ios`)
 | 
			
		||||
  - share_plus (from `.symlinks/plugins/share_plus/ios`)
 | 
			
		||||
@@ -286,6 +292,8 @@ EXTERNAL SOURCES:
 | 
			
		||||
    :path: Flutter
 | 
			
		||||
  flutter_inappwebview_ios:
 | 
			
		||||
    :path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
 | 
			
		||||
  flutter_keyboard_visibility:
 | 
			
		||||
    :path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
 | 
			
		||||
  flutter_native_splash:
 | 
			
		||||
    :path: ".symlinks/plugins/flutter_native_splash/ios"
 | 
			
		||||
  flutter_platform_alert:
 | 
			
		||||
@@ -320,6 +328,8 @@ EXTERNAL SOURCES:
 | 
			
		||||
    :path: ".symlinks/plugins/pasteboard/ios"
 | 
			
		||||
  path_provider_foundation:
 | 
			
		||||
    :path: ".symlinks/plugins/path_provider_foundation/darwin"
 | 
			
		||||
  pointer_interceptor_ios:
 | 
			
		||||
    :path: ".symlinks/plugins/pointer_interceptor_ios/ios"
 | 
			
		||||
  receive_sharing_intent:
 | 
			
		||||
    :path: ".symlinks/plugins/receive_sharing_intent/ios"
 | 
			
		||||
  record_ios:
 | 
			
		||||
@@ -351,15 +361,16 @@ SPEC CHECKSUMS:
 | 
			
		||||
  DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
 | 
			
		||||
  DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
 | 
			
		||||
  file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
 | 
			
		||||
  Firebase: 3435bc66b4d494c2f22c79fd3aae4c1db6662327
 | 
			
		||||
  firebase_core: 700bac7ed92bb754fd70fbf01d72b36ecdd6d450
 | 
			
		||||
  firebase_messaging: 860c017fcfbb5e27c163062d1d3135388f3ef954
 | 
			
		||||
  FirebaseCore: c692c7f1c75305ab6aff2b367f25e11d73aa8bd0
 | 
			
		||||
  FirebaseCoreInternal: 29d7b3af4aaf0b8f3ed20b568c13df399b06f68c
 | 
			
		||||
  FirebaseInstallations: 0ee9074f2c1e86561ace168ee1470dc67aabaf02
 | 
			
		||||
  FirebaseMessaging: 195bbdb73e6ca1dbc76cd46e73f3552c084ef6e4
 | 
			
		||||
  Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
 | 
			
		||||
  firebase_core: c727a02c560a53f1f1e56e18f16515eb5753c492
 | 
			
		||||
  firebase_messaging: 4158969b04b667f5435731ec9d6e453bb58b0c4c
 | 
			
		||||
  FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
 | 
			
		||||
  FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
 | 
			
		||||
  FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
 | 
			
		||||
  FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
 | 
			
		||||
  Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
 | 
			
		||||
  flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
 | 
			
		||||
  flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
 | 
			
		||||
  flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
 | 
			
		||||
  flutter_platform_alert: bf3b5fcd4ac14bd637e20527e9c471633071afd3
 | 
			
		||||
  flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
 | 
			
		||||
@@ -371,8 +382,8 @@ SPEC CHECKSUMS:
 | 
			
		||||
  GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
 | 
			
		||||
  image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
 | 
			
		||||
  irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
 | 
			
		||||
  Kingfisher: 0621d0ac0c78fecb19f6dc5303bde2b52abaf2f5
 | 
			
		||||
  livekit_client: 9e901890552514206e5ff828903ed271531da264
 | 
			
		||||
  Kingfisher: ff82cb91d9266ddb56cbb2f72d32c26f00d3e5be
 | 
			
		||||
  livekit_client: 3f79d79233a5bd13d5b541732624ef959d7c538e
 | 
			
		||||
  local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
 | 
			
		||||
  media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
 | 
			
		||||
  media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
 | 
			
		||||
@@ -382,6 +393,7 @@ SPEC CHECKSUMS:
 | 
			
		||||
  package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
 | 
			
		||||
  pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c
 | 
			
		||||
  path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
 | 
			
		||||
  pointer_interceptor_ios: ec847ef8b0915778bed2b2cef636f4d177fa8eed
 | 
			
		||||
  PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
 | 
			
		||||
  receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
 | 
			
		||||
  record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								lib/models/auto_completion.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								lib/models/auto_completion.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
 | 
			
		||||
part 'auto_completion.freezed.dart';
 | 
			
		||||
part 'auto_completion.g.dart';
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class AutoCompletionResponse with _$AutoCompletionResponse {
 | 
			
		||||
  const factory AutoCompletionResponse.account({
 | 
			
		||||
    required String type,
 | 
			
		||||
    required List<AutoCompletionItem> items,
 | 
			
		||||
  }) = AutoCompletionAccountResponse;
 | 
			
		||||
 | 
			
		||||
  const factory AutoCompletionResponse.sticker({
 | 
			
		||||
    required String type,
 | 
			
		||||
    required List<AutoCompletionItem> items,
 | 
			
		||||
  }) = AutoCompletionStickerResponse;
 | 
			
		||||
 | 
			
		||||
  factory AutoCompletionResponse.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$AutoCompletionResponseFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class AutoCompletionItem with _$AutoCompletionItem {
 | 
			
		||||
  const factory AutoCompletionItem({
 | 
			
		||||
    required String id,
 | 
			
		||||
    required String displayName,
 | 
			
		||||
    required String? secondaryText,
 | 
			
		||||
    required String type,
 | 
			
		||||
    required dynamic data,
 | 
			
		||||
  }) = _AutoCompletionItem;
 | 
			
		||||
 | 
			
		||||
  factory AutoCompletionItem.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$AutoCompletionItemFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										410
									
								
								lib/models/auto_completion.freezed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										410
									
								
								lib/models/auto_completion.freezed.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,410 @@
 | 
			
		||||
// dart format width=80
 | 
			
		||||
// coverage:ignore-file
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
// ignore_for_file: type=lint
 | 
			
		||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
 | 
			
		||||
 | 
			
		||||
part of 'auto_completion.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// FreezedGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
// dart format off
 | 
			
		||||
T _$identity<T>(T value) => value;
 | 
			
		||||
AutoCompletionResponse _$AutoCompletionResponseFromJson(
 | 
			
		||||
  Map<String, dynamic> json
 | 
			
		||||
) {
 | 
			
		||||
        switch (json['runtimeType']) {
 | 
			
		||||
                  case 'account':
 | 
			
		||||
          return AutoCompletionAccountResponse.fromJson(
 | 
			
		||||
            json
 | 
			
		||||
          );
 | 
			
		||||
                case 'sticker':
 | 
			
		||||
          return AutoCompletionStickerResponse.fromJson(
 | 
			
		||||
            json
 | 
			
		||||
          );
 | 
			
		||||
        
 | 
			
		||||
          default:
 | 
			
		||||
            throw CheckedFromJsonException(
 | 
			
		||||
  json,
 | 
			
		||||
  'runtimeType',
 | 
			
		||||
  'AutoCompletionResponse',
 | 
			
		||||
  'Invalid union type "${json['runtimeType']}"!'
 | 
			
		||||
);
 | 
			
		||||
        }
 | 
			
		||||
      
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$AutoCompletionResponse {
 | 
			
		||||
 | 
			
		||||
 String get type; List<AutoCompletionItem> get items;
 | 
			
		||||
/// Create a copy of AutoCompletionResponse
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$AutoCompletionResponseCopyWith<AutoCompletionResponse> get copyWith => _$AutoCompletionResponseCopyWithImpl<AutoCompletionResponse>(this as AutoCompletionResponse, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this AutoCompletionResponse to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is AutoCompletionResponse&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.items, items));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(items));
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'AutoCompletionResponse(type: $type, items: $items)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $AutoCompletionResponseCopyWith<$Res>  {
 | 
			
		||||
  factory $AutoCompletionResponseCopyWith(AutoCompletionResponse value, $Res Function(AutoCompletionResponse) _then) = _$AutoCompletionResponseCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String type, List<AutoCompletionItem> items
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$AutoCompletionResponseCopyWithImpl<$Res>
 | 
			
		||||
    implements $AutoCompletionResponseCopyWith<$Res> {
 | 
			
		||||
  _$AutoCompletionResponseCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final AutoCompletionResponse _self;
 | 
			
		||||
  final $Res Function(AutoCompletionResponse) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of AutoCompletionResponse
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? items = null,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<AutoCompletionItem>,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class AutoCompletionAccountResponse implements AutoCompletionResponse {
 | 
			
		||||
  const AutoCompletionAccountResponse({required this.type, required final  List<AutoCompletionItem> items, final  String? $type}): _items = items,$type = $type ?? 'account';
 | 
			
		||||
  factory AutoCompletionAccountResponse.fromJson(Map<String, dynamic> json) => _$AutoCompletionAccountResponseFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override final  String type;
 | 
			
		||||
 final  List<AutoCompletionItem> _items;
 | 
			
		||||
@override List<AutoCompletionItem> get items {
 | 
			
		||||
  if (_items is EqualUnmodifiableListView) return _items;
 | 
			
		||||
  // ignore: implicit_dynamic_type
 | 
			
		||||
  return EqualUnmodifiableListView(_items);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@JsonKey(name: 'runtimeType')
 | 
			
		||||
final String $type;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Create a copy of AutoCompletionResponse
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$AutoCompletionAccountResponseCopyWith<AutoCompletionAccountResponse> get copyWith => _$AutoCompletionAccountResponseCopyWithImpl<AutoCompletionAccountResponse>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$AutoCompletionAccountResponseToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is AutoCompletionAccountResponse&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._items, _items));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(_items));
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'AutoCompletionResponse.account(type: $type, items: $items)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $AutoCompletionAccountResponseCopyWith<$Res> implements $AutoCompletionResponseCopyWith<$Res> {
 | 
			
		||||
  factory $AutoCompletionAccountResponseCopyWith(AutoCompletionAccountResponse value, $Res Function(AutoCompletionAccountResponse) _then) = _$AutoCompletionAccountResponseCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String type, List<AutoCompletionItem> items
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$AutoCompletionAccountResponseCopyWithImpl<$Res>
 | 
			
		||||
    implements $AutoCompletionAccountResponseCopyWith<$Res> {
 | 
			
		||||
  _$AutoCompletionAccountResponseCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final AutoCompletionAccountResponse _self;
 | 
			
		||||
  final $Res Function(AutoCompletionAccountResponse) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of AutoCompletionResponse
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? items = null,}) {
 | 
			
		||||
  return _then(AutoCompletionAccountResponse(
 | 
			
		||||
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<AutoCompletionItem>,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class AutoCompletionStickerResponse implements AutoCompletionResponse {
 | 
			
		||||
  const AutoCompletionStickerResponse({required this.type, required final  List<AutoCompletionItem> items, final  String? $type}): _items = items,$type = $type ?? 'sticker';
 | 
			
		||||
  factory AutoCompletionStickerResponse.fromJson(Map<String, dynamic> json) => _$AutoCompletionStickerResponseFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override final  String type;
 | 
			
		||||
 final  List<AutoCompletionItem> _items;
 | 
			
		||||
@override List<AutoCompletionItem> get items {
 | 
			
		||||
  if (_items is EqualUnmodifiableListView) return _items;
 | 
			
		||||
  // ignore: implicit_dynamic_type
 | 
			
		||||
  return EqualUnmodifiableListView(_items);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@JsonKey(name: 'runtimeType')
 | 
			
		||||
final String $type;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Create a copy of AutoCompletionResponse
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$AutoCompletionStickerResponseCopyWith<AutoCompletionStickerResponse> get copyWith => _$AutoCompletionStickerResponseCopyWithImpl<AutoCompletionStickerResponse>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$AutoCompletionStickerResponseToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is AutoCompletionStickerResponse&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._items, _items));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(_items));
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'AutoCompletionResponse.sticker(type: $type, items: $items)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $AutoCompletionStickerResponseCopyWith<$Res> implements $AutoCompletionResponseCopyWith<$Res> {
 | 
			
		||||
  factory $AutoCompletionStickerResponseCopyWith(AutoCompletionStickerResponse value, $Res Function(AutoCompletionStickerResponse) _then) = _$AutoCompletionStickerResponseCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String type, List<AutoCompletionItem> items
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$AutoCompletionStickerResponseCopyWithImpl<$Res>
 | 
			
		||||
    implements $AutoCompletionStickerResponseCopyWith<$Res> {
 | 
			
		||||
  _$AutoCompletionStickerResponseCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final AutoCompletionStickerResponse _self;
 | 
			
		||||
  final $Res Function(AutoCompletionStickerResponse) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of AutoCompletionResponse
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? items = null,}) {
 | 
			
		||||
  return _then(AutoCompletionStickerResponse(
 | 
			
		||||
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<AutoCompletionItem>,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$AutoCompletionItem {
 | 
			
		||||
 | 
			
		||||
 String get id; String get displayName; String? get secondaryText; String get type; dynamic get data;
 | 
			
		||||
/// Create a copy of AutoCompletionItem
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$AutoCompletionItemCopyWith<AutoCompletionItem> get copyWith => _$AutoCompletionItemCopyWithImpl<AutoCompletionItem>(this as AutoCompletionItem, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this AutoCompletionItem to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is AutoCompletionItem&&(identical(other.id, id) || other.id == id)&&(identical(other.displayName, displayName) || other.displayName == displayName)&&(identical(other.secondaryText, secondaryText) || other.secondaryText == secondaryText)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.data, data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,displayName,secondaryText,type,const DeepCollectionEquality().hash(data));
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'AutoCompletionItem(id: $id, displayName: $displayName, secondaryText: $secondaryText, type: $type, data: $data)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $AutoCompletionItemCopyWith<$Res>  {
 | 
			
		||||
  factory $AutoCompletionItemCopyWith(AutoCompletionItem value, $Res Function(AutoCompletionItem) _then) = _$AutoCompletionItemCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, String displayName, String? secondaryText, String type, dynamic data
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$AutoCompletionItemCopyWithImpl<$Res>
 | 
			
		||||
    implements $AutoCompletionItemCopyWith<$Res> {
 | 
			
		||||
  _$AutoCompletionItemCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final AutoCompletionItem _self;
 | 
			
		||||
  final $Res Function(AutoCompletionItem) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of AutoCompletionItem
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? displayName = null,Object? secondaryText = freezed,Object? type = null,Object? data = freezed,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,displayName: null == displayName ? _self.displayName : displayName // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,secondaryText: freezed == secondaryText ? _self.secondaryText : secondaryText // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as dynamic,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _AutoCompletionItem implements AutoCompletionItem {
 | 
			
		||||
  const _AutoCompletionItem({required this.id, required this.displayName, required this.secondaryText, required this.type, required this.data});
 | 
			
		||||
  factory _AutoCompletionItem.fromJson(Map<String, dynamic> json) => _$AutoCompletionItemFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override final  String id;
 | 
			
		||||
@override final  String displayName;
 | 
			
		||||
@override final  String? secondaryText;
 | 
			
		||||
@override final  String type;
 | 
			
		||||
@override final  dynamic data;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of AutoCompletionItem
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$AutoCompletionItemCopyWith<_AutoCompletionItem> get copyWith => __$AutoCompletionItemCopyWithImpl<_AutoCompletionItem>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$AutoCompletionItemToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AutoCompletionItem&&(identical(other.id, id) || other.id == id)&&(identical(other.displayName, displayName) || other.displayName == displayName)&&(identical(other.secondaryText, secondaryText) || other.secondaryText == secondaryText)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.data, data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,displayName,secondaryText,type,const DeepCollectionEquality().hash(data));
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'AutoCompletionItem(id: $id, displayName: $displayName, secondaryText: $secondaryText, type: $type, data: $data)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$AutoCompletionItemCopyWith<$Res> implements $AutoCompletionItemCopyWith<$Res> {
 | 
			
		||||
  factory _$AutoCompletionItemCopyWith(_AutoCompletionItem value, $Res Function(_AutoCompletionItem) _then) = __$AutoCompletionItemCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, String displayName, String? secondaryText, String type, dynamic data
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$AutoCompletionItemCopyWithImpl<$Res>
 | 
			
		||||
    implements _$AutoCompletionItemCopyWith<$Res> {
 | 
			
		||||
  __$AutoCompletionItemCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _AutoCompletionItem _self;
 | 
			
		||||
  final $Res Function(_AutoCompletionItem) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of AutoCompletionItem
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? displayName = null,Object? secondaryText = freezed,Object? type = null,Object? data = freezed,}) {
 | 
			
		||||
  return _then(_AutoCompletionItem(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,displayName: null == displayName ? _self.displayName : displayName // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,secondaryText: freezed == secondaryText ? _self.secondaryText : secondaryText // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as dynamic,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dart format on
 | 
			
		||||
							
								
								
									
										63
									
								
								lib/models/auto_completion.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								lib/models/auto_completion.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'auto_completion.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// JsonSerializableGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
AutoCompletionAccountResponse _$AutoCompletionAccountResponseFromJson(
 | 
			
		||||
  Map<String, dynamic> json,
 | 
			
		||||
) => AutoCompletionAccountResponse(
 | 
			
		||||
  type: json['type'] as String,
 | 
			
		||||
  items:
 | 
			
		||||
      (json['items'] as List<dynamic>)
 | 
			
		||||
          .map((e) => AutoCompletionItem.fromJson(e as Map<String, dynamic>))
 | 
			
		||||
          .toList(),
 | 
			
		||||
  $type: json['runtimeType'] as String?,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$AutoCompletionAccountResponseToJson(
 | 
			
		||||
  AutoCompletionAccountResponse instance,
 | 
			
		||||
) => <String, dynamic>{
 | 
			
		||||
  'type': instance.type,
 | 
			
		||||
  'items': instance.items.map((e) => e.toJson()).toList(),
 | 
			
		||||
  'runtimeType': instance.$type,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
AutoCompletionStickerResponse _$AutoCompletionStickerResponseFromJson(
 | 
			
		||||
  Map<String, dynamic> json,
 | 
			
		||||
) => AutoCompletionStickerResponse(
 | 
			
		||||
  type: json['type'] as String,
 | 
			
		||||
  items:
 | 
			
		||||
      (json['items'] as List<dynamic>)
 | 
			
		||||
          .map((e) => AutoCompletionItem.fromJson(e as Map<String, dynamic>))
 | 
			
		||||
          .toList(),
 | 
			
		||||
  $type: json['runtimeType'] as String?,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$AutoCompletionStickerResponseToJson(
 | 
			
		||||
  AutoCompletionStickerResponse instance,
 | 
			
		||||
) => <String, dynamic>{
 | 
			
		||||
  'type': instance.type,
 | 
			
		||||
  'items': instance.items.map((e) => e.toJson()).toList(),
 | 
			
		||||
  'runtimeType': instance.$type,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
_AutoCompletionItem _$AutoCompletionItemFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
    _AutoCompletionItem(
 | 
			
		||||
      id: json['id'] as String,
 | 
			
		||||
      displayName: json['display_name'] as String,
 | 
			
		||||
      secondaryText: json['secondary_text'] as String?,
 | 
			
		||||
      type: json['type'] as String,
 | 
			
		||||
      data: json['data'],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$AutoCompletionItemToJson(_AutoCompletionItem instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'id': instance.id,
 | 
			
		||||
      'display_name': instance.displayName,
 | 
			
		||||
      'secondary_text': instance.secondaryText,
 | 
			
		||||
      'type': instance.type,
 | 
			
		||||
      'data': instance.data,
 | 
			
		||||
    };
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/screens/about.dart';
 | 
			
		||||
import 'package:island/screens/developers/apps.dart';
 | 
			
		||||
import 'package:island/screens/developers/edit_app.dart';
 | 
			
		||||
import 'package:island/screens/developers/new_app.dart';
 | 
			
		||||
import 'package:island/screens/developers/hub.dart';
 | 
			
		||||
import 'package:island/screens/discovery/articles.dart';
 | 
			
		||||
import 'package:island/screens/posts/post_search.dart';
 | 
			
		||||
import 'package:island/widgets/app_wrapper.dart';
 | 
			
		||||
import 'package:island/screens/tabs.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:island/screens/explore.dart';
 | 
			
		||||
import 'package:island/screens/article_detail_screen.dart';
 | 
			
		||||
import 'package:island/screens/account.dart';
 | 
			
		||||
@@ -220,6 +222,19 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          // Web articles
 | 
			
		||||
          GoRoute(
 | 
			
		||||
            path: '/feeds/articles',
 | 
			
		||||
            builder: (context, state) => const ArticlesScreen(),
 | 
			
		||||
          ),
 | 
			
		||||
          GoRoute(
 | 
			
		||||
            path: '/feeds/articles/:id',
 | 
			
		||||
            builder: (context, state) {
 | 
			
		||||
              final id = state.pathParameters['id']!;
 | 
			
		||||
              return ArticleDetailScreen(articleId: id);
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          // Auth routes
 | 
			
		||||
          GoRoute(
 | 
			
		||||
            path: '/auth/login',
 | 
			
		||||
@@ -235,6 +250,10 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
            path: '/settings',
 | 
			
		||||
            builder: (context, state) => const SettingsScreen(),
 | 
			
		||||
          ),
 | 
			
		||||
          GoRoute(
 | 
			
		||||
            path: '/about',
 | 
			
		||||
            builder: (context, state) => const AboutScreen(),
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          // Main tabs with TabsScreen shell
 | 
			
		||||
          ShellRoute(
 | 
			
		||||
@@ -243,18 +262,6 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
              return TabsScreen(child: child);
 | 
			
		||||
            },
 | 
			
		||||
            routes: [
 | 
			
		||||
              // Article detail route
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/articles/:id',
 | 
			
		||||
                pageBuilder: (context, state) {
 | 
			
		||||
                  final id = state.pathParameters['id']!;
 | 
			
		||||
                  return MaterialPage(
 | 
			
		||||
                    key: state.pageKey,
 | 
			
		||||
                    child: ArticleDetailScreen(articleId: id),
 | 
			
		||||
                  );
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
 | 
			
		||||
              // Explore tab
 | 
			
		||||
              ShellRoute(
 | 
			
		||||
                builder:
 | 
			
		||||
@@ -264,6 +271,10 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
                    path: '/',
 | 
			
		||||
                    builder: (context, state) => const ExploreScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: '/posts/search',
 | 
			
		||||
                    builder: (context, state) => const PostSearchScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: '/posts/:id',
 | 
			
		||||
                    builder: (context, state) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										300
									
								
								lib/screens/about.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								lib/screens/about.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,300 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
import 'package:package_info_plus/package_info_plus.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
 | 
			
		||||
class AboutScreen extends StatefulWidget {
 | 
			
		||||
  const AboutScreen({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<AboutScreen> createState() => _AboutScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _AboutScreenState extends State<AboutScreen> {
 | 
			
		||||
  PackageInfo _packageInfo = PackageInfo(
 | 
			
		||||
    appName: 'Island',
 | 
			
		||||
    packageName: 'com.example.island',
 | 
			
		||||
    version: '1.0.0',
 | 
			
		||||
    buildNumber: '1',
 | 
			
		||||
  );
 | 
			
		||||
  bool _isLoading = true;
 | 
			
		||||
  String? _errorMessage;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    _initPackageInfo();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _initPackageInfo() async {
 | 
			
		||||
    try {
 | 
			
		||||
      final info = await PackageInfo.fromPlatform();
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        setState(() {
 | 
			
		||||
          _packageInfo = info;
 | 
			
		||||
          _isLoading = false;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        setState(() {
 | 
			
		||||
          _errorMessage = 'Failed to load package info: $e';
 | 
			
		||||
          _isLoading = false;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _launchURL(String url) async {
 | 
			
		||||
    final uri = Uri.parse(url);
 | 
			
		||||
    if (await canLaunchUrl(uri)) {
 | 
			
		||||
      await launchUrl(uri);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final theme = Theme.of(context);
 | 
			
		||||
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(title: const Text('About'), elevation: 0),
 | 
			
		||||
      body:
 | 
			
		||||
          _isLoading
 | 
			
		||||
              ? const Center(child: CircularProgressIndicator())
 | 
			
		||||
              : _errorMessage != null
 | 
			
		||||
              ? Center(child: Text(_errorMessage!))
 | 
			
		||||
              : SingleChildScrollView(
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  crossAxisAlignment: CrossAxisAlignment.center,
 | 
			
		||||
                  children: [
 | 
			
		||||
                    const SizedBox(height: 24),
 | 
			
		||||
                    // App Icon and Name
 | 
			
		||||
                    CircleAvatar(
 | 
			
		||||
                      radius: 50,
 | 
			
		||||
                      backgroundColor: theme.colorScheme.primary.withOpacity(
 | 
			
		||||
                        0.1,
 | 
			
		||||
                      ),
 | 
			
		||||
                      child: Image.asset(
 | 
			
		||||
                        'assets/icons/icon.png',
 | 
			
		||||
                        width: 56,
 | 
			
		||||
                        height: 56,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    const SizedBox(height: 16),
 | 
			
		||||
                    Text(
 | 
			
		||||
                      _packageInfo.appName,
 | 
			
		||||
                      style: theme.textTheme.headlineSmall?.copyWith(
 | 
			
		||||
                        fontWeight: FontWeight.bold,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Text(
 | 
			
		||||
                      'Version ${_packageInfo.version} (${_packageInfo.buildNumber})',
 | 
			
		||||
                      style: theme.textTheme.bodyMedium?.copyWith(
 | 
			
		||||
                        color: theme.textTheme.bodySmall?.color,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    const SizedBox(height: 32),
 | 
			
		||||
 | 
			
		||||
                    // App Info Card
 | 
			
		||||
                    _buildSection(
 | 
			
		||||
                      context,
 | 
			
		||||
                      title: 'App Information',
 | 
			
		||||
                      children: [
 | 
			
		||||
                        _buildInfoItem(
 | 
			
		||||
                          context,
 | 
			
		||||
                          icon: Icons.info_outline,
 | 
			
		||||
                          label: 'Package Name',
 | 
			
		||||
                          value: _packageInfo.packageName,
 | 
			
		||||
                        ),
 | 
			
		||||
                        _buildInfoItem(
 | 
			
		||||
                          context,
 | 
			
		||||
                          icon: Icons.update,
 | 
			
		||||
                          label: 'Version',
 | 
			
		||||
                          value: _packageInfo.version,
 | 
			
		||||
                        ),
 | 
			
		||||
                        _buildInfoItem(
 | 
			
		||||
                          context,
 | 
			
		||||
                          icon: Icons.build,
 | 
			
		||||
                          label: 'Build Number',
 | 
			
		||||
                          value: _packageInfo.buildNumber,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                    ),
 | 
			
		||||
 | 
			
		||||
                    const SizedBox(height: 16),
 | 
			
		||||
 | 
			
		||||
                    // Links Card
 | 
			
		||||
                    _buildSection(
 | 
			
		||||
                      context,
 | 
			
		||||
                      title: 'Links',
 | 
			
		||||
                      children: [
 | 
			
		||||
                        _buildListTile(
 | 
			
		||||
                          context,
 | 
			
		||||
                          icon: Icons.privacy_tip_outlined,
 | 
			
		||||
                          title: 'Privacy Policy',
 | 
			
		||||
                          onTap:
 | 
			
		||||
                              () => _launchURL(
 | 
			
		||||
                                'https://solsynth.dev/terms/privacy-policy',
 | 
			
		||||
                              ),
 | 
			
		||||
                        ),
 | 
			
		||||
                        _buildListTile(
 | 
			
		||||
                          context,
 | 
			
		||||
                          icon: Icons.description_outlined,
 | 
			
		||||
                          title: 'Terms of Service',
 | 
			
		||||
                          onTap:
 | 
			
		||||
                              () => _launchURL(
 | 
			
		||||
                                'https://example.com/terms/basic-law',
 | 
			
		||||
                              ),
 | 
			
		||||
                        ),
 | 
			
		||||
                        _buildListTile(
 | 
			
		||||
                          context,
 | 
			
		||||
                          icon: Icons.code,
 | 
			
		||||
                          title: 'Open Source Licenses',
 | 
			
		||||
                          onTap: () {
 | 
			
		||||
                            showLicensePage(
 | 
			
		||||
                              context: context,
 | 
			
		||||
                              applicationName: _packageInfo.appName,
 | 
			
		||||
                              applicationVersion:
 | 
			
		||||
                                  'Version ${_packageInfo.version}',
 | 
			
		||||
                            );
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                    ),
 | 
			
		||||
 | 
			
		||||
                    const SizedBox(height: 16),
 | 
			
		||||
 | 
			
		||||
                    // Developer Info
 | 
			
		||||
                    _buildSection(
 | 
			
		||||
                      context,
 | 
			
		||||
                      title: 'Developer',
 | 
			
		||||
                      children: [
 | 
			
		||||
                        _buildListTile(
 | 
			
		||||
                          context,
 | 
			
		||||
                          icon: Icons.email_outlined,
 | 
			
		||||
                          title: 'Contact Us',
 | 
			
		||||
                          subtitle: 'lily@solsynth.dev',
 | 
			
		||||
                          onTap: () => _launchURL('mailto:lily@solsynth.dev'),
 | 
			
		||||
                        ),
 | 
			
		||||
                        _buildListTile(
 | 
			
		||||
                          context,
 | 
			
		||||
                          icon: Icons.copyright,
 | 
			
		||||
                          title: 'License',
 | 
			
		||||
                          subtitle:
 | 
			
		||||
                              'Copyright reserved © ${DateTime.now().year} Solsynth\nGNU Affero General Public License v3.0',
 | 
			
		||||
                          onTap:
 | 
			
		||||
                              () => _launchURL(
 | 
			
		||||
                                'https://github.com/Solsynth/Solian/blob/v3/LICENSE.txt',
 | 
			
		||||
                              ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                    ),
 | 
			
		||||
 | 
			
		||||
                    const SizedBox(height: 32),
 | 
			
		||||
 | 
			
		||||
                    // Copyright
 | 
			
		||||
                    Padding(
 | 
			
		||||
                      padding: const EdgeInsets.all(16.0),
 | 
			
		||||
                      child: Text(
 | 
			
		||||
                        '© ${DateTime.now().year} ${_packageInfo.appName}. All rights reserved.',
 | 
			
		||||
                        style: theme.textTheme.bodySmall,
 | 
			
		||||
                        textAlign: TextAlign.center,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildSection(
 | 
			
		||||
    BuildContext context, {
 | 
			
		||||
    required String title,
 | 
			
		||||
    required List<Widget> children,
 | 
			
		||||
  }) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
 | 
			
		||||
      child: Column(
 | 
			
		||||
        crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
        children: [
 | 
			
		||||
          Padding(
 | 
			
		||||
            padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
 | 
			
		||||
            child: Text(
 | 
			
		||||
              title,
 | 
			
		||||
              style: Theme.of(
 | 
			
		||||
                context,
 | 
			
		||||
              ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const Divider(height: 1),
 | 
			
		||||
          ...children,
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildInfoItem(
 | 
			
		||||
    BuildContext context, {
 | 
			
		||||
    required IconData icon,
 | 
			
		||||
    required String label,
 | 
			
		||||
    required String value,
 | 
			
		||||
  }) {
 | 
			
		||||
    return Padding(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
 | 
			
		||||
      child: Row(
 | 
			
		||||
        children: [
 | 
			
		||||
          Icon(icon, size: 20, color: Theme.of(context).hintColor),
 | 
			
		||||
          const SizedBox(width: 16),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: Column(
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
              children: [
 | 
			
		||||
                Text(label, style: Theme.of(context).textTheme.bodySmall),
 | 
			
		||||
                const SizedBox(height: 2),
 | 
			
		||||
                SelectableText(
 | 
			
		||||
                  value,
 | 
			
		||||
                  style: Theme.of(context).textTheme.bodyMedium,
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          if (value.startsWith('http') || value.contains('@'))
 | 
			
		||||
            IconButton(
 | 
			
		||||
              icon: const Icon(Icons.copy, size: 16),
 | 
			
		||||
              onPressed: () {
 | 
			
		||||
                Clipboard.setData(ClipboardData(text: value));
 | 
			
		||||
                ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                  const SnackBar(content: Text('Copied to clipboard')),
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
              padding: EdgeInsets.zero,
 | 
			
		||||
              constraints: const BoxConstraints(),
 | 
			
		||||
              tooltip: 'Copy to clipboard',
 | 
			
		||||
            ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildListTile(
 | 
			
		||||
    BuildContext context, {
 | 
			
		||||
    required IconData icon,
 | 
			
		||||
    required String title,
 | 
			
		||||
    String? subtitle,
 | 
			
		||||
    required VoidCallback onTap,
 | 
			
		||||
  }) {
 | 
			
		||||
    return Column(
 | 
			
		||||
      children: [
 | 
			
		||||
        ListTile(
 | 
			
		||||
          leading: Icon(icon),
 | 
			
		||||
          title: Text(title),
 | 
			
		||||
          subtitle: subtitle != null ? Text(subtitle) : null,
 | 
			
		||||
          trailing: const Icon(Icons.chevron_right),
 | 
			
		||||
          onTap: onTap,
 | 
			
		||||
          contentPadding: const EdgeInsets.symmetric(horizontal: 16),
 | 
			
		||||
          minLeadingWidth: 24,
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -281,6 +281,16 @@ class AccountScreen extends HookConsumerWidget {
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
            const Divider(height: 1).padding(vertical: 8),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              minTileHeight: 48,
 | 
			
		||||
              leading: const Icon(Symbols.info),
 | 
			
		||||
              trailing: const Icon(Symbols.chevron_right),
 | 
			
		||||
              contentPadding: EdgeInsets.symmetric(horizontal: 24),
 | 
			
		||||
              title: Text('about').tr(),
 | 
			
		||||
              onTap: () {
 | 
			
		||||
                context.push('/about');
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              minTileHeight: 48,
 | 
			
		||||
              leading: const Icon(Symbols.logout),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										142
									
								
								lib/screens/discovery/articles.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								lib/screens/discovery/articles.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/webfeed.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/widgets/web_article_card.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
 | 
			
		||||
 | 
			
		||||
part 'articles.g.dart';
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
class ArticlesListNotifier extends _$ArticlesListNotifier
 | 
			
		||||
    with CursorPagingNotifierMixin<SnWebArticle> {
 | 
			
		||||
  static const int _pageSize = 20;
 | 
			
		||||
 | 
			
		||||
  Map<String, dynamic> _params = {};
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<CursorPagingData<SnWebArticle>> build({
 | 
			
		||||
    String? feedId,
 | 
			
		||||
    String? publisherId,
 | 
			
		||||
  }) async {
 | 
			
		||||
    _params = {
 | 
			
		||||
      if (feedId != null) 'feedId': feedId,
 | 
			
		||||
      if (publisherId != null) 'publisherId': publisherId,
 | 
			
		||||
    };
 | 
			
		||||
    return fetch(cursor: null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<CursorPagingData<SnWebArticle>> fetch({
 | 
			
		||||
    required String? cursor,
 | 
			
		||||
  }) async {
 | 
			
		||||
    final client = ref.read(apiClientProvider);
 | 
			
		||||
    final offset = cursor == null ? 0 : int.parse(cursor);
 | 
			
		||||
 | 
			
		||||
    final queryParams = {'limit': _pageSize, 'offset': offset, ..._params};
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final response = await client.get(
 | 
			
		||||
        '/feeds/articles',
 | 
			
		||||
        queryParameters: queryParams,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final List<dynamic> data = response.data;
 | 
			
		||||
      final articles =
 | 
			
		||||
          data
 | 
			
		||||
              .map(
 | 
			
		||||
                (json) => SnWebArticle.fromJson(json as Map<String, dynamic>),
 | 
			
		||||
              )
 | 
			
		||||
              .toList();
 | 
			
		||||
 | 
			
		||||
      final total = int.tryParse(response.headers.value('X-Total') ?? '0') ?? 0;
 | 
			
		||||
      final hasMore = offset + articles.length < total;
 | 
			
		||||
      final nextCursor = hasMore ? (offset + articles.length).toString() : null;
 | 
			
		||||
 | 
			
		||||
      return CursorPagingData(
 | 
			
		||||
        items: articles,
 | 
			
		||||
        hasMore: hasMore,
 | 
			
		||||
        nextCursor: nextCursor,
 | 
			
		||||
      );
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      debugPrint('Error fetching articles: $e');
 | 
			
		||||
      rethrow;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SliverArticlesList extends ConsumerWidget {
 | 
			
		||||
  final String? feedId;
 | 
			
		||||
  final String? publisherId;
 | 
			
		||||
  final Color? backgroundColor;
 | 
			
		||||
  final EdgeInsets? padding;
 | 
			
		||||
  final Function? onRefresh;
 | 
			
		||||
 | 
			
		||||
  const SliverArticlesList({
 | 
			
		||||
    super.key,
 | 
			
		||||
    this.feedId,
 | 
			
		||||
    this.publisherId,
 | 
			
		||||
    this.backgroundColor,
 | 
			
		||||
    this.padding,
 | 
			
		||||
    this.onRefresh,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    return PagingHelperSliverView(
 | 
			
		||||
      provider: articlesListNotifierProvider(
 | 
			
		||||
        feedId: feedId,
 | 
			
		||||
        publisherId: publisherId,
 | 
			
		||||
      ),
 | 
			
		||||
      futureRefreshable:
 | 
			
		||||
          articlesListNotifierProvider(
 | 
			
		||||
            feedId: feedId,
 | 
			
		||||
            publisherId: publisherId,
 | 
			
		||||
          ).future,
 | 
			
		||||
      notifierRefreshable:
 | 
			
		||||
          articlesListNotifierProvider(
 | 
			
		||||
            feedId: feedId,
 | 
			
		||||
            publisherId: publisherId,
 | 
			
		||||
          ).notifier,
 | 
			
		||||
      contentBuilder:
 | 
			
		||||
          (data, widgetCount, endItemView) => SliverList.builder(
 | 
			
		||||
            itemCount: widgetCount,
 | 
			
		||||
            itemBuilder: (context, index) {
 | 
			
		||||
              if (index == widgetCount - 1) {
 | 
			
		||||
                return endItemView;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              final article = data.items[index];
 | 
			
		||||
              return WebArticleCard(article: article, showDetails: true);
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ArticlesScreen extends ConsumerWidget {
 | 
			
		||||
  final String? feedId;
 | 
			
		||||
  final String? publisherId;
 | 
			
		||||
  final String? title;
 | 
			
		||||
 | 
			
		||||
  const ArticlesScreen({super.key, this.feedId, this.publisherId, this.title});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(title: Text(title ?? 'Articles')),
 | 
			
		||||
      body: CustomScrollView(
 | 
			
		||||
        slivers: [
 | 
			
		||||
          SliverPadding(
 | 
			
		||||
            padding: const EdgeInsets.only(top: 8, left: 8, right: 8),
 | 
			
		||||
            sliver: SliverArticlesList(
 | 
			
		||||
              feedId: feedId,
 | 
			
		||||
              publisherId: publisherId,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										206
									
								
								lib/screens/discovery/articles.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								lib/screens/discovery/articles.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,206 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'articles.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// RiverpodGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
String _$articlesListNotifierHash() =>
 | 
			
		||||
    r'924f2344c3bbf0ff7b92fe69e88d3b64a534b538';
 | 
			
		||||
 | 
			
		||||
/// Copied from Dart SDK
 | 
			
		||||
class _SystemHash {
 | 
			
		||||
  _SystemHash._();
 | 
			
		||||
 | 
			
		||||
  static int combine(int hash, int value) {
 | 
			
		||||
    // ignore: parameter_assignments
 | 
			
		||||
    hash = 0x1fffffff & (hash + value);
 | 
			
		||||
    // ignore: parameter_assignments
 | 
			
		||||
    hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
 | 
			
		||||
    return hash ^ (hash >> 6);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static int finish(int hash) {
 | 
			
		||||
    // ignore: parameter_assignments
 | 
			
		||||
    hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
 | 
			
		||||
    // ignore: parameter_assignments
 | 
			
		||||
    hash = hash ^ (hash >> 11);
 | 
			
		||||
    return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
abstract class _$ArticlesListNotifier
 | 
			
		||||
    extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnWebArticle>> {
 | 
			
		||||
  late final String? feedId;
 | 
			
		||||
  late final String? publisherId;
 | 
			
		||||
 | 
			
		||||
  FutureOr<CursorPagingData<SnWebArticle>> build({
 | 
			
		||||
    String? feedId,
 | 
			
		||||
    String? publisherId,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [ArticlesListNotifier].
 | 
			
		||||
@ProviderFor(ArticlesListNotifier)
 | 
			
		||||
const articlesListNotifierProvider = ArticlesListNotifierFamily();
 | 
			
		||||
 | 
			
		||||
/// See also [ArticlesListNotifier].
 | 
			
		||||
class ArticlesListNotifierFamily
 | 
			
		||||
    extends Family<AsyncValue<CursorPagingData<SnWebArticle>>> {
 | 
			
		||||
  /// See also [ArticlesListNotifier].
 | 
			
		||||
  const ArticlesListNotifierFamily();
 | 
			
		||||
 | 
			
		||||
  /// See also [ArticlesListNotifier].
 | 
			
		||||
  ArticlesListNotifierProvider call({String? feedId, String? publisherId}) {
 | 
			
		||||
    return ArticlesListNotifierProvider(
 | 
			
		||||
      feedId: feedId,
 | 
			
		||||
      publisherId: publisherId,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ArticlesListNotifierProvider getProviderOverride(
 | 
			
		||||
    covariant ArticlesListNotifierProvider provider,
 | 
			
		||||
  ) {
 | 
			
		||||
    return call(feedId: provider.feedId, publisherId: provider.publisherId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _dependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get dependencies => _dependencies;
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
 | 
			
		||||
      _allTransitiveDependencies;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get name => r'articlesListNotifierProvider';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [ArticlesListNotifier].
 | 
			
		||||
class ArticlesListNotifierProvider
 | 
			
		||||
    extends
 | 
			
		||||
        AutoDisposeAsyncNotifierProviderImpl<
 | 
			
		||||
          ArticlesListNotifier,
 | 
			
		||||
          CursorPagingData<SnWebArticle>
 | 
			
		||||
        > {
 | 
			
		||||
  /// See also [ArticlesListNotifier].
 | 
			
		||||
  ArticlesListNotifierProvider({String? feedId, String? publisherId})
 | 
			
		||||
    : this._internal(
 | 
			
		||||
        () =>
 | 
			
		||||
            ArticlesListNotifier()
 | 
			
		||||
              ..feedId = feedId
 | 
			
		||||
              ..publisherId = publisherId,
 | 
			
		||||
        from: articlesListNotifierProvider,
 | 
			
		||||
        name: r'articlesListNotifierProvider',
 | 
			
		||||
        debugGetCreateSourceHash:
 | 
			
		||||
            const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
                ? null
 | 
			
		||||
                : _$articlesListNotifierHash,
 | 
			
		||||
        dependencies: ArticlesListNotifierFamily._dependencies,
 | 
			
		||||
        allTransitiveDependencies:
 | 
			
		||||
            ArticlesListNotifierFamily._allTransitiveDependencies,
 | 
			
		||||
        feedId: feedId,
 | 
			
		||||
        publisherId: publisherId,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  ArticlesListNotifierProvider._internal(
 | 
			
		||||
    super._createNotifier, {
 | 
			
		||||
    required super.name,
 | 
			
		||||
    required super.dependencies,
 | 
			
		||||
    required super.allTransitiveDependencies,
 | 
			
		||||
    required super.debugGetCreateSourceHash,
 | 
			
		||||
    required super.from,
 | 
			
		||||
    required this.feedId,
 | 
			
		||||
    required this.publisherId,
 | 
			
		||||
  }) : super.internal();
 | 
			
		||||
 | 
			
		||||
  final String? feedId;
 | 
			
		||||
  final String? publisherId;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOr<CursorPagingData<SnWebArticle>> runNotifierBuild(
 | 
			
		||||
    covariant ArticlesListNotifier notifier,
 | 
			
		||||
  ) {
 | 
			
		||||
    return notifier.build(feedId: feedId, publisherId: publisherId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Override overrideWith(ArticlesListNotifier Function() create) {
 | 
			
		||||
    return ProviderOverride(
 | 
			
		||||
      origin: this,
 | 
			
		||||
      override: ArticlesListNotifierProvider._internal(
 | 
			
		||||
        () =>
 | 
			
		||||
            create()
 | 
			
		||||
              ..feedId = feedId
 | 
			
		||||
              ..publisherId = publisherId,
 | 
			
		||||
        from: from,
 | 
			
		||||
        name: null,
 | 
			
		||||
        dependencies: null,
 | 
			
		||||
        allTransitiveDependencies: null,
 | 
			
		||||
        debugGetCreateSourceHash: null,
 | 
			
		||||
        feedId: feedId,
 | 
			
		||||
        publisherId: publisherId,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  AutoDisposeAsyncNotifierProviderElement<
 | 
			
		||||
    ArticlesListNotifier,
 | 
			
		||||
    CursorPagingData<SnWebArticle>
 | 
			
		||||
  >
 | 
			
		||||
  createElement() {
 | 
			
		||||
    return _ArticlesListNotifierProviderElement(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return other is ArticlesListNotifierProvider &&
 | 
			
		||||
        other.feedId == feedId &&
 | 
			
		||||
        other.publisherId == publisherId;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode {
 | 
			
		||||
    var hash = _SystemHash.combine(0, runtimeType.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, feedId.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, publisherId.hashCode);
 | 
			
		||||
 | 
			
		||||
    return _SystemHash.finish(hash);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
mixin ArticlesListNotifierRef
 | 
			
		||||
    on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnWebArticle>> {
 | 
			
		||||
  /// The parameter `feedId` of this provider.
 | 
			
		||||
  String? get feedId;
 | 
			
		||||
 | 
			
		||||
  /// The parameter `publisherId` of this provider.
 | 
			
		||||
  String? get publisherId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ArticlesListNotifierProviderElement
 | 
			
		||||
    extends
 | 
			
		||||
        AutoDisposeAsyncNotifierProviderElement<
 | 
			
		||||
          ArticlesListNotifier,
 | 
			
		||||
          CursorPagingData<SnWebArticle>
 | 
			
		||||
        >
 | 
			
		||||
    with ArticlesListNotifierRef {
 | 
			
		||||
  _ArticlesListNotifierProviderElement(super.provider);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get feedId => (origin as ArticlesListNotifierProvider).feedId;
 | 
			
		||||
  @override
 | 
			
		||||
  String? get publisherId =>
 | 
			
		||||
      (origin as ArticlesListNotifierProvider).publisherId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ignore_for_file: type=lint
 | 
			
		||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
 | 
			
		||||
@@ -93,32 +93,39 @@ class ExploreScreen extends HookConsumerWidget {
 | 
			
		||||
      extendBody: false, // Prevent conflicts with tabs navigation
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        toolbarHeight: 0,
 | 
			
		||||
        bottom: TabBar(
 | 
			
		||||
        bottom: PreferredSize(
 | 
			
		||||
          preferredSize: const Size.fromHeight(48),
 | 
			
		||||
          child: Row(
 | 
			
		||||
            children: [
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: TabBar(
 | 
			
		||||
                  controller: tabController,
 | 
			
		||||
                  tabAlignment: TabAlignment.start,
 | 
			
		||||
                  isScrollable: true,
 | 
			
		||||
                  tabs: [
 | 
			
		||||
                    Tab(
 | 
			
		||||
              child: Text(
 | 
			
		||||
                'explore'.tr(),
 | 
			
		||||
                textAlign: TextAlign.center,
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
                      icon: Tooltip(
 | 
			
		||||
                        message: 'explore'.tr(),
 | 
			
		||||
                        child: Icon(
 | 
			
		||||
                          Symbols.explore,
 | 
			
		||||
                          color: Theme.of(context).appBarTheme.foregroundColor!,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Tab(
 | 
			
		||||
              child: Text(
 | 
			
		||||
                'exploreFilterSubscriptions'.tr(),
 | 
			
		||||
                textAlign: TextAlign.center,
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
                      icon: Tooltip(
 | 
			
		||||
                        message: 'exploreFilterSubscriptions'.tr(),
 | 
			
		||||
                        child: Icon(
 | 
			
		||||
                          Symbols.subscriptions,
 | 
			
		||||
                          color: Theme.of(context).appBarTheme.foregroundColor!,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Tab(
 | 
			
		||||
              child: Text(
 | 
			
		||||
                'exploreFilterFriends'.tr(),
 | 
			
		||||
                textAlign: TextAlign.center,
 | 
			
		||||
                style: TextStyle(
 | 
			
		||||
                      icon: Tooltip(
 | 
			
		||||
                        message: 'exploreFilterFriends'.tr(),
 | 
			
		||||
                        child: Icon(
 | 
			
		||||
                          Symbols.people,
 | 
			
		||||
                          color: Theme.of(context).appBarTheme.foregroundColor!,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
@@ -126,6 +133,31 @@ class ExploreScreen extends HookConsumerWidget {
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Spacer(),
 | 
			
		||||
              IconButton(
 | 
			
		||||
                onPressed: () {
 | 
			
		||||
                  context.push('/feeds/articles');
 | 
			
		||||
                },
 | 
			
		||||
                icon: Icon(
 | 
			
		||||
                  Symbols.auto_stories,
 | 
			
		||||
                  color: Theme.of(context).appBarTheme.foregroundColor!,
 | 
			
		||||
                ),
 | 
			
		||||
                tooltip: 'webArticlesStand'.tr(),
 | 
			
		||||
              ),
 | 
			
		||||
              IconButton(
 | 
			
		||||
                onPressed: () {
 | 
			
		||||
                  context.push('/posts/search');
 | 
			
		||||
                },
 | 
			
		||||
                icon: Icon(
 | 
			
		||||
                  Symbols.search,
 | 
			
		||||
                  color: Theme.of(context).appBarTheme.foregroundColor!,
 | 
			
		||||
                ),
 | 
			
		||||
                tooltip: 'search'.tr(),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ).padding(horizontal: 8),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
      floatingActionButton: FloatingActionButton(
 | 
			
		||||
        heroTag: Key("explore-page-fab"),
 | 
			
		||||
        onPressed: () {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										165
									
								
								lib/screens/posts/post_search.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								lib/screens/posts/post_search.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/widgets/post/post_item.dart';
 | 
			
		||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
 | 
			
		||||
 | 
			
		||||
final postSearchNotifierProvider = StateNotifierProvider.autoDispose<
 | 
			
		||||
  PostSearchNotifier,
 | 
			
		||||
  AsyncValue<CursorPagingData<SnPost>>
 | 
			
		||||
>((ref) => PostSearchNotifier(ref));
 | 
			
		||||
 | 
			
		||||
class PostSearchNotifier
 | 
			
		||||
    extends StateNotifier<AsyncValue<CursorPagingData<SnPost>>> {
 | 
			
		||||
  final AutoDisposeRef ref;
 | 
			
		||||
  static const int _pageSize = 20;
 | 
			
		||||
  String _currentQuery = '';
 | 
			
		||||
  bool _isLoading = false;
 | 
			
		||||
 | 
			
		||||
  PostSearchNotifier(this.ref) : super(const AsyncValue.loading()) {
 | 
			
		||||
    state = const AsyncValue.data(
 | 
			
		||||
      CursorPagingData(items: [], hasMore: false, nextCursor: null),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> search(String query) async {
 | 
			
		||||
    if (_isLoading) return;
 | 
			
		||||
 | 
			
		||||
    _currentQuery = query.trim();
 | 
			
		||||
    if (_currentQuery.isEmpty) {
 | 
			
		||||
      state = AsyncValue.data(
 | 
			
		||||
        CursorPagingData(items: [], hasMore: false, nextCursor: null),
 | 
			
		||||
      );
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await fetch(cursor: null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> fetch({String? cursor}) async {
 | 
			
		||||
    if (_isLoading) return;
 | 
			
		||||
 | 
			
		||||
    _isLoading = true;
 | 
			
		||||
    state = const AsyncValue.loading();
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final client = ref.read(apiClientProvider);
 | 
			
		||||
      final offset = cursor == null ? 0 : int.parse(cursor);
 | 
			
		||||
 | 
			
		||||
      final response = await client.get(
 | 
			
		||||
        '/posts/search',
 | 
			
		||||
        queryParameters: {
 | 
			
		||||
          'query': _currentQuery,
 | 
			
		||||
          'offset': offset,
 | 
			
		||||
          'take': _pageSize,
 | 
			
		||||
          'useVector': true,
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final data = response.data as List;
 | 
			
		||||
      final posts = data.map((json) => SnPost.fromJson(json)).toList();
 | 
			
		||||
      final hasMore = posts.length == _pageSize;
 | 
			
		||||
      final nextCursor = hasMore ? (offset + posts.length).toString() : null;
 | 
			
		||||
 | 
			
		||||
      state = AsyncValue.data(
 | 
			
		||||
        CursorPagingData(
 | 
			
		||||
          items: posts,
 | 
			
		||||
          hasMore: hasMore,
 | 
			
		||||
          nextCursor: nextCursor,
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    } catch (e, stack) {
 | 
			
		||||
      state = AsyncValue.error(e, stack);
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isLoading = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PostSearchScreen extends ConsumerStatefulWidget {
 | 
			
		||||
  const PostSearchScreen({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ConsumerState<PostSearchScreen> createState() => _PostSearchScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PostSearchScreenState extends ConsumerState<PostSearchScreen> {
 | 
			
		||||
  final _searchController = TextEditingController();
 | 
			
		||||
  final _debounce = Duration(milliseconds: 500);
 | 
			
		||||
  Timer? _debounceTimer;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    _searchController.dispose();
 | 
			
		||||
    _debounceTimer?.cancel();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _onSearchChanged(String query) {
 | 
			
		||||
    if (_debounceTimer?.isActive ?? false) _debounceTimer!.cancel();
 | 
			
		||||
 | 
			
		||||
    _debounceTimer = Timer(_debounce, () {
 | 
			
		||||
      ref.read(postSearchNotifierProvider.notifier).search(query);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: TextField(
 | 
			
		||||
          controller: _searchController,
 | 
			
		||||
          decoration: InputDecoration(
 | 
			
		||||
            hintText: 'Search posts...',
 | 
			
		||||
            border: InputBorder.none,
 | 
			
		||||
            hintStyle: TextStyle(
 | 
			
		||||
              color: Theme.of(context).appBarTheme.foregroundColor,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          style: TextStyle(
 | 
			
		||||
            color: Theme.of(context).appBarTheme.foregroundColor,
 | 
			
		||||
          ),
 | 
			
		||||
          onChanged: _onSearchChanged,
 | 
			
		||||
          onSubmitted: (value) {
 | 
			
		||||
            ref.read(postSearchNotifierProvider.notifier).search(value);
 | 
			
		||||
          },
 | 
			
		||||
          autofocus: true,
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
      body: Consumer(
 | 
			
		||||
        builder: (context, ref, child) {
 | 
			
		||||
          final searchState = ref.watch(postSearchNotifierProvider);
 | 
			
		||||
 | 
			
		||||
          return searchState.when(
 | 
			
		||||
            data: (data) {
 | 
			
		||||
              if (data.items.isEmpty && _searchController.text.isNotEmpty) {
 | 
			
		||||
                return const Center(child: Text('No results found'));
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              return ListView.builder(
 | 
			
		||||
                itemCount: data.items.length + (data.hasMore ? 1 : 0),
 | 
			
		||||
                itemBuilder: (context, index) {
 | 
			
		||||
                  if (index >= data.items.length) {
 | 
			
		||||
                    ref
 | 
			
		||||
                        .read(postSearchNotifierProvider.notifier)
 | 
			
		||||
                        .fetch(cursor: data.nextCursor);
 | 
			
		||||
                    return const Center(child: CircularProgressIndicator());
 | 
			
		||||
                  }
 | 
			
		||||
 | 
			
		||||
                  final post = data.items[index];
 | 
			
		||||
                  return Column(
 | 
			
		||||
                    children: [PostItem(item: post), const Divider(height: 1)],
 | 
			
		||||
                  );
 | 
			
		||||
                },
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
            loading: () => const Center(child: CircularProgressIndicator()),
 | 
			
		||||
            error: (error, stack) => Center(child: Text('Error: $error')),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,15 +2,22 @@ import 'package:cached_network_image/cached_network_image.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:island/models/webfeed.dart';
 | 
			
		||||
import 'package:island/services/time.dart';
 | 
			
		||||
 | 
			
		||||
class WebArticleCard extends StatelessWidget {
 | 
			
		||||
  final SnWebArticle article;
 | 
			
		||||
  final double? maxWidth;
 | 
			
		||||
  final bool showDetails;
 | 
			
		||||
 | 
			
		||||
  const WebArticleCard({super.key, required this.article, this.maxWidth});
 | 
			
		||||
  const WebArticleCard({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.article,
 | 
			
		||||
    this.maxWidth,
 | 
			
		||||
    this.showDetails = false,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  void _onTap(BuildContext context) {
 | 
			
		||||
    context.push('/articles/${article.id}');
 | 
			
		||||
    context.push('/feeds/articles/${article.id}');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@@ -74,6 +81,10 @@ class WebArticleCard extends StatelessWidget {
 | 
			
		||||
                      mainAxisAlignment: MainAxisAlignment.end,
 | 
			
		||||
                      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                      children: [
 | 
			
		||||
                        if (showDetails)
 | 
			
		||||
                          const SizedBox(height: 8)
 | 
			
		||||
                        else
 | 
			
		||||
                          Spacer(),
 | 
			
		||||
                        Text(
 | 
			
		||||
                          article.title,
 | 
			
		||||
                          style: theme.textTheme.titleSmall?.copyWith(
 | 
			
		||||
@@ -81,10 +92,32 @@ class WebArticleCard extends StatelessWidget {
 | 
			
		||||
                            fontWeight: FontWeight.bold,
 | 
			
		||||
                            height: 1.3,
 | 
			
		||||
                          ),
 | 
			
		||||
                          maxLines: 2,
 | 
			
		||||
                          maxLines: showDetails ? 3 : 2,
 | 
			
		||||
                          overflow: TextOverflow.ellipsis,
 | 
			
		||||
                        ),
 | 
			
		||||
                        if (showDetails &&
 | 
			
		||||
                            article.author?.isNotEmpty == true) ...[
 | 
			
		||||
                          const SizedBox(height: 4),
 | 
			
		||||
                          Text(
 | 
			
		||||
                            article.author!,
 | 
			
		||||
                            style: TextStyle(
 | 
			
		||||
                              fontSize: 10,
 | 
			
		||||
                              color: Colors.white.withOpacity(0.9),
 | 
			
		||||
                              fontWeight: FontWeight.w500,
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                        if (showDetails) const Spacer(),
 | 
			
		||||
                        if (showDetails && article.publishedAt != null) ...[
 | 
			
		||||
                          Text(
 | 
			
		||||
                            '${article.publishedAt!.formatSystem()} · ${article.publishedAt!.formatRelative(context)}',
 | 
			
		||||
                            style: const TextStyle(
 | 
			
		||||
                              fontSize: 9,
 | 
			
		||||
                              color: Colors.white70,
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 2),
 | 
			
		||||
                        ],
 | 
			
		||||
                        Text(
 | 
			
		||||
                          article.feed?.title ?? 'Unknown Source',
 | 
			
		||||
                          style: const TextStyle(
 | 
			
		||||
 
 | 
			
		||||
@@ -11,32 +11,32 @@ PODS:
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - file_selector_macos (0.0.1):
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - Firebase/CoreOnly (11.13.0):
 | 
			
		||||
    - FirebaseCore (~> 11.13.0)
 | 
			
		||||
  - Firebase/Messaging (11.13.0):
 | 
			
		||||
  - Firebase/CoreOnly (11.15.0):
 | 
			
		||||
    - FirebaseCore (~> 11.15.0)
 | 
			
		||||
  - Firebase/Messaging (11.15.0):
 | 
			
		||||
    - Firebase/CoreOnly
 | 
			
		||||
    - FirebaseMessaging (~> 11.13.0)
 | 
			
		||||
  - firebase_core (3.14.0):
 | 
			
		||||
    - Firebase/CoreOnly (~> 11.13.0)
 | 
			
		||||
    - FirebaseMessaging (~> 11.15.0)
 | 
			
		||||
  - firebase_core (3.15.0):
 | 
			
		||||
    - Firebase/CoreOnly (~> 11.15.0)
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - firebase_messaging (15.2.7):
 | 
			
		||||
    - Firebase/CoreOnly (~> 11.13.0)
 | 
			
		||||
    - Firebase/Messaging (~> 11.13.0)
 | 
			
		||||
  - firebase_messaging (15.2.8):
 | 
			
		||||
    - Firebase/CoreOnly (~> 11.15.0)
 | 
			
		||||
    - Firebase/Messaging (~> 11.15.0)
 | 
			
		||||
    - firebase_core
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - FirebaseCore (11.13.0):
 | 
			
		||||
    - FirebaseCoreInternal (~> 11.13.0)
 | 
			
		||||
  - FirebaseCore (11.15.0):
 | 
			
		||||
    - FirebaseCoreInternal (~> 11.15.0)
 | 
			
		||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
			
		||||
    - GoogleUtilities/Logger (~> 8.1)
 | 
			
		||||
  - FirebaseCoreInternal (11.13.0):
 | 
			
		||||
  - FirebaseCoreInternal (11.15.0):
 | 
			
		||||
    - "GoogleUtilities/NSData+zlib (~> 8.1)"
 | 
			
		||||
  - FirebaseInstallations (11.13.0):
 | 
			
		||||
    - FirebaseCore (~> 11.13.0)
 | 
			
		||||
  - FirebaseInstallations (11.15.0):
 | 
			
		||||
    - FirebaseCore (~> 11.15.0)
 | 
			
		||||
    - GoogleUtilities/Environment (~> 8.1)
 | 
			
		||||
    - GoogleUtilities/UserDefaults (~> 8.1)
 | 
			
		||||
    - PromisesObjC (~> 2.4)
 | 
			
		||||
  - FirebaseMessaging (11.13.0):
 | 
			
		||||
    - FirebaseCore (~> 11.13.0)
 | 
			
		||||
  - FirebaseMessaging (11.15.0):
 | 
			
		||||
    - FirebaseCore (~> 11.15.0)
 | 
			
		||||
    - FirebaseInstallations (~> 11.0)
 | 
			
		||||
    - GoogleDataTransport (~> 10.0)
 | 
			
		||||
    - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
 | 
			
		||||
@@ -92,7 +92,7 @@ PODS:
 | 
			
		||||
    - GoogleUtilities/Privacy
 | 
			
		||||
  - irondash_engine_context (0.0.1):
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - livekit_client (2.4.8):
 | 
			
		||||
  - livekit_client (2.4.9):
 | 
			
		||||
    - flutter_webrtc
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
    - WebRTC-SDK (= 125.6422.07)
 | 
			
		||||
@@ -291,13 +291,13 @@ SPEC CHECKSUMS:
 | 
			
		||||
  device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
 | 
			
		||||
  file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
 | 
			
		||||
  file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
 | 
			
		||||
  Firebase: 3435bc66b4d494c2f22c79fd3aae4c1db6662327
 | 
			
		||||
  firebase_core: 1095fcf33161d99bc34aa10f7c0d89414a208d15
 | 
			
		||||
  firebase_messaging: 6417056ffb85141607618ddfef9fec9f3caab3ea
 | 
			
		||||
  FirebaseCore: c692c7f1c75305ab6aff2b367f25e11d73aa8bd0
 | 
			
		||||
  FirebaseCoreInternal: 29d7b3af4aaf0b8f3ed20b568c13df399b06f68c
 | 
			
		||||
  FirebaseInstallations: 0ee9074f2c1e86561ace168ee1470dc67aabaf02
 | 
			
		||||
  FirebaseMessaging: 195bbdb73e6ca1dbc76cd46e73f3552c084ef6e4
 | 
			
		||||
  Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
 | 
			
		||||
  firebase_core: 177f51be1650b15d2d5b9f1abf48792619288070
 | 
			
		||||
  firebase_messaging: 8748a5d4bb435993cffa7f5501292f3e914a23d7
 | 
			
		||||
  FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
 | 
			
		||||
  FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
 | 
			
		||||
  FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
 | 
			
		||||
  FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
 | 
			
		||||
  flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
 | 
			
		||||
  flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
 | 
			
		||||
  flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54
 | 
			
		||||
@@ -309,7 +309,7 @@ SPEC CHECKSUMS:
 | 
			
		||||
  GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
 | 
			
		||||
  GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
 | 
			
		||||
  irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
 | 
			
		||||
  livekit_client: 6a35243df3da61750c98e266e02dedcf5d25c888
 | 
			
		||||
  livekit_client: c9d9f41996f5cf22b9ba0e8483e6af4ca5094059
 | 
			
		||||
  local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
 | 
			
		||||
  media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65
 | 
			
		||||
  media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								pubspec.lock
									
									
									
									
									
								
							@@ -13,10 +13,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: _flutterfire_internals
 | 
			
		||||
      sha256: dda4fd7909a732a014239009aa52537b136f8ce568de23c212587097887e2307
 | 
			
		||||
      sha256: "50e24b769bd1e725732f0aff18b806b8731c1fbcf4e8018ab98e7c4805a2a52f"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.3.56"
 | 
			
		||||
    version: "1.3.57"
 | 
			
		||||
  analyzer:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -629,50 +629,50 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_core
 | 
			
		||||
      sha256: "420d9111dcf095341f1ea8fdce926eef750cf7b9745d21f38000de780c94f608"
 | 
			
		||||
      sha256: "5bba5924139e91d26446fd2601c18a6aa62c1161c768a989bb5e245dcdc20644"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.14.0"
 | 
			
		||||
    version: "3.15.0"
 | 
			
		||||
  firebase_core_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_core_platform_interface
 | 
			
		||||
      sha256: d7253d255ff10f85cfd2adaba9ac17bae878fa3ba577462451163bd9f1d1f0bf
 | 
			
		||||
      sha256: "5d2ab45779d91af2aa0252dec9fe4ee1caa015d83377de255454dcaa1526a0e0"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "5.4.0"
 | 
			
		||||
    version: "5.4.1"
 | 
			
		||||
  firebase_core_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_core_web
 | 
			
		||||
      sha256: ddd72baa6f727e5b23f32d9af23d7d453d67946f380bd9c21daf474ee0f7326e
 | 
			
		||||
      sha256: eb3afccfc452b2b2075acbe0c4b27de62dd596802b4e5e19869c1e926cbb20b3
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.23.0"
 | 
			
		||||
    version: "2.24.0"
 | 
			
		||||
  firebase_messaging:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_messaging
 | 
			
		||||
      sha256: "758461f67b96aa5ad27625aaae39882fd6d1961b1c7e005301f9a74b6336100b"
 | 
			
		||||
      sha256: c6711cf2f455532b84a94022c7aaf85088849763af2f01b775ca79d82d10a01a
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "15.2.7"
 | 
			
		||||
    version: "15.2.8"
 | 
			
		||||
  firebase_messaging_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_messaging_platform_interface
 | 
			
		||||
      sha256: "614db1b0df0f53e541e41cc182b6d7ede5763c400f6ba232a5f8d0e1b5e5de32"
 | 
			
		||||
      sha256: "1c9dacccb1aee1bf17ba519dda5563a16fdd2ec1e79b5f2e421cb4bf75a166f7"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.6.7"
 | 
			
		||||
    version: "4.6.8"
 | 
			
		||||
  firebase_messaging_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_messaging_web
 | 
			
		||||
      sha256: b5fbbcdd3e0e7f3fde72b0c119410f22737638fed5fc428b54bba06bc1455d81
 | 
			
		||||
      sha256: "54317c26fa92f0d90a2017977ac791cb0504eca29fcf397f06adf727d4a7a2d5"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.10.7"
 | 
			
		||||
    version: "3.10.8"
 | 
			
		||||
  fixnum:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -798,6 +798,54 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.6.0"
 | 
			
		||||
  flutter_keyboard_visibility:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_keyboard_visibility
 | 
			
		||||
      sha256: "98664be7be0e3ffca00de50f7f6a287ab62c763fc8c762e0a21584584a3ff4f8"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "6.0.0"
 | 
			
		||||
  flutter_keyboard_visibility_linux:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_keyboard_visibility_linux
 | 
			
		||||
      sha256: "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.0"
 | 
			
		||||
  flutter_keyboard_visibility_macos:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_keyboard_visibility_macos
 | 
			
		||||
      sha256: c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.0"
 | 
			
		||||
  flutter_keyboard_visibility_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_keyboard_visibility_platform_interface
 | 
			
		||||
      sha256: e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.0.0"
 | 
			
		||||
  flutter_keyboard_visibility_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_keyboard_visibility_web
 | 
			
		||||
      sha256: d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.0.0"
 | 
			
		||||
  flutter_keyboard_visibility_windows:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_keyboard_visibility_windows
 | 
			
		||||
      sha256: fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.0"
 | 
			
		||||
  flutter_launcher_icons:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -960,6 +1008,14 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.1.1"
 | 
			
		||||
  flutter_typeahead:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_typeahead
 | 
			
		||||
      sha256: d64712c65db240b1057559b952398ebb6e498077baeebf9b0731dade62438a6d
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "5.2.0"
 | 
			
		||||
  flutter_udid:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -977,10 +1033,10 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_webrtc
 | 
			
		||||
      sha256: dd47ca103b5b6217771e6277882674276d9621bbf9eb23da3c03898b507844e3
 | 
			
		||||
      sha256: "792aa1e5838a719f29ae52c0773dbb5dd781fc33b1bf87c321b274e55ab51ad1"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.14.1"
 | 
			
		||||
    version: "0.14.2"
 | 
			
		||||
  font_awesome_flutter:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1305,10 +1361,10 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: livekit_client
 | 
			
		||||
      sha256: c270720a49b935591960c6f3296fd8f00c09b45a70cd64aef78cd0a8f8257913
 | 
			
		||||
      sha256: "5d182f40cc9aafce60a9acf936bad8bc69010b5cbf0a949f6f27dc4390f2fcce"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.4.8"
 | 
			
		||||
    version: "2.4.9"
 | 
			
		||||
  local_auth:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1685,6 +1741,38 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.8"
 | 
			
		||||
  pointer_interceptor:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: pointer_interceptor
 | 
			
		||||
      sha256: "57210410680379aea8b1b7ed6ae0c3ad349bfd56fe845b8ea934a53344b9d523"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.10.1+2"
 | 
			
		||||
  pointer_interceptor_ios:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: pointer_interceptor_ios
 | 
			
		||||
      sha256: a6906772b3205b42c44614fcea28f818b1e5fdad73a4ca742a7bd49818d9c917
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.10.1"
 | 
			
		||||
  pointer_interceptor_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: pointer_interceptor_platform_interface
 | 
			
		||||
      sha256: "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.10.0+1"
 | 
			
		||||
  pointer_interceptor_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: pointer_interceptor_web
 | 
			
		||||
      sha256: "460b600e71de6fcea2b3d5f662c92293c049c4319e27f0829310e5a953b3ee2a"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.10.3"
 | 
			
		||||
  pool:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1697,10 +1785,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: posix
 | 
			
		||||
      sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62
 | 
			
		||||
      sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "6.0.2"
 | 
			
		||||
    version: "6.0.3"
 | 
			
		||||
  protobuf:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1817,10 +1905,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: record_web
 | 
			
		||||
      sha256: "024c81eb7f51468b1833a3eca8b461c7ca25c04899dba37abe580bb57afd32e4"
 | 
			
		||||
      sha256: a12856d0b3dd03d336b4b10d7520a8b3e21649a06a8f95815318feaa8f07adbb
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.1.8"
 | 
			
		||||
    version: "1.1.9"
 | 
			
		||||
  record_windows:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
 | 
			
		||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 | 
			
		||||
# In Windows, build-name is used as the major, minor, and patch parts
 | 
			
		||||
# of the product and file versions while build-number is used as the build suffix.
 | 
			
		||||
version: 3.0.0+109
 | 
			
		||||
version: 3.0.0+110
 | 
			
		||||
 | 
			
		||||
environment:
 | 
			
		||||
  sdk: ^3.7.2
 | 
			
		||||
@@ -37,7 +37,7 @@ dependencies:
 | 
			
		||||
  flutter_hooks: ^0.21.2
 | 
			
		||||
  hooks_riverpod: ^2.6.1
 | 
			
		||||
  bitsdojo_window: ^0.1.6
 | 
			
		||||
  go_router: ^15.2.4
 | 
			
		||||
  go_router: ^15.1.3
 | 
			
		||||
  styled_widget: ^0.4.1
 | 
			
		||||
  shared_preferences: ^2.5.3
 | 
			
		||||
  flutter_riverpod: ^2.6.1
 | 
			
		||||
@@ -128,6 +128,7 @@ dependencies:
 | 
			
		||||
      ref: fixes/allow-controller-re-registration
 | 
			
		||||
  mime: ^2.0.0
 | 
			
		||||
  html2md: ^1.3.2
 | 
			
		||||
  flutter_typeahead: ^5.2.0
 | 
			
		||||
 | 
			
		||||
dev_dependencies:
 | 
			
		||||
  flutter_test:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user