Skip to content

Commit

Permalink
fix: country selection in remittance flow (#157)
Browse files Browse the repository at this point in the history
  • Loading branch information
ethan-tbd authored May 16, 2024
1 parent 820fa35 commit 6331686
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 92 deletions.
20 changes: 0 additions & 20 deletions lib/config/config.local
Original file line number Diff line number Diff line change
@@ -1,24 +1,4 @@
import 'package:didpay/features/countries/country.dart';
import 'package:didpay/features/pfis/pfi.dart';

class Config {
static const countryToPfiMap = {
_us: _tbdPfi,
_mx: _tbdPfi,
};

static const _us = Country(name: 'United States', code: 'US');
static const _mx = Country(name: 'Mexico', code: 'MX');

static const _tbdPfi = Pfi(
id: '1',
name: 'TBD PFI',
didUri: 'did:web:localhost%3A8892:ingress',
);

static List<Country> devCountries = [_us, _mx];
static List<Pfi> devPfis = [_tbdPfi];

static const pfisJsonUrl =
'https://raw.githubusercontent.com/TBD54566975/pfi-providers-data/main/pfis.json';

Expand Down
2 changes: 2 additions & 0 deletions lib/features/account/account_did_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class AccountDidPage extends HookConsumerWidget {
title: Text(did),
trailing: const Icon(Icons.content_copy),
onTap: () {
ScaffoldMessenger.of(context).removeCurrentSnackBar();

Clipboard.setData(ClipboardData(text: did));
final snackBar = SnackBar(
content: Text(
Expand Down
31 changes: 30 additions & 1 deletion lib/features/app/app_tabs.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'package:didpay/features/account/account_page.dart';
import 'package:didpay/features/home/home_page.dart';
import 'package:didpay/features/pfis/pfi.dart';
import 'package:didpay/features/pfis/pfis_notifier.dart';
import 'package:didpay/features/send/send_page.dart';
import 'package:didpay/l10n/app_localizations.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
Expand All @@ -19,6 +22,7 @@ class AppTabs extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final selectedIndex = useState(0);
final pfis = ref.read(pfisProvider);

final tabs = [
_TabItem(
Expand Down Expand Up @@ -47,7 +51,7 @@ class AppTabs extends HookConsumerWidget {
fixedColor: Theme.of(context).colorScheme.primary,
selectedFontSize: 12,
currentIndex: selectedIndex.value,
onTap: (index) => selectedIndex.value = index,
onTap: (index) => _onTabTapped(context, index, selectedIndex, pfis),
items: tabs
.map(
(tab) => BottomNavigationBarItem(
Expand All @@ -59,4 +63,29 @@ class AppTabs extends HookConsumerWidget {
),
);
}

void _onTabTapped(
BuildContext context,
int index,
ValueNotifier<int> selectedIndex,
List<Pfi> pfis,
) {
if (index == 1 && pfis.isEmpty) {
ScaffoldMessenger.of(context).removeCurrentSnackBar();

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
Loc.of(context).mustAddPfiBeforeSending,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
),
),
backgroundColor: Theme.of(context).colorScheme.secondary,
),
);
return;
}
selectedIndex.value = index;
}
}
3 changes: 3 additions & 0 deletions lib/features/did_qr/did_qr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,14 @@ class DidQr {
TextEditingController didTextController,
ValueNotifier<String?> errorText,
) async {
ScaffoldMessenger.of(context).removeCurrentSnackBar();

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(Loc.of(context).simulatedQrCodeScan),
),
);

final did = await DidDht.create(publish: true);
didTextController.text = did.uri;
errorText.value = null;
Expand Down
74 changes: 33 additions & 41 deletions lib/features/remittance/countries_notifier.dart
Original file line number Diff line number Diff line change
@@ -1,62 +1,54 @@
import 'dart:async';
import 'dart:convert';

import 'package:didpay/config/config.dart';
import 'package:didpay/features/remittance/countries.dart';
import 'package:didpay/features/storage/storage_service.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

final countriesProvider =
AsyncNotifierProvider<CountriesAsyncNotifier, List<Country>>(
CountriesAsyncNotifier.new,
StateNotifierProvider<CountriesNotifier, List<Country>>(
(ref) => throw UnimplementedError(),
);

class CountriesAsyncNotifier extends AsyncNotifier<List<Country>> {
final _cacheKey = 'didpay:countries_cache';
class CountriesNotifier extends StateNotifier<List<Country>> {
static const String prefsKey = 'countries';
final SharedPreferences prefs;

@override
FutureOr<List<Country>> build() => _loadFromCache();
CountriesNotifier(this.prefs, super.state);

void addCountry(Country newCountry) {
final currentCountries = state.value ?? [];
final updatedCountries = [...currentCountries, newCountry];
state = AsyncData(updatedCountries);
}

Future<void> reload() async {
final countries = await _loadFromCache();
// Show loading indicator if cache is empty
state = countries.isEmpty ? const AsyncLoading() : AsyncData(countries);
Future<Country> add(String code, String name) async {
final country = Country(code: code, name: name);

final response = await http.get(Uri.parse(Config.pfisJsonUrl));
if (response.statusCode != 200) {
state = AsyncError('Failed to load countries', StackTrace.current);
return;
}
state = [...state, country];
await _save();
return country;
}

await ref
.read(sharedPreferencesProvider)
.setString(_cacheKey, response.body);
Future<void> remove(Country country) async {
state = state.where((elem) => elem.code != country.code).toList();
await _save();
}

state = AsyncData(
List<Country>.from(
(json.decode(response.body) as List)
.map((item) => Country.fromJson(item as Map<String, dynamic>)),
),
);
Future<void> _save() async {
final toSave = state.map((e) => e.code).toList();
await prefs.setStringList('countries', toSave);
}

Future<List<Country>> _loadFromCache() async {
final cachedData = ref.read(sharedPreferencesProvider).getString(_cacheKey);
if (cachedData == null) {
Future<List<Country>> loadSavedCountryCodes() async {
final saved = prefs.getStringList(prefsKey);

if (saved == null) {
return [];
}
final countries = <Country>[];
for (final country in saved) {
try {
countries.add(Country.fromJson(jsonDecode(country)));
} on Exception catch (e) {
throw Exception('Failed to load saved country: $e');
}
}

return List<Country>.from(
(json.decode(cachedData) as List).map(
(item) => Country.fromJson(item as Map<String, dynamic>),
),
);
return countries;
}
}
43 changes: 15 additions & 28 deletions lib/features/remittance/remittance_country_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,9 @@ class RemittanceCountryPage extends HookConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final countries = ref.read(countriesProvider);
final country = useState<Country?>(null);

useEffect(
() {
Future.delayed(
Duration.zero,
() => ref.read(countriesProvider.notifier).reload(),
);
return null;
},
[],
);

return Scaffold(
appBar: AppBar(),
body: SafeArea(
Expand All @@ -38,7 +28,7 @@ class RemittanceCountryPage extends HookConsumerWidget {
Loc.of(context).selectCountryToGetStarted,
),
Expanded(
child: _buildCountryList(context, ref, country),
child: _buildCountryList(context, ref, countries, country),
),
_buildNextButton(context, country.value),
],
Expand Down Expand Up @@ -79,26 +69,23 @@ class RemittanceCountryPage extends HookConsumerWidget {
Widget _buildCountryList(
BuildContext context,
WidgetRef ref,
List<Country> countries,
ValueNotifier<Country?> selectedCountry,
) =>
ref.watch(countriesProvider).when(
data: (countries) => ListView.builder(
itemCount: countries.length,
itemBuilder: (context, index) {
final country = countries[index];
final isSelected = selectedCountry.value?.code == country.code;
ListView.builder(
itemCount: countries.length,
itemBuilder: (context, index) {
final country = countries[index];
final isSelected = selectedCountry.value?.code == country.code;

return Country.buildCountryTile(
context,
country,
isSelected: isSelected,
onTap: () => selectedCountry.value = country,
);
},
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => Center(child: Text(error.toString())),
return Country.buildCountryTile(
context,
country,
isSelected: isSelected,
onTap: () => selectedCountry.value = country,
);
},
);

Widget _buildNextButton(
BuildContext context,
Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,6 @@
"noPfisFound": "No PFIs found",
"startByAddingAPfi": "Start by adding a PFI",
"pfiAdded": "PFI added!",
"addingPfi": "Adding PFI..."
"addingPfi": "Adding PFI...",
"mustAddPfiBeforeSending": "Must add a PFI before sending funds!"
}
6 changes: 6 additions & 0 deletions lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,12 @@ abstract class Loc {
/// In en, this message translates to:
/// **'Adding PFI...'**
String get addingPfi;

/// No description provided for @mustAddPfiBeforeSending.
///
/// In en, this message translates to:
/// **'Must add a PFI before sending funds!'**
String get mustAddPfiBeforeSending;
}

class _LocDelegate extends LocalizationsDelegate<Loc> {
Expand Down
3 changes: 3 additions & 0 deletions lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,7 @@ class LocEn extends Loc {

@override
String get addingPfi => 'Adding PFI...';

@override
String get mustAddPfiBeforeSending => 'Must add a PFI before sending funds!';
}
1 change: 0 additions & 1 deletion test/features/app/app_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:didpay/features/app/app.dart';
import 'package:didpay/features/app/app_tabs.dart';
import 'package:didpay/features/pfis/pfis_notifier.dart';
import 'package:didpay/features/tbdex/tbdex_providers.dart';
Expand Down
File renamed without changes.

0 comments on commit 6331686

Please sign in to comment.