Wearable badge

This commit is contained in:
2025-03-02 21:08:38 +08:00
parent 72e6a6a1f6
commit 6e03a00280
12 changed files with 312 additions and 136 deletions

View File

@ -20,6 +20,7 @@ import 'package:surface/types/post.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/universal_image.dart';
import 'package:surface/theme.dart';
const Map<String, (String, IconData, Color)> kBadgesMeta = {
'company.staff': (
@ -43,8 +44,7 @@ class UserScreen extends StatefulWidget {
State<UserScreen> createState() => _UserScreenState();
}
class _UserScreenState extends State<UserScreen>
with SingleTickerProviderStateMixin {
class _UserScreenState extends State<UserScreen> with SingleTickerProviderStateMixin {
late final ScrollController _scrollController = ScrollController();
SnAccount? _account;
@ -70,8 +70,7 @@ class _UserScreenState extends State<UserScreen>
Future<void> _getCheckInRecords() async {
try {
final sn = context.read<SnNetworkProvider>();
final resp =
await sn.client.get('/cgi/id/users/${widget.name}/check-in?take=14');
final resp = await sn.client.get('/cgi/id/users/${widget.name}/check-in?take=14');
setState(() {
_records = List.from(
resp.data['data']?.map((x) => SnCheckInRecord.fromJson(x)) ?? [],
@ -104,8 +103,7 @@ class _UserScreenState extends State<UserScreen>
Future<void> _fetchPublishers() async {
try {
final sn = context.read<SnNetworkProvider>();
final resp =
await sn.client.get('/cgi/co/publishers?user=${widget.name}');
final resp = await sn.client.get('/cgi/co/publishers?user=${widget.name}');
_publishers = List<SnPublisher>.from(
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? [],
);
@ -151,8 +149,7 @@ class _UserScreenState extends State<UserScreen>
'related': _account!.name,
});
if (!mounted) return;
context.showSnackbar(
'userBlocked'.tr(args: ['@${_account?.name ?? 'unknown'}']));
context.showSnackbar('userBlocked'.tr(args: ['@${_account?.name ?? 'unknown'}']));
} catch (err) {
if (!mounted) return;
context.showErrorDialog(err);
@ -168,11 +165,9 @@ class _UserScreenState extends State<UserScreen>
try {
final rel = context.read<SnRelationshipProvider>();
await rel.updateRelationship(
_account!.id, 1, _accountRelationship?.permNodes ?? {});
await rel.updateRelationship(_account!.id, 1, _accountRelationship?.permNodes ?? {});
if (!mounted) return;
context.showSnackbar(
'userUnblocked'.tr(args: ['@${_account?.name ?? 'unknown'}']));
context.showSnackbar('userUnblocked'.tr(args: ['@${_account?.name ?? 'unknown'}']));
} catch (err) {
if (!mounted) return;
context.showErrorDialog(err);
@ -198,14 +193,12 @@ class _UserScreenState extends State<UserScreen>
double _appBarBlur = 0.0;
late final _appBarWidth = MediaQuery.of(context).size.width;
late final _appBarHeight =
(_appBarWidth * kBannerAspectRatio).roundToDouble();
late final _appBarHeight = (_appBarWidth * kBannerAspectRatio).roundToDouble();
void _updateAppBarBlur() {
if (_scrollController.offset > _appBarHeight) return;
setState(() {
_appBarBlur =
(_scrollController.offset / _appBarHeight * 10).clamp(0.0, 10.0);
_appBarBlur = (_scrollController.offset / _appBarHeight * 10).clamp(0.0, 10.0);
});
}
@ -273,20 +266,18 @@ class _UserScreenState extends State<UserScreen>
text: TextSpan(children: [
TextSpan(
text: _account!.nick,
style:
Theme.of(context).textTheme.titleLarge!.copyWith(
color: Colors.white,
shadows: labelShadows,
),
style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Colors.white,
shadows: labelShadows,
),
),
const TextSpan(text: '\n'),
TextSpan(
text: '@${_account!.name}',
style:
Theme.of(context).textTheme.bodySmall!.copyWith(
color: Colors.white,
shadows: labelShadows,
),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Colors.white,
shadows: labelShadows,
),
),
]),
),
@ -354,8 +345,7 @@ class _UserScreenState extends State<UserScreen>
PopupMenuButton(
padding: EdgeInsets.zero,
style: ButtonStyle(
visualDensity:
VisualDensity(horizontal: -4, vertical: -4),
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
),
itemBuilder: (context) => [
PopupMenuItem(
@ -415,9 +405,7 @@ class _UserScreenState extends State<UserScreen>
Symbols.circle,
fill: 1,
size: 16,
color: (_status?.isOnline ?? false)
? Colors.green
: Colors.grey,
color: (_status?.isOnline ?? false) ? Colors.green : Colors.grey,
).padding(all: 4),
const Gap(8),
Text(
@ -427,9 +415,7 @@ class _UserScreenState extends State<UserScreen>
: 'accountStatusOffline'.tr()
: 'loading'.tr(),
),
if (_status != null &&
!_status!.isOnline &&
_status!.lastSeenAt != null)
if (_status != null && !_status!.isOnline && _status!.lastSeenAt != null)
Text(
'accountStatusLastSeen'.tr(args: [
_status!.lastSeenAt != null
@ -450,13 +436,12 @@ class _UserScreenState extends State<UserScreen>
richMessage: TextSpan(
children: [
TextSpan(
text: kBadgesMeta[ele.type]?.$1.tr() ??
'unknown'.tr()),
text: kBadgesMeta[ele.type]?.$1.tr() ?? 'unknown'.tr(),
),
if (ele.metadata['title'] != null)
TextSpan(
text: '\n${ele.metadata['title']}',
style: const TextStyle(
fontWeight: FontWeight.bold),
style: const TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(text: '\n'),
TextSpan(
@ -465,9 +450,10 @@ class _UserScreenState extends State<UserScreen>
],
),
child: Icon(
kBadgesMeta[ele.type]?.$2 ??
Symbols.question_mark,
color: kBadgesMeta[ele.type]?.$3,
kBadgesMeta[ele.type]?.$2 ?? Symbols.question_mark,
color: ele.metadata['color'] != null
? HexColor.fromHex(ele.metadata['color']!)
: kBadgesMeta[ele.type]?.$3,
fill: 1,
),
),
@ -482,9 +468,7 @@ class _UserScreenState extends State<UserScreen>
children: [
const Icon(Symbols.calendar_add_on),
const Gap(8),
Text('publisherJoinedAt').tr(args: [
DateFormat('y/M/d').format(_account!.createdAt)
]),
Text('publisherJoinedAt').tr(args: [DateFormat('y/M/d').format(_account!.createdAt)]),
],
),
Row(
@ -517,24 +501,17 @@ class _UserScreenState extends State<UserScreen>
children: [
const Icon(Symbols.star),
const Gap(8),
Text(
'Lv${getLevelFromExp(_account?.profile?.experience ?? 0)}'),
Text('Lv${getLevelFromExp(_account?.profile?.experience ?? 0)}'),
const Gap(8),
Text(calcLevelUpProgressLevel(
_account?.profile?.experience ?? 0))
.fontSize(11)
.opacity(0.5),
Text(calcLevelUpProgressLevel(_account?.profile?.experience ?? 0)).fontSize(11).opacity(0.5),
const Gap(8),
Container(
width: double.infinity,
constraints: const BoxConstraints(maxWidth: 160),
child: LinearProgressIndicator(
value: calcLevelUpProgress(
_account?.profile?.experience ?? 0),
value: calcLevelUpProgress(_account?.profile?.experience ?? 0),
borderRadius: BorderRadius.circular(8),
backgroundColor: Theme.of(context)
.colorScheme
.surfaceContainer,
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
).alignment(Alignment.centerLeft),
),
],
@ -554,11 +531,7 @@ class _UserScreenState extends State<UserScreen>
return Text(
'accountCheckInNoRecords',
textAlign: TextAlign.center,
)
.tr()
.fontWeight(FontWeight.bold)
.center()
.padding(horizontal: 20, vertical: 8);
).tr().fontWeight(FontWeight.bold).center().padding(horizontal: 20, vertical: 8);
}
return SizedBox(
width: double.infinity,
@ -579,11 +552,7 @@ class _UserScreenState extends State<UserScreen>
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('accountBadge')
.bold()
.fontSize(17)
.tr()
.padding(horizontal: 20, bottom: 4),
Text('accountBadge').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4),
SizedBox(
height: 80,
width: double.infinity,
@ -597,9 +566,10 @@ class _UserScreenState extends State<UserScreen>
child: Card(
child: ListTile(
leading: Icon(
kBadgesMeta[badge.type]?.$2 ??
Symbols.question_mark,
color: kBadgesMeta[badge.type]?.$3,
kBadgesMeta[badge.type]?.$2 ?? Symbols.question_mark,
color: badge.metadata['color'] != null
? HexColor.fromHex(badge.metadata['color']!)
: kBadgesMeta[badge.type]?.$3,
fill: 1,
),
title: Text(
@ -608,8 +578,7 @@ class _UserScreenState extends State<UserScreen>
subtitle: badge.metadata['title'] != null
? Text(badge.metadata['title'])
: Text(
DateFormat('y/M/d')
.format(badge.createdAt),
DateFormat('y/M/d').format(badge.createdAt),
),
),
),
@ -705,8 +674,7 @@ class CheckInRecordChart extends StatelessWidget {
),
)
.toList(),
getTooltipColor: (_) =>
Theme.of(context).colorScheme.surfaceContainerHigh,
getTooltipColor: (_) => Theme.of(context).colorScheme.surfaceContainerHigh,
),
),
titlesData: FlTitlesData(