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

feat(cat-voices): TokenField and DocumentTokenValueTile #1443

Merged
merged 63 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
3ab52df
feat(cat-voices): In-page Information Cards (#1242)
LynxLynxx Nov 26, 2024
720522d
chore(general): merge main into mve3 (#1282)
dtscalac Nov 27, 2024
4c1b56b
feat(cat-voices): discovery page mve3 (#1281)
dtscalac Nov 27, 2024
67930c6
feat(cat-voices): campaign modal (#1289)
damian-molinski Nov 29, 2024
82ffa5a
feat(cat-voices): Date & Time input widget (#1224)
LynxLynxx Nov 29, 2024
0e903a2
feat(cat-voices): admin preview tools (#1309)
dtscalac Dec 2, 2024
c186759
feat(cat-voices): Campaign managment status UI (#1314)
LynxLynxx Dec 3, 2024
b41f138
refactor(cat-voices): Move UI models to view_model to models package …
damian-molinski Dec 3, 2024
98082ee
feat(cat-voices): vit ss endpoints generating (#1302)
damian-molinski Dec 3, 2024
93f6aba
feat(cat-voices): Campaign info dialog on discovery (#1321)
dtscalac Dec 5, 2024
4ef4b77
feat(cat-voices): admin tools events timer (#1323)
dtscalac Dec 5, 2024
f93dae6
feat(cat-voices): Merge main into mve3 (#1334)
LynxLynxx Dec 5, 2024
32cab1a
feat(cat-voices): Admin view for overall spaces menu (#1326)
LynxLynxx Dec 5, 2024
812402b
feat(cat-voices): proposal editor template data flow (#1335)
damian-molinski Dec 6, 2024
7ab48ac
refactor(cat-voices): move api to repository package (#1336)
damian-molinski Dec 6, 2024
d1b9b32
feat(cat-voices): admin tools mocked data (#1356)
dtscalac Dec 9, 2024
dcf0ad8
Missing autofocus param (#1361)
LynxLynxx Dec 9, 2024
a96639d
feat(dart/catalyst_cardano_serialization): replace ulid by UUID in au…
dtscalac Dec 11, 2024
51e5b14
fix: missing scaffold for global snackbars (#1373)
damian-molinski Dec 11, 2024
2dfd4c3
feat(dart/catalyst_cose): Catalyst COSE_SIGN support (#1374)
dtscalac Dec 12, 2024
d582ffe
feat(cat-voices): Cached vault unlock state (#1372)
damian-molinski Dec 13, 2024
04286ca
feat(cat-voices): app config model (#1378)
damian-molinski Dec 13, 2024
35cef8a
feat(cat-voices): caching user (#1391)
damian-molinski Dec 16, 2024
727b4cf
feat(cat-voices): setup campaign stage dates (#1396)
dtscalac Dec 16, 2024
af1d8c1
feat(cat-voices): multi proposal workspace (#1409)
damian-molinski Dec 19, 2024
0289c36
Merge branch 'main' into mve3
dtscalac Dec 23, 2024
bd56a5c
Merge branch 'main' into mve3
dtscalac Dec 23, 2024
6cbe94c
chore: missing build_runner for repository, restore wrongly deleted file
dtscalac Dec 23, 2024
e9d9eac
feat(cat-voices): proposal template models (#1363)
LynxLynxx Dec 23, 2024
d01dff1
Merge branch 'main' into mve3
dtscalac Dec 23, 2024
14c01e4
feat(cat-voices): encode/decode cose documents (#1408)
dtscalac Dec 24, 2024
d434c7e
Merge branch 'main' into mve3
dtscalac Dec 27, 2024
6685708
fix: add missing config after solving merge conflicts
dtscalac Dec 27, 2024
707a207
chore: work in progress
damian-molinski Dec 30, 2024
081d579
refactor: VoicesIntField extends VoicesNumField
damian-molinski Dec 30, 2024
4b9b261
feat: validation + expose onChanged
damian-molinski Dec 30, 2024
eea07ca
refactor: simplify token validator
damian-molinski Dec 30, 2024
31d0243
Merge branch 'mve3' into feat/token_field_1411
damian-molinski Dec 30, 2024
0015f9f
feat: expose readOnly
damian-molinski Dec 30, 2024
1c2b041
refactor: Use Range in TokenField
damian-molinski Dec 30, 2024
e96966f
Merge main into feat/token_field_1411
damian-molinski Dec 30, 2024
c4665ea
fix: merge conflict
damian-molinski Dec 30, 2024
e55786a
refactor: move buildDecoration as private state function
damian-molinski Dec 31, 2024
fcc0561
Merge branch 'main' into feat/token_field_1411
damian-molinski Dec 31, 2024
bacb39d
chore: work in progress
damian-molinski Dec 31, 2024
0aa1b02
feat: auto focus in edit mode
damian-molinski Jan 2, 2025
225cf55
refactor: move token_field to widgets package
damian-molinski Jan 2, 2025
4988829
feat: selectable token value tile
damian-molinski Jan 2, 2025
c51dadf
chore: cleanup WorkspacePage
damian-molinski Jan 2, 2025
14037d8
Merge main into feat/token_field_1411
damian-molinski Jan 2, 2025
fad5a83
feat: document change callback
damian-molinski Jan 2, 2025
a52ebce
refactor: unify property names. Dispose focusNode
damian-molinski Jan 2, 2025
bdde35a
revert: VoicesTextField ignore pointer in readOnly mode
damian-molinski Jan 2, 2025
73a6814
refactor: expose ignorePointers in VoicesTextField
damian-molinski Jan 2, 2025
395f2e2
refactor: move super.initState to the top
damian-molinski Jan 2, 2025
2433704
feat: build DocumentTokenValueTile inside DocumentPropertyBuilderWidget
damian-molinski Jan 2, 2025
11b4564
Merge branch 'main' into feat/token_field_1411
damian-molinski Jan 2, 2025
d890e3e
Merge branch 'main' into feat/token_field_1411
damian-molinski Jan 3, 2025
004469b
Merge branch 'main' into feat/token_field_1411
damian-molinski Jan 3, 2025
8be0b04
Merge main into feat/token_field_1411
damian-molinski Jan 3, 2025
a3df8ab
feat: update dummy validation
damian-molinski Jan 3, 2025
05f96b1
Merge branch 'main' into feat/token_field_1411
damian-molinski Jan 3, 2025
9ce9cd5
Merge branch 'main' into feat/token_field_1411
damian-molinski Jan 7, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'package:catalyst_voices/widgets/text_field/token_field.dart';
import 'package:catalyst_voices/widgets/text_field/voices_int_field.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:flutter/material.dart';

class DocumentTokenValueWidget extends StatefulWidget {
final DocumentNodeId id;
final String label;
final int? value;
final Currency currency;
final Range<int>? range;
final bool isEditMode;
final bool isRequired;
final ValueChanged<DocumentChange> onChanged;

const DocumentTokenValueWidget({
super.key,
required this.id,
required this.label,
this.value,
required this.currency,
this.range,
this.isEditMode = false,
this.isRequired = true,
required this.onChanged,
});

@override
State<DocumentTokenValueWidget> createState() {
return _DocumentTokenValueWidgetState();
}
}

class _DocumentTokenValueWidgetState extends State<DocumentTokenValueWidget> {
late final VoicesIntFieldController _controller;
late final FocusNode _focusNode;

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

_controller = VoicesIntFieldController(widget.value);
_controller.addListener(_handleControllerChange);
_focusNode = FocusNode(canRequestFocus: widget.isEditMode);
}

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

if (widget.value != oldWidget.value) {
_controller.value = widget.value;
}

if (widget.isEditMode != oldWidget.isEditMode) {
_handleEditModeChanged();
}
}

@override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
var label = widget.label;
if (widget.isRequired) {
label = '*$label';
}

return TokenField(
controller: _controller,
focusNode: _focusNode,
onFieldSubmitted: _notifyChangeListener,
labelText: label,
range: widget.range,
currency: widget.currency,
showHelper: widget.isEditMode,
readOnly: !widget.isEditMode,
ignorePointers: !widget.isEditMode,
);
}

void _handleControllerChange() {
final value = _controller.value;
_notifyChangeListener(value);
}

void _handleEditModeChanged() {
_focusNode.canRequestFocus = widget.isEditMode;

if (widget.isEditMode) {
_focusNode.requestFocus();
} else {
_focusNode.unfocus();
}
}

void _notifyChangeListener(int? value) {
final change = DocumentChange(nodeId: widget.id, value: value);
widget.onChanged(change);
}
}
118 changes: 118 additions & 0 deletions catalyst_voices/apps/voices/lib/widgets/text_field/token_field.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:flutter/material.dart';

class TokenField extends StatelessWidget {
final VoicesIntFieldController? controller;
final ValueChanged<int?>? onFieldSubmitted;
final ValueChanged<VoicesTextFieldStatus>? onStatusChanged;
final String? labelText;
final String? errorText;
final FocusNode? focusNode;
final Range<int>? range;
final Currency currency;
final bool showHelper;
final bool readOnly;
final bool? ignorePointers;

const TokenField({
super.key,
this.controller,
required this.onFieldSubmitted,
this.onStatusChanged,
this.labelText,
this.errorText,
this.focusNode,
this.range,
this.currency = const Currency.ada(),
this.showHelper = true,
this.readOnly = false,
this.ignorePointers,
}) : assert(
currency == const Currency.ada(),
'Only supports ADA at the moment',
);

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

return VoicesIntField(
controller: controller,
focusNode: focusNode,
decoration: VoicesTextFieldDecoration(
labelText: labelText,
errorText: errorText,
prefixText: currency.symbol,
hintText: range != null ? '${range.min}' : null,
filled: true,
helper: range != null && showHelper
? _Helper(
symbol: currency.symbol,
range: range,
)
: null,
),
validator: (int? value, text) => _validate(context, value, text),
onStatusChanged: onStatusChanged,
onFieldSubmitted: onFieldSubmitted,
readOnly: readOnly,
ignorePointers: ignorePointers,
);
}

VoicesTextFieldValidationResult _validate(
BuildContext context,
int? value,
String text,
) {
// Value could not be parsed into int.
if (value == null && text.isNotEmpty) {
final message = context.l10n.errorValidationTokenNotParsed;
return VoicesTextFieldValidationResult.error(message);
}

if (value != null && !(range?.contains(value) ?? true)) {
// Do not append any text
return const VoicesTextFieldValidationResult.error();
}

return const VoicesTextFieldValidationResult.none();
}
}

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

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

@override
Widget build(BuildContext context) {
// 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),
),
],
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:catalyst_voices/widgets/text_field/voices_num_field.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

class VoicesIntFieldController extends VoicesNumFieldController<int> {
VoicesIntFieldController([super.value]);
}

class VoicesIntField extends VoicesNumField<int> {
VoicesIntField({
super.key,
VoicesIntFieldController? super.controller,
super.focusNode,
super.decoration,
super.onChanged,
super.validator,
super.onStatusChanged,
required super.onFieldSubmitted,
List<TextInputFormatter>? inputFormatters,
super.enabled,
super.readOnly,
super.ignorePointers,
}) : super(
codec: const IntCodec(),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
// Note. int.parse returns incorrect values for bigger Strings.
// If more is required use BigInt
if (kIsWeb) LengthLimitingTextInputFormatter(16),
...?inputFormatters,
],
);
}
Loading
Loading