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

let user specify exemptfee #636

Merged
merged 3 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions lib/bloc/payment_options/form_validator.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import 'package:breez_translations/breez_translations_locales.dart';

String? baseFeeValidator(
String? exemptFeeValidator(
String? value,
) {
final texts = getSystemAppLocalizations();
if (value == null) {
return texts.payment_options_base_fee_label;
return texts.payment_options_exemptfee_label;
}
if (value.isEmpty) {
return texts.payment_options_base_fee_label;
return texts.payment_options_exemptfee_label;
}
try {
final newBaseFee = int.parse(value);
if (newBaseFee < 0) {
return texts.payment_options_base_fee_label;
final newExemptFee = int.parse(value);
if (newExemptFee < 0) {
return texts.payment_options_exemptfee_label;
}
} catch (e) {
return texts.payment_options_base_fee_label;
return texts.payment_options_exemptfee_label;
}
return null;
}
Expand Down
17 changes: 15 additions & 2 deletions lib/bloc/payment_options/payment_options_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,30 @@ class PaymentOptionsBloc extends Cubit<PaymentOptionsState> {
));
}

Future<void> setExemptfeeMsat(int exemptfeeMsat) async {
emit(state.copyWith(
exemptFeeMsat: exemptfeeMsat,
saveEnabled: true,
));
}

Future<void> resetFees() async {
_log.v("Resetting payments override settings to default: enabled: $kDefaultOverrideFee, "
"proportional: $kDefaultProportionalFee");
await _preferences.setPaymentOptionsOverrideFeeEnabled(kDefaultOverrideFee);
await _preferences.setPaymentOptionsProportionalFee(kDefaultProportionalFee);
await _preferences.setPaymentOptionsExemptFee(kDefaultExemptFeeMsat);
emit(const PaymentOptionsState.initial());
}

Future<void> saveFees() async {
final state = this.state;
_log.v("Saving payments override settings: enabled: ${state.overrideFeeEnabled}, "
"proportional: ${state.proportionalFee}");
"proportional: ${state.proportionalFee}"
"Exemptfee: ${state.exemptFeeMsat}");
await _preferences.setPaymentOptionsOverrideFeeEnabled(state.overrideFeeEnabled);
await _preferences.setPaymentOptionsProportionalFee(state.proportionalFee);
await _preferences.setPaymentOptionsExemptFee(state.exemptFeeMsat);
emit(state.copyWith(saveEnabled: false));
}

Expand All @@ -56,10 +66,13 @@ class PaymentOptionsBloc extends Cubit<PaymentOptionsState> {
_log.v("Fetching payments override settings");
final enabled = await _preferences.getPaymentOptionsOverrideFeeEnabled();
final proportional = await _preferences.getPaymentOptionsProportionalFee();
_log.v("Payments override fetched: enabled: $enabled, proportional: $proportional");
final exemptFeeMsat = await _preferences.getPaymentOptionsExemptFee();
_log.v(
"Payments override fetched: enabled: $enabled, proportional: $proportional, exemptFeeMsat: $exemptFeeMsat");
emit(PaymentOptionsState(
overrideFeeEnabled: enabled,
proportionalFee: proportional,
exemptFeeMsat: exemptFeeMsat,
saveEnabled: false,
));
}
Expand Down
10 changes: 8 additions & 2 deletions lib/bloc/payment_options/payment_options_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import 'package:c_breez/utils/preferences.dart';
class PaymentOptionsState {
final bool overrideFeeEnabled;
final double proportionalFee;
final int exemptFeeMsat;
final bool saveEnabled;

const PaymentOptionsState({
this.overrideFeeEnabled = kDefaultOverrideFee,
this.proportionalFee = kDefaultProportionalFee,
this.exemptFeeMsat = kDefaultExemptFeeMsat,
this.saveEnabled = false,
});

Expand All @@ -16,18 +18,20 @@ class PaymentOptionsState {
PaymentOptionsState copyWith({
bool? overrideFeeEnabled,
double? proportionalFee,
int? exemptFeeMsat,
bool? saveEnabled,
}) {
return PaymentOptionsState(
overrideFeeEnabled: overrideFeeEnabled ?? this.overrideFeeEnabled,
proportionalFee: proportionalFee ?? this.proportionalFee,
exemptFeeMsat: exemptFeeMsat ?? this.exemptFeeMsat,
saveEnabled: saveEnabled ?? this.saveEnabled,
);
}

@override
String toString() => 'PaymentOptionsState{overrideFeeEnabled: $overrideFeeEnabled, '
'proportionalFee: $proportionalFee, saveEnabled: $saveEnabled}';
'proportionalFee: $proportionalFee, saveEnabled: $saveEnabled, exemptFeeMsat: $exemptFeeMsat}';

@override
bool operator ==(Object other) =>
Expand All @@ -36,8 +40,10 @@ class PaymentOptionsState {
runtimeType == other.runtimeType &&
overrideFeeEnabled == other.overrideFeeEnabled &&
proportionalFee == other.proportionalFee &&
exemptFeeMsat == other.exemptFeeMsat &&
saveEnabled == other.saveEnabled;

@override
int get hashCode => overrideFeeEnabled.hashCode ^ proportionalFee.hashCode ^ saveEnabled.hashCode;
int get hashCode =>
overrideFeeEnabled.hashCode ^ proportionalFee.hashCode ^ saveEnabled.hashCode ^ exemptFeeMsat.hashCode;
}
15 changes: 15 additions & 0 deletions lib/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class Config {
maxfeePercent: await _configuredMaxFeePercent(serviceInjector, defaultConf),
workingDir: await _workingDir(),
mempoolspaceUrl: await _mempoolSpaceUrl(serviceInjector, defaultConf),
exemptfeeMsat: await _configuredExempMsatFee(serviceInjector, defaultConf),
apiKey: breezConfig.apiKey,
);
}
Expand All @@ -90,6 +91,20 @@ class Config {
return defaultConf.maxfeePercent;
}

static Future<int> _configuredExempMsatFee(
ServiceInjector serviceInjector,
sdk.Config defaultConf,
) async {
final preferences = serviceInjector.preferences;
final configuredExemptFeeEnabled = await preferences.getPaymentOptionsOverrideFeeEnabled();
if (configuredExemptFeeEnabled) {
final configuredExemptFee = await preferences.getPaymentOptionsExemptFee();
_log.v("Using exemptMsatFee from preferences: $configuredExemptFee");
return configuredExemptFee;
}
return defaultConf.exemptfeeMsat;
}

static Future<String> _mempoolSpaceUrl(
ServiceInjector serviceInjector,
sdk.Config defaultConf,
Expand Down
2 changes: 2 additions & 0 deletions lib/routes/payment_options/payment_options_page.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:breez_translations/breez_translations_locales.dart';
import 'package:c_breez/bloc/payment_options/payment_options_bloc.dart';
import 'package:c_breez/routes/payment_options/widget/actions_fee.dart';
import 'package:c_breez/routes/payment_options/widget/exempt_fee_widget.dart';
import 'package:c_breez/routes/payment_options/widget/header_fee.dart';
import 'package:c_breez/routes/payment_options/widget/override_fee.dart';
import 'package:c_breez/routes/payment_options/widget/proportional_fee_widget.dart';
Expand Down Expand Up @@ -36,6 +37,7 @@ class PaymentOptionsPage extends StatelessWidget {
children: const [
HeaderFee(),
OverrideFee(),
ExemptfeeMsatWidget(),
ProportionalFeeWidget(),
ActionsFee(),
],
Expand Down
98 changes: 98 additions & 0 deletions lib/routes/payment_options/widget/exempt_fee_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import 'dart:async';

import 'package:breez_translations/breez_translations_locales.dart';
import 'package:c_breez/bloc/payment_options/form_validator.dart';
import 'package:c_breez/bloc/payment_options/payment_options_bloc.dart';
import 'package:c_breez/bloc/payment_options/payment_options_state.dart';
import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rxdart/rxdart.dart';

final _log = FimberLog("ExemptfeeMsatWidget");

class ExemptfeeMsatWidget extends StatefulWidget {
const ExemptfeeMsatWidget({
Key? key,
}) : super(key: key);

@override
State<ExemptfeeMsatWidget> createState() => _ExemptfeeMsatState();
}

class _ExemptfeeMsatState extends State<ExemptfeeMsatWidget> {
final _exemptFeeController = TextEditingController();

StreamSubscription<PaymentOptionsState>? _subscription;

@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) => _initializeListeners());
}

@override
void dispose() {
super.dispose();
_subscription?.cancel();
}

@override
Widget build(BuildContext context) {
final texts = context.texts();

return Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 8.0),
child: Form(
child: BlocBuilder<PaymentOptionsBloc, PaymentOptionsState>(
builder: (context, state) {
return TextFormField(
enabled: state.overrideFeeEnabled,
keyboardType: const TextInputType.numberWithOptions(),
controller: _exemptFeeController,
decoration: InputDecoration(
labelText: texts.payment_options_exemptfee_label,
border: const UnderlineInputBorder(),
),
validator: exemptFeeValidator,
onChanged: (value) {
_log.v("onChanged: $value");
int exemptFeeSat;
try {
exemptFeeSat = int.parse(value);
} catch (_) {
_log.v("Failed to parse $value as int");
return;
}
context.read<PaymentOptionsBloc>().setExemptfeeMsat(exemptFeeSat * 1000);
},
);
},
),
),
),
),
],
);
}

void _initializeListeners() {
final bloc = context.read<PaymentOptionsBloc>();
_subscription = bloc.stream.startWith(bloc.state).distinct().listen((state) {
if (!state.saveEnabled) {
final exemptFeeSat = (state.exemptFeeMsat ~/ 1000).toString();

if (_exemptFeeController.text != exemptFeeSat) {
_log.v("Setting exemptFee to $exemptFeeSat");
setState(() {
_exemptFeeController.text = exemptFeeSat;
});
}
}
});
}
}
13 changes: 13 additions & 0 deletions lib/utils/preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import 'package:shared_preferences/shared_preferences.dart';

const kDefaultOverrideFee = false;
const kDefaultProportionalFee = 1.0;
const kDefaultExemptFeeMsat = 20000;

const _mempoolSpaceUrlKey = "mempool_space_url";
const _kPaymentOptionOverrideFee = "payment_options_override_fee";
const _kPaymentOptionProportionalFee = "payment_options_proportional_fee";
const _kPaymentOptionExemptFee = "payment_options_exempt_fee";

final _log = FimberLog("preferences");

Expand Down Expand Up @@ -48,4 +50,15 @@ class Preferences {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble(_kPaymentOptionProportionalFee, fee);
}

Future<int> getPaymentOptionsExemptFee() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getInt(_kPaymentOptionExemptFee) ?? kDefaultExemptFeeMsat;
}

Future<void> setPaymentOptionsExemptFee(int exemptfeeMsat) async {
_log.d("set payment options exempt fee : $exemptfeeMsat");
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(_kPaymentOptionExemptFee, exemptfeeMsat);
}
}
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "815bae7c82d4260744fbeddbb91fa71453891ac8"
resolved-ref: "815bae7c82d4260744fbeddbb91fa71453891ac8"
ref: "0304720f24266d8c1e887c86d8e41e80bf217d92"
resolved-ref: "0304720f24266d8c1e887c86d8e41e80bf217d92"
url: "https://github.com/breez/Breez-Translations"
source: git
version: "1.0.0"
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies:
breez_translations:
git:
url: https://github.com/breez/Breez-Translations
ref: 815bae7c82d4260744fbeddbb91fa71453891ac8
ref: 0304720f24266d8c1e887c86d8e41e80bf217d92
clipboard_watcher: ^0.2.0
csv: ^5.0.2
connectivity_plus: ^4.0.2
Expand Down
26 changes: 13 additions & 13 deletions test/bloc/payment_options/form_validator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,29 @@ import 'package:flutter_test/flutter_test.dart';

void main() {
group('base fee validator', () {
test('should return payment_options_base_fee_label when value is null', () {
final result = baseFeeValidator(null);
expect(result, _texts().payment_options_base_fee_label);
test('should return payment_options_exemptfee_label when value is null', () {
final result = exemptFeeValidator(null);
expect(result, _texts().payment_options_exemptfee_label);
});

test('should return payment_options_base_fee_label when value is empty', () {
final result = baseFeeValidator('');
expect(result, _texts().payment_options_base_fee_label);
test('should return payment_options_exemptfee_label when value is empty', () {
final result = exemptFeeValidator('');
expect(result, _texts().payment_options_exemptfee_label);
});

test('should return payment_options_base_fee_label when value is negative', () {
final result = baseFeeValidator('-1');
expect(result, _texts().payment_options_base_fee_label);
test('should return payment_options_exemptfee_label when value is negative', () {
final result = exemptFeeValidator('-1');
expect(result, _texts().payment_options_exemptfee_label);
});

test('should return null when value is valid', () {
final result = baseFeeValidator('1');
final result = exemptFeeValidator('1');
expect(result, null);
});

test('should return payment_options_base_fee_label when value is not a number', () {
final result = baseFeeValidator('a');
expect(result, _texts().payment_options_base_fee_label);
test('should return payment_options_exemptfee_label when value is not a number', () {
final result = exemptFeeValidator('a');
expect(result, _texts().payment_options_exemptfee_label);
});
});

Expand Down
Loading