From 6ecdf89d2035dfcaf8d86e65851b3073aa8ff71a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 3 Dec 2025 01:34:02 +0800 Subject: [PATCH] :sparkles: Unactivated account hint, close #199 --- assets/i18n/en-US.json | 6 +++- lib/database/drift_db.dart | 1 + lib/models/account.dart | 1 + lib/models/account.freezed.dart | 43 ++++++++++++------------ lib/models/account.g.dart | 5 +++ lib/screens/account.dart | 2 ++ lib/screens/explore.dart | 13 ++++++-- lib/widgets/account/account_name.dart | 47 +++++++++++++++++++++++++++ 8 files changed, 95 insertions(+), 23 deletions(-) diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 85f8269d..827986ad 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -1485,5 +1485,9 @@ "createAccountNoAltAccounts": "Multiple or alternative accounts are banned from the Solar Network, that will violates our terms of services.", "createAccountAgreeTerms": "I've read these terms and agree to the terms of service.", "createAccountProfile": "Create your profile", - "createAccountToS": "Review Terms & Conditions" + "createAccountToS": "Review Terms & Conditions", + "accountActivationAlert": "Remember to activate your account", + "accountActivationAlertHint": "Unactivated account may leads to various of permission issues, activate your account by clicking the link we sent to your email inbox.", + "accountActivationResendHint": "Didn't see it? Try click the button below to resend one. If you need to update your email while your account was unactivated, feel free to contact our customer service.", + "accountActivationResend": "Resend" } diff --git a/lib/database/drift_db.dart b/lib/database/drift_db.dart index 7c030137..02100504 100644 --- a/lib/database/drift_db.dart +++ b/lib/database/drift_db.dart @@ -266,6 +266,7 @@ class AppDatabase extends _$AppDatabase { id: 'unknown', name: 'unknown', nick: dbMessage.senderId, // Show the ID instead of Unknown + activatedAt: null, profile: SnAccountProfile( picture: null, id: 'unknown', diff --git a/lib/models/account.dart b/lib/models/account.dart index 96ee5622..a99faab7 100644 --- a/lib/models/account.dart +++ b/lib/models/account.dart @@ -21,6 +21,7 @@ sealed class SnAccount with _$SnAccount { required SnWalletSubscriptionRef? perkSubscription, @Default([]) List badges, @Default([]) List contacts, + required DateTime? activatedAt, required DateTime createdAt, required DateTime updatedAt, required DateTime? deletedAt, diff --git a/lib/models/account.freezed.dart b/lib/models/account.freezed.dart index 152293c3..70ace1be 100644 --- a/lib/models/account.freezed.dart +++ b/lib/models/account.freezed.dart @@ -15,7 +15,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$SnAccount { - String get id; String get name; String get nick; String get language; String get region; bool get isSuperuser; String? get automatedId; SnAccountProfile get profile; SnWalletSubscriptionRef? get perkSubscription; List get badges; List get contacts; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; + String get id; String get name; String get nick; String get language; String get region; bool get isSuperuser; String? get automatedId; SnAccountProfile get profile; SnWalletSubscriptionRef? get perkSubscription; List get badges; List get contacts; DateTime? get activatedAt; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; /// Create a copy of SnAccount /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -28,16 +28,16 @@ $SnAccountCopyWith get copyWith => _$SnAccountCopyWithImpl @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.region, region) || other.region == region)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.automatedId, automatedId) || other.automatedId == automatedId)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.perkSubscription, perkSubscription) || other.perkSubscription == perkSubscription)&&const DeepCollectionEquality().equals(other.badges, badges)&&const DeepCollectionEquality().equals(other.contacts, contacts)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.region, region) || other.region == region)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.automatedId, automatedId) || other.automatedId == automatedId)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.perkSubscription, perkSubscription) || other.perkSubscription == perkSubscription)&&const DeepCollectionEquality().equals(other.badges, badges)&&const DeepCollectionEquality().equals(other.contacts, contacts)&&(identical(other.activatedAt, activatedAt) || other.activatedAt == activatedAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,name,nick,language,region,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(badges),const DeepCollectionEquality().hash(contacts),createdAt,updatedAt,deletedAt); +int get hashCode => Object.hash(runtimeType,id,name,nick,language,region,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(badges),const DeepCollectionEquality().hash(contacts),activatedAt,createdAt,updatedAt,deletedAt); @override String toString() { - return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, region: $region, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, contacts: $contacts, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; + return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, region: $region, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, contacts: $contacts, activatedAt: $activatedAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; } @@ -48,7 +48,7 @@ abstract mixin class $SnAccountCopyWith<$Res> { factory $SnAccountCopyWith(SnAccount value, $Res Function(SnAccount) _then) = _$SnAccountCopyWithImpl; @useResult $Res call({ - String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt + String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime? activatedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt }); @@ -65,7 +65,7 @@ class _$SnAccountCopyWithImpl<$Res> /// Create a copy of SnAccount /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? region = null,Object? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = null,Object? contacts = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? region = null,Object? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = null,Object? contacts = null,Object? activatedAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_self.copyWith( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable @@ -78,7 +78,8 @@ as String?,profile: null == profile ? _self.profile : profile // ignore: cast_nu as SnAccountProfile,perkSubscription: freezed == perkSubscription ? _self.perkSubscription : perkSubscription // ignore: cast_nullable_to_non_nullable as SnWalletSubscriptionRef?,badges: null == badges ? _self.badges : badges // ignore: cast_nullable_to_non_nullable as List,contacts: null == contacts ? _self.contacts : contacts // ignore: cast_nullable_to_non_nullable -as List,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as List,activatedAt: freezed == activatedAt ? _self.activatedAt : activatedAt // ignore: cast_nullable_to_non_nullable +as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?, @@ -184,10 +185,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime? activatedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _SnAccount() when $default != null: -return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: +return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.activatedAt,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return orElse(); } @@ -205,10 +206,10 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime? activatedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; switch (_that) { case _SnAccount(): -return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.createdAt,_that.updatedAt,_that.deletedAt);} +return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.activatedAt,_that.createdAt,_that.updatedAt,_that.deletedAt);} } /// A variant of `when` that fallback to returning `null` /// @@ -222,10 +223,10 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime? activatedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; switch (_that) { case _SnAccount() when $default != null: -return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: +return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.activatedAt,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return null; } @@ -237,7 +238,7 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that @JsonSerializable() class _SnAccount implements SnAccount { - const _SnAccount({required this.id, required this.name, required this.nick, required this.language, this.region = "", required this.isSuperuser, required this.automatedId, required this.profile, required this.perkSubscription, final List badges = const [], final List contacts = const [], required this.createdAt, required this.updatedAt, required this.deletedAt}): _badges = badges,_contacts = contacts; + const _SnAccount({required this.id, required this.name, required this.nick, required this.language, this.region = "", required this.isSuperuser, required this.automatedId, required this.profile, required this.perkSubscription, final List badges = const [], final List contacts = const [], required this.activatedAt, required this.createdAt, required this.updatedAt, required this.deletedAt}): _badges = badges,_contacts = contacts; factory _SnAccount.fromJson(Map json) => _$SnAccountFromJson(json); @override final String id; @@ -263,6 +264,7 @@ class _SnAccount implements SnAccount { return EqualUnmodifiableListView(_contacts); } +@override final DateTime? activatedAt; @override final DateTime createdAt; @override final DateTime updatedAt; @override final DateTime? deletedAt; @@ -280,16 +282,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.region, region) || other.region == region)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.automatedId, automatedId) || other.automatedId == automatedId)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.perkSubscription, perkSubscription) || other.perkSubscription == perkSubscription)&&const DeepCollectionEquality().equals(other._badges, _badges)&&const DeepCollectionEquality().equals(other._contacts, _contacts)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.region, region) || other.region == region)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.automatedId, automatedId) || other.automatedId == automatedId)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.perkSubscription, perkSubscription) || other.perkSubscription == perkSubscription)&&const DeepCollectionEquality().equals(other._badges, _badges)&&const DeepCollectionEquality().equals(other._contacts, _contacts)&&(identical(other.activatedAt, activatedAt) || other.activatedAt == activatedAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,name,nick,language,region,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(_badges),const DeepCollectionEquality().hash(_contacts),createdAt,updatedAt,deletedAt); +int get hashCode => Object.hash(runtimeType,id,name,nick,language,region,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(_badges),const DeepCollectionEquality().hash(_contacts),activatedAt,createdAt,updatedAt,deletedAt); @override String toString() { - return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, region: $region, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, contacts: $contacts, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; + return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, region: $region, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, contacts: $contacts, activatedAt: $activatedAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; } @@ -300,7 +302,7 @@ abstract mixin class _$SnAccountCopyWith<$Res> implements $SnAccountCopyWith<$Re factory _$SnAccountCopyWith(_SnAccount value, $Res Function(_SnAccount) _then) = __$SnAccountCopyWithImpl; @override @useResult $Res call({ - String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt + String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List badges, List contacts, DateTime? activatedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt }); @@ -317,7 +319,7 @@ class __$SnAccountCopyWithImpl<$Res> /// Create a copy of SnAccount /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? region = null,Object? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = null,Object? contacts = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? region = null,Object? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = null,Object? contacts = null,Object? activatedAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_SnAccount( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable @@ -330,7 +332,8 @@ as String?,profile: null == profile ? _self.profile : profile // ignore: cast_nu as SnAccountProfile,perkSubscription: freezed == perkSubscription ? _self.perkSubscription : perkSubscription // ignore: cast_nullable_to_non_nullable as SnWalletSubscriptionRef?,badges: null == badges ? _self._badges : badges // ignore: cast_nullable_to_non_nullable as List,contacts: null == contacts ? _self._contacts : contacts // ignore: cast_nullable_to_non_nullable -as List,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as List,activatedAt: freezed == activatedAt ? _self.activatedAt : activatedAt // ignore: cast_nullable_to_non_nullable +as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?, diff --git a/lib/models/account.g.dart b/lib/models/account.g.dart index f48572a5..d2bdca71 100644 --- a/lib/models/account.g.dart +++ b/lib/models/account.g.dart @@ -31,6 +31,10 @@ _SnAccount _$SnAccountFromJson(Map json) => _SnAccount( ?.map((e) => SnContactMethod.fromJson(e as Map)) .toList() ?? const [], + activatedAt: + json['activated_at'] == null + ? null + : DateTime.parse(json['activated_at'] as String), createdAt: DateTime.parse(json['created_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String), deletedAt: @@ -52,6 +56,7 @@ Map _$SnAccountToJson(_SnAccount instance) => 'perk_subscription': instance.perkSubscription?.toJson(), 'badges': instance.badges.map((e) => e.toJson()).toList(), 'contacts': instance.contacts.map((e) => e.toJson()).toList(), + 'activated_at': instance.activatedAt?.toIso8601String(), 'created_at': instance.createdAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), diff --git a/lib/screens/account.dart b/lib/screens/account.dart index ffa34290..06bb5a84 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -178,6 +178,8 @@ class AccountScreen extends HookConsumerWidget { ], ), ).padding(horizontal: 8), + if (user.value?.activatedAt == null) + AccountUnactivatedCard().padding(horizontal: 12, bottom: 4), Card( margin: EdgeInsets.zero, child: Column( diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart index 89c49f17..e24c6fb4 100644 --- a/lib/screens/explore.dart +++ b/lib/screens/explore.dart @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/models/account.dart'; import 'package:island/models/activity.dart'; import 'package:island/models/publisher.dart'; import 'package:island/models/realm.dart'; @@ -15,6 +16,7 @@ import 'package:island/pods/userinfo.dart'; import 'package:island/screens/auth/login_modal.dart'; import 'package:island/screens/notification.dart'; import 'package:island/services/responsive.dart'; +import 'package:island/widgets/account/account_name.dart'; import 'package:island/widgets/account/friends_overview.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/models/post.dart'; @@ -343,7 +345,7 @@ class ExploreScreen extends HookConsumerWidget { BuildContext context, WidgetRef ref, Widget filterBar, - AsyncValue user, + AsyncValue user, AsyncValue notificationCount, ValueNotifier query, AsyncValue> events, @@ -382,8 +384,11 @@ class ExploreScreen extends HookConsumerWidget { child: Column( spacing: 8, children: [ + const Gap(4), + if (user.value?.activatedAt == null) + AccountUnactivatedCard(), CheckInWidget( - margin: EdgeInsets.only(top: 12), + margin: EdgeInsets.zero, onChecked: () { ref.invalidate(eventCalendarProvider(query.value)); }, @@ -584,6 +589,10 @@ class ExploreScreen extends HookConsumerWidget { child: CustomScrollView( slivers: [ const SliverGap(8), + if (user.value?.activatedAt == null) + SliverToBoxAdapter( + child: AccountUnactivatedCard().padding(bottom: 8), + ), if (user.value != null) SliverToBoxAdapter( child: CheckInWidget( diff --git a/lib/widgets/account/account_name.dart b/lib/widgets/account/account_name.dart index 04d65095..6ac98568 100644 --- a/lib/widgets/account/account_name.dart +++ b/lib/widgets/account/account_name.dart @@ -1,8 +1,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/account.dart'; import 'package:island/models/wallet.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/widgets/alert.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -428,3 +431,47 @@ class VerificationStatusCard extends StatelessWidget { ).padding(horizontal: 24, vertical: 16); } } + +class AccountUnactivatedCard extends HookConsumerWidget { + const AccountUnactivatedCard({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Card( + margin: EdgeInsets.zero, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon( + Symbols.warning_amber_rounded, + size: 40, + fill: 1, + color: Colors.amber, + ), + const Gap(4), + Text('accountActivationAlert').tr().fontSize(16).bold(), + Text('accountActivationAlertHint').tr(), + const Gap(4), + Text('accountActivationResendHint').tr().opacity(0.8), + const Gap(16), + FilledButton.icon( + icon: const Icon(Symbols.email), + label: Text('accountActivationResend').tr(), + onPressed: () async { + final client = ref.watch(apiClientProvider); + try { + showLoadingModal(context); + await client.post('/pass/spells/activation/resend'); + showSnackBar("Activation magic spell has been resend"); + } catch (err) { + showErrorAlert(err); + } finally { + if (context.mounted) hideLoadingModal(context); + } + }, + ).width(double.infinity), + ], + ).padding(horizontal: 24, vertical: 16), + ); + } +}