Skip to content
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
2 changes: 1 addition & 1 deletion bitassets/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: bitassets
description: UI for Drivechain (BIP300/301) based sidechains
publish_to: "none" # Remove this line if you wish to publish to pub.dev

version: 1.0.354
version: 1.0.355

environment:
sdk: 3.12.2
Expand Down
2 changes: 1 addition & 1 deletion bitnames/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: bitnames
description: UI for Drivechain (BIP300/301) based sidechains
publish_to: "none" # Remove this line if you wish to publish to pub.dev

version: 1.0.355
version: 1.0.356

environment:
sdk: 3.12.2
Expand Down
31 changes: 28 additions & 3 deletions bitwindow/lib/dialogs/freebank_bid_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,45 @@ class FreeBankBidDialog extends StatefulWidget {

class _FreeBankBidDialogState extends State<FreeBankBidDialog> {
final FreeBankRPC _rpc = GetIt.I.get<FreeBankRPC>();
final BMMProvider _bmm = GetIt.I.get<BMMProvider>();
final TextEditingController _name = TextEditingController();
String? _saved;
String? _error;
bool _saving = false;

bool get _hasName => _saved != null && _saved!.isNotEmpty;

/// Why the engine's controls stay shut, or null when a bid can go out. Every
/// block a bid wins carries the name, and a node that restarts answers no RPC
/// until it is up again.
String? get _blockedReason {
if (!_hasName) {
return 'Save a name above to start bidding.';
}
// The orchestrator keeps a started session, and it bids on. So the Stop
// control stays reachable while the node is down.
if (!_rpc.connected && !_bmm.running) {
Comment thread
octobocto marked this conversation as resolved.
return 'Waiting for FreeBank to start.';
}
return null;
}

File get _conf => File(filePath([_rpc.binary.datadirNetwork(), 'freebank.conf']));

@override
void initState() {
super.initState();
_rpc.addListener(_onConnectionChanged);
_bmm.addListener(_onConnectionChanged);
_load();
}

void _onConnectionChanged() {
if (mounted) {
setState(() {});
}
}

Future<void> _load() async {
try {
final conf = await _conf.exists() ? await _conf.readAsString() : '';
Expand Down Expand Up @@ -97,6 +121,8 @@ class _FreeBankBidDialogState extends State<FreeBankBidDialog> {

@override
void dispose() {
_rpc.removeListener(_onConnectionChanged);
_bmm.removeListener(_onConnectionChanged);
_name.dispose();
super.dispose();
}
Expand Down Expand Up @@ -146,13 +172,12 @@ class _FreeBankBidDialogState extends State<FreeBankBidDialog> {
scrollDirection: Axis.horizontal,
child: SizedBox(
width: _bmmTabWidth,
// Every block a bid wins carries the name, so bidding waits for one.
child: _hasName
child: _blockedReason == null
Comment thread
octobocto marked this conversation as resolved.
? const BMMTab()
: Stack(
children: [
const IgnorePointer(child: Opacity(opacity: 0.35, child: BMMTab())),
Center(child: SailText.primary15('Save a name above to start bidding.')),
Center(child: SailText.primary15(_blockedReason!)),
],
),
),
Expand Down
20 changes: 16 additions & 4 deletions bitwindow/lib/utils/freebank_conf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const coinbaseTagMaxBytes = 64;
/// belongs to that network only.
final _sectionHeader = RegExp(r'^\[[^\]]*\]$');

/// A `coinbasetag` line. Core takes whitespace around the `=`, so a valid entry
/// reads `coinbasetag = alice` as well.
final _tagLine = RegExp('^$coinbaseTagKey\\s*=\\s*(.*)\$');
Comment thread
octobocto marked this conversation as resolved.

/// The network section freebankd reads. BitWindow starts the node with no chain
/// flag, so it runs its default main chain.
const activeNetworkSection = 'main';
Expand All @@ -29,10 +33,11 @@ String? readCoinbaseTag(String conf) {
section = trimmed.substring(1, trimmed.length - 1);
continue;
}
if (!trimmed.startsWith('$coinbaseTagKey=')) {
final tag = _tagLine.firstMatch(trimmed);
if (tag == null) {
continue;
}
final value = trimmed.substring(coinbaseTagKey.length + 1).trim();
final value = _withoutComment(tag.group(1)!);
if (section == null) {
global = value;
} else if (section == activeNetworkSection) {
Expand All @@ -42,6 +47,13 @@ String? readCoinbaseTag(String conf) {
return active ?? global;
}

/// The value without an inline `# comment`, as the node's own conf parser reads
/// it.
String _withoutComment(String value) {
final hash = value.indexOf('#');
return (hash < 0 ? value : value.substring(0, hash)).trim();
}

/// Why freebankd would refuse this name, or null if it takes it. It mirrors the
/// node's own check (1 to 64 printable ASCII bytes), plus the two things the conf
/// file itself would bend: a `#` starts a comment, and quotes become part of the
Expand Down Expand Up @@ -88,7 +100,7 @@ String setCoinbaseTag(String conf, String name) {
written = true;
}
section = trimmed.substring(1, trimmed.length - 1);
} else if (section == target && trimmed.startsWith('$coinbaseTagKey=')) {
} else if (section == target && _tagLine.hasMatch(trimmed)) {
if (!written) {
kept.add(line);
written = true;
Expand All @@ -114,7 +126,7 @@ bool _tagInActiveSection(List<String> lines) {
final trimmed = l.trim();
if (_sectionHeader.hasMatch(trimmed)) {
section = trimmed.substring(1, trimmed.length - 1);
} else if (section == activeNetworkSection && trimmed.startsWith('$coinbaseTagKey=')) {
} else if (section == activeNetworkSection && _tagLine.hasMatch(trimmed)) {
return true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion bitwindow/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description:
"A frontend for interacting with a Drivechain-enabled layer 1 bitcoin network."
publish_to: "none"

version: 0.2.232
version: 0.2.233

environment:
sdk: 3.12.2
Expand Down
17 changes: 17 additions & 0 deletions bitwindow/test/freebank_conf_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ void main() {
expect(readCoinbaseTag('server=1\n# coinbasetag=old\n'), isNull);
});

test('drops an inline comment, as the node does', () {
expect(readCoinbaseTag('coinbasetag = alice # my pool\n'), 'alice');
expect(readCoinbaseTag('[main]\ncoinbasetag=bob#x\n'), 'bob');
});

test('takes whitespace around the delimiter, as Core does', () {
expect(readCoinbaseTag('coinbasetag = alice\n'), 'alice');
expect(readCoinbaseTag('[main]\ncoinbasetag\t=\tbob\n'), 'bob');
});

test('takes the [main] name over the global one', () {
expect(readCoinbaseTag('coinbasetag=global\n[main]\ncoinbasetag=active\n'), 'active');
expect(readCoinbaseTag('[main]\ncoinbasetag=active\n'), 'active');
Expand Down Expand Up @@ -62,6 +72,13 @@ void main() {
);
});

test('changes a spaced entry in place, and adds no second line', () {
expect(
setCoinbaseTag('[main]\ncoinbasetag = alice\n', 'bob'),
'[main]\ncoinbasetag=bob\n',
);
});

test('changes the [main] name, because that one wins', () {
expect(
setCoinbaseTag('coinbasetag=global\n[main]\ncoinbasetag=old\n', 'new'),
Expand Down
42 changes: 13 additions & 29 deletions bitwindow/test/notification_banner_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'package:sail_ui/sail_ui.dart';

import 'test_utils.dart';

Future<bool> _noop(BuildContext context, NotificationItem item) async => true;

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

Expand Down Expand Up @@ -155,37 +157,19 @@ void main() {
expect(find.text('drynet3 is out'), findsNothing);
});

// A handler that opens its own dialog carries the whole message. A generic
// card in front of it reads as two dialogs for one warning.
testWidgets('an own-dialog action opens no generic card', (tester) async {
final p = await freshProvider();
var ran = false;
if (GetIt.I.isRegistered<NotificationActions>()) {
GetIt.I.unregister<NotificationActions>();
}
GetIt.I.registerSingleton<NotificationActions>(
NotificationActions({'act': (_, _) async => ran = true}, ownDialogs: const {'act'}),
);
p.add(
id: 'first',
title: 'The blocks on disk are from alphanet',
content: 'But you are on betanet.',
dialogType: DialogType.error,
style: NotificationStyle.modalThenBanner,
action: 'act',
);
// A handler that opens its own dialog carries the whole message, so the
// generic card stays shut. The key decides, and main.dart passes it.
test('an action key can own its dialog', () {
const actions = NotificationActions({'act': _noop}, ownDialogs: {'act'});

// A bare tree, with no SailApp: the handler owns the dialog, so the banner
// paints no strip and reads no theme. A heavier harness waits on frames
// this assertion does not need.
await tester.pumpWidget(const MaterialApp(home: NotificationBanner()));
for (var frame = 0; frame < 3; frame++) {
await tester.pump(const Duration(milliseconds: 50));
}
expect(actions.ownDialogs, contains('act'));
expect(actions['act'], isNotNull);
});

test('an action key owns no dialog by default', () {
const actions = NotificationActions({'act': _noop});

expect(ran, isTrue);
expect(find.text('Confirm'), findsNothing);
expect(p.activeBanner, isNull, reason: 'the handler reported the repair done');
expect(actions.ownDialogs, isEmpty);
});

testWidgets('the ✕ marks it read without running the action', (tester) async {
Expand Down
2 changes: 1 addition & 1 deletion coinshift/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: coinshift
description: UI for CoinShift Drivechain sidechain with L1/L2 swap functionality
publish_to: "none" # Remove this line if you wish to publish to pub.dev

version: 1.0.20
version: 1.0.21

environment:
sdk: 3.12.2
Expand Down
2 changes: 1 addition & 1 deletion photon/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: photon
description: UI for Drivechain (BIP300/301) based sidechains
publish_to: "none" # Remove this line if you wish to publish to pub.dev

version: 1.0.225
version: 1.0.226

environment:
sdk: 3.12.2
Expand Down
42 changes: 32 additions & 10 deletions sail_ui/lib/widgets/containers/notification_banner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,17 @@ class _NotificationBannerState extends State<NotificationBanner> {
super.dispose();
}

void _onNotifications() {
if (_provider != null && _provider!.pendingModal != null) {
unawaited(_openPendingModal(_provider!));
void _onNotifications() => _openWhenFree();

/// Opens a pending modal while the screen is free. The screen frees up at the
/// end of an action and at the end of the loop, and a modal can arrive at any
/// moment, so all three call this.
void _openWhenFree() {
final provider = _provider;
if (!mounted || _modalOpen || _actionRunning || provider == null || provider.pendingModal == null) {
return;
}
unawaited(_openPendingModal(provider));
}

/// Opens the modal of every item that waits for one, in turn. The mark goes
Expand All @@ -75,14 +82,24 @@ class _NotificationBannerState extends State<NotificationBanner> {
if (item == null) {
return;
}
await provider.markModalShown(item.id);
if (!mounted) {
return;
}
if (_ownsDialog(item.action)) {
await _runAction(context, provider, item);
// The handler carries the whole message, and _actionRunning stops a
// second copy, so the mark waits for the action. A mark before a busy
// action drops the modal for good. The loop ends while another action
// owns the screen, and the end of that action opens this one.
if (!await _runAction(context, provider, item)) {
return;
}
await provider.markModalShown(item.id);
Comment thread
octobocto marked this conversation as resolved.
continue;
}
await provider.markModalShown(item.id);
if (!mounted) {
return;
}
final confirmed = await showThemedDialog<bool>(
context: context,
builder: (context) => SailAlertCard(
Expand All @@ -97,6 +114,7 @@ class _NotificationBannerState extends State<NotificationBanner> {
}
} finally {
_modalOpen = false;
_openWhenFree();
}
}

Expand All @@ -105,23 +123,27 @@ class _NotificationBannerState extends State<NotificationBanner> {
GetIt.I.isRegistered<NotificationActions>() &&
GetIt.I.get<NotificationActions>().ownDialogs.contains(action);

/// Runs the item's action. One at a time: the modal closes before the action
/// ends, and a tap on the banner behind it would start a second one.
Future<void> _runAction(BuildContext context, NotificationProvider provider, NotificationItem item) async {
/// Runs the item's action, and reports whether it ran. One at a time: the modal
/// closes before the action ends, and a tap on the banner behind it would start
/// a second one. False means another action owns the screen, so the caller
/// leaves this item for its turn.
Future<bool> _runAction(BuildContext context, NotificationProvider provider, NotificationItem item) async {
if (_actionRunning) {
return;
return false;
}
_actionRunning = true;
try {
final handler = GetIt.I.isRegistered<NotificationActions>()
? GetIt.I.get<NotificationActions>()[item.action]
: null;
if (handler != null && !await handler(context, item)) {
return;
return true;
}
await provider.markRead(item.id);
return true;
} finally {
_actionRunning = false;
_openWhenFree();
}
}

Expand Down
2 changes: 1 addition & 1 deletion thunder/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: thunder
description: UI for Drivechain (BIP300/301) based sidechains
publish_to: "none" # Remove this line if you wish to publish to pub.dev

version: 1.0.354
version: 1.0.355

environment:
sdk: 3.12.2
Expand Down
2 changes: 1 addition & 1 deletion truthcoin/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: truthcoin
description: UI for Bitcoin Hivemind prediction market sidechain
publish_to: "none" # Remove this line if you wish to publish to pub.dev

version: 1.0.225
version: 1.0.226

environment:
sdk: 3.12.2
Expand Down
2 changes: 1 addition & 1 deletion zside/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: zside
description: UI for Drivechain (BIP300/301) based sidechains
publish_to: "none" # Remove this line if you wish to publish to pub.dev

version: 1.0.351
version: 1.0.352

environment:
sdk: 3.12.2
Expand Down
Loading