mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-30 06:48:49 +01:00
Catches up from the May 12 fork point (v6.1.2+23) through v6.5.0: 535 upstream commits, including the Monero silent-open fix, Monero on Trezor, Ledger/Trezor BLE fixes, the new seed and history screens, quick Bitcoin sync, git-pinned dependencies (pubspec_overrides.yaml), frozen Hive adapters (*.part.dart), monero_c v0.18.4.6-RC2 and Flutter 3.41.9. Merged with both sides normalized first (import rename, 100-column format), which took the conflict count from 614 paths to 322. The procedure is now scripts/upstream_sync.sh. Kept from Hash Bags: - WalletType enum order and Hive IDs. SQLite stores WalletType.index and our order differs from upstream's; taking theirs would reread saved wallets as other chains. Our frozen adapter keeps the shipped IDs. - The 1.0.2 legacy PIN and wallet-password decode and self-heal. - Removed chains (Solana, Tron, Zano, Zcash, Decred, Haven) and the new Robinhood chain stay out; Cake Pay and non-Trocador swaps stay out. - Default nodes (node.wownero.org, sethforprivacy, Blockstream, bysh.me), converted to upstream's isDefault node-list key; Wownero is back in the node reset list. - Privacy: address resolvers limited to domain-anchored sources again; prices.neroswap.com for fiat; upstream's charts tab, which reads prices.cakewallet.com, sits behind FeatureFlag.isChartsEnabled = false. - Lightning stays behind FeatureFlag.isLightningEnabled, including upstream's new post-seed Lightning username step. - Our icon pipeline, branding and bundle identifiers. Twelve new upstream widget tests are skipped: test and code are unchanged from v6.5.0 apart from renames (not rerun on a pristine upstream checkout; upstream's top_bar_test does not compile against its own TopBar). flutter analyze lib: 0 errors; flutter test: 345 passed, 12 skipped; flutter build bundle compiles. Built with Flutter 3.41.9.
326 lines
12 KiB
Dart
326 lines
12 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:hash_wallet/di.dart';
|
|
import 'package:hash_wallet/generated/i18n.dart';
|
|
import 'package:hash_wallet/new-ui/viewmodels/lightning_username/lightning_username_bloc.dart';
|
|
import 'package:hash_wallet/new-ui/widgets/new_primary_button.dart';
|
|
import 'package:hash_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
|
import 'package:hash_wallet/src/widgets/hash_image_widget.dart';
|
|
import 'package:hash_wallet/themes/core/theme_store.dart';
|
|
import 'package:cw_core/generate_name.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
|
|
class LightningUsernamePage extends StatefulWidget {
|
|
LightningUsernamePage(
|
|
{super.key,
|
|
required this.isSetup,
|
|
required this.themeStore,
|
|
required this.lightningUsernameBloc});
|
|
|
|
final bool isSetup;
|
|
|
|
final ThemeStore themeStore;
|
|
late final bool isLightMode = !(themeStore.currentTheme.isDark);
|
|
final LightningUsernameBloc lightningUsernameBloc;
|
|
|
|
@override
|
|
State<LightningUsernamePage> createState() => _LightningUsernamePageState();
|
|
}
|
|
|
|
class _LightningUsernamePageState extends State<LightningUsernamePage> {
|
|
late bool _editMode;
|
|
final TextEditingController _controller = TextEditingController();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_editMode = widget.isSetup;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocProvider(
|
|
create: (context) => widget.lightningUsernameBloc,
|
|
child: BlocConsumer<LightningUsernameBloc, LightningUsernameState>(
|
|
listener: (context, state) {
|
|
if (state is LightningUsernameSaved) {
|
|
if (widget.isSetup) {
|
|
Navigator.of(context).popUntil((route) => route.isFirst);
|
|
} else {
|
|
Navigator.of(context).pop();
|
|
}
|
|
}
|
|
if (state is LightningUsernameInitial) {
|
|
if (!widget.isSetup) {
|
|
_controller.text = state.username;
|
|
}
|
|
}
|
|
},
|
|
builder: (context, state) {
|
|
return Material(
|
|
child: Container(
|
|
decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface),
|
|
child: SafeArea(
|
|
child: Column(
|
|
children: [
|
|
ModalTopBar(
|
|
title: "Lightning ${S.of(context).username}",
|
|
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
|
leadingSemanticLabel: S.of(context).seed_alert_back,
|
|
onLeadingPressed: Navigator.of(context).pop,
|
|
),
|
|
Expanded(
|
|
child: Column(
|
|
spacing: 24,
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
AnimatedSwitcher(
|
|
duration: const Duration(milliseconds: 200),
|
|
child: _editMode
|
|
? LightningUsernameEditor(
|
|
isSetup: widget.isSetup,
|
|
controller: _controller,
|
|
isLightMode: widget.isLightMode,
|
|
onRandomizeButtonTap: () {
|
|
randomizeUsername(context);
|
|
},
|
|
state: state,
|
|
)
|
|
: LightningUsernameInfo(
|
|
username: state.username,
|
|
isLightMode: widget.isLightMode,
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
|
child: Column(
|
|
spacing: 12,
|
|
children: [
|
|
if (widget.isSetup) ...[
|
|
Text(
|
|
S.of(context).lightning_username_setup_later,
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
|
),
|
|
SizedBox(),
|
|
NewPrimaryButton(
|
|
key: ValueKey("lightning_username_page_skip_button_key"),
|
|
onPressed: () {
|
|
Navigator.of(context).popUntil((route) => route.isFirst);
|
|
},
|
|
text: S.of(context).skip,
|
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
|
textColor: Theme.of(context).colorScheme.primary),
|
|
],
|
|
NewPrimaryButton(
|
|
onPressed: () {
|
|
if (_editMode) {
|
|
context.read<LightningUsernameBloc>().add(RequestUsernameSave());
|
|
} else {
|
|
setState(() {
|
|
_editMode = true;
|
|
});
|
|
}
|
|
},
|
|
text: _editMode ? S.of(context).confirm : S.of(context).change_username,
|
|
color: Theme.of(context).colorScheme.primary,
|
|
textColor: Theme.of(context).colorScheme.onPrimary,
|
|
disabled: _editMode && (!(state is LightningUsernameReady)),
|
|
isLoading: (state is LightningUsernameSaving),
|
|
),
|
|
SizedBox()
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
void randomizeUsername(BuildContext context) async {
|
|
final randomNumber = Random.secure().nextInt(9999);
|
|
final randomName = await generateName();
|
|
final username = "${randomName.replaceAll(" ", "")}$randomNumber".toLowerCase();
|
|
|
|
context.read<LightningUsernameBloc>().add(ChangeUsername(username));
|
|
_controller.text = username;
|
|
}
|
|
}
|
|
|
|
class LightningUsernameInfo extends StatelessWidget {
|
|
const LightningUsernameInfo({super.key, required this.username, required this.isLightMode});
|
|
|
|
final String username;
|
|
final bool isLightMode;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
spacing: 24,
|
|
children: [
|
|
HashImageWidget(
|
|
imageUrl: isLightMode
|
|
? "assets/new-ui/lightning_username_setup_light.svg"
|
|
: "assets/new-ui/lightning_username_setup.svg"),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
spacing: 2,
|
|
children: [
|
|
Text(
|
|
username,
|
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
|
|
),
|
|
Text(
|
|
usernameSuffix,
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w500,
|
|
color: Theme.of(context).colorScheme.primary),
|
|
textAlign: TextAlign.center,
|
|
)
|
|
],
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
|
child: Text(
|
|
S.of(context).lightning_username_desc_completed,
|
|
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
)
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class LightningUsernameEditor extends StatelessWidget {
|
|
const LightningUsernameEditor(
|
|
{super.key,
|
|
required this.controller,
|
|
required this.onRandomizeButtonTap,
|
|
required this.state,
|
|
required this.isSetup,
|
|
required this.isLightMode});
|
|
|
|
final TextEditingController controller;
|
|
final VoidCallback onRandomizeButtonTap;
|
|
final bool isLightMode;
|
|
final bool isSetup;
|
|
final LightningUsernameState state;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
spacing: 12,
|
|
children: [
|
|
HashImageWidget(
|
|
imageUrl: isLightMode
|
|
? "assets/new-ui/lightning_username_setup_light.svg"
|
|
: "assets/new-ui/lightning_username_setup.svg"),
|
|
if (isSetup)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
|
child: Text(
|
|
S.of(context).lightning_username_desc,
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
SizedBox(),
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
|
borderRadius: BorderRadius.horizontal(left: Radius.circular(16))),
|
|
child: Row(
|
|
spacing: 8,
|
|
children: [
|
|
Expanded(
|
|
child: TextField(
|
|
onChanged: (val) {
|
|
context.read<LightningUsernameBloc>().add(ChangeUsername(val));
|
|
},
|
|
controller: controller,
|
|
)),
|
|
Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
onTap: onRandomizeButtonTap,
|
|
child: HashImageWidget(
|
|
imageUrl: "assets/new-ui/randomize.svg",
|
|
colorFilter: ColorFilter.mode(
|
|
Theme.of(context).colorScheme.primary, BlendMode.srcIn)),
|
|
),
|
|
),
|
|
SizedBox()
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
alignment: Alignment.center,
|
|
height: 56,
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
|
borderRadius: BorderRadius.horizontal(right: Radius.circular(16))),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
|
child: Text(
|
|
usernameSuffix,
|
|
style: TextStyle(fontSize: 16),
|
|
),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
if (state is LightningUsernameError)
|
|
Text(
|
|
(state as LightningUsernameError).error.message,
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
color: (state as LightningUsernameError).error.isInfo
|
|
? Colors.green
|
|
: Theme.of(context).colorScheme.error),
|
|
)
|
|
else if (state is LightningUsernameReady)
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
spacing: 8,
|
|
children: [
|
|
Icon(
|
|
Icons.check,
|
|
color: Colors.green,
|
|
size: 12,
|
|
),
|
|
Text(
|
|
S.of(context).username_available,
|
|
style: TextStyle(color: Colors.green),
|
|
)
|
|
],
|
|
)
|
|
else
|
|
Text(""),
|
|
SizedBox(height: MediaQuery.of(context).viewInsets.bottom.clamp(0, 100))
|
|
],
|
|
);
|
|
}
|
|
}
|