✨ Subscriptions
This commit is contained in:
@ -21,6 +21,15 @@ Future<SnAccount> account(Ref ref, String uname) async {
|
||||
return SnAccount.fromJson(resp.data);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<List<SnAccountBadge>> accountBadges(Ref ref, String uname) async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final resp = await apiClient.get("/accounts/$uname/badges");
|
||||
return List<SnAccountBadge>.from(
|
||||
resp.data.map((x) => SnAccountBadge.fromJson(x)),
|
||||
);
|
||||
}
|
||||
|
||||
@RoutePage()
|
||||
class AccountProfileScreen extends HookConsumerWidget {
|
||||
final String name;
|
||||
|
@ -145,5 +145,127 @@ class _AccountProviderElement
|
||||
String get uname => (origin as AccountProvider).uname;
|
||||
}
|
||||
|
||||
String _$accountBadgesHash() => r'4bfe5fb0d6ac0d4cde4563460bde289289188f6d';
|
||||
|
||||
/// See also [accountBadges].
|
||||
@ProviderFor(accountBadges)
|
||||
const accountBadgesProvider = AccountBadgesFamily();
|
||||
|
||||
/// See also [accountBadges].
|
||||
class AccountBadgesFamily extends Family<AsyncValue<List<SnAccountBadge>>> {
|
||||
/// See also [accountBadges].
|
||||
const AccountBadgesFamily();
|
||||
|
||||
/// See also [accountBadges].
|
||||
AccountBadgesProvider call(String uname) {
|
||||
return AccountBadgesProvider(uname);
|
||||
}
|
||||
|
||||
@override
|
||||
AccountBadgesProvider getProviderOverride(
|
||||
covariant AccountBadgesProvider provider,
|
||||
) {
|
||||
return call(provider.uname);
|
||||
}
|
||||
|
||||
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'accountBadgesProvider';
|
||||
}
|
||||
|
||||
/// See also [accountBadges].
|
||||
class AccountBadgesProvider
|
||||
extends AutoDisposeFutureProvider<List<SnAccountBadge>> {
|
||||
/// See also [accountBadges].
|
||||
AccountBadgesProvider(String uname)
|
||||
: this._internal(
|
||||
(ref) => accountBadges(ref as AccountBadgesRef, uname),
|
||||
from: accountBadgesProvider,
|
||||
name: r'accountBadgesProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$accountBadgesHash,
|
||||
dependencies: AccountBadgesFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
AccountBadgesFamily._allTransitiveDependencies,
|
||||
uname: uname,
|
||||
);
|
||||
|
||||
AccountBadgesProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.uname,
|
||||
}) : super.internal();
|
||||
|
||||
final String uname;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<List<SnAccountBadge>> Function(AccountBadgesRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: AccountBadgesProvider._internal(
|
||||
(ref) => create(ref as AccountBadgesRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
uname: uname,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<List<SnAccountBadge>> createElement() {
|
||||
return _AccountBadgesProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is AccountBadgesProvider && other.uname == uname;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, uname.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin AccountBadgesRef on AutoDisposeFutureProviderRef<List<SnAccountBadge>> {
|
||||
/// The parameter `uname` of this provider.
|
||||
String get uname;
|
||||
}
|
||||
|
||||
class _AccountBadgesProviderElement
|
||||
extends AutoDisposeFutureProviderElement<List<SnAccountBadge>>
|
||||
with AccountBadgesRef {
|
||||
_AccountBadgesProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get uname => (origin as AccountBadgesProvider).uname;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -1,11 +1,16 @@
|
||||
import 'package:auto_route/annotations.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/models/user.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/widgets/account/badge.dart';
|
||||
import 'package:island/widgets/account/status.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/post/post_list.dart';
|
||||
@ -21,6 +26,27 @@ Future<SnPublisher> publisher(Ref ref, String uname) async {
|
||||
return SnPublisher.fromJson(resp.data);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<List<SnAccountBadge>> publisherBadges(Ref ref, String pubName) async {
|
||||
final pub = await ref.watch(publisherProvider(pubName).future);
|
||||
if (pub.publisherType != 0) return [];
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final resp = await apiClient.get("/accounts/${pub.name}/badges");
|
||||
return List<SnAccountBadge>.from(
|
||||
resp.data.map((x) => SnAccountBadge.fromJson(x)),
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<SnSubscriptionStatus> publisherSubscriptionStatus(
|
||||
Ref ref,
|
||||
String pubName,
|
||||
) async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final resp = await apiClient.get("/publishers/$pubName/subscription");
|
||||
return SnSubscriptionStatus.fromJson(resp.data);
|
||||
}
|
||||
|
||||
@RoutePage()
|
||||
class PublisherProfileScreen extends HookConsumerWidget {
|
||||
final String name;
|
||||
@ -32,6 +58,38 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final publisher = ref.watch(publisherProvider(name));
|
||||
final badges = ref.watch(publisherBadgesProvider(name));
|
||||
final subStatus = ref.watch(publisherSubscriptionStatusProvider(name));
|
||||
|
||||
final subscribing = useState(false);
|
||||
|
||||
Future<void> subscribe() async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
subscribing.value = true;
|
||||
try {
|
||||
await apiClient.post("/publishers/$name/subscribe", data: {'tier': 0});
|
||||
ref.invalidate(publisherSubscriptionStatusProvider(name));
|
||||
HapticFeedback.heavyImpact();
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
} finally {
|
||||
subscribing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> unsubscribe() async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
subscribing.value = true;
|
||||
try {
|
||||
await apiClient.post("/publishers/$name/unsubscribe");
|
||||
ref.invalidate(publisherSubscriptionStatusProvider(name));
|
||||
HapticFeedback.heavyImpact();
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
} finally {
|
||||
subscribing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
final iconShadow = Shadow(
|
||||
color: Colors.black54,
|
||||
@ -64,6 +122,41 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
subStatus.when(
|
||||
data:
|
||||
(status) => IconButton(
|
||||
onPressed:
|
||||
subscribing.value
|
||||
? null
|
||||
: (status.isSubscribed
|
||||
? unsubscribe
|
||||
: subscribe),
|
||||
icon: Icon(
|
||||
status.isSubscribed
|
||||
? Icons.remove_circle
|
||||
: Icons.add_circle,
|
||||
shadows: [iconShadow],
|
||||
),
|
||||
),
|
||||
error: (_, __) => const SizedBox(),
|
||||
loading:
|
||||
() => const SizedBox(
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Gap(8),
|
||||
],
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Row(
|
||||
@ -95,14 +188,14 @@ class PublisherProfileScreen extends HookConsumerWidget {
|
||||
],
|
||||
).padding(horizontal: 24, top: 24, bottom: 24),
|
||||
),
|
||||
// if (data.badges.isNotEmpty)
|
||||
// SliverToBoxAdapter(
|
||||
// child: BadgeList(
|
||||
// badges: data.badges,
|
||||
// ).padding(horizontal: 24, bottom: 24),
|
||||
// )
|
||||
// else
|
||||
// const Gap(16),
|
||||
if (badges.value?.isNotEmpty ?? false)
|
||||
SliverToBoxAdapter(
|
||||
child: BadgeList(
|
||||
badges: badges.value!,
|
||||
).padding(horizontal: 24, bottom: 24),
|
||||
)
|
||||
else
|
||||
const SliverGap(16),
|
||||
SliverToBoxAdapter(child: const Divider(height: 1)),
|
||||
if (data.bio.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
|
@ -145,5 +145,259 @@ class _PublisherProviderElement
|
||||
String get uname => (origin as PublisherProvider).uname;
|
||||
}
|
||||
|
||||
String _$publisherBadgesHash() => r'69a5bbc9e1528da65ae8b1e5e6c4f57c3dcf4071';
|
||||
|
||||
/// See also [publisherBadges].
|
||||
@ProviderFor(publisherBadges)
|
||||
const publisherBadgesProvider = PublisherBadgesFamily();
|
||||
|
||||
/// See also [publisherBadges].
|
||||
class PublisherBadgesFamily extends Family<AsyncValue<List<SnAccountBadge>>> {
|
||||
/// See also [publisherBadges].
|
||||
const PublisherBadgesFamily();
|
||||
|
||||
/// See also [publisherBadges].
|
||||
PublisherBadgesProvider call(String pubName) {
|
||||
return PublisherBadgesProvider(pubName);
|
||||
}
|
||||
|
||||
@override
|
||||
PublisherBadgesProvider getProviderOverride(
|
||||
covariant PublisherBadgesProvider provider,
|
||||
) {
|
||||
return call(provider.pubName);
|
||||
}
|
||||
|
||||
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'publisherBadgesProvider';
|
||||
}
|
||||
|
||||
/// See also [publisherBadges].
|
||||
class PublisherBadgesProvider
|
||||
extends AutoDisposeFutureProvider<List<SnAccountBadge>> {
|
||||
/// See also [publisherBadges].
|
||||
PublisherBadgesProvider(String pubName)
|
||||
: this._internal(
|
||||
(ref) => publisherBadges(ref as PublisherBadgesRef, pubName),
|
||||
from: publisherBadgesProvider,
|
||||
name: r'publisherBadgesProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$publisherBadgesHash,
|
||||
dependencies: PublisherBadgesFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
PublisherBadgesFamily._allTransitiveDependencies,
|
||||
pubName: pubName,
|
||||
);
|
||||
|
||||
PublisherBadgesProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.pubName,
|
||||
}) : super.internal();
|
||||
|
||||
final String pubName;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<List<SnAccountBadge>> Function(PublisherBadgesRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: PublisherBadgesProvider._internal(
|
||||
(ref) => create(ref as PublisherBadgesRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
pubName: pubName,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<List<SnAccountBadge>> createElement() {
|
||||
return _PublisherBadgesProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is PublisherBadgesProvider && other.pubName == pubName;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, pubName.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin PublisherBadgesRef on AutoDisposeFutureProviderRef<List<SnAccountBadge>> {
|
||||
/// The parameter `pubName` of this provider.
|
||||
String get pubName;
|
||||
}
|
||||
|
||||
class _PublisherBadgesProviderElement
|
||||
extends AutoDisposeFutureProviderElement<List<SnAccountBadge>>
|
||||
with PublisherBadgesRef {
|
||||
_PublisherBadgesProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get pubName => (origin as PublisherBadgesProvider).pubName;
|
||||
}
|
||||
|
||||
String _$publisherSubscriptionStatusHash() =>
|
||||
r'4eb6741c40775c814e71b6a98b8f1e2d84bf7e30';
|
||||
|
||||
/// See also [publisherSubscriptionStatus].
|
||||
@ProviderFor(publisherSubscriptionStatus)
|
||||
const publisherSubscriptionStatusProvider = PublisherSubscriptionStatusFamily();
|
||||
|
||||
/// See also [publisherSubscriptionStatus].
|
||||
class PublisherSubscriptionStatusFamily
|
||||
extends Family<AsyncValue<SnSubscriptionStatus>> {
|
||||
/// See also [publisherSubscriptionStatus].
|
||||
const PublisherSubscriptionStatusFamily();
|
||||
|
||||
/// See also [publisherSubscriptionStatus].
|
||||
PublisherSubscriptionStatusProvider call(String pubName) {
|
||||
return PublisherSubscriptionStatusProvider(pubName);
|
||||
}
|
||||
|
||||
@override
|
||||
PublisherSubscriptionStatusProvider getProviderOverride(
|
||||
covariant PublisherSubscriptionStatusProvider provider,
|
||||
) {
|
||||
return call(provider.pubName);
|
||||
}
|
||||
|
||||
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'publisherSubscriptionStatusProvider';
|
||||
}
|
||||
|
||||
/// See also [publisherSubscriptionStatus].
|
||||
class PublisherSubscriptionStatusProvider
|
||||
extends AutoDisposeFutureProvider<SnSubscriptionStatus> {
|
||||
/// See also [publisherSubscriptionStatus].
|
||||
PublisherSubscriptionStatusProvider(String pubName)
|
||||
: this._internal(
|
||||
(ref) => publisherSubscriptionStatus(
|
||||
ref as PublisherSubscriptionStatusRef,
|
||||
pubName,
|
||||
),
|
||||
from: publisherSubscriptionStatusProvider,
|
||||
name: r'publisherSubscriptionStatusProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$publisherSubscriptionStatusHash,
|
||||
dependencies: PublisherSubscriptionStatusFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
PublisherSubscriptionStatusFamily._allTransitiveDependencies,
|
||||
pubName: pubName,
|
||||
);
|
||||
|
||||
PublisherSubscriptionStatusProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.pubName,
|
||||
}) : super.internal();
|
||||
|
||||
final String pubName;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<SnSubscriptionStatus> Function(
|
||||
PublisherSubscriptionStatusRef provider,
|
||||
)
|
||||
create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: PublisherSubscriptionStatusProvider._internal(
|
||||
(ref) => create(ref as PublisherSubscriptionStatusRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
pubName: pubName,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<SnSubscriptionStatus> createElement() {
|
||||
return _PublisherSubscriptionStatusProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is PublisherSubscriptionStatusProvider &&
|
||||
other.pubName == pubName;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, pubName.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin PublisherSubscriptionStatusRef
|
||||
on AutoDisposeFutureProviderRef<SnSubscriptionStatus> {
|
||||
/// The parameter `pubName` of this provider.
|
||||
String get pubName;
|
||||
}
|
||||
|
||||
class _PublisherSubscriptionStatusProviderElement
|
||||
extends AutoDisposeFutureProviderElement<SnSubscriptionStatus>
|
||||
with PublisherSubscriptionStatusRef {
|
||||
_PublisherSubscriptionStatusProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get pubName => (origin as PublisherSubscriptionStatusProvider).pubName;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
Reference in New Issue
Block a user