371 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			371 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'package:easy_localization/easy_localization.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:gap/gap.dart';
 | 
						|
import 'package:island/models/account.dart';
 | 
						|
import 'package:island/models/wallet.dart';
 | 
						|
import 'package:material_symbols_icons/symbols.dart';
 | 
						|
import 'package:styled_widget/styled_widget.dart';
 | 
						|
 | 
						|
const Map<String, Color> kUsernamePlainColors = {
 | 
						|
  'red': Colors.red,
 | 
						|
  'blue': Colors.blue,
 | 
						|
  'green': Colors.green,
 | 
						|
  'yellow': Colors.yellow,
 | 
						|
  'purple': Colors.purple,
 | 
						|
  'orange': Colors.orange,
 | 
						|
  'pink': Colors.pink,
 | 
						|
  'cyan': Colors.cyan,
 | 
						|
  'lime': Colors.lime,
 | 
						|
  'indigo': Colors.indigo,
 | 
						|
  'teal': Colors.teal,
 | 
						|
  'amber': Colors.amber,
 | 
						|
  'brown': Colors.brown,
 | 
						|
  'grey': Colors.grey,
 | 
						|
  'black': Colors.black,
 | 
						|
  'white': Colors.white,
 | 
						|
};
 | 
						|
 | 
						|
const kVerificationMarkColors = [
 | 
						|
  Colors.teal,
 | 
						|
  Colors.blue,
 | 
						|
  Colors.amber,
 | 
						|
  Colors.blueGrey,
 | 
						|
  Colors.lightBlue,
 | 
						|
];
 | 
						|
 | 
						|
class AccountName extends StatelessWidget {
 | 
						|
  final SnAccount account;
 | 
						|
  final TextStyle? style;
 | 
						|
  final String? textOverride;
 | 
						|
  final bool ignorePermissions;
 | 
						|
  final bool hideVerificationMark;
 | 
						|
  const AccountName({
 | 
						|
    super.key,
 | 
						|
    required this.account,
 | 
						|
    this.style,
 | 
						|
    this.textOverride,
 | 
						|
    this.ignorePermissions = false,
 | 
						|
    this.hideVerificationMark = false,
 | 
						|
  });
 | 
						|
 | 
						|
  Alignment _parseGradientDirection(String direction) {
 | 
						|
    switch (direction) {
 | 
						|
      case 'to right':
 | 
						|
        return Alignment.centerLeft;
 | 
						|
      case 'to left':
 | 
						|
        return Alignment.centerRight;
 | 
						|
      case 'to bottom':
 | 
						|
        return Alignment.topCenter;
 | 
						|
      case 'to top':
 | 
						|
        return Alignment.bottomCenter;
 | 
						|
      case 'to bottom right':
 | 
						|
        return Alignment.topLeft;
 | 
						|
      case 'to bottom left':
 | 
						|
        return Alignment.topRight;
 | 
						|
      case 'to top right':
 | 
						|
        return Alignment.bottomLeft;
 | 
						|
      case 'to top left':
 | 
						|
        return Alignment.bottomRight;
 | 
						|
      default:
 | 
						|
        return Alignment.centerLeft;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Alignment _parseGradientEnd(String direction) {
 | 
						|
    switch (direction) {
 | 
						|
      case 'to right':
 | 
						|
        return Alignment.centerRight;
 | 
						|
      case 'to left':
 | 
						|
        return Alignment.centerLeft;
 | 
						|
      case 'to bottom':
 | 
						|
        return Alignment.bottomCenter;
 | 
						|
      case 'to top':
 | 
						|
        return Alignment.topCenter;
 | 
						|
      case 'to bottom right':
 | 
						|
        return Alignment.bottomRight;
 | 
						|
      case 'to bottom left':
 | 
						|
        return Alignment.bottomLeft;
 | 
						|
      case 'to top right':
 | 
						|
        return Alignment.topRight;
 | 
						|
      case 'to top left':
 | 
						|
        return Alignment.topLeft;
 | 
						|
      default:
 | 
						|
        return Alignment.centerRight;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    var nameStyle = (style ?? TextStyle());
 | 
						|
 | 
						|
    // Apply username color based on membership tier and custom settings
 | 
						|
    if (account.profile.usernameColor != null) {
 | 
						|
      final usernameColor = account.profile.usernameColor!;
 | 
						|
      final tier = account.perkSubscription?.identifier;
 | 
						|
 | 
						|
      // Check tier restrictions
 | 
						|
      final canUseCustomColor =
 | 
						|
          ignorePermissions ||
 | 
						|
          switch (tier) {
 | 
						|
            'solian.stellar.primary' =>
 | 
						|
              usernameColor.type == 'plain' &&
 | 
						|
                  kUsernamePlainColors.containsKey(usernameColor.value),
 | 
						|
            'solian.stellar.nova' => usernameColor.type == 'plain',
 | 
						|
            'solian.stellar.supernova' => true,
 | 
						|
            _ => false,
 | 
						|
          };
 | 
						|
 | 
						|
      if (canUseCustomColor) {
 | 
						|
        if (usernameColor.type == 'plain') {
 | 
						|
          // Plain color
 | 
						|
          Color? color;
 | 
						|
          if (kUsernamePlainColors.containsKey(usernameColor.value)) {
 | 
						|
            color = kUsernamePlainColors[usernameColor.value];
 | 
						|
          } else if (usernameColor.value != null) {
 | 
						|
            // Try to parse hex color
 | 
						|
            try {
 | 
						|
              color = Color(
 | 
						|
                int.parse(
 | 
						|
                      usernameColor.value!.replaceFirst('#', ''),
 | 
						|
                      radix: 16,
 | 
						|
                    ) +
 | 
						|
                    0xFF000000,
 | 
						|
              );
 | 
						|
            } catch (_) {
 | 
						|
              // Invalid hex, ignore
 | 
						|
            }
 | 
						|
          }
 | 
						|
          if (color != null) {
 | 
						|
            nameStyle = nameStyle.copyWith(color: color);
 | 
						|
          }
 | 
						|
        } else if (usernameColor.type == 'gradient' &&
 | 
						|
            usernameColor.colors != null &&
 | 
						|
            usernameColor.colors!.isNotEmpty) {
 | 
						|
          // Gradient - use ShaderMask for text gradient
 | 
						|
          final colors = <Color>[];
 | 
						|
          for (final colorStr in usernameColor.colors!) {
 | 
						|
            Color? color;
 | 
						|
            if (kUsernamePlainColors.containsKey(colorStr)) {
 | 
						|
              color = kUsernamePlainColors[colorStr];
 | 
						|
            } else {
 | 
						|
              // Try to parse hex color
 | 
						|
              try {
 | 
						|
                color = Color(
 | 
						|
                  int.parse(colorStr.replaceFirst('#', ''), radix: 16) +
 | 
						|
                      0xFF000000,
 | 
						|
                );
 | 
						|
              } catch (_) {
 | 
						|
                // Invalid hex, skip
 | 
						|
                continue;
 | 
						|
              }
 | 
						|
            }
 | 
						|
            if (color != null) {
 | 
						|
              colors.add(color);
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          if (colors.isNotEmpty) {
 | 
						|
            final gradient = LinearGradient(
 | 
						|
              colors: colors,
 | 
						|
              begin: _parseGradientDirection(
 | 
						|
                usernameColor.direction ?? 'to right',
 | 
						|
              ),
 | 
						|
              end: _parseGradientEnd(usernameColor.direction ?? 'to right'),
 | 
						|
            );
 | 
						|
 | 
						|
            return Row(
 | 
						|
              mainAxisSize: MainAxisSize.min,
 | 
						|
              spacing: 4,
 | 
						|
              children: [
 | 
						|
                Flexible(
 | 
						|
                  child: ShaderMask(
 | 
						|
                    shaderCallback: (bounds) => gradient.createShader(bounds),
 | 
						|
                    child: Text(
 | 
						|
                      textOverride ?? account.nick,
 | 
						|
                      style: nameStyle.copyWith(color: Colors.white),
 | 
						|
                      maxLines: 1,
 | 
						|
                      overflow: TextOverflow.ellipsis,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                ),
 | 
						|
                if (account.perkSubscription != null)
 | 
						|
                  StellarMembershipMark(membership: account.perkSubscription!),
 | 
						|
                if (account.profile.verification != null &&
 | 
						|
                    !hideVerificationMark)
 | 
						|
                  VerificationMark(mark: account.profile.verification!),
 | 
						|
                if (account.automatedId != null)
 | 
						|
                  Tooltip(
 | 
						|
                    message: 'accountAutomated'.tr(),
 | 
						|
                    child: Icon(
 | 
						|
                      Symbols.smart_toy,
 | 
						|
                      size: 16,
 | 
						|
                      color: nameStyle.color,
 | 
						|
                      fill: 1,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
              ],
 | 
						|
            );
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } else if (account.perkSubscription != null) {
 | 
						|
      // Default membership colors if no custom color is set
 | 
						|
      nameStyle = nameStyle.copyWith(
 | 
						|
        color: (switch (account.perkSubscription!.identifier) {
 | 
						|
          'solian.stellar.primary' => Colors.blueAccent,
 | 
						|
          'solian.stellar.nova' => Color.fromRGBO(57, 197, 187, 1),
 | 
						|
          'solian.stellar.supernova' => Colors.amberAccent,
 | 
						|
          _ => null,
 | 
						|
        }),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    return Row(
 | 
						|
      mainAxisSize: MainAxisSize.min,
 | 
						|
      spacing: 4,
 | 
						|
      children: [
 | 
						|
        Flexible(
 | 
						|
          child: Text(
 | 
						|
            account.nick,
 | 
						|
            style: nameStyle,
 | 
						|
            maxLines: 1,
 | 
						|
            overflow: TextOverflow.ellipsis,
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
        if (account.perkSubscription != null)
 | 
						|
          StellarMembershipMark(membership: account.perkSubscription!),
 | 
						|
        if (account.profile.verification != null)
 | 
						|
          VerificationMark(mark: account.profile.verification!),
 | 
						|
        if (account.automatedId != null)
 | 
						|
          Tooltip(
 | 
						|
            message: 'accountAutomated'.tr(),
 | 
						|
            child: Icon(
 | 
						|
              Symbols.smart_toy,
 | 
						|
              size: 16,
 | 
						|
              color: nameStyle.color,
 | 
						|
              fill: 1,
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
      ],
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class VerificationMark extends StatelessWidget {
 | 
						|
  final SnVerificationMark mark;
 | 
						|
  const VerificationMark({super.key, required this.mark});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    return Tooltip(
 | 
						|
      richMessage: TextSpan(
 | 
						|
        text: mark.title ?? 'No title',
 | 
						|
        children: [
 | 
						|
          TextSpan(text: '\n'),
 | 
						|
          TextSpan(
 | 
						|
            text: mark.description ?? 'descriptionNone'.tr(),
 | 
						|
            style: TextStyle(fontWeight: FontWeight.normal),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
        style: TextStyle(fontWeight: FontWeight.bold),
 | 
						|
      ),
 | 
						|
      child: Icon(
 | 
						|
        mark.type == 4
 | 
						|
            ? Symbols.play_circle
 | 
						|
            : mark.type == 0
 | 
						|
            ? Symbols.build_circle
 | 
						|
            : Symbols.verified,
 | 
						|
        size: 16,
 | 
						|
        color: kVerificationMarkColors[mark.type],
 | 
						|
        fill: 1,
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class StellarMembershipMark extends StatelessWidget {
 | 
						|
  final SnWalletSubscriptionRef membership;
 | 
						|
  const StellarMembershipMark({super.key, required this.membership});
 | 
						|
 | 
						|
  String _getMembershipTierName(String identifier) {
 | 
						|
    switch (identifier) {
 | 
						|
      case 'solian.stellar.primary':
 | 
						|
        return 'membershipTierStellar'.tr();
 | 
						|
      case 'solian.stellar.nova':
 | 
						|
        return 'membershipTierNova'.tr();
 | 
						|
      case 'solian.stellar.supernova':
 | 
						|
        return 'membershipTierSupernova'.tr();
 | 
						|
      default:
 | 
						|
        return 'membershipTierUnknown'.tr();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Color _getMembershipTierColor(String identifier) {
 | 
						|
    switch (identifier) {
 | 
						|
      case 'solian.stellar.primary':
 | 
						|
        return Colors.blue;
 | 
						|
      case 'solian.stellar.nova':
 | 
						|
        return Color.fromRGBO(57, 197, 187, 1);
 | 
						|
      case 'solian.stellar.supernova':
 | 
						|
        return Colors.amber;
 | 
						|
      default:
 | 
						|
        return Colors.grey;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    if (!membership.isActive) return const SizedBox.shrink();
 | 
						|
 | 
						|
    final tierName = _getMembershipTierName(membership.identifier);
 | 
						|
    final tierColor = _getMembershipTierColor(membership.identifier);
 | 
						|
    final tierIcon = Symbols.kid_star;
 | 
						|
 | 
						|
    return Tooltip(
 | 
						|
      richMessage: TextSpan(
 | 
						|
        text: 'stellarMembership'.tr(),
 | 
						|
        children: [
 | 
						|
          TextSpan(text: '\n'),
 | 
						|
          TextSpan(
 | 
						|
            text: 'currentMembershipMember'.tr(args: [tierName]),
 | 
						|
            style: TextStyle(fontWeight: FontWeight.normal),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
        style: TextStyle(fontWeight: FontWeight.bold),
 | 
						|
      ),
 | 
						|
      child: Icon(tierIcon, size: 16, color: tierColor, fill: 1),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class VerificationStatusCard extends StatelessWidget {
 | 
						|
  final SnVerificationMark mark;
 | 
						|
  const VerificationStatusCard({super.key, required this.mark});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context) {
 | 
						|
    return Column(
 | 
						|
      crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
						|
      children: [
 | 
						|
        Icon(
 | 
						|
          mark.type == 4
 | 
						|
              ? Symbols.play_circle
 | 
						|
              : mark.type == 0
 | 
						|
              ? Symbols.build_circle
 | 
						|
              : Symbols.verified,
 | 
						|
          size: 32,
 | 
						|
          color: kVerificationMarkColors[mark.type],
 | 
						|
          fill: 1,
 | 
						|
        ).alignment(Alignment.centerLeft),
 | 
						|
        const Gap(8),
 | 
						|
        Text(mark.title ?? 'No title').bold(),
 | 
						|
        Text(mark.description ?? 'descriptionNone'.tr()),
 | 
						|
        const Gap(6),
 | 
						|
        Text(
 | 
						|
          'Verified by\n${mark.verifiedBy ?? 'No one verified it'}',
 | 
						|
        ).fontSize(11).opacity(0.8),
 | 
						|
      ],
 | 
						|
    ).padding(horizontal: 24, vertical: 16);
 | 
						|
  }
 | 
						|
}
 |