230 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'dart:math' as math;
 | 
						|
 | 
						|
import 'package:easy_localization/easy_localization.dart';
 | 
						|
import 'package:flutter/foundation.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:go_router/go_router.dart';
 | 
						|
import 'package:flutter_popup_card/flutter_popup_card.dart';
 | 
						|
import 'package:gap/gap.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
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/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';
 | 
						|
import 'package:material_symbols_icons/symbols.dart';
 | 
						|
import 'package:styled_widget/styled_widget.dart';
 | 
						|
 | 
						|
class AccountProfileCard extends HookConsumerWidget {
 | 
						|
  final String uname;
 | 
						|
  const AccountProfileCard({super.key, required this.uname});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    final account = ref.watch(accountProvider(uname));
 | 
						|
    final width =
 | 
						|
        math.min(MediaQuery.of(context).size.width - 80, 360).toDouble();
 | 
						|
    return PopupCard(
 | 
						|
      elevation: 8,
 | 
						|
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
 | 
						|
      child: SizedBox(
 | 
						|
        width: width,
 | 
						|
        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: [
 | 
						|
                          ProfilePictureWidget(file: data.profile.picture),
 | 
						|
                          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.star,
 | 
						|
                              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);
 | 
						|
                          }
 | 
						|
                        }(),
 | 
						|
                      if (data.badges.isNotEmpty)
 | 
						|
                        BadgeList(badges: data.badges).padding(top: 12),
 | 
						|
                      LevelingProgressCard(
 | 
						|
                        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),
 | 
						|
                    ],
 | 
						|
                  ).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(),
 | 
						|
              ),
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class AccountPfcGestureDetector extends StatelessWidget {
 | 
						|
  final String? uname;
 | 
						|
  final Widget child;
 | 
						|
  const AccountPfcGestureDetector({
 | 
						|
    super.key,
 | 
						|
    required this.uname,
 | 
						|
    required this.child,
 | 
						|
  });
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    return GestureDetector(
 | 
						|
      child: child,
 | 
						|
      onTapDown: (details) {
 | 
						|
        if (uname != null) {
 | 
						|
          showAccountProfileCard(
 | 
						|
            context,
 | 
						|
            uname!,
 | 
						|
            offset: details.localPosition,
 | 
						|
          );
 | 
						|
        }
 | 
						|
      },
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
Future<void> showAccountProfileCard(
 | 
						|
  BuildContext context,
 | 
						|
  String uname, {
 | 
						|
  Offset? offset,
 | 
						|
}) async {
 | 
						|
  await showPopupCard<void>(
 | 
						|
    offset: offset ?? Offset.zero,
 | 
						|
    context: context,
 | 
						|
    builder: (context) => AccountProfileCard(uname: uname),
 | 
						|
    alignment: Alignment.center,
 | 
						|
    dimBackground: true,
 | 
						|
  );
 | 
						|
}
 |