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 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 bool ignorePermissions; const AccountName({ super.key, required this.account, this.style, this.ignorePermissions = 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 = []; 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( 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) 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); } }