🐛 Fix some bugs in creator hub

This commit is contained in:
2025-12-06 21:26:00 +08:00
parent 7516e197fe
commit 9c370647dd
5 changed files with 430 additions and 439 deletions

View File

@@ -5,16 +5,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:island/models/webfeed.dart';
import 'package:island/pods/network.dart';
final webFeedListProvider = FutureProvider.family<List<SnWebFeed>, String>((
ref,
pubName,
) async {
final client = ref.watch(apiClientProvider);
final response = await client.get('/sphere/publishers/$pubName/feeds');
return (response.data as List)
.map((json) => SnWebFeed.fromJson(json))
.toList();
});
final webFeedListProvider = FutureProvider.autoDispose
.family<List<SnWebFeed>, String>((ref, pubName) async {
final client = ref.watch(apiClientProvider);
final response = await client.get('/sphere/publishers/$pubName/feeds');
return (response.data as List)
.map((json) => SnWebFeed.fromJson(json))
.toList();
});
class WebFeedNotifier extends AsyncNotifier<SnWebFeed> {
final ({String pubName, String? feedId}) arg;
@@ -51,10 +49,9 @@ class WebFeedNotifier extends AsyncNotifier<SnWebFeed> {
final client = ref.read(apiClientProvider);
final url = '/sphere/publishers/${feed.publisherId}/feeds';
final response =
feed.id.isEmpty
? await client.post(url, data: feed.toJson())
: await client.patch('$url/${feed.id}', data: feed.toJson());
final response = feed.id.isEmpty
? await client.post(url, data: feed.toJson())
: await client.patch('$url/${feed.id}', data: feed.toJson());
state = AsyncValue.data(SnWebFeed.fromJson(response.data));
} catch (error, stackTrace) {

View File

@@ -20,6 +20,7 @@ import 'package:island/widgets/navigation/fab_menu.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:relative_time/relative_time.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
import 'package:island/pods/chat/chat_room.dart';
@@ -50,84 +51,124 @@ class ChatRoomListTile extends HookConsumerWidget {
if (validMembers.isNotEmpty) {
final userInfo = ref.watch(userInfoProvider);
if (userInfo.value != null) {
validMembers =
validMembers
.where((e) => e.accountId != userInfo.value!.id)
.toList();
validMembers = validMembers
.where((e) => e.accountId != userInfo.value!.id)
.toList();
}
}
Widget buildSubtitle() {
if (subtitle != null) return subtitle!;
return summary.when(
data: (data) {
if (data == null) {
return isDirect && room.description == null
? Text(
validMembers.map((e) => '@${e.account.name}').join(', '),
maxLines: 1,
)
: Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (data.unreadCount > 0)
Text(
'unreadMessages'.plural(data.unreadCount),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
layoutBuilder: (currentChild, previousChildren) => Stack(
alignment: Alignment.centerLeft,
children: [
...previousChildren,
if (currentChild != null) currentChild,
],
),
child: summary.when(
data: (data) => Container(
key: const ValueKey('data'),
child: data == null
? isDirect && room.description == null
? Text(
validMembers
.map((e) => '@${e.account.name}')
.join(', '),
maxLines: 1,
)
: Text(
room.description ?? 'descriptionNone'.tr(),
maxLines: 1,
)
: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (data.unreadCount > 0)
Text(
'unreadMessages'.plural(data.unreadCount),
style: Theme.of(context).textTheme.bodySmall
?.copyWith(
color: Theme.of(context).colorScheme.primary,
),
),
if (data.lastMessage == null)
Text(
room.description ?? 'descriptionNone'.tr(),
maxLines: 1,
)
else
Row(
spacing: 4,
children: [
Badge(
label: Text(
data.lastMessage!.sender.account.nick,
),
textColor: Theme.of(
context,
).colorScheme.onPrimary,
backgroundColor: Theme.of(
context,
).colorScheme.primary,
),
Expanded(
child: Text(
(data.lastMessage!.content?.isNotEmpty ?? false)
? data.lastMessage!.content!
: 'messageNone'.tr(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall,
),
),
Align(
alignment: Alignment.centerRight,
child: Text(
RelativeTime(
context,
).format(data.lastMessage!.createdAt),
style: Theme.of(context).textTheme.bodySmall,
),
),
],
),
],
),
),
if (data.lastMessage == null)
Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1)
else
Row(
spacing: 4,
children: [
Badge(
label: Text(data.lastMessage!.sender.account.nick),
textColor: Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).colorScheme.primary,
),
Expanded(
child: Text(
(data.lastMessage!.content?.isNotEmpty ?? false)
? data.lastMessage!.content!
: 'messageNone'.tr(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall,
),
),
Align(
alignment: Alignment.centerRight,
child: Text(
RelativeTime(
context,
).format(data.lastMessage!.createdAt),
style: Theme.of(context).textTheme.bodySmall,
),
),
],
),
],
);
},
loading: () => const SizedBox.shrink(),
error:
(_, _) =>
isDirect && room.description == null
? Text(
validMembers.map((e) => '@${e.account.name}').join(', '),
maxLines: 1,
)
: Text(
room.description ?? 'descriptionNone'.tr(),
maxLines: 1,
),
),
loading: () => Container(
key: const ValueKey('loading'),
child: Builder(
builder: (context) {
final seed = DateTime.now().microsecondsSinceEpoch;
final len = 4 + (seed % 17); // 4..20 inclusive
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
var s = seed;
final buffer = StringBuffer();
for (var i = 0; i < len; i++) {
s = (s * 1103515245 + 12345) & 0x7fffffff;
buffer.write(chars[s % chars.length]);
}
return Skeletonizer(
enabled: true,
child: Text(buffer.toString()),
);
},
),
),
error: (_, _) => Container(
key: const ValueKey('error'),
child: isDirect && room.description == null
? Text(
validMembers.map((e) => '@${e.account.name}').join(', '),
maxLines: 1,
)
: Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1),
),
),
);
}
@@ -149,17 +190,15 @@ class ChatRoomListTile extends HookConsumerWidget {
loading: () => false,
error: (_, _) => false,
),
child:
(isDirect && room.picture?.id == null)
? SplitAvatarWidget(
filesId:
validMembers
.map((e) => e.account.profile.picture?.id)
.toList(),
)
: room.picture?.id == null
? CircleAvatar(child: Text(room.name![0].toUpperCase()))
: ProfilePictureWidget(fileId: room.picture?.id),
child: (isDirect && room.picture?.id == null)
? SplitAvatarWidget(
filesId: validMembers
.map((e) => e.account.profile.picture?.id)
.toList(),
)
: room.picture?.id == null
? CircleAvatar(child: Text(room.name![0].toUpperCase()))
: ProfilePictureWidget(fileId: room.picture?.id),
),
title: Text(titleText),
subtitle: buildSubtitle(),
@@ -199,74 +238,67 @@ class ChatListBodyWidget extends HookConsumerWidget {
builder: (context, ref, _) {
final summaryState = ref.watch(chatSummaryProvider);
return summaryState.maybeWhen(
loading:
() => const LinearProgressIndicator(
minHeight: 2,
borderRadius: BorderRadius.zero,
),
loading: () => const LinearProgressIndicator(
minHeight: 2,
borderRadius: BorderRadius.zero,
),
orElse: () => const SizedBox.shrink(),
);
},
),
Expanded(
child: chats.when(
data:
(items) => RefreshIndicator(
onRefresh:
() => Future.sync(() {
ref.invalidate(chatRoomJoinedProvider);
}),
child: SuperListView.builder(
padding: EdgeInsets.only(bottom: 96),
itemCount:
items
.where(
(item) =>
selectedTab.value == 0 ||
(selectedTab.value == 1 && item.type == 1) ||
(selectedTab.value == 2 && item.type != 1),
)
.length,
itemBuilder: (context, index) {
final filteredItems =
items
.where(
(item) =>
selectedTab.value == 0 ||
(selectedTab.value == 1 &&
item.type == 1) ||
(selectedTab.value == 2 && item.type != 1),
)
.toList();
final item = filteredItems[index];
return ChatRoomListTile(
room: item,
isDirect: item.type == 1,
onTap: () {
if (isWideScreen(context)) {
context.replaceNamed(
'chatRoom',
pathParameters: {'id': item.id},
);
} else {
context.pushNamed(
'chatRoom',
pathParameters: {'id': item.id},
);
}
},
);
data: (items) => RefreshIndicator(
onRefresh: () => Future.sync(() {
ref.invalidate(chatRoomJoinedProvider);
}),
child: SuperListView.builder(
padding: EdgeInsets.only(bottom: 96),
itemCount: items
.where(
(item) =>
selectedTab.value == 0 ||
(selectedTab.value == 1 && item.type == 1) ||
(selectedTab.value == 2 && item.type != 1),
)
.length,
itemBuilder: (context, index) {
final filteredItems = items
.where(
(item) =>
selectedTab.value == 0 ||
(selectedTab.value == 1 && item.type == 1) ||
(selectedTab.value == 2 && item.type != 1),
)
.toList();
final item = filteredItems[index];
return ChatRoomListTile(
room: item,
isDirect: item.type == 1,
onTap: () {
if (isWideScreen(context)) {
context.replaceNamed(
'chatRoom',
pathParameters: {'id': item.id},
);
} else {
context.pushNamed(
'chatRoom',
pathParameters: {'id': item.id},
);
}
},
),
),
);
},
),
),
loading: () => const Center(child: CircularProgressIndicator()),
error:
(error, stack) => ResponseErrorWidget(
error: error,
onRetry: () {
ref.invalidate(chatRoomJoinedProvider);
},
),
error: (error, stack) => ResponseErrorWidget(
error: error,
onRetry: () {
ref.invalidate(chatRoomJoinedProvider);
},
),
),
),
],
@@ -552,53 +584,47 @@ class _ChatInvitesSheet extends HookConsumerWidget {
),
],
child: invites.when(
data:
(items) =>
items.isEmpty
? Center(
child:
Text(
'invitesEmpty',
textAlign: TextAlign.center,
).tr(),
)
: ListView.builder(
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (context, index) {
final invite = items[index];
return ChatRoomListTile(
room: invite.chatRoom!,
isDirect: invite.chatRoom!.type == 1,
subtitle: Row(
spacing: 6,
children: [
if (invite.chatRoom!.type == 1)
Badge(
label: const Text('directMessage').tr(),
backgroundColor:
Theme.of(context).colorScheme.primary,
textColor:
Theme.of(context).colorScheme.onPrimary,
),
],
data: (items) => items.isEmpty
? Center(
child: Text('invitesEmpty', textAlign: TextAlign.center).tr(),
)
: ListView.builder(
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (context, index) {
final invite = items[index];
return ChatRoomListTile(
room: invite.chatRoom!,
isDirect: invite.chatRoom!.type == 1,
subtitle: Row(
spacing: 6,
children: [
if (invite.chatRoom!.type == 1)
Badge(
label: const Text('directMessage').tr(),
backgroundColor: Theme.of(
context,
).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Symbols.check),
onPressed: () => acceptInvite(invite),
),
IconButton(
icon: const Icon(Symbols.close),
onPressed: () => declineInvite(invite),
),
],
),
);
},
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Symbols.check),
onPressed: () => acceptInvite(invite),
),
IconButton(
icon: const Icon(Symbols.close),
onPressed: () => declineInvite(invite),
),
],
),
);
},
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
),

View File

@@ -44,11 +44,10 @@ class PollListNotifier extends AsyncNotifier<List<SnPollWithStats>>
queryParameters: queryParams,
);
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
final items =
response.data
.map((json) => SnPollWithStats.fromJson(json))
.cast<SnPollWithStats>()
.toList();
final items = response.data
.map((json) => SnPollWithStats.fromJson(json))
.cast<SnPollWithStats>()
.toList();
return items;
}
@@ -91,6 +90,7 @@ class CreatorPollListScreen extends HookConsumerWidget {
body: ExtendedRefreshIndicator(
onRefresh: () => ref.refresh(pollListNotifierProvider(pubName).future),
child: PaginationList(
footerSkeletonMaxWidth: 640,
provider: pollListNotifierProvider(pubName),
notifier: pollListNotifierProvider(pubName).notifier,
padding: const EdgeInsets.only(top: 12),
@@ -119,10 +119,9 @@ class _CreatorPollItem extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final ended = pollWithStats.endedAt;
final endedText =
ended == null
? 'No end'
: MaterialLocalizations.of(context).formatFullDate(ended);
final endedText = ended == null
? 'No end'
: MaterialLocalizations.of(context).formatFullDate(ended);
return Card(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
@@ -152,78 +151,69 @@ class _CreatorPollItem extends HookConsumerWidget {
],
),
trailing: PopupMenuButton<String>(
itemBuilder:
(context) => [
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.edit),
const Gap(16),
Text('edit').tr(),
itemBuilder: (context) => [
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.edit),
const Gap(16),
Text('edit').tr(),
],
),
onTap: () async {
final result = await showModalBottomSheet<SnPoll>(
context: context,
isScrollControlled: true,
isDismissible: false,
builder: (context) => PollEditorScreen(
initialPublisher: pubName,
initialPollId: pollWithStats.id,
),
);
if (result != null && context.mounted) {
ref.invalidate(pollListNotifierProvider(pubName));
}
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.delete, color: Colors.red),
const Gap(16),
Text('delete').tr().textColor(Colors.red),
],
),
onTap: () async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('Delete Poll'),
content: Text('Are you sure you want to delete this poll?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('Delete'),
),
],
),
onTap: () async {
final result = await showModalBottomSheet<SnPoll>(
context: context,
isScrollControlled: true,
isDismissible: false,
builder:
(context) => PollEditorScreen(
initialPublisher: pubName,
initialPollId: pollWithStats.id,
),
);
if (result != null && context.mounted) {
ref.invalidate(pollListNotifierProvider(pubName));
}
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.delete, color: Colors.red),
const Gap(16),
Text('delete').tr().textColor(Colors.red),
],
),
onTap: () async {
final confirmed = await showDialog<bool>(
context: context,
builder:
(context) => AlertDialog(
title: Text('Delete Poll'),
content: Text(
'Are you sure you want to delete this poll?',
),
actions: [
TextButton(
onPressed:
() => Navigator.of(context).pop(false),
child: Text('Cancel'),
),
TextButton(
onPressed:
() => Navigator.of(context).pop(true),
child: Text('Delete'),
),
],
),
);
if (confirmed == true) {
try {
final client = ref.read(apiClientProvider);
await client.delete(
'/sphere/polls/${pollWithStats.id}',
);
ref.invalidate(pollListNotifierProvider(pubName));
showSnackBar('Poll deleted successfully');
} catch (e) {
showErrorAlert(e);
}
}
},
),
],
);
if (confirmed == true) {
try {
final client = ref.read(apiClientProvider);
await client.delete('/sphere/polls/${pollWithStats.id}');
ref.invalidate(pollListNotifierProvider(pubName));
showSnackBar('Poll deleted successfully');
} catch (e) {
showErrorAlert(e);
}
}
},
),
],
),
onTap: () {
showModalBottomSheet(

View File

@@ -12,7 +12,6 @@ import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/paging/pagination_list.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
import 'package:styled_widget/styled_widget.dart';
final siteListNotifierProvider = AsyncNotifierProvider.family.autoDispose(
@@ -38,11 +37,10 @@ class SiteListNotifier extends AsyncNotifier<List<SnPublicationSite>>
queryParameters: queryParams,
);
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
final items =
response.data
.map((json) => SnPublicationSite.fromJson(json))
.cast<SnPublicationSite>()
.toList();
final items = response.data
.map((json) => SnPublicationSite.fromJson(json))
.cast<SnPublicationSite>()
.toList();
return items;
}
@@ -70,23 +68,17 @@ class CreatorSiteListScreen extends HookConsumerWidget {
onPressed: () => _createSite(context),
child: Icon(Icons.add),
),
body: ExtendedRefreshIndicator(
onRefresh: () => ref.refresh(siteListNotifierProvider(pubName).future),
child: CustomScrollView(
slivers: [
const SliverGap(8),
PaginationList(
provider: siteListNotifierProvider(pubName),
notifier: siteListNotifierProvider(pubName).notifier,
itemBuilder: (context, index, site) {
return ConstrainedBox(
constraints: BoxConstraints(maxWidth: 640),
child: _CreatorSiteItem(site: site, pubName: pubName),
).center();
},
),
],
),
body: PaginationList(
footerSkeletonMaxWidth: 640,
provider: siteListNotifierProvider(pubName),
notifier: siteListNotifierProvider(pubName).notifier,
padding: const EdgeInsets.only(top: 12),
itemBuilder: (context, index, site) {
return ConstrainedBox(
constraints: BoxConstraints(maxWidth: 640),
child: _CreatorSiteItem(site: site, pubName: pubName),
).center();
},
),
);
}
@@ -148,73 +140,65 @@ class _CreatorSiteItem extends HookConsumerWidget {
),
),
PopupMenuButton<String>(
itemBuilder:
(context) => [
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.edit),
const Gap(16),
Text('edit').tr(),
itemBuilder: (context) => [
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.edit),
const Gap(16),
Text('edit').tr(),
],
),
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) =>
SiteForm(pubName: pubName, siteSlug: site.slug),
);
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.delete, color: Colors.red),
const Gap(16),
Text('delete').tr().textColor(Colors.red),
],
),
onTap: () async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('deleteSite'.tr()),
content: Text('deleteSiteConfirm'.tr()),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('cancel'.tr()),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('delete'.tr()),
),
],
),
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SiteForm(
pubName: pubName,
siteSlug: site.slug,
),
);
if (confirmed == true) {
try {
final client = ref.read(apiClientProvider);
await client.delete(
'/zone/sites/$pubName/${site.slug}',
);
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.delete, color: Colors.red),
const Gap(16),
Text('delete').tr().textColor(Colors.red),
],
),
onTap: () async {
final confirmed = await showDialog<bool>(
context: context,
builder:
(context) => AlertDialog(
title: Text('deleteSite'.tr()),
content: Text('deleteSiteConfirm'.tr()),
actions: [
TextButton(
onPressed:
() =>
Navigator.of(context).pop(false),
child: Text('cancel'.tr()),
),
TextButton(
onPressed:
() => Navigator.of(context).pop(true),
child: Text('delete'.tr()),
),
],
),
);
if (confirmed == true) {
try {
final client = ref.read(apiClientProvider);
await client.delete(
'/zone/sites/$pubName/${site.slug}',
);
ref.invalidate(siteListNotifierProvider(pubName));
showSnackBar('siteDeletedSuccess'.tr());
} catch (e) {
showErrorAlert(e);
}
}
},
),
],
ref.invalidate(siteListNotifierProvider(pubName));
showSnackBar('siteDeletedSuccess'.tr());
} catch (e) {
showErrorAlert(e);
}
}
},
),
],
),
],
),

View File

@@ -41,11 +41,10 @@ class StickersScreen extends HookConsumerWidget {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: 'createStickerPack'.tr(),
child: StickerPackForm(pubName: pubName),
),
builder: (context) => SheetScaffold(
titleText: 'createStickerPack'.tr(),
child: StickerPackForm(pubName: pubName),
),
).then((value) {
if (value != null) {
ref.invalidate(stickerPacksProvider(pubName));
@@ -54,24 +53,23 @@ class StickersScreen extends HookConsumerWidget {
},
child: const Icon(Symbols.add),
),
body:
isWideScreen(context)
? Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 640),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
),
body: isWideScreen(context)
? Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 640),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
),
margin: const EdgeInsets.only(top: 16),
child: content,
),
margin: const EdgeInsets.only(top: 16),
child: content,
),
)
: content,
),
)
: content,
);
}
}
@@ -83,6 +81,7 @@ class SliverStickerPacksList extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return PaginationList(
padding: EdgeInsets.zero,
provider: stickerPacksProvider(pubName),
notifier: stickerPacksProvider(pubName).notifier,
itemBuilder: (context, index, sticker) {
@@ -97,40 +96,38 @@ class SliverStickerPacksList extends HookConsumerWidget {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: sticker.name,
actions: [
IconButton(
icon: const Icon(Symbols.add_circle),
onPressed: () {
final id = sticker.id;
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: 'createSticker'.tr(),
child: StickerForm(packId: id),
),
).then((value) {
if (value != null) {
ref.invalidate(stickerPackContentProvider(id));
}
});
},
),
StickerPackActionMenu(
pubName: pubName,
packId: sticker.id,
iconShadow: Shadow(),
),
],
child: StickerPackDetailContent(
id: sticker.id,
pubName: pubName,
),
builder: (context) => SheetScaffold(
titleText: sticker.name,
actions: [
IconButton(
icon: const Icon(Symbols.add_circle),
onPressed: () {
final id = sticker.id;
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => SheetScaffold(
titleText: 'createSticker'.tr(),
child: StickerForm(packId: id),
),
).then((value) {
if (value != null) {
ref.invalidate(stickerPackContentProvider(id));
}
});
},
),
StickerPackActionMenu(
pubName: pubName,
packId: sticker.id,
iconShadow: Shadow(),
),
],
child: StickerPackDetailContent(
id: sticker.id,
pubName: pubName,
),
),
);
},
);
@@ -165,11 +162,10 @@ class StickerPacksNotifier extends AsyncNotifier<List<SnStickerPack>>
);
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
final stickers =
response.data
.map((e) => SnStickerPack.fromJson(e))
.cast<SnStickerPack>()
.toList();
final stickers = response.data
.map((e) => SnStickerPack.fromJson(e))
.cast<SnStickerPack>()
.toList();
return stickers;
} catch (err) {
@@ -262,10 +258,9 @@ class StickerPackForm extends HookConsumerWidget {
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child:
(icon.value?.isEmpty ?? true)
? const SizedBox.shrink()
: CloudImageWidget(fileId: icon.value!),
child: (icon.value?.isEmpty ?? true)
? const SizedBox.shrink()
: CloudImageWidget(fileId: icon.value!),
),
),
),
@@ -273,10 +268,9 @@ class StickerPackForm extends HookConsumerWidget {
onPressed: () {
showModalBottomSheet(
context: context,
builder:
(context) => CloudFilePicker(
allowedTypes: {UniversalFileType.image},
),
builder: (context) => CloudFilePicker(
allowedTypes: {UniversalFileType.image},
),
).then((value) {
if (value == null) return;
icon.value = value[0].id;
@@ -300,8 +294,8 @@ class StickerPackForm extends HookConsumerWidget {
}
return null;
},
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
),
TextFormField(
controller: descriptionController,
@@ -314,8 +308,8 @@ class StickerPackForm extends HookConsumerWidget {
),
minLines: 3,
maxLines: null,
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
),
TextFormField(
controller: prefixController,
@@ -332,8 +326,8 @@ class StickerPackForm extends HookConsumerWidget {
}
return null;
},
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
),
],
),