💄 Optimize the pfc and show the activities
This commit is contained in:
@@ -206,7 +206,7 @@ class _AccountProfileDetail extends StatelessWidget {
|
||||
child: Row(
|
||||
spacing: 6,
|
||||
children: [
|
||||
Icon(Symbols.star, size: 17, fill: 1).padding(right: 2),
|
||||
Icon(Symbols.attribution, size: 17, fill: 1).padding(right: 2),
|
||||
Text('${data.profile.socialCredits.toStringAsFixed(2)} pts'),
|
||||
Text('·').bold(),
|
||||
switch (data.profile.socialCreditsLevel) {
|
||||
|
||||
@@ -11,8 +11,9 @@ import 'package:island/screens/account/profile.dart';
|
||||
import 'package:island/services/time.dart';
|
||||
import 'package:island/services/timezone/native.dart';
|
||||
import 'package:island/widgets/account/account_name.dart';
|
||||
import 'package:island/widgets/account/activity_presence.dart';
|
||||
import 'package:island/widgets/account/badge.dart';
|
||||
import 'package:island/widgets/account/leveling_progress.dart';
|
||||
|
||||
import 'package:island/widgets/account/status.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/response.dart';
|
||||
@@ -54,7 +55,30 @@ class AccountProfileCard extends HookConsumerWidget {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ProfilePictureWidget(file: data.profile.picture),
|
||||
GestureDetector(
|
||||
child: Badge(
|
||||
isLabelVisible: true,
|
||||
padding: EdgeInsets.all(2),
|
||||
label: Icon(
|
||||
Symbols.launch,
|
||||
size: 12,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
offset: Offset(4, 28),
|
||||
child: ProfilePictureWidget(
|
||||
file: data.profile.picture,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
context.pushNamed(
|
||||
'accountProfile',
|
||||
pathParameters: {'name': data.name},
|
||||
);
|
||||
},
|
||||
),
|
||||
const Gap(12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -81,7 +105,7 @@ class AccountProfileCard extends HookConsumerWidget {
|
||||
spacing: 6,
|
||||
children: [
|
||||
Icon(
|
||||
Symbols.star,
|
||||
Symbols.attribution,
|
||||
size: 17,
|
||||
fill: 1,
|
||||
).padding(right: 2),
|
||||
@@ -144,25 +168,40 @@ class AccountProfileCard extends HookConsumerWidget {
|
||||
).padding(top: 2);
|
||||
}
|
||||
}(),
|
||||
Row(
|
||||
spacing: 6,
|
||||
children: [
|
||||
Icon(
|
||||
Symbols.stairs,
|
||||
size: 17,
|
||||
fill: 1,
|
||||
).padding(right: 2),
|
||||
Text(
|
||||
'levelingProgressLevel'.tr(
|
||||
args: [data.profile.level.toString()],
|
||||
),
|
||||
).fontSize(12),
|
||||
Expanded(
|
||||
child: Tooltip(
|
||||
message:
|
||||
'${(data.profile.levelingProgress * 100).toStringAsFixed(2)}%',
|
||||
child: LinearProgressIndicator(
|
||||
value: data.profile.levelingProgress,
|
||||
stopIndicatorRadius: 0,
|
||||
trackGap: 0,
|
||||
minHeight: 4,
|
||||
).padding(top: 1),
|
||||
),
|
||||
),
|
||||
],
|
||||
).padding(top: 2),
|
||||
if (data.badges.isNotEmpty)
|
||||
BadgeList(badges: data.badges).padding(top: 12),
|
||||
LevelingProgressCard(
|
||||
ActivityPresenceWidget(
|
||||
uname: uname,
|
||||
isCompact: true,
|
||||
level: data.profile.level,
|
||||
experience: data.profile.experience,
|
||||
progress: data.profile.levelingProgress,
|
||||
).padding(top: 12),
|
||||
FilledButton.tonalIcon(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
context.pushNamed(
|
||||
'accountProfile',
|
||||
pathParameters: {'name': data.name},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Symbols.launch),
|
||||
label: Text('accountProfileView').tr(),
|
||||
).padding(top: 12, horizontal: 2),
|
||||
compactPadding: const EdgeInsets.only(top: 12),
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 24, vertical: 16),
|
||||
],
|
||||
|
||||
@@ -68,8 +68,15 @@ const kPresenseActivityIcons = <IconData>[
|
||||
|
||||
class ActivityPresenceWidget extends ConsumerWidget {
|
||||
final String uname;
|
||||
final bool isCompact;
|
||||
final EdgeInsets compactPadding;
|
||||
|
||||
const ActivityPresenceWidget({super.key, required this.uname});
|
||||
const ActivityPresenceWidget({
|
||||
super.key,
|
||||
required this.uname,
|
||||
this.isCompact = false,
|
||||
this.compactPadding = EdgeInsets.zero,
|
||||
});
|
||||
|
||||
List<Widget> _buildDiscordImages(WidgetRef ref, SnPresenceActivity activity) {
|
||||
final List<Widget> images = [];
|
||||
@@ -139,6 +146,106 @@ class ActivityPresenceWidget extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final activitiesAsync = ref.watch(presenceActivitiesProvider(uname));
|
||||
|
||||
if (isCompact) {
|
||||
return activitiesAsync.when(
|
||||
data: (activities) {
|
||||
if (activities.isEmpty) return const SizedBox.shrink();
|
||||
final activity = activities.first;
|
||||
return Padding(
|
||||
padding: compactPadding,
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (activity.largeImage != null &&
|
||||
activity.largeImage!.startsWith('discord:'))
|
||||
ref
|
||||
.watch(
|
||||
discordAssetsUrlProvider(
|
||||
activity,
|
||||
activity.largeImage!.substring('discord:'.length),
|
||||
),
|
||||
)
|
||||
.when(
|
||||
data:
|
||||
(url) =>
|
||||
url != null
|
||||
? ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: url,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
loading:
|
||||
() => const SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: CircularProgressIndicator(strokeWidth: 1),
|
||||
),
|
||||
error: (error, stack) => const SizedBox.shrink(),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
(activity.title?.isEmpty ?? true)
|
||||
? 'unknown'.tr()
|
||||
: activity.title!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
).fontSize(13),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
kPresenseActivityTypes[activity.type],
|
||||
).tr().fontSize(11),
|
||||
Icon(
|
||||
kPresenseActivityIcons[activity.type],
|
||||
size: 15,
|
||||
fill: 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: Stream.periodic(const Duration(seconds: 1)),
|
||||
builder: (context, snapshot) {
|
||||
final now = DateTime.now();
|
||||
|
||||
// Check if lease has expired and refresh if needed
|
||||
if (now.isAfter(activity.leaseExpiresAt)) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.invalidate(presenceActivitiesProvider(uname));
|
||||
});
|
||||
}
|
||||
|
||||
final duration = now.difference(activity.createdAt);
|
||||
final hours = duration.inHours.toString().padLeft(2, '0');
|
||||
final minutes = (duration.inMinutes % 60)
|
||||
.toString()
|
||||
.padLeft(2, '0');
|
||||
final seconds = (duration.inSeconds % 60)
|
||||
.toString()
|
||||
.padLeft(2, '0');
|
||||
return Text(
|
||||
'$hours:$minutes:$seconds',
|
||||
).textColor(Colors.green).fontSize(12);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () => const SizedBox.shrink(),
|
||||
error: (error, stack) => const SizedBox.shrink(),
|
||||
);
|
||||
}
|
||||
|
||||
return activitiesAsync.when(
|
||||
data:
|
||||
(activities) => Card(
|
||||
@@ -206,8 +313,12 @@ class ActivityPresenceWidget extends ConsumerWidget {
|
||||
|
||||
// Check if lease has expired and refresh if needed
|
||||
if (now.isAfter(activity.leaseExpiresAt)) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.invalidate(presenceActivitiesProvider(uname));
|
||||
WidgetsBinding.instance.addPostFrameCallback((
|
||||
_,
|
||||
) {
|
||||
ref.invalidate(
|
||||
presenceActivitiesProvider(uname),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -238,23 +349,36 @@ class ActivityPresenceWidget extends ConsumerWidget {
|
||||
Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (activity.titleUrl != null && activity.titleUrl!.isNotEmpty)
|
||||
if (activity.titleUrl != null &&
|
||||
activity.titleUrl!.isNotEmpty)
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => launchUrlString(activity.titleUrl!),
|
||||
onPressed:
|
||||
() =>
|
||||
launchUrlString(activity.titleUrl!),
|
||||
icon: const Icon(Symbols.link, size: 16),
|
||||
label: const Text('Open Title Link'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
textStyle: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
if (activity.subtitleUrl != null && activity.subtitleUrl!.isNotEmpty)
|
||||
if (activity.subtitleUrl != null &&
|
||||
activity.subtitleUrl!.isNotEmpty)
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => launchUrlString(activity.subtitleUrl!),
|
||||
onPressed:
|
||||
() => launchUrlString(
|
||||
activity.subtitleUrl!,
|
||||
),
|
||||
icon: const Icon(Symbols.link, size: 16),
|
||||
label: const Text('Open Subtitle Link'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
textStyle: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user