💫 Animated profile card
This commit is contained in:
@@ -27,200 +27,198 @@ class AccountProfileCard extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final account = ref.watch(accountProvider(uname));
|
final account = ref.watch(accountProvider(uname));
|
||||||
final width =
|
final width = math
|
||||||
math.min(MediaQuery.of(context).size.width - 80, 360).toDouble();
|
.min(MediaQuery.of(context).size.width - 80, 360)
|
||||||
|
.toDouble();
|
||||||
|
|
||||||
|
final child = account.when(
|
||||||
|
data: (data) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
if (data.profile.background != null)
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(12),
|
||||||
|
),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
child: CloudImageWidget(file: data.profile.background),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
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(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
AccountName(
|
||||||
|
account: data,
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Text('@${data.name}').fontSize(12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
AccountStatusWidget(uname: data.name, padding: EdgeInsets.zero),
|
||||||
|
Tooltip(
|
||||||
|
message: 'creditsStatus'.tr(),
|
||||||
|
child: Row(
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.attribution,
|
||||||
|
size: 17,
|
||||||
|
fill: 1,
|
||||||
|
).padding(right: 2),
|
||||||
|
Text(
|
||||||
|
'${data.profile.socialCredits.toStringAsFixed(2)} pts',
|
||||||
|
).fontSize(12),
|
||||||
|
switch (data.profile.socialCreditsLevel) {
|
||||||
|
-1 => Text('socialCreditsLevelPoor').tr(),
|
||||||
|
0 => Text('socialCreditsLevelNormal').tr(),
|
||||||
|
1 => Text('socialCreditsLevelGood').tr(),
|
||||||
|
2 => Text('socialCreditsLevelExcellent').tr(),
|
||||||
|
_ => Text('unknown').tr(),
|
||||||
|
}.fontSize(12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (data.automatedId != null)
|
||||||
|
Row(
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.smart_toy,
|
||||||
|
size: 17,
|
||||||
|
fill: 1,
|
||||||
|
).padding(right: 2),
|
||||||
|
Text('accountAutomated').tr().fontSize(12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (data.profile.timeZone.isNotEmpty && !kIsWeb)
|
||||||
|
() {
|
||||||
|
try {
|
||||||
|
final tzInfo = getTzInfo(data.profile.timeZone);
|
||||||
|
return Row(
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.alarm,
|
||||||
|
size: 17,
|
||||||
|
fill: 1,
|
||||||
|
).padding(right: 2),
|
||||||
|
Text(
|
||||||
|
tzInfo.$2.formatCustomGlobal('HH:mm'),
|
||||||
|
).fontSize(12),
|
||||||
|
Text(tzInfo.$1.formatOffsetLocal()).fontSize(12),
|
||||||
|
],
|
||||||
|
).padding(top: 2);
|
||||||
|
} catch (e) {
|
||||||
|
return Row(
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.alarm,
|
||||||
|
size: 17,
|
||||||
|
fill: 1,
|
||||||
|
).padding(right: 2),
|
||||||
|
Text('timezoneNotFound'.tr()).fontSize(12),
|
||||||
|
],
|
||||||
|
).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),
|
||||||
|
ActivityPresenceWidget(
|
||||||
|
uname: uname,
|
||||||
|
isCompact: true,
|
||||||
|
compactPadding: const EdgeInsets.only(top: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, vertical: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
error: (err, _) => ResponseErrorWidget(
|
||||||
|
error: err,
|
||||||
|
onRetry: () => ref.invalidate(accountProvider(uname)),
|
||||||
|
),
|
||||||
|
loading: () => SizedBox(
|
||||||
|
width: width,
|
||||||
|
height: width,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
).center(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return PopupCard(
|
return PopupCard(
|
||||||
elevation: 8,
|
elevation: 8,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||||
child: SizedBox(
|
child: AnimatedSize(
|
||||||
width: width,
|
duration: const Duration(milliseconds: 200),
|
||||||
child: account.when(
|
curve: Curves.easeInOut,
|
||||||
data:
|
child: SizedBox(
|
||||||
(data) => Column(
|
width: width,
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: AnimatedSwitcher(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
duration: const Duration(milliseconds: 200),
|
||||||
children: [
|
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||||
if (data.profile.background != null)
|
return FadeTransition(opacity: animation, child: child);
|
||||||
ClipRRect(
|
},
|
||||||
borderRadius: const BorderRadius.vertical(
|
child: child,
|
||||||
top: Radius.circular(12),
|
),
|
||||||
),
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 16 / 9,
|
|
||||||
child: CloudImageWidget(file: data.profile.background),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
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(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
AccountName(
|
|
||||||
account: data,
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
Text('@${data.name}').fontSize(12),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Gap(12),
|
|
||||||
AccountStatusWidget(
|
|
||||||
uname: data.name,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
),
|
|
||||||
Tooltip(
|
|
||||||
message: 'creditsStatus'.tr(),
|
|
||||||
child: Row(
|
|
||||||
spacing: 6,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Symbols.attribution,
|
|
||||||
size: 17,
|
|
||||||
fill: 1,
|
|
||||||
).padding(right: 2),
|
|
||||||
Text(
|
|
||||||
'${data.profile.socialCredits.toStringAsFixed(2)} pts',
|
|
||||||
).fontSize(12),
|
|
||||||
switch (data.profile.socialCreditsLevel) {
|
|
||||||
-1 => Text('socialCreditsLevelPoor').tr(),
|
|
||||||
0 => Text('socialCreditsLevelNormal').tr(),
|
|
||||||
1 => Text('socialCreditsLevelGood').tr(),
|
|
||||||
2 => Text('socialCreditsLevelExcellent').tr(),
|
|
||||||
_ => Text('unknown').tr(),
|
|
||||||
}.fontSize(12),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (data.automatedId != null)
|
|
||||||
Row(
|
|
||||||
spacing: 6,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Symbols.smart_toy,
|
|
||||||
size: 17,
|
|
||||||
fill: 1,
|
|
||||||
).padding(right: 2),
|
|
||||||
Text('accountAutomated').tr().fontSize(12),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (data.profile.timeZone.isNotEmpty && !kIsWeb)
|
|
||||||
() {
|
|
||||||
try {
|
|
||||||
final tzInfo = getTzInfo(data.profile.timeZone);
|
|
||||||
return Row(
|
|
||||||
spacing: 6,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Symbols.alarm,
|
|
||||||
size: 17,
|
|
||||||
fill: 1,
|
|
||||||
).padding(right: 2),
|
|
||||||
Text(
|
|
||||||
tzInfo.$2.formatCustomGlobal('HH:mm'),
|
|
||||||
).fontSize(12),
|
|
||||||
Text(
|
|
||||||
tzInfo.$1.formatOffsetLocal(),
|
|
||||||
).fontSize(12),
|
|
||||||
],
|
|
||||||
).padding(top: 2);
|
|
||||||
} catch (e) {
|
|
||||||
return Row(
|
|
||||||
spacing: 6,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Symbols.alarm,
|
|
||||||
size: 17,
|
|
||||||
fill: 1,
|
|
||||||
).padding(right: 2),
|
|
||||||
Text('timezoneNotFound'.tr()).fontSize(12),
|
|
||||||
],
|
|
||||||
).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),
|
|
||||||
ActivityPresenceWidget(
|
|
||||||
uname: uname,
|
|
||||||
isCompact: true,
|
|
||||||
compactPadding: const EdgeInsets.only(top: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padding(horizontal: 24, vertical: 16),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
error:
|
|
||||||
(err, _) => ResponseErrorWidget(
|
|
||||||
error: err,
|
|
||||||
onRetry: () => ref.invalidate(accountProvider(uname)),
|
|
||||||
),
|
|
||||||
loading:
|
|
||||||
() => SizedBox(
|
|
||||||
width: width,
|
|
||||||
height: width,
|
|
||||||
child:
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
).center(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ class _MentionChipContent extends HookConsumerWidget {
|
|||||||
isHovered,
|
isHovered,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
error: (_, __) => Text(
|
error: (_, _) => Text(
|
||||||
alias,
|
alias,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
|
|||||||
Reference in New Issue
Block a user