Skip to content

Commit

Permalink
feat(cat-voices): PlaceholderRichText and TokenValue helper handles n…
Browse files Browse the repository at this point in the history
…ullable range (#1489)

* feat: PlaceholderRichText and TokenValue helper handles nullable range

* chore: clean up

* chore: fix import

* chore: add or equal to loc file

* chore: adjust strings
  • Loading branch information
damian-molinski authored Jan 9, 2025
1 parent 4908eeb commit 771ee64
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:catalyst_voices/widgets/rich_text/markdown_text.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
Expand Down Expand Up @@ -36,7 +35,9 @@ class _DocumentCheckboxBuilderWidgetState
late bool _currentEditValue;

DocumentNodeId get _nodeId => widget.nodeId;

MarkdownData get _description => MarkdownData(widget.description);

bool get _defaultValue => widget.definition.defaultValue;

@override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';

final _kPlaceholderRegExp = RegExp(r'{(\w*)}');

typedef PlaceholderSpanBuilder = InlineSpan Function(
BuildContext context,
String placeholder,
);

class PlaceholderRichText extends StatefulWidget {
final String text;
final PlaceholderSpanBuilder placeholderSpanBuilder;
final TextStyle? style;
final TextAlign textAlign;

const PlaceholderRichText(
this.text, {
super.key,
required this.placeholderSpanBuilder,
this.style,
this.textAlign = TextAlign.start,
});

@override
State<PlaceholderRichText> createState() => _PlaceholderRichTextState();
}

class _PlaceholderRichTextState extends State<PlaceholderRichText> {
List<InlineSpan> _spans = [];

@override
void didChangeDependencies() {
super.didChangeDependencies();

// Context colors may have changed, rebuild it.
_spans = _calculateSpans();
}

@override
void didUpdateWidget(covariant PlaceholderRichText oldWidget) {
super.didUpdateWidget(oldWidget);

if (widget.text != oldWidget.text) {
_spans = _calculateSpans();
}
}

@override
Widget build(BuildContext context) {
return Text.rich(
TextSpan(children: _spans),
textAlign: widget.textAlign,
style: widget.style,
);
}

List<InlineSpan> _calculateSpans() {
final text = widget.text;
final spans = <InlineSpan>[];

text.splitMapJoin(
_kPlaceholderRegExp,
onMatch: (match) {
final placeholder = match.group(0)!;
// removes "{" and "}"
final key = placeholder.substring(1, placeholder.length - 1);

final span = widget.placeholderSpanBuilder(context, key);

spans.add(span);

return '';
},
onNonMatch: (match) {
spans.add(TextSpan(text: match));
return '';
},
);

return spans;
}
}
71 changes: 46 additions & 25 deletions catalyst_voices/apps/voices/lib/widgets/text_field/token_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class TokenField extends StatelessWidget {
filled: true,
helper: range != null && showHelper
? _Helper(
symbol: currency.symbol,
currency: currency,
range: range,
)
: null,
Expand Down Expand Up @@ -87,37 +87,58 @@ class TokenField extends StatelessWidget {
}

class _Helper extends StatelessWidget {
final String symbol;
final Currency currency;
final Range<int> range;

const _Helper({
required this.symbol,
required this.currency,
required this.range,
});

@override
Widget build(BuildContext context) {
// TODO(damian-molinski): Range can accept null as min/max
// meaning they are unconstrained, handle it
// TODO(damian-molinski): Refactor text formatting with smarter syntax
return Text.rich(
TextSpan(
children: [
TextSpan(text: context.l10n.requestedAmountShouldBeBetween),
const TextSpan(text: ' '),
TextSpan(
text: '$symbol${range.min}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
const TextSpan(text: ' '),
TextSpan(text: context.l10n.and),
const TextSpan(text: ' '),
TextSpan(
text: '$symbol${range.max}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
);
final min = range.min;
final max = range.max;

const boldStyle = TextStyle(fontWeight: FontWeight.bold);

if (min != null && max != null) {
return PlaceholderRichText(
context.l10n.requestedAmountShouldBeBetweenMinAndMax,
placeholderSpanBuilder: (context, placeholder) {
return switch (placeholder) {
'min' => TextSpan(text: currency.format(min), style: boldStyle),
'max' => TextSpan(text: currency.format(max), style: boldStyle),
_ => throw ArgumentError('Unknown placeholder[$placeholder]'),
};
},
);
}

if (min != null) {
return PlaceholderRichText(
context.l10n.requestedAmountShouldBeMoreThan,
placeholderSpanBuilder: (context, placeholder) {
return switch (placeholder) {
'min' => TextSpan(text: currency.format(min), style: boldStyle),
_ => throw ArgumentError('Unknown placeholder[$placeholder]'),
};
},
);
}

if (max != null) {
return PlaceholderRichText(
context.l10n.requestedAmountShouldBeLessThan,
placeholderSpanBuilder: (context, placeholder) {
return switch (placeholder) {
'max' => TextSpan(text: currency.format(max), style: boldStyle),
_ => throw ArgumentError('Unknown placeholder[$placeholder]'),
};
},
);
}

return const Text('');
}
}
2 changes: 2 additions & 0 deletions catalyst_voices/apps/voices/lib/widgets/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export 'navigation/sections_controller.dart';
export 'navigation/sections_list_view.dart';
export 'navigation/sections_list_view_builder.dart';
export 'navigation/sections_menu.dart';
export 'rich_text/markdown_text.dart';
export 'rich_text/placeholder_rich_text.dart';
export 'scrollbar/voices_scrollbar.dart';
export 'seed_phrase/seed_phrases_completer.dart';
export 'seed_phrase/seed_phrases_picker.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1180,11 +1180,9 @@
"searchProposals": "Search Proposals",
"search": "Search…",
"agree": "I Agree",
"requestedAmountShouldBeBetween": "Requested amount should be between",
"and": "and",
"@and": {
"description": "General text. May be used in context of A4000 and $5000"
},
"requestedAmountShouldBeBetweenMinAndMax": "Requested amount should be between {min} and {max}",
"requestedAmountShouldBeMoreThan": "Requested amount should be at least {min}",
"requestedAmountShouldBeLessThan": "Requested amount should be at most {max}",
"errorValidationTokenNotParsed": "Invalid input. Could not parse parse.",
"@errorValidationTokenNotParsed": {
"description": "A validation error when user enters input which cannot be parsed into token value."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ final class Currency extends Equatable {
symbol: '₳',
);

String format(num money) => '$symbol$money';

@override
List<Object?> get props => [
name,
Expand Down

0 comments on commit 771ee64

Please sign in to comment.