💄 Optimized featured post

This commit is contained in:
2025-12-21 13:07:44 +08:00
parent 7a56e7882e
commit cb7eef943c
6 changed files with 102 additions and 171 deletions

View File

@@ -1508,7 +1508,7 @@
"createAccountAgreeTerms": "I've read these terms and agree to the terms of service.",
"createAccountProfile": "Create your profile",
"createAccountToS": "Review Terms & Conditions",
"accountActivationAlert": "Remember to activate your account",
"accountActivationAlert": "Activate your account",
"accountActivationAlertHint": "Unactivated account may leads to various of permission issues, activate your account by clicking the link we sent to your email inbox.",
"accountActivationResendHint": "Didn't see it? Try click the button below to resend one. If you need to update your email while your account was unactivated, feel free to contact our customer service.",
"accountActivationResend": "Resend",

View File

@@ -1486,7 +1486,7 @@
"createAccountAgreeTerms": "我已阅读并同意服务条款。",
"createAccountProfile": "创建您的个人资料",
"createAccountToS": "查看条款与条件",
"accountActivationAlert": "请记住激活您的账户",
"accountActivationAlert": "激活您的账户",
"accountActivationAlertHint": "未激活的账户可能会导致各种权限问题,请点击我们发送到您邮箱收件箱的链接来激活您的账户。",
"accountActivationResendHint": "没收到?请尝试点击下方按钮重新发送。如果您在账户未激活期间需要更新邮箱,请随时联系我们的客服。",
"accountActivationResend": "重新发送",

View File

@@ -13,12 +13,12 @@ import 'package:island/pods/userinfo.dart';
import 'package:island/screens/chat/chat.dart';
import 'package:island/services/event_bus.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/account_name.dart';
import 'package:island/widgets/account/fortune_graph.dart';
import 'package:island/widgets/account/friends_overview.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/notification_tile.dart';
import 'package:island/widgets/post/post_featured.dart';
import 'package:island/widgets/post/post_item.dart';
import 'package:island/widgets/check_in.dart';
import 'package:island/screens/notification.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
@@ -124,9 +124,13 @@ class _DashboardGridWide extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userInfo = ref.watch(userInfoProvider);
return Row(
spacing: 16,
children: [
if (userInfo.value != null && userInfo.value?.activatedAt == null)
SizedBox(width: 400, child: AccountUnactivatedCard()),
SizedBox(
width: 400,
child: Column(
@@ -151,7 +155,7 @@ class _DashboardGridWide extends HookConsumerWidget {
],
),
),
SizedBox(width: 400, child: FeaturedPostCard()),
SizedBox(width: 400, child: PostFeaturedList(collapsable: false)),
SizedBox(
width: 400,
child: Column(
@@ -179,12 +183,19 @@ class _DashboardGridNarrow extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userInfo = ref.watch(userInfoProvider);
return Column(
spacing: 16,
children: [
if (userInfo.value != null && userInfo.value?.activatedAt == null)
AccountUnactivatedCard(),
CheckInWidget(margin: EdgeInsets.zero, checkInOnly: true),
FortuneCard(),
SizedBox(height: 400, child: FeaturedPostCard()),
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 400),
child: PostFeaturedList(),
),
FriendsOverviewWidget(),
NotificationsCard(),
ChatListCard(),
@@ -318,65 +329,6 @@ class ClockCard extends HookConsumerWidget {
}
}
class FeaturedPostCard extends HookConsumerWidget {
const FeaturedPostCard({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final featuredPostsAsync = ref.watch(featuredPostsProvider);
return ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Card(
color: Theme.of(context).colorScheme.surfaceContainerHigh,
margin: EdgeInsets.zero,
child: Column(
children: [
SizedBox(
height: 48,
child: Row(
spacing: 8,
children: [
const Icon(Symbols.highlight),
Text('highlightPost').tr(),
],
).padding(horizontal: 16, vertical: 8),
),
Expanded(
child: featuredPostsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
data: (posts) {
if (posts.isEmpty) {
return Padding(
padding: EdgeInsets.all(16),
child: Center(
child: Text('noFeaturedPostsAvailable').tr(),
),
);
}
return PageView.builder(
scrollDirection: Axis.horizontal,
itemCount: posts.length,
itemBuilder: (context, index) {
return SingleChildScrollView(
child: PostActionableItem(
item: posts[index],
borderRadius: 8,
),
);
},
);
},
),
),
],
),
),
);
}
}
class NotificationsCard extends HookConsumerWidget {
const NotificationsCard({super.key});

View File

@@ -346,29 +346,7 @@ class ExploreScreen extends HookConsumerWidget {
child: Align(
alignment: Alignment.topCenter,
child: SingleChildScrollView(
child: Column(
spacing: 8,
children: [
const Gap(4),
if (user.value?.activatedAt == null)
AccountUnactivatedCard(),
CheckInWidget(
margin: EdgeInsets.zero,
onChecked: () {
ref.invalidate(eventCalendarProvider(query.value));
},
),
if (notificationCount.value != null &&
notificationCount.value! > 0)
notificationIndicatorWidget(
context,
count: notificationCount.value ?? 0,
margin: EdgeInsets.zero,
),
PostFeaturedList(),
FriendsOverviewWidget(),
],
),
child: Column(spacing: 8, children: [const Gap(4)]),
),
),
)

View File

@@ -219,20 +219,20 @@ class AccountName extends StatelessWidget {
if (account.automatedId != null)
hideOverlay
? Icon(
Symbols.smart_toy,
size: 16,
color: nameStyle.color,
fill: 1,
)
: Tooltip(
message: 'accountAutomated'.tr(),
child: Icon(
Symbols.smart_toy,
size: 16,
color: nameStyle.color,
fill: 1,
)
: Tooltip(
message: 'accountAutomated'.tr(),
child: Icon(
Symbols.smart_toy,
size: 16,
color: nameStyle.color,
fill: 1,
),
),
),
],
);
}
@@ -275,20 +275,20 @@ class AccountName extends StatelessWidget {
if (account.automatedId != null)
hideOverlay
? Icon(
Symbols.smart_toy,
size: 16,
color: nameStyle.color,
fill: 1,
)
: Tooltip(
message: 'accountAutomated'.tr(),
child: Icon(
Symbols.smart_toy,
size: 16,
color: nameStyle.color,
fill: 1,
)
: Tooltip(
message: 'accountAutomated'.tr(),
child: Icon(
Symbols.smart_toy,
size: 16,
color: nameStyle.color,
fill: 1,
),
),
),
],
);
}
@@ -310,29 +310,28 @@ class VerificationMark extends StatelessWidget {
? kVerificationMarkIcons[mark.type]
: Symbols.verified,
size: 16,
color:
(kVerificationMarkColors.length > mark.type && mark.type >= 0)
? kVerificationMarkColors[mark.type]
: Colors.blue,
color: (kVerificationMarkColors.length > mark.type && mark.type >= 0)
? kVerificationMarkColors[mark.type]
: Colors.blue,
fill: 1,
);
return hideOverlay
? icon
: Tooltip(
richMessage: TextSpan(
text: mark.title ?? 'No title',
children: [
TextSpan(text: '\n'),
TextSpan(
text: mark.description ?? 'descriptionNone'.tr(),
style: TextStyle(fontWeight: FontWeight.normal),
),
],
style: TextStyle(fontWeight: FontWeight.bold),
),
child: icon,
);
richMessage: TextSpan(
text: mark.title ?? 'No title',
children: [
TextSpan(text: '\n'),
TextSpan(
text: mark.description ?? 'descriptionNone'.tr(),
style: TextStyle(fontWeight: FontWeight.normal),
),
],
style: TextStyle(fontWeight: FontWeight.bold),
),
child: icon,
);
}
}
@@ -384,19 +383,19 @@ class StellarMembershipMark extends StatelessWidget {
return hideOverlay
? icon
: Tooltip(
richMessage: TextSpan(
text: 'stellarMembership'.tr(),
children: [
TextSpan(text: '\n'),
TextSpan(
text: 'currentMembershipMember'.tr(args: [tierName]),
style: TextStyle(fontWeight: FontWeight.normal),
),
],
style: TextStyle(fontWeight: FontWeight.bold),
),
child: icon,
);
richMessage: TextSpan(
text: 'stellarMembership'.tr(),
children: [
TextSpan(text: '\n'),
TextSpan(
text: 'currentMembershipMember'.tr(args: [tierName]),
style: TextStyle(fontWeight: FontWeight.normal),
),
],
style: TextStyle(fontWeight: FontWeight.bold),
),
child: icon,
);
}
}
@@ -414,10 +413,9 @@ class VerificationStatusCard extends StatelessWidget {
? kVerificationMarkIcons[mark.type]
: Symbols.verified,
size: 32,
color:
(kVerificationMarkColors.length > mark.type && mark.type >= 0)
? kVerificationMarkColors[mark.type]
: Colors.blue,
color: (kVerificationMarkColors.length > mark.type && mark.type >= 0)
? kVerificationMarkColors[mark.type]
: Colors.blue,
fill: 1,
).alignment(Alignment.centerLeft),
const Gap(8),

View File

@@ -21,7 +21,8 @@ Future<List<SnPost>> featuredPosts(Ref ref) async {
}
class PostFeaturedList extends HookConsumerWidget {
const PostFeaturedList({super.key});
final bool collapsable;
const PostFeaturedList({super.key, this.collapsable = true});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -86,6 +87,7 @@ class PostFeaturedList extends HookConsumerWidget {
color: Theme.of(context).colorScheme.surfaceContainerHigh,
margin: EdgeInsets.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 48,
@@ -121,36 +123,37 @@ class PostFeaturedList extends HookConsumerWidget {
},
icon: const Icon(Symbols.arrow_right),
),
IconButton(
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
constraints: const BoxConstraints(),
onPressed: () {
isCollapsed.value = !isCollapsed.value;
talker.info(
'Manual toggle. isCollapsed set to ${isCollapsed.value}',
);
if (isCollapsed.value &&
featuredPostsAsync.hasValue &&
featuredPostsAsync.value!.isNotEmpty) {
prefs.setString(
kFeaturedPostsCollapsedId,
featuredPostsAsync.value!.first.id,
);
if (collapsable)
IconButton(
padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
constraints: const BoxConstraints(),
onPressed: () {
isCollapsed.value = !isCollapsed.value;
talker.info(
'Stored collapsed ID: ${featuredPostsAsync.value!.first.id}',
'Manual toggle. isCollapsed set to ${isCollapsed.value}',
);
} else {
prefs.remove(kFeaturedPostsCollapsedId);
talker.info('Removed stored collapsed ID.');
}
},
icon: Icon(
isCollapsed.value
? Symbols.expand_more
: Symbols.expand_less,
if (isCollapsed.value &&
featuredPostsAsync.hasValue &&
featuredPostsAsync.value!.isNotEmpty) {
prefs.setString(
kFeaturedPostsCollapsedId,
featuredPostsAsync.value!.first.id,
);
talker.info(
'Stored collapsed ID: ${featuredPostsAsync.value!.first.id}',
);
} else {
prefs.remove(kFeaturedPostsCollapsedId);
talker.info('Removed stored collapsed ID.');
}
},
icon: Icon(
isCollapsed.value
? Symbols.expand_more
: Symbols.expand_less,
),
),
),
],
).padding(horizontal: 16, vertical: 8),
),
@@ -158,14 +161,14 @@ class PostFeaturedList extends HookConsumerWidget {
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
child: Visibility(
visible: !isCollapsed.value,
visible: collapsable ? !isCollapsed.value : true,
child: featuredPostsAsync.when(
loading:
() => const Center(child: CircularProgressIndicator()),
loading: () =>
const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
data: (posts) {
return SizedBox(
height: 320,
height: 344,
child: PageView.builder(
controller: pageViewController,
scrollDirection: Axis.horizontal,