Explore shuffle post

This commit is contained in:
2025-09-04 00:52:02 +08:00
parent 3aece9316c
commit e9f09bbe54
6 changed files with 164 additions and 87 deletions

View File

@@ -637,6 +637,7 @@
"realmJoinSuccess": "Successfully joined the realm.", "realmJoinSuccess": "Successfully joined the realm.",
"discoverRealms": "Realms", "discoverRealms": "Realms",
"discoverPublishers": "Publishers", "discoverPublishers": "Publishers",
"discoverShuffledPost": "Random Posts",
"search": "Search", "search": "Search",
"publisherMembers": "Collaborators", "publisherMembers": "Collaborators",
"developerHub": "Developer Hub", "developerHub": "Developer Hub",

View File

@@ -7,7 +7,7 @@ part of 'room_detail.dart';
// ************************************************************************** // **************************************************************************
String _$totalMessagesCountHash() => String _$totalMessagesCountHash() =>
r'a15c03461f25c2d4d39c0926509bf626ae2550a6'; r'd55f1507aba2acdce5e468c1c2e15dba7640c571';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

View File

@@ -399,39 +399,48 @@ class _DiscoveryActivityItem extends StatelessWidget {
final items = data['items'] as List; final items = data['items'] as List;
final type = items.firstOrNull?['type'] ?? 'unknown'; final type = items.firstOrNull?['type'] ?? 'unknown';
return Card( var flexWeights = isWideScreen(context) ? <int>[3, 2, 1] : <int>[4, 1];
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), if (type == 'post') flexWeights = <int>[3, 2];
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, final height = type == 'post' ? 280.0 : 180.0;
children: [
Row( final contentWidget = switch (type) {
crossAxisAlignment: CrossAxisAlignment.center, 'post' => ListView.separated(
children: [ scrollDirection: Axis.horizontal,
const Icon(Symbols.explore, size: 19), itemCount: items.length,
const Gap(8), separatorBuilder: (context, index) => const Gap(12),
Text( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
(switch (type) { itemBuilder: (context, index) {
'realm' => 'discoverRealms', final item = items[index];
'publisher' => 'discoverPublishers', return Container(
'article' => 'discoverWebArticles', width: 320,
_ => 'unknown', decoration: BoxDecoration(
}).tr(), border: Border.all(
style: Theme.of(context).textTheme.titleMedium, width: 1 / MediaQuery.of(context).devicePixelRatio,
).padding(top: 1), color: Theme.of(context).dividerColor.withOpacity(0.5),
], ),
).padding(horizontal: 20, top: 8, bottom: 4), borderRadius: const BorderRadius.all(Radius.circular(8)),
SizedBox( ),
height: 180, child: ClipRRect(
child: ConstrainedBox( borderRadius: const BorderRadius.all(Radius.circular(8)),
constraints: const BoxConstraints(maxHeight: 200), child: SingleChildScrollView(
child: CarouselView.weighted( child: PostActionableItem(
flexWeights: item: SnPost.fromJson(item['data']),
isWideScreen(context) ? <int>[3, 2, 1] : <int>[4, 1], isCompact: true,
),
),
),
);
},
),
_ => CarouselView.weighted(
flexWeights: flexWeights,
consumeMaxWeight: false, consumeMaxWeight: false,
enableSplash: false, enableSplash: false,
shape: RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)), borderRadius: BorderRadius.all(Radius.circular(8)),
), ),
itemSnapping: false,
children: [ children: [
for (final item in items) for (final item in items)
switch (type) { switch (type) {
@@ -447,11 +456,43 @@ class _DiscoveryActivityItem extends StatelessWidget {
article: SnWebArticle.fromJson(item['data']), article: SnWebArticle.fromJson(item['data']),
maxWidth: 280, maxWidth: 280,
), ),
_ => Placeholder(), _ => const Placeholder(),
}, },
], ],
), ),
), };
return Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(switch (type) {
'realm' => Symbols.public,
'publisher' => Symbols.account_circle,
'article' => Symbols.auto_stories,
'post' => Symbols.shuffle,
_ => Symbols.explore,
}, size: 19),
const Gap(8),
Text(
(switch (type) {
'realm' => 'discoverRealms',
'publisher' => 'discoverPublishers',
'article' => 'discoverWebArticles',
'post' => 'discoverShuffledPost',
_ => 'unknown',
}).tr(),
style: Theme.of(context).textTheme.titleMedium,
).padding(top: 1),
],
).padding(horizontal: 20, top: 8, bottom: 4),
SizedBox(
height: height,
child: contentWidget,
).padding(bottom: 8, horizontal: 8), ).padding(bottom: 8, horizontal: 8),
], ],
), ),
@@ -569,7 +610,8 @@ class ActivityListNotifier extends _$ActivityListNotifier
if (cursor != null) 'cursor': cursor, if (cursor != null) 'cursor': cursor,
'take': take, 'take': take,
if (filter != null) 'filter': filter, if (filter != null) 'filter': filter,
if (kDebugMode) 'debugInclude': 'realms,publishers,articles', if (kDebugMode)
'debugInclude': 'realms,publishers,articles,shuffledPosts',
}; };
final response = await client.get( final response = await client.get(
@@ -584,12 +626,13 @@ class ActivityListNotifier extends _$ActivityListNotifier
final hasMore = (items.firstOrNull?.type ?? 'empty') != 'empty'; final hasMore = (items.firstOrNull?.type ?? 'empty') != 'empty';
final nextCursor = final nextCursor =
items items.isNotEmpty
? items
.map((x) => x.createdAt) .map((x) => x.createdAt)
.lastOrNull .reduce((a, b) => a.isAfter(b) ? a : b)
?.toUtc() .toUtc()
.toIso8601String() .toIso8601String()
.toString(); : null;
return CursorPagingData( return CursorPagingData(
items: items, items: items,

View File

@@ -7,7 +7,7 @@ part of 'explore.dart';
// ************************************************************************** // **************************************************************************
String _$activityListNotifierHash() => String _$activityListNotifierHash() =>
r'b75fd5c08d5f84ca433e16b7387d317ea72b91c9'; r'a4968856ac34b59d47cfd4a7cbb39289aef2a1b1';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

View File

@@ -36,6 +36,7 @@ class PostActionableItem extends HookConsumerWidget {
final bool isShowReference; final bool isShowReference;
final bool isEmbedReply; final bool isEmbedReply;
final bool isEmbedOpenable; final bool isEmbedOpenable;
final bool isCompact;
final double? borderRadius; final double? borderRadius;
final VoidCallback? onRefresh; final VoidCallback? onRefresh;
final Function(SnPost)? onUpdate; final Function(SnPost)? onUpdate;
@@ -48,6 +49,7 @@ class PostActionableItem extends HookConsumerWidget {
this.isShowReference = true, this.isShowReference = true,
this.isEmbedReply = true, this.isEmbedReply = true,
this.isEmbedOpenable = false, this.isEmbedOpenable = false,
this.isCompact = false,
this.borderRadius, this.borderRadius,
this.onRefresh, this.onRefresh,
this.onUpdate, this.onUpdate,
@@ -76,6 +78,7 @@ class PostActionableItem extends HookConsumerWidget {
isEmbedReply: isEmbedReply, isEmbedReply: isEmbedReply,
isEmbedOpenable: isEmbedOpenable, isEmbedOpenable: isEmbedOpenable,
isTextSelectable: false, isTextSelectable: false,
isCompact: isCompact,
onRefresh: onRefresh, onRefresh: onRefresh,
onUpdate: onUpdate, onUpdate: onUpdate,
onOpen: onOpen, onOpen: onOpen,
@@ -298,6 +301,7 @@ class PostItem extends HookConsumerWidget {
final bool isEmbedOpenable; final bool isEmbedOpenable;
final bool isTextSelectable; final bool isTextSelectable;
final bool isTranslatable; final bool isTranslatable;
final bool isCompact;
final VoidCallback? onRefresh; final VoidCallback? onRefresh;
final Function(SnPost)? onUpdate; final Function(SnPost)? onUpdate;
final VoidCallback? onOpen; final VoidCallback? onOpen;
@@ -311,6 +315,7 @@ class PostItem extends HookConsumerWidget {
this.isEmbedOpenable = false, this.isEmbedOpenable = false,
this.isTextSelectable = true, this.isTextSelectable = true,
this.isTranslatable = true, this.isTranslatable = true,
this.isCompact = false,
this.onRefresh, this.onRefresh,
this.onUpdate, this.onUpdate,
this.onOpen, this.onOpen,
@@ -465,8 +470,12 @@ class PostItem extends HookConsumerWidget {
PostHeader( PostHeader(
item: item, item: item,
isFullPost: isFullPost, isFullPost: isFullPost,
isCompact: isCompact,
renderingPadding: renderingPadding, renderingPadding: renderingPadding,
trailing: IconButton( trailing:
isCompact
? null
: IconButton(
icon: icon:
mostReaction == null mostReaction == null
? const Icon(Symbols.add_reaction) ? const Icon(Symbols.add_reaction)
@@ -482,7 +491,8 @@ class PostItem extends HookConsumerWidget {
backgroundColor: Theme.of( backgroundColor: Theme.of(
context, context,
).colorScheme.primary.withOpacity(0.75), ).colorScheme.primary.withOpacity(0.75),
textColor: Theme.of(context).colorScheme.onPrimary, textColor:
Theme.of(context).colorScheme.onPrimary,
child: Text( child: Text(
kReactionTemplates[mostReaction]?.icon ?? '', kReactionTemplates[mostReaction]?.icon ?? '',
style: const TextStyle(fontSize: 20), style: const TextStyle(fontSize: 20),
@@ -491,7 +501,9 @@ class PostItem extends HookConsumerWidget {
style: ButtonStyle( style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll( backgroundColor: WidgetStatePropertyAll(
(item.reactionsMade[mostReaction] ?? false) (item.reactionsMade[mostReaction] ?? false)
? Theme.of(context).colorScheme.primary.withOpacity(0.5) ? Theme.of(
context,
).colorScheme.primary.withOpacity(0.5)
: null, : null,
), ),
), ),
@@ -511,7 +523,10 @@ class PostItem extends HookConsumerWidget {
); );
}, },
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
visualDensity: const VisualDensity(horizontal: -3, vertical: -3), visualDensity: const VisualDensity(
horizontal: -3,
vertical: -3,
),
), ),
), ),
PostBody( PostBody(

View File

@@ -532,6 +532,7 @@ class PostHeader extends StatelessWidget {
final bool isInteractive; final bool isInteractive;
final EdgeInsets renderingPadding; final EdgeInsets renderingPadding;
final bool isRelativeTime; final bool isRelativeTime;
final bool isCompact;
const PostHeader({ const PostHeader({
super.key, super.key,
@@ -541,6 +542,7 @@ class PostHeader extends StatelessWidget {
this.isInteractive = true, this.isInteractive = true,
this.renderingPadding = EdgeInsets.zero, this.renderingPadding = EdgeInsets.zero,
this.isRelativeTime = true, this.isRelativeTime = true,
this.isCompact = false,
}); });
@override @override
@@ -584,11 +586,27 @@ class PostHeader extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
spacing: 4, spacing: 4,
children: [ children: [
Text(item.publisher.nick).bold(), Flexible(
child:
Text(
item.publisher.nick,
maxLines: 1,
overflow: TextOverflow.ellipsis,
).bold(),
),
if (item.publisher.verification != null) if (item.publisher.verification != null)
VerificationMark(mark: item.publisher.verification!), VerificationMark(mark: item.publisher.verification!),
if (item.realm == null) if (item.realm == null)
Text('@${item.publisher.name}').fontSize(11) Flexible(
child:
isCompact
? const SizedBox.shrink()
: Text(
'@${item.publisher.name}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
).fontSize(11),
)
else else
...([ ...([
const Icon(Symbols.arrow_right, size: 14), const Icon(Symbols.arrow_right, size: 14),