diff --git a/lib/screens/post/post_publisher.dart b/lib/screens/post/post_publisher.dart index 745e044..f9379e7 100644 --- a/lib/screens/post/post_publisher.dart +++ b/lib/screens/post/post_publisher.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; +import 'dart:developer'; import 'dart:ui'; import 'package:easy_localization/easy_localization.dart'; @@ -51,6 +53,63 @@ class _PostPublisherScreenState extends State { } } + bool _isSubscribing = false; + SnSubscription? _subscription; + + Future _fetchSubscription() async { + try { + setState(() => _isSubscribing = true); + + final sn = context.read(); + final resp = await sn.client.get( + '/cgi/co/subscriptions/users/${_publisher!.id}', + ); + if (!mounted) return; + _subscription = SnSubscription.fromJson(resp.data); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isSubscribing = false); + } + } + + Future _toggleSubscription() async { + if (_subscription == null) { + try { + setState(() => _isSubscribing = true); + + final sn = context.read(); + final resp = await sn.client.post( + '/cgi/co/subscriptions/users/${_publisher!.id}', + ); + if (!mounted) return; + _subscription = SnSubscription.fromJson(resp.data); + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isSubscribing = false); + } + } else { + try { + setState(() => _isSubscribing = true); + + final sn = context.read(); + await sn.client.delete( + '/cgi/co/subscriptions/users/${_publisher!.id}', + ); + if (!mounted) return; + _subscription = null; + } catch (err) { + if (!mounted) return; + context.showErrorDialog(err); + } finally { + setState(() => _isSubscribing = false); + } + } + } + double _appBarBlur = 0.0; late final _appBarWidth = MediaQuery.of(context).size.width; @@ -95,6 +154,7 @@ class _PostPublisherScreenState extends State { super.initState(); _fetchPublisher().then((_) { _fetchPosts(); + _fetchSubscription(); }); _scrollController.addListener(_updateAppBarBlur); } @@ -233,12 +293,28 @@ class _PostPublisherScreenState extends State { ], ), ), - ElevatedButton( - style: ButtonStyle( - elevation: WidgetStatePropertyAll(0)), - onPressed: () {}, - child: Text('subscribe').tr(), - ), + if (_subscription == null) + ElevatedButton.icon( + style: ButtonStyle( + elevation: WidgetStatePropertyAll(0), + ), + onPressed: _isSubscribing + ? null + : _toggleSubscription, + label: Text('subscribe').tr(), + icon: const Icon(Symbols.add), + ) + else + OutlinedButton.icon( + style: ButtonStyle( + elevation: WidgetStatePropertyAll(0), + ), + onPressed: _isSubscribing + ? null + : _toggleSubscription, + label: Text('unsubscribe').tr(), + icon: const Icon(Symbols.remove), + ), ], ).padding(right: 8), const Gap(12), diff --git a/lib/types/post.dart b/lib/types/post.dart index 7555b07..250c74b 100644 --- a/lib/types/post.dart +++ b/lib/types/post.dart @@ -125,3 +125,18 @@ class SnPublisher with _$SnPublisher { factory SnPublisher.fromJson(Map json) => _$SnPublisherFromJson(json); } + +@freezed +class SnSubscription with _$SnSubscription { + const factory SnSubscription({ + required int id, + required DateTime createdAt, + required DateTime updatedAt, + required DateTime? deletedAt, + required int followerId, + required int accountId, + }) = _SnSubscription; + + factory SnSubscription.fromJson(Map json) => + _$SnSubscriptionFromJson(json); +} diff --git a/lib/types/post.freezed.dart b/lib/types/post.freezed.dart index 49869c5..6aa7349 100644 --- a/lib/types/post.freezed.dart +++ b/lib/types/post.freezed.dart @@ -2326,3 +2326,263 @@ abstract class _SnPublisher implements SnPublisher { _$$SnPublisherImplCopyWith<_$SnPublisherImpl> get copyWith => throw _privateConstructorUsedError; } + +SnSubscription _$SnSubscriptionFromJson(Map json) { + return _SnSubscription.fromJson(json); +} + +/// @nodoc +mixin _$SnSubscription { + int get id => throw _privateConstructorUsedError; + DateTime get createdAt => throw _privateConstructorUsedError; + DateTime get updatedAt => throw _privateConstructorUsedError; + DateTime? get deletedAt => throw _privateConstructorUsedError; + int get followerId => throw _privateConstructorUsedError; + int get accountId => throw _privateConstructorUsedError; + + /// Serializes this SnSubscription to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of SnSubscription + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SnSubscriptionCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SnSubscriptionCopyWith<$Res> { + factory $SnSubscriptionCopyWith( + SnSubscription value, $Res Function(SnSubscription) then) = + _$SnSubscriptionCopyWithImpl<$Res, SnSubscription>; + @useResult + $Res call( + {int id, + DateTime createdAt, + DateTime updatedAt, + DateTime? deletedAt, + int followerId, + int accountId}); +} + +/// @nodoc +class _$SnSubscriptionCopyWithImpl<$Res, $Val extends SnSubscription> + implements $SnSubscriptionCopyWith<$Res> { + _$SnSubscriptionCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SnSubscription + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? deletedAt = freezed, + Object? followerId = null, + Object? accountId = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + deletedAt: freezed == deletedAt + ? _value.deletedAt + : deletedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + followerId: null == followerId + ? _value.followerId + : followerId // ignore: cast_nullable_to_non_nullable + as int, + accountId: null == accountId + ? _value.accountId + : accountId // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$SnSubscriptionImplCopyWith<$Res> + implements $SnSubscriptionCopyWith<$Res> { + factory _$$SnSubscriptionImplCopyWith(_$SnSubscriptionImpl value, + $Res Function(_$SnSubscriptionImpl) then) = + __$$SnSubscriptionImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + DateTime createdAt, + DateTime updatedAt, + DateTime? deletedAt, + int followerId, + int accountId}); +} + +/// @nodoc +class __$$SnSubscriptionImplCopyWithImpl<$Res> + extends _$SnSubscriptionCopyWithImpl<$Res, _$SnSubscriptionImpl> + implements _$$SnSubscriptionImplCopyWith<$Res> { + __$$SnSubscriptionImplCopyWithImpl( + _$SnSubscriptionImpl _value, $Res Function(_$SnSubscriptionImpl) _then) + : super(_value, _then); + + /// Create a copy of SnSubscription + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? deletedAt = freezed, + Object? followerId = null, + Object? accountId = null, + }) { + return _then(_$SnSubscriptionImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + deletedAt: freezed == deletedAt + ? _value.deletedAt + : deletedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + followerId: null == followerId + ? _value.followerId + : followerId // ignore: cast_nullable_to_non_nullable + as int, + accountId: null == accountId + ? _value.accountId + : accountId // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SnSubscriptionImpl implements _SnSubscription { + const _$SnSubscriptionImpl( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.deletedAt, + required this.followerId, + required this.accountId}); + + factory _$SnSubscriptionImpl.fromJson(Map json) => + _$$SnSubscriptionImplFromJson(json); + + @override + final int id; + @override + final DateTime createdAt; + @override + final DateTime updatedAt; + @override + final DateTime? deletedAt; + @override + final int followerId; + @override + final int accountId; + + @override + String toString() { + return 'SnSubscription(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, followerId: $followerId, accountId: $accountId)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SnSubscriptionImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt) && + (identical(other.deletedAt, deletedAt) || + other.deletedAt == deletedAt) && + (identical(other.followerId, followerId) || + other.followerId == followerId) && + (identical(other.accountId, accountId) || + other.accountId == accountId)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, id, createdAt, updatedAt, deletedAt, followerId, accountId); + + /// Create a copy of SnSubscription + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SnSubscriptionImplCopyWith<_$SnSubscriptionImpl> get copyWith => + __$$SnSubscriptionImplCopyWithImpl<_$SnSubscriptionImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$SnSubscriptionImplToJson( + this, + ); + } +} + +abstract class _SnSubscription implements SnSubscription { + const factory _SnSubscription( + {required final int id, + required final DateTime createdAt, + required final DateTime updatedAt, + required final DateTime? deletedAt, + required final int followerId, + required final int accountId}) = _$SnSubscriptionImpl; + + factory _SnSubscription.fromJson(Map json) = + _$SnSubscriptionImpl.fromJson; + + @override + int get id; + @override + DateTime get createdAt; + @override + DateTime get updatedAt; + @override + DateTime? get deletedAt; + @override + int get followerId; + @override + int get accountId; + + /// Create a copy of SnSubscription + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SnSubscriptionImplCopyWith<_$SnSubscriptionImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/types/post.g.dart b/lib/types/post.g.dart index 41b665a..943e4b2 100644 --- a/lib/types/post.g.dart +++ b/lib/types/post.g.dart @@ -218,3 +218,26 @@ Map _$$SnPublisherImplToJson(_$SnPublisherImpl instance) => 'realm_id': instance.realmId, 'account_id': instance.accountId, }; + +_$SnSubscriptionImpl _$$SnSubscriptionImplFromJson(Map json) => + _$SnSubscriptionImpl( + id: (json['id'] as num).toInt(), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + deletedAt: json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), + followerId: (json['follower_id'] as num).toInt(), + accountId: (json['account_id'] as num).toInt(), + ); + +Map _$$SnSubscriptionImplToJson( + _$SnSubscriptionImpl instance) => + { + 'id': instance.id, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'deleted_at': instance.deletedAt?.toIso8601String(), + 'follower_id': instance.followerId, + 'account_id': instance.accountId, + };