Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(cat-voices): user and wallet model #999

Merged
merged 10 commits into from
Oct 15, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ class AccountCompletedPanel extends StatelessWidget {
info: context.l10n.registrationCompletedKeychainInfo,
),
BlocSelector<RegistrationCubit, RegistrationState, String>(
selector: (state) =>
state.walletLinkStateData.selectedWallet?.wallet.name
.capitalize() ??
'',
selector: (state) {
final wallet = state.walletLinkStateData.selectedWallet;
final name = wallet?.meta.name ?? '';
return name.capitalize();
},
builder: (context, walletName) {
return _SummaryItem(
image: VoicesAssets.images.registrationSummaryWallet,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class _BlocSummary extends StatelessWidget {
RegistrationState,
({
Set<AccountRole> roles,
CardanoWalletDetails selectedWallet,
WalletHeader selectedWallet,
Coin transactionFee,
})?>(
selector: (state) {
Expand Down Expand Up @@ -135,7 +135,7 @@ class _BlocSummary extends StatelessWidget {

class _Summary extends StatelessWidget {
final Set<AccountRole> roles;
final CardanoWalletDetails walletDetails;
final WalletHeader walletDetails;
final Coin transactionFee;

const _Summary({
Expand Down Expand Up @@ -164,8 +164,7 @@ class _Summary extends StatelessWidget {
),
const SizedBox(height: 12),
Text(
context.l10n
.walletLinkTransactionLinkItem(walletDetails.wallet.name),
context.l10n.walletLinkTransactionLinkItem(walletDetails.meta.name),
style: Theme.of(context).textTheme.bodySmall,
),
for (final role in roles) ...[
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import 'dart:async';

import 'package:catalyst_cardano/catalyst_cardano.dart';
import 'package:catalyst_voices/pages/registration/wallet_link/bloc_wallet_link_builder.dart';
import 'package:catalyst_voices/pages/registration/widgets/registration_stage_message.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:flutter/material.dart';
import 'package:result_type/result_type.dart';

/// Callback called when a [wallet] is selected.
typedef _OnSelectWallet = Future<void> Function(CardanoWallet wallet);
typedef _OnSelectWallet = Future<void> Function(WalletMetadata wallet);

class SelectWalletPanel extends StatefulWidget {
const SelectWalletPanel({
Expand Down Expand Up @@ -67,7 +67,7 @@ class _SelectWalletPanelState extends State<SelectWalletPanel> {
unawaited(RegistrationCubit.of(context).walletLink.refreshWallets());
}

Future<void> _onSelectWallet(CardanoWallet wallet) async {
Future<void> _onSelectWallet(WalletMetadata wallet) async {
final registration = RegistrationCubit.of(context);

final success = await registration.walletLink.selectWallet(wallet);
Expand All @@ -88,7 +88,7 @@ class _BlocWallets extends StatelessWidget {

@override
Widget build(BuildContext context) {
return BlocWalletLinkBuilder<Result<List<CardanoWallet>, Exception>?>(
return BlocWalletLinkBuilder<Result<List<WalletMetadata>, Exception>?>(
selector: (state) => state.wallets,
builder: (context, state) {
return _Wallets(
Expand All @@ -102,7 +102,7 @@ class _BlocWallets extends StatelessWidget {
}

class _Wallets extends StatelessWidget {
final Result<List<CardanoWallet>, Exception>? result;
final Result<List<WalletMetadata>, Exception>? result;
final _OnSelectWallet onSelectWallet;
final VoidCallback onRefreshTap;

Expand All @@ -119,13 +119,15 @@ class _Wallets extends StatelessWidget {
? _WalletsList(wallets: value, onSelectWallet: onSelectWallet)
: _WalletsEmpty(onRetry: onRefreshTap),
Failure() => _WalletsError(onRetry: onRefreshTap),
_ => const Center(child: VoicesCircularProgressIndicator()),
_ => const Center(
child: DelayedWidget(child: VoicesCircularProgressIndicator()),
),
};
}
}

class _WalletsList extends StatelessWidget {
final List<CardanoWallet> wallets;
final List<WalletMetadata> wallets;
final _OnSelectWallet onSelectWallet;

const _WalletsList({
Expand All @@ -148,7 +150,7 @@ class _WalletsList extends StatelessWidget {
}

class _WalletTile extends StatefulWidget {
final CardanoWallet wallet;
final WalletMetadata wallet;
final _OnSelectWallet onSelectWallet;

const _WalletTile({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class _BlocWalletDetailsText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocWalletLinkBuilder<String?>(
selector: (state) => state.selectedWallet?.wallet.name,
selector: (state) => state.selectedWallet?.meta.name,
builder: (context, state) {
return Text(
context.l10n.walletLinkWalletDetailsContent(state ?? ''),
Expand Down Expand Up @@ -101,7 +101,7 @@ class _BlocNavigation extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocWalletLinkBuilder<bool>(
selector: (state) => state.selectedWallet?.hasEnoughBalance ?? false,
selector: (state) => state.hasEnoughBalance,
builder: (context, state) {
if (state) {
return const RegistrationBackNextNavigation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SessionStateHeader extends StatelessWidget {
VisitorSessionState() => const _VisitorButton(),
GuestSessionState() => const _GuestButton(),
ActiveUserSessionState(:final user) => AccountPopup(
avatarLetter: user.acronym ?? 'A',
avatarLetter: user.acronym,
onLockAccountTap: () => debugPrint('Lock account'),
onProfileKeychainTap: () => unawaited(
const AccountRoute().push<void>(context),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,18 @@ final class RecoverCubit extends Cubit<RecoverStateData>
return;
}

final walletDetails =
final walletHeader =
await _registrationService.recoverCardanoWalletDetails(seedPhrase);

final accountDetails = AccountSummaryData(
walletConnection: WalletConnectionData(
name: walletDetails.wallet.name,
icon: walletDetails.wallet.icon,
name: walletHeader.meta.name,
icon: walletHeader.meta.icon,
),
walletSummary: WalletSummaryData(
balance: CryptocurrencyFormatter.formatAmount(walletDetails.balance),
address: WalletAddressFormatter.formatShort(walletDetails.address),
clipboardAddress: walletDetails.address.toBech32(),
balance: CryptocurrencyFormatter.formatAmount(walletHeader.balance),
address: WalletAddressFormatter.formatShort(walletHeader.address),
clipboardAddress: walletHeader.address.toBech32(),
showLowBalance: false,
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:catalyst_voices_services/catalyst_voices_services.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:collection/collection.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:result_type/result_type.dart';

Expand All @@ -14,7 +15,7 @@ final _logger = Logger('WalletLinkCubit');
abstract interface class WalletLinkManager {
Future<void> refreshWallets();

Future<bool> selectWallet(CardanoWallet wallet);
Future<bool> selectWallet(WalletMetadata mete);
damian-molinski marked this conversation as resolved.
Show resolved Hide resolved

void selectRoles(Set<AccountRole> roles);
}
Expand All @@ -24,45 +25,72 @@ final class WalletLinkCubit extends Cubit<WalletLinkStateData>
implements WalletLinkManager {
final RegistrationService registrationService;

final _wallets = <CardanoWallet>[];
CardanoWallet? _selectedWallet;

WalletLinkCubit({required this.registrationService})
: super(const WalletLinkStateData());

CardanoWallet? get selectedWallet => _selectedWallet;

@override
Future<void> refreshWallets() async {
try {
_wallets.clear();
emit(state.copyWith(wallets: const Optional.empty()));

final wallets =
await registrationService.getCardanoWallets().withMinimumDelay();

emit(state.copyWith(wallets: Optional(Success(wallets))));
_wallets
..clear()
..addAll(wallets);

final walletsMetaList =
wallets.map(WalletMetadata.fromCardanoWallet).toList();

emit(state.copyWith(wallets: Optional(Success(walletsMetaList))));
} on Exception catch (error, stackTrace) {
_logger.severe('refreshWallets', error, stackTrace);

_wallets.clear();

emit(state.copyWith(wallets: Optional(Failure(error))));
}
}

@override
Future<bool> selectWallet(CardanoWallet wallet) async {
Future<bool> selectWallet(WalletMetadata meta) async {
try {
final walletDetails =
final wallet =
_wallets.firstWhereOrNull((wallet) => wallet.name == meta.name);

if (wallet == null) {
throw const LocalizedRegistrationWalletNotFoundException();
}

_selectedWallet = wallet;

final walletHeader =
await registrationService.getCardanoWalletDetails(wallet);

final walletConnection = WalletConnectionData(
name: wallet.name,
icon: wallet.icon,
name: walletHeader.meta.name,
icon: walletHeader.meta.icon,
isConnected: true,
);
final walletSummary = WalletSummaryData(
balance: CryptocurrencyFormatter.formatAmount(walletDetails.balance),
address: WalletAddressFormatter.formatShort(walletDetails.address),
clipboardAddress: walletDetails.address.toBech32(),
balance: CryptocurrencyFormatter.formatAmount(walletHeader.balance),
address: WalletAddressFormatter.formatShort(walletHeader.address),
clipboardAddress: walletHeader.address.toBech32(),
showLowBalance:
walletDetails.balance < CardanoWalletDetails.minAdaForRegistration,
walletHeader.balance < CardanoWalletDetails.minAdaForRegistration,
);

final newState = state.copyWith(
selectedWallet: Optional(walletDetails),
selectedWallet: Optional(walletHeader),
hasEnoughBalance:
walletHeader.balance >= CardanoWalletDetails.minAdaForRegistration,
walletConnection: Optional(walletConnection),
walletSummary: Optional(walletSummary),
);
Expand All @@ -73,6 +101,8 @@ final class WalletLinkCubit extends Cubit<WalletLinkStateData>
} catch (error, stackTrace) {
_logger.severe('selectWallet', error, stackTrace);

_selectedWallet = null;

emit(
state.copyWith(
selectedWallet: const Optional.empty(),
Expand All @@ -81,6 +111,8 @@ final class WalletLinkCubit extends Cubit<WalletLinkStateData>
),
);

emitError(error);

return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ final class RegistrationCubit extends Cubit<RegistrationState>
);

final unsignedTx = await registrationService.prepareRegistration(
wallet: _walletLinkState.selectedCardanoWallet!,
wallet: _walletLinkCubit.selectedWallet!,
// TODO(dtscalac): inject the networkId
networkId: NetworkId.testnet,
seedPhrase: _keychainState.seedPhrase!,
Expand Down Expand Up @@ -163,7 +163,7 @@ final class RegistrationCubit extends Cubit<RegistrationState>
);

final signedTx = await registrationService.submitRegistration(
wallet: _walletLinkState.selectedCardanoWallet!,
wallet: _walletLinkCubit.selectedWallet!,
unsignedTx: _registrationState.unsignedTx!.success,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import 'package:catalyst_cardano/catalyst_cardano.dart';
import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:equatable/equatable.dart';
import 'package:result_type/result_type.dart';

final class WalletLinkStateData extends Equatable {
final Result<List<CardanoWallet>, Exception>? wallets;
final CardanoWalletDetails? selectedWallet;
final Result<List<WalletMetadata>, Exception>? wallets;
final WalletHeader? selectedWallet;
final bool hasEnoughBalance;
final WalletConnectionData? walletConnection;
final WalletSummaryData? walletSummary;
final Set<AccountRole>? selectedRoles;

const WalletLinkStateData({
this.wallets,
this.selectedWallet,
this.hasEnoughBalance = false,
this.walletConnection,
this.walletSummary,
this.selectedRoles,
Expand All @@ -26,19 +27,18 @@ final class WalletLinkStateData extends Equatable {
/// Returns the default roles every account will have.
Set<AccountRole> get defaultRoles => {AccountRole.voter};

/// Returns the selected & enabled cardano wallet.
CardanoWallet? get selectedCardanoWallet => selectedWallet?.wallet;

WalletLinkStateData copyWith({
Optional<Result<List<CardanoWallet>, Exception>>? wallets,
Optional<CardanoWalletDetails>? selectedWallet,
Optional<Result<List<WalletMetadata>, Exception>>? wallets,
Optional<WalletHeader>? selectedWallet,
bool? hasEnoughBalance,
Optional<WalletConnectionData>? walletConnection,
Optional<WalletSummaryData>? walletSummary,
Optional<Set<AccountRole>>? selectedRoles,
}) {
return WalletLinkStateData(
wallets: wallets.dataOr(this.wallets),
selectedWallet: selectedWallet.dataOr(this.selectedWallet),
hasEnoughBalance: hasEnoughBalance ?? this.hasEnoughBalance,
walletConnection: walletConnection.dataOr(this.walletConnection),
walletSummary: walletSummary.dataOr(this.walletSummary),
selectedRoles: selectedRoles.dataOr(this.selectedRoles),
Expand All @@ -49,6 +49,7 @@ final class WalletLinkStateData extends Equatable {
List<Object?> get props => [
wallets,
selectedWallet,
hasEnoughBalance,
walletConnection,
walletSummary,
selectedRoles,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart';
import 'package:catalyst_voices_blocs/src/session/session_event.dart';
import 'package:catalyst_voices_blocs/src/session/session_state.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
Expand Down Expand Up @@ -88,7 +89,25 @@ final class SessionBloc extends Bloc<SessionEvent, SessionState> {
}

/// Temporary implementation for testing purposes.
User get _dummyUser => const User(name: 'Account');
User get _dummyUser {
/* cSpell:disable */
return User(
profile: Profile(
walletHeader: WalletHeader(
meta: const WalletMetadata(
name: 'Dummy Wallet',
icon: null,
),
balance: Coin.fromAda(10),
address: ShelleyAddress.fromBech32(
'addr_test1vzpwq95z3xyum8vqndgdd'
'9mdnmafh3djcxnc6jemlgdmswcve6tkw',
),
),
),
);
/* cSpell:enable */
}

/// Temporary implementation for testing purposes.
SeedPhrase get _dummySeedPhrase => SeedPhrase.fromMnemonic(
Expand Down
Loading
Loading