Skip to content

Commit

Permalink
feat: Add swap drawer
Browse files Browse the repository at this point in the history
Co-authored-by: Richard Holzeis <[email protected]>
  • Loading branch information
Restioson and holzeis committed Dec 8, 2023
1 parent a35028d commit 70276f7
Show file tree
Hide file tree
Showing 25 changed files with 1,329 additions and 794 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

- Feat: Open scanner on send button
- Feat: Add bidirectional swap drawer

## [1.7.0] - 2023-12-07

Expand Down
169 changes: 169 additions & 0 deletions mobile/lib/common/amount_input_modalised.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:get_10101/common/amount_text.dart';
import 'package:get_10101/common/amount_text_input_form_field.dart';
import 'package:get_10101/common/domain/model.dart';

void _doNothing(int? v) {}

enum BtcOrFiat {
btc,
fiat,
}

String format(BtcOrFiat type, int amount) {
final formatter = NumberFormat("#,###,##0.00", "en");
return type == BtcOrFiat.btc ? formatSats(Amount(amount)) : "\$${formatter.format(amount)}";
}

class AmountInputModalisedField extends StatelessWidget {
final void Function(int?) onChange;
final String? Function(String?)? validator;
final BtcOrFiat type;
final int? amount;
const AmountInputModalisedField(
{super.key, this.onChange = _doNothing, required this.type, this.amount, this.validator});

@override
Widget build(BuildContext context) {
return FormField<int?>(
initialValue: amount,
validator: validator != null ? (amt) => validator!(amt.toString()) : null,
builder: (FormFieldState<int?> field) {
onValueChange(int? val) {
onChange(val);
field.didChange(val);
}

return InputDecorator(
decoration: InputDecoration(
errorText: field.errorText,
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.zero),
child: OutlinedButton(
onPressed: () => _showModal(context, type, amount, onValueChange, validator),
style: OutlinedButton.styleFrom(
minimumSize: const Size(20, 50),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
amount != null ? format(type, amount!) : "Set amount",
style: const TextStyle(color: Colors.black87, fontSize: 16),
),
const Icon(Icons.edit, size: 20)
],
)),
);
});
}
}

void _showModal(BuildContext context, BtcOrFiat type, int? amount, void Function(int?) onSetAmount,
String? Function(String?)? validator) {
showModalBottomSheet<void>(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAlias,
isScrollControlled: true,
useRootNavigator: true,
context: context,
builder: (BuildContext context) {
return SafeArea(
child: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
// the GestureDetector ensures that we can close the keyboard by tapping into the modal
child: GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);

if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: SingleChildScrollView(
child: SizedBox(
// TODO: Find a way to make height dynamic depending on the children size
// This is needed because otherwise the keyboard does not push the sheet up correctly
height: 200,
child: EnterAmountModal(
amount: amount,
onSetAmount: onSetAmount,
validator: validator,
type: type),
),
),
)));
});
}

class EnterAmountModal extends StatefulWidget {
final int? amount;
final BtcOrFiat type;
final void Function(int?) onSetAmount;
final String? Function(String?)? validator;

const EnterAmountModal(
{super.key, this.amount, required this.onSetAmount, required this.type, this.validator});

@override
State<EnterAmountModal> createState() => _EnterAmountModalState();
}

class _EnterAmountModalState extends State<EnterAmountModal> {
int? amount = 0;
final _formKey = GlobalKey<FormState>();

@override
void initState() {
super.initState();
amount = widget.amount;
}

@override
Widget build(BuildContext context) {
String hint = widget.type == BtcOrFiat.btc ? formatSats(Amount(50000)) : "\$100";

return Padding(
padding: const EdgeInsets.only(left: 20.0, top: 30.0, right: 20.0),
child: Form(
key: _formKey,
autovalidateMode: AutovalidateMode.always,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AmountInputField(
value: widget.amount != null ? Amount(widget.amount!) : Amount.zero(),
hint: "e.g. $hint",
label: "Amount",
validator: widget.validator,
onChanged: (value) {
if (value.isEmpty) {
amount = null;
}
amount = Amount.parseAmount(value).sats;
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
widget.onSetAmount(amount);
GoRouter.of(context).pop();
}
},
child: const Text("Set Amount", style: TextStyle(fontSize: 16)))
],
),
),
);
}
}
5 changes: 5 additions & 0 deletions mobile/lib/common/domain/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ class Amount {
_sats = Decimal.fromInt(sats);
}

// TODO: this is bad for precision
Amount.fromBtc(double btc) {
_sats = Decimal.fromInt((btc * (100000000.0)).round());
}

int get sats => _sats.toBigInt().toInt();

int get toInt => _sats.toBigInt().toInt();
Expand Down
8 changes: 5 additions & 3 deletions mobile/lib/common/init_service.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import 'package:flutter/material.dart';
import 'package:get_10101/common/application/lsp_change_notifier.dart';
import 'package:get_10101/common/domain/lsp_config.dart';
import 'package:get_10101/features/swap/swap_trade_values_service.dart';
import 'package:get_10101/features/trade/candlestick_change_notifier.dart';
import 'package:get_10101/features/trade/order_change_notifier.dart';
import 'package:get_10101/features/trade/position_change_notifier.dart';
import 'package:get_10101/common/amount_denomination_change_notifier.dart';
import 'package:get_10101/common/collab_revert_change_notifier.dart';
import 'package:get_10101/common/service_status_notifier.dart';
import 'package:get_10101/common/recover_dlc_change_notifier.dart';
import 'package:get_10101/features/stable/stable_value_change_notifier.dart';
import 'package:get_10101/features/swap/swap_value_change_notifier.dart';
import 'package:get_10101/features/wallet/application/faucet_service.dart';
import 'package:get_10101/features/trade/rollover_change_notifier.dart';
import 'package:get_10101/features/trade/trade_value_change_notifier.dart';
Expand Down Expand Up @@ -42,13 +43,14 @@ List<SingleChildWidget> createProviders() {

const ChannelInfoService channelInfoService = ChannelInfoService();
var tradeValuesService = TradeValuesService();
var swapTradeValuesService = SwapTradeValuesService();

var providers = [
ChangeNotifierProvider(create: (context) {
return TradeValuesChangeNotifier(tradeValuesService, channelInfoService);
}),
ChangeNotifierProvider(create: (context) {
return StableValuesChangeNotifier(tradeValuesService, channelInfoService);
return SwapValuesChangeNotifier(swapTradeValuesService, channelInfoService);
}),
ChangeNotifierProvider(create: (context) => AmountDenominationChangeNotifier()),
ChangeNotifierProvider(create: (context) => SubmitOrderChangeNotifier(OrderService())),
Expand Down Expand Up @@ -87,7 +89,7 @@ void subscribeToNotifiers(BuildContext context) {
final submitOrderChangeNotifier = context.read<SubmitOrderChangeNotifier>();
final serviceStatusNotifier = context.read<ServiceStatusNotifier>();
final channelStatusNotifier = context.read<ChannelStatusNotifier>();
final stableValuesChangeNotifier = context.read<StableValuesChangeNotifier>();
final stableValuesChangeNotifier = context.read<SwapValuesChangeNotifier>();
final asyncOrderChangeNotifier = context.read<AsyncOrderChangeNotifier>();
final rolloverChangeNotifier = context.read<RolloverChangeNotifier>();
final recoverDlcChangeNotifier = context.read<RecoverDlcChangeNotifier>();
Expand Down
6 changes: 0 additions & 6 deletions mobile/lib/common/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import 'package:get_10101/common/settings/force_close_screen.dart';
import 'package:get_10101/common/settings/settings_screen.dart';
import 'package:get_10101/common/settings/share_logs_screen.dart';
import 'package:get_10101/features/welcome/onboarding.dart';
import 'package:get_10101/features/stable/stable_screen.dart';
import 'package:get_10101/features/trade/trade_screen.dart';
import 'package:get_10101/features/wallet/domain/wallet_type.dart';
import 'package:get_10101/features/wallet/onboarding/onboarding_screen.dart';
Expand Down Expand Up @@ -191,11 +190,6 @@ GoRouter createRoutes() {
),
],
),
GoRoute(
path: StableScreen.route,
builder: (BuildContext context, GoRouterState state) {
return const StableScreen();
}),
GoRoute(
path: TradeScreen.route,
builder: (BuildContext context, GoRouterState state) {
Expand Down
17 changes: 3 additions & 14 deletions mobile/lib/common/scaffold_with_nav_bar.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get_10101/common/app_bar_wrapper.dart';
import 'package:get_10101/features/stable/stable_screen.dart';
import 'package:get_10101/features/trade/trade_screen.dart';
import 'package:get_10101/features/wallet/wallet_screen.dart';
import 'package:get_10101/util/constants.dart';
Expand All @@ -26,10 +25,6 @@ class ScaffoldWithNavBar extends StatelessWidget {
preferredSize: Size.fromHeight(40), child: SafeArea(child: AppBarWrapper())),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Container(key: tabStable, child: const Icon(Icons.currency_exchange)),
label: StableScreen.label,
),
BottomNavigationBarItem(
icon: Container(key: tabWallet, child: const Icon(Icons.wallet)),
label: WalletScreen.label,
Expand All @@ -48,27 +43,21 @@ class ScaffoldWithNavBar extends StatelessWidget {

static int _calculateSelectedIndex(BuildContext context) {
final String location = GoRouterState.of(context).location;
if (location.startsWith(StableScreen.route)) {
return 0;
}
if (location.startsWith(WalletScreen.route)) {
return 1;
return 0;
}
if (location.startsWith(TradeScreen.route)) {
return 2;
return 1;
}
return 1;
}

void _onItemTapped(int index, BuildContext context) {
switch (index) {
case 0:
GoRouter.of(context).go(StableScreen.route);
break;
case 1:
GoRouter.of(context).go(WalletScreen.route);
break;
case 2:
case 1:
GoRouter.of(context).go(TradeScreen.route);
break;
}
Expand Down
5 changes: 4 additions & 1 deletion mobile/lib/common/value_data_row.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:get_10101/common/amount_text.dart';
import 'package:get_10101/common/fiat_text.dart';
import 'package:intl/intl.dart';

enum ValueType { date, amount, fiat, percentage, contracts, loading, text }
enum ValueType { date, amount, fiat, percentage, contracts, loading, text, widget }

class ValueDataRow extends StatelessWidget {
final ValueType type;
Expand Down Expand Up @@ -57,6 +57,9 @@ class ValueDataRow extends StatelessWidget {
style: valueTextStyle,
overflow: TextOverflow.ellipsis));
break;
case ValueType.widget:
widget = value;
break;
}
} else {
// Gracefully handle the case when we passed in a null value
Expand Down
Loading

0 comments on commit 70276f7

Please sign in to comment.