💄 Optimize the pfc and show the activities
This commit is contained in:
@@ -206,7 +206,7 @@ class _AccountProfileDetail extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
children: [
|
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('${data.profile.socialCredits.toStringAsFixed(2)} pts'),
|
||||||
Text('·').bold(),
|
Text('·').bold(),
|
||||||
switch (data.profile.socialCreditsLevel) {
|
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/time.dart';
|
||||||
import 'package:island/services/timezone/native.dart';
|
import 'package:island/services/timezone/native.dart';
|
||||||
import 'package:island/widgets/account/account_name.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/badge.dart';
|
||||||
import 'package:island/widgets/account/leveling_progress.dart';
|
|
||||||
import 'package:island/widgets/account/status.dart';
|
import 'package:island/widgets/account/status.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
import 'package:island/widgets/response.dart';
|
import 'package:island/widgets/response.dart';
|
||||||
@@ -54,7 +55,30 @@ class AccountProfileCard extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
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),
|
const Gap(12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -81,7 +105,7 @@ class AccountProfileCard extends HookConsumerWidget {
|
|||||||
spacing: 6,
|
spacing: 6,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Symbols.star,
|
Symbols.attribution,
|
||||||
size: 17,
|
size: 17,
|
||||||
fill: 1,
|
fill: 1,
|
||||||
).padding(right: 2),
|
).padding(right: 2),
|
||||||
@@ -144,25 +168,40 @@ class AccountProfileCard extends HookConsumerWidget {
|
|||||||
).padding(top: 2);
|
).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)
|
if (data.badges.isNotEmpty)
|
||||||
BadgeList(badges: data.badges).padding(top: 12),
|
BadgeList(badges: data.badges).padding(top: 12),
|
||||||
LevelingProgressCard(
|
ActivityPresenceWidget(
|
||||||
|
uname: uname,
|
||||||
isCompact: true,
|
isCompact: true,
|
||||||
level: data.profile.level,
|
compactPadding: const EdgeInsets.only(top: 12),
|
||||||
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),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 24, vertical: 16),
|
).padding(horizontal: 24, vertical: 16),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -68,8 +68,15 @@ const kPresenseActivityIcons = <IconData>[
|
|||||||
|
|
||||||
class ActivityPresenceWidget extends ConsumerWidget {
|
class ActivityPresenceWidget extends ConsumerWidget {
|
||||||
final String uname;
|
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) {
|
List<Widget> _buildDiscordImages(WidgetRef ref, SnPresenceActivity activity) {
|
||||||
final List<Widget> images = [];
|
final List<Widget> images = [];
|
||||||
@@ -139,6 +146,106 @@ class ActivityPresenceWidget extends ConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final activitiesAsync = ref.watch(presenceActivitiesProvider(uname));
|
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(
|
return activitiesAsync.when(
|
||||||
data:
|
data:
|
||||||
(activities) => Card(
|
(activities) => Card(
|
||||||
@@ -206,8 +313,12 @@ class ActivityPresenceWidget extends ConsumerWidget {
|
|||||||
|
|
||||||
// Check if lease has expired and refresh if needed
|
// Check if lease has expired and refresh if needed
|
||||||
if (now.isAfter(activity.leaseExpiresAt)) {
|
if (now.isAfter(activity.leaseExpiresAt)) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((
|
||||||
ref.invalidate(presenceActivitiesProvider(uname));
|
_,
|
||||||
|
) {
|
||||||
|
ref.invalidate(
|
||||||
|
presenceActivitiesProvider(uname),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,23 +349,36 @@ class ActivityPresenceWidget extends ConsumerWidget {
|
|||||||
Row(
|
Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
if (activity.titleUrl != null && activity.titleUrl!.isNotEmpty)
|
if (activity.titleUrl != null &&
|
||||||
|
activity.titleUrl!.isNotEmpty)
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () => launchUrlString(activity.titleUrl!),
|
onPressed:
|
||||||
|
() =>
|
||||||
|
launchUrlString(activity.titleUrl!),
|
||||||
icon: const Icon(Symbols.link, size: 16),
|
icon: const Icon(Symbols.link, size: 16),
|
||||||
label: const Text('Open Title Link'),
|
label: const Text('Open Title Link'),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 6,
|
||||||
|
),
|
||||||
textStyle: const TextStyle(fontSize: 12),
|
textStyle: const TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (activity.subtitleUrl != null && activity.subtitleUrl!.isNotEmpty)
|
if (activity.subtitleUrl != null &&
|
||||||
|
activity.subtitleUrl!.isNotEmpty)
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () => launchUrlString(activity.subtitleUrl!),
|
onPressed:
|
||||||
|
() => launchUrlString(
|
||||||
|
activity.subtitleUrl!,
|
||||||
|
),
|
||||||
icon: const Icon(Symbols.link, size: 16),
|
icon: const Icon(Symbols.link, size: 16),
|
||||||
label: const Text('Open Subtitle Link'),
|
label: const Text('Open Subtitle Link'),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 6,
|
||||||
|
),
|
||||||
textStyle: const TextStyle(fontSize: 12),
|
textStyle: const TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user