mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'dev' into cw-1440-new-buy-sell-flow
# Conflicts: # lib/src/widgets/cake_image_widget.dart
This commit is contained in:
@@ -268,6 +268,7 @@ class _ScanPageState extends State<ScanPage> {
|
||||
),
|
||||
FloatingIconButton(
|
||||
iconPath: "assets/new-ui/paste.svg",
|
||||
semanticLabel: S.of(context).paste,
|
||||
onPressed: () async {
|
||||
final data = await Clipboard.getData("text/plain");
|
||||
if (data?.text != null) {
|
||||
|
||||
@@ -17,12 +17,17 @@ class TokenImageWidget extends StatefulWidget {
|
||||
required this.imageUrl,
|
||||
required this.size,
|
||||
this.errorWidget,
|
||||
this.semanticsLabel,
|
||||
});
|
||||
|
||||
final String imageUrl;
|
||||
final double size;
|
||||
final Widget? errorWidget;
|
||||
|
||||
/// Accessible name for the token artwork. Leave `null` (the default) when the
|
||||
/// token is already named by adjacent text, so the image stays decorative.
|
||||
final String? semanticsLabel;
|
||||
|
||||
@override
|
||||
State<TokenImageWidget> createState() => _TokenImageWidgetState();
|
||||
}
|
||||
@@ -132,6 +137,7 @@ class _TokenImageWidgetState extends State<TokenImageWidget> {
|
||||
fit: BoxFit.cover,
|
||||
filterQuality: FilterQuality.high,
|
||||
errorWidget: widget.errorWidget,
|
||||
semanticsLabel: widget.semanticsLabel,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -51,85 +51,104 @@ class ReceiveQrCode extends StatelessWidget {
|
||||
height: 45,
|
||||
)),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: TweenAnimationBuilder<double>(
|
||||
tween: Tween<double>(begin: 0, end: targetY),
|
||||
duration: animDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
builder: (context, value, child) {
|
||||
return Transform.translate(
|
||||
offset: Offset(0, value),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: Observer(
|
||||
builder: (_) => Column(
|
||||
children: [
|
||||
AnimatedScale(
|
||||
curve: Curves.easeOutCubic,
|
||||
alignment: Alignment.bottomCenter,
|
||||
scale: resolvedScale,
|
||||
duration: animDuration,
|
||||
child: AnimatedContainer(
|
||||
duration: animDuration,
|
||||
// The QR, and the payjoin badge below it, share one tap target, so they
|
||||
// are merged into a single button node named by the QR's own label
|
||||
// rather than each becoming a separate focus stop.
|
||||
MergeSemantics(
|
||||
child: Semantics(
|
||||
hint: largeQrMode ? S.of(context).qr_close_fullscreen : S.of(context).qr_fullscreen,
|
||||
button: true,
|
||||
enabled: true,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: TweenAnimationBuilder<double>(
|
||||
tween: Tween<double>(begin: 0, end: targetY),
|
||||
duration: animDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
builder: (context, value, child) {
|
||||
return Transform.translate(
|
||||
offset: Offset(0, value),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: Observer(
|
||||
builder: (_) => Column(
|
||||
children: [
|
||||
AnimatedScale(
|
||||
curve: Curves.easeOutCubic,
|
||||
width: resolvedSize,
|
||||
height: resolvedSize,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
scale: resolvedScale,
|
||||
duration: animDuration,
|
||||
child: AnimatedContainer(
|
||||
duration: animDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width: resolvedSize,
|
||||
height: resolvedSize,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
color: addressListViewModel.hasPayjoin && !largeQrMode
|
||||
? Theme.of(context).colorScheme.surfaceContainer
|
||||
: Colors.transparent),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Observer(
|
||||
builder: (_) => QrImage(
|
||||
data: addressListViewModel.uri.toString(),
|
||||
size: resolvedSize,
|
||||
embeddedImagePath:
|
||||
addressListViewModel.tokenCurrency != null
|
||||
? addressListViewModel.tokenCurrency ==
|
||||
CryptoCurrency.btcln
|
||||
? addressListViewModel.qrImage
|
||||
: addressListViewModel.tokenCurrency!.iconPath
|
||||
: addressListViewModel.qrImage,
|
||||
))),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
color: addressListViewModel.hasPayjoin && !largeQrMode
|
||||
? Theme.of(context).colorScheme.surfaceContainer
|
||||
: Colors.transparent),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Observer(
|
||||
builder: (_) => QrImage(
|
||||
data: addressListViewModel.uri.toString(),
|
||||
size: resolvedSize,
|
||||
semanticsLabel: S.of(context).qr_code_receive_address,
|
||||
embeddedImagePath:
|
||||
addressListViewModel.tokenCurrency != null
|
||||
? addressListViewModel.tokenCurrency ==
|
||||
CryptoCurrency.btcln
|
||||
? addressListViewModel.qrImage
|
||||
: addressListViewModel
|
||||
.tokenCurrency!.iconPath
|
||||
: addressListViewModel.qrImage,
|
||||
))),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (addressListViewModel.hasPayjoin)
|
||||
Opacity(
|
||||
opacity: largeQrMode ? 0 : 1,
|
||||
child: Container(
|
||||
width: resolvedSize,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.vertical(bottom: Radius.circular(16)),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
CakeImageWidget(imageUrl: "assets/new-ui/payjoin.svg"),
|
||||
Text(S.of(context).payjoin_enabled)
|
||||
],
|
||||
),
|
||||
))),
|
||||
AnimatedSize(
|
||||
duration: animDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
child: SizedBox(height: largeQrMode ? largeQrModeBottomPadding + 40 : 0))
|
||||
],
|
||||
if (addressListViewModel.hasPayjoin)
|
||||
// Hidden but still mounted in large-QR mode, so keep it
|
||||
// out of the semantics tree there.
|
||||
ExcludeSemantics(
|
||||
excluding: largeQrMode,
|
||||
child: Opacity(
|
||||
opacity: largeQrMode ? 0 : 1,
|
||||
child: Container(
|
||||
width: resolvedSize,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.vertical(bottom: Radius.circular(16)),
|
||||
color: Theme.of(context).colorScheme.surfaceContainer),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
CakeImageWidget(imageUrl: "assets/new-ui/payjoin.svg"),
|
||||
Text(S.of(context).payjoin_enabled)
|
||||
],
|
||||
),
|
||||
))),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: animDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
child: SizedBox(height: largeQrMode ? largeQrModeBottomPadding + 40 : 0))
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -3,28 +3,45 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class FloatingIconButton extends StatelessWidget {
|
||||
const FloatingIconButton({super.key, required this.iconPath, required this.onPressed});
|
||||
const FloatingIconButton({
|
||||
super.key,
|
||||
required this.iconPath,
|
||||
required this.onPressed,
|
||||
required this.semanticLabel,
|
||||
});
|
||||
|
||||
final String iconPath;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
/// Localized accessible name for this icon-only button. The icon itself stays
|
||||
/// decorative, so this is the only name a screen reader can announce.
|
||||
final String semanticLabel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
child: InkWell(
|
||||
return MergeSemantics(
|
||||
child: Semantics(
|
||||
label: semanticLabel,
|
||||
button: true,
|
||||
enabled: true,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
onTap: onPressed,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: iconPath,
|
||||
width: 22,
|
||||
height: 22,
|
||||
colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
),
|
||||
)),
|
||||
child: InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
onTap: onPressed,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: iconPath,
|
||||
width: 22,
|
||||
height: 22,
|
||||
colorFilter:
|
||||
ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,16 +137,19 @@ class _NewSendAddressInputState extends State<NewSendAddressInput> {
|
||||
SizedBox.shrink(),
|
||||
FloatingIconButton(
|
||||
iconPath: "assets/new-ui/paste.svg",
|
||||
semanticLabel: S.of(context).paste,
|
||||
onPressed: () async {
|
||||
_pasteAddress(context);
|
||||
}),
|
||||
FloatingIconButton(
|
||||
iconPath: "assets/new-ui/scan.svg",
|
||||
semanticLabel: S.of(context).scan_qr_code,
|
||||
onPressed: () {
|
||||
_presentQRScanner(context);
|
||||
}),
|
||||
FloatingIconButton(
|
||||
iconPath: "assets/new-ui/contacts_outlined.svg",
|
||||
semanticLabel: S.of(context).address_book,
|
||||
onPressed: () {
|
||||
_presetAddressBookPicker(context);
|
||||
}),
|
||||
|
||||
@@ -97,6 +97,7 @@ class _NewSendAmountInputState extends State<NewSendAmountInput> {
|
||||
),
|
||||
FloatingIconButton(
|
||||
iconPath: "assets/new-ui/paste.svg",
|
||||
semanticLabel: S.of(context).paste,
|
||||
onPressed: () async {
|
||||
final data = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
if (data != null && data.text != null) {
|
||||
|
||||
@@ -46,6 +46,7 @@ class NewSendMemoInput extends StatelessWidget {
|
||||
SizedBox(width: 12),
|
||||
FloatingIconButton(
|
||||
iconPath: "assets/new-ui/paste.svg",
|
||||
semanticLabel: S.of(context).paste,
|
||||
onPressed: () async {
|
||||
final data = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
if (data != null && data.text != null) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -13,6 +14,7 @@ class QrImage extends StatelessWidget {
|
||||
this.errorCorrectionLevel = qr.QrErrorCorrectLevel.H,
|
||||
this.embeddedImagePath,
|
||||
this.badgeImageOnEmbeddedImagePath,
|
||||
this.semanticsLabel,
|
||||
});
|
||||
|
||||
final double? size;
|
||||
@@ -24,6 +26,11 @@ class QrImage extends StatelessWidget {
|
||||
final String? embeddedImagePath;
|
||||
final String? badgeImageOnEmbeddedImagePath;
|
||||
|
||||
/// Accessible name announced for the code itself. Defaults to a generic
|
||||
/// localized "QR code" so the code is always discoverable; pass a more
|
||||
/// specific label when the host screen knows what the code encodes.
|
||||
final String? semanticsLabel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final imagePath = embeddedImagePath ?? 'assets/images/qr-cake.png';
|
||||
@@ -68,8 +75,13 @@ class QrImage extends StatelessWidget {
|
||||
foregroundColor: foregroundColor,
|
||||
backgroundColor: backgroundColor,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
// The package default is a hardcoded English "qr code"; use the
|
||||
// localized string so the code is announced in the user's language.
|
||||
semanticsLabel: semanticsLabel ?? S.of(context).scan_qr_code,
|
||||
),
|
||||
centerImageToUse,
|
||||
// The embedded logo and its badge are purely decorative — the node
|
||||
// above already announces the code — so keep them out of the tree.
|
||||
ExcludeSemantics(child: centerImageToUse),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class CakeImageWidget extends StatelessWidget {
|
||||
this.alignment,
|
||||
this.allowDrawingOutsideViewBox,
|
||||
this.filterQuality,
|
||||
this.semanticsLabel,
|
||||
});
|
||||
|
||||
final String? imageUrl;
|
||||
@@ -33,6 +34,16 @@ class CakeImageWidget extends StatelessWidget {
|
||||
final double borderRadius;
|
||||
final FilterQuality? filterQuality;
|
||||
|
||||
/// Accessible name for this image.
|
||||
///
|
||||
/// Leave `null` (the default) for decorative imagery — the image then
|
||||
/// contributes nothing at all to the semantics tree, so screen readers do not
|
||||
/// stop on an unnamed node. Pass a localized string only when the image is the
|
||||
/// sole carrier of information for the user.
|
||||
final String? semanticsLabel;
|
||||
|
||||
bool get _isDecorative => semanticsLabel == null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (imageUrl == null || imageUrl!.isEmpty) {
|
||||
@@ -53,6 +64,8 @@ class CakeImageWidget extends StatelessWidget {
|
||||
alignment: alignment ?? Alignment.center,
|
||||
allowDrawingOutsideViewBox: allowDrawingOutsideViewBox ?? false,
|
||||
colorFilter: effectiveColorFilter,
|
||||
semanticsLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
fit: fit ?? BoxFit.contain, errorBuilder: (context, e, trace) {
|
||||
return SvgPicture.asset(
|
||||
imageUrl!,
|
||||
@@ -62,6 +75,8 @@ class CakeImageWidget extends StatelessWidget {
|
||||
width: width,
|
||||
errorBuilder: (_, __, ___) => SizedBox(height: height, width: width),
|
||||
colorFilter: effectiveColorFilter,
|
||||
semanticsLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
);
|
||||
});
|
||||
@@ -73,6 +88,8 @@ class CakeImageWidget extends StatelessWidget {
|
||||
fit: fit,
|
||||
color: color,
|
||||
filterQuality: filterQuality ?? FilterQuality.medium,
|
||||
semanticLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
errorBuilder: (_, __, ___) => _buildErrorWidget(context),
|
||||
);
|
||||
}
|
||||
@@ -86,13 +103,9 @@ class CakeImageWidget extends StatelessWidget {
|
||||
alignment: alignment ?? Alignment.center,
|
||||
allowDrawingOutsideViewBox: allowDrawingOutsideViewBox ?? false,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
placeholderBuilder: (_) {
|
||||
return loadingWidget ??
|
||||
SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: Center(child: CupertinoActivityIndicator()));
|
||||
},
|
||||
semanticsLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
placeholderBuilder: (_) => _buildLoadingWidget(),
|
||||
errorBuilder: (_, __, ___) => _buildErrorWidget(context),
|
||||
)
|
||||
: Image.network(
|
||||
@@ -102,13 +115,11 @@ class CakeImageWidget extends StatelessWidget {
|
||||
fit: fit ?? BoxFit.cover,
|
||||
color: color,
|
||||
filterQuality: filterQuality ?? FilterQuality.medium,
|
||||
semanticLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
loadingBuilder: (_, Widget child, ImageChunkEvent? progress) {
|
||||
if (progress == null) return child;
|
||||
return loadingWidget ??
|
||||
SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: Center(child: CupertinoActivityIndicator()));
|
||||
return _buildLoadingWidget();
|
||||
},
|
||||
errorBuilder: (_, __, ___) => _buildErrorWidget(context),
|
||||
);
|
||||
@@ -117,8 +128,16 @@ class CakeImageWidget extends StatelessWidget {
|
||||
return imageWidget;
|
||||
}
|
||||
|
||||
/// A caller-supplied [loadingWidget] owns its own semantics; the built-in
|
||||
/// spinner is purely visual and must not become an unnamed focus stop.
|
||||
Widget _buildLoadingWidget() =>
|
||||
loadingWidget ?? const ExcludeSemantics(child:SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: Center(child: CupertinoActivityIndicator())));
|
||||
|
||||
Widget _buildErrorWidget(BuildContext context) {
|
||||
return Container(
|
||||
final Widget placeholder = Container(
|
||||
height: height,
|
||||
width: width,
|
||||
decoration: BoxDecoration(
|
||||
@@ -134,5 +153,20 @@ class CakeImageWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Several call sites render meaningful content (e.g. the asset's initials) as
|
||||
// their [errorWidget], so leave its semantics to the caller.
|
||||
if (errorWidget != null) {
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
return _isDecorative
|
||||
? ExcludeSemantics(child: placeholder)
|
||||
: Semantics(
|
||||
container: true,
|
||||
image: true,
|
||||
label: semanticsLabel,
|
||||
child: ExcludeSemantics(child: placeholder),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ class RoundedCheckbox extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return value
|
||||
// Unchecked renders nothing at all, so without an explicit `checked` state
|
||||
// the selection is invisible to screen readers.
|
||||
final Widget indicator = value
|
||||
? Container(
|
||||
height: 20.0,
|
||||
width: 20.0,
|
||||
@@ -23,5 +25,9 @@ class RoundedCheckbox extends StatelessWidget {
|
||||
size: 14.0,
|
||||
))
|
||||
: Offstage();
|
||||
|
||||
// Deliberately not a semantics container: the state merges into the
|
||||
// enclosing row/option node instead of adding a second focus stop.
|
||||
return Semantics(checked: value, child: indicator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,13 +80,26 @@ class AddressFormatter {
|
||||
spans.add(TextSpan(text: '${chunks[i]} ', style: style));
|
||||
}
|
||||
|
||||
return RichText(
|
||||
text: TextSpan(children: spans),
|
||||
textAlign: textAlign ?? TextAlign.start,
|
||||
overflow: TextOverflow.visible,
|
||||
return _withAddressSemantics(
|
||||
address: address,
|
||||
child: RichText(
|
||||
text: TextSpan(children: spans),
|
||||
textAlign: textAlign ?? TextAlign.start,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// The visual segmentation turns an address into space-separated pseudo-words
|
||||
/// (and, when truncated, into a literal "..."), which cannot be used to verify
|
||||
/// an address by ear. Announce the full address instead, exactly the way
|
||||
/// `Text.semanticsLabel` does.
|
||||
static Widget _withAddressSemantics({required String address, required Widget child}) =>
|
||||
Semantics(
|
||||
label: address,
|
||||
child: ExcludeSemantics(child: child),
|
||||
);
|
||||
|
||||
static Widget _buildTruncatedAddress({
|
||||
required String address,
|
||||
required bool isMWEB,
|
||||
@@ -112,10 +125,13 @@ class AddressFormatter {
|
||||
TextSpan(text: lastChunk, style: evenTextStyle),
|
||||
];
|
||||
|
||||
return RichText(
|
||||
text: TextSpan(children: spans),
|
||||
textAlign: textAlign ?? TextAlign.start,
|
||||
overflow: TextOverflow.visible,
|
||||
return _withAddressSemantics(
|
||||
address: address,
|
||||
child: RichText(
|
||||
text: TextSpan(children: spans),
|
||||
textAlign: textAlign ?? TextAlign.start,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final int digitCount = chunkSize;
|
||||
@@ -142,10 +158,13 @@ class AddressFormatter {
|
||||
TextSpan(text: lastPart, style: evenTextStyle),
|
||||
];
|
||||
|
||||
return RichText(
|
||||
text: TextSpan(children: spans),
|
||||
textAlign: textAlign ?? TextAlign.start,
|
||||
overflow: TextOverflow.visible,
|
||||
return _withAddressSemantics(
|
||||
address: address,
|
||||
child: RichText(
|
||||
text: TextSpan(children: spans),
|
||||
textAlign: textAlign ?? TextAlign.start,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
"available_balance": "Available Balance",
|
||||
"available_balance_description": "The “Available Balance” or “Confirmed Balance” are funds that can be spent immediately. If funds appear in the lower balance but not the top balance, then you must wait a few minutes for the incoming funds to get more network confirmations. After they get more confirmations, they will be spendable.",
|
||||
"available_balance_short": "Avl: ${amount}",
|
||||
"available_providers": "Available Providers",
|
||||
"avg_savings": "Avg. Savings",
|
||||
"avl": "Avl",
|
||||
"awaitDAppProcessing": "Kindly wait for the dApp to finish processing.",
|
||||
@@ -866,9 +867,11 @@
|
||||
"purchase_gift_card": "Purchase Gift Card",
|
||||
"purchase_with": "Purchase with",
|
||||
"purple_dark_theme": "Purple Dark Theme",
|
||||
"qr_close_fullscreen": "Tap to close full screen QR code",
|
||||
"qr_code_format": "QR Code Format",
|
||||
"qr_code_format_body": "BCUR (Blockchain Commons Uniform Resources) is the industry standard, and most widely compatible with Bitcoin hardware wallets, and should be selected by most users. BBQR (Better Bitcoin QR) is a unique format used only by COLDCARD hardware wallets, starting with their model Q.",
|
||||
"qr_code_format_note": "Different QR code formats are used by different hardware wallets in the Bitcoin space",
|
||||
"qr_code_receive_address": "QR code for your receive address",
|
||||
"qr_fullscreen": "Tap to open full screen QR code",
|
||||
"qr_instruction": "Use this address to receive any token or collectible on",
|
||||
"qr_parts_scanned": "Scanned ${scanned} of ${total} QR code parts",
|
||||
|
||||
@@ -7,7 +7,7 @@ cd "$(dirname "$0")"
|
||||
# - .github/workflows/pr_test_build_linux.yml
|
||||
# https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz
|
||||
|
||||
HASH=79e5361f43b8aadd340d2900009cf09590b2a0bb
|
||||
HASH=a8601eaef00ac4a9465a0979b7d7a16df90bc364
|
||||
|
||||
if [[ ! -d "torch_dart/.git" ]];
|
||||
then
|
||||
|
||||
Reference in New Issue
Block a user