mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-11-07 08:04:04 +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.
259 lines
12 KiB
Dart
259 lines
12 KiB
Dart
import 'package:hash_wallet/entities/bitcoin_amount_display_mode.dart';
|
|
import 'package:hash_wallet/entities/language_service.dart';
|
|
import 'package:hash_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
|
|
import 'package:hash_wallet/entities/new_ui_entities/list_item/list_item_selector.dart';
|
|
import 'package:hash_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart';
|
|
import 'package:hash_wallet/entities/sync_status_display_mode.dart';
|
|
import 'package:hash_wallet/generated/i18n.dart';
|
|
import 'package:hash_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart';
|
|
import 'package:hash_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
|
import 'package:hash_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
|
|
import 'package:hash_wallet/src/screens/settings/widgets/settings_theme_choice.dart';
|
|
import 'package:hash_wallet/src/widgets/alert_with_two_actions.dart';
|
|
import 'package:hash_wallet/src/widgets/new_list_row/new_list_section.dart';
|
|
import 'package:hash_wallet/new-ui/widgets/modal_page_wrapper.dart';
|
|
import 'package:hash_wallet/src/widgets/picker.dart';
|
|
import 'package:hash_wallet/src/widgets/standard_list.dart';
|
|
import 'package:hash_wallet/utils/device_info.dart';
|
|
import 'package:hash_wallet/utils/feature_flag.dart';
|
|
import 'package:hash_wallet/utils/responsive_layout_util.dart';
|
|
import 'package:hash_wallet/utils/show_pop_up.dart';
|
|
import 'package:hash_wallet/view_model/settings/choices_list_item.dart';
|
|
import 'package:hash_wallet/view_model/settings/display_settings_view_model.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
|
|
class DisplaySettingsPage extends StatelessWidget {
|
|
DisplaySettingsPage(this._displaySettingsViewModel);
|
|
|
|
final DisplaySettingsViewModel _displaySettingsViewModel;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ModalPageWrapper(
|
|
topBar: ModalTopBar(
|
|
title: S.of(context).display,
|
|
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
|
leadingSemanticLabel: S.of(context).seed_alert_back,
|
|
onLeadingPressed: () => Navigator.of(context).pop(),
|
|
),
|
|
content: Column(
|
|
spacing: 16,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (responsiveLayoutUtil.shouldRenderMobileUI && DeviceInfo.instance.isMobile) ...[
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 14, top: 14),
|
|
child: Text(
|
|
S.of(context).appearance,
|
|
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
|
height: 0.2,
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
decoration: ShapeDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
|
shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(18))),
|
|
child: Column(
|
|
children: [
|
|
SettingsChoicesCell(
|
|
ChoicesListItem<ThemeMode>(
|
|
title: "",
|
|
items: ThemeMode.values,
|
|
selectedItem: _displaySettingsViewModel.themeMode,
|
|
onItemSelected: (ThemeMode themeMode) =>
|
|
_displaySettingsViewModel.setThemeMode(themeMode),
|
|
displayItem: (ThemeMode themeMode) {
|
|
return themeMode.name[0].toUpperCase() +
|
|
themeMode.name.substring(1).toLowerCase();
|
|
},
|
|
),
|
|
useGenericColor: false,
|
|
padding: EdgeInsets.all(14),
|
|
),
|
|
Container(
|
|
decoration: ShapeDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
|
shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(18))),
|
|
child: Column(
|
|
children: [
|
|
Semantics(
|
|
label: S.of(context).color_theme,
|
|
child: SettingsThemeChoicesCell(_displaySettingsViewModel),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
Observer(
|
|
builder: (_) => NewListSections(
|
|
sections: {
|
|
"": [
|
|
ListItemToggle(
|
|
keyValue: "apps",
|
|
label: S.of(context).apps,
|
|
value: _displaySettingsViewModel.shouldShowMarketPlaceInDashboard,
|
|
onChanged: (val) {
|
|
_displaySettingsViewModel.setShouldShowMarketPlaceInDashbaord(val);
|
|
}),
|
|
ListItemToggle(
|
|
keyValue: "display_settings_show_address_book_popup",
|
|
label: S.of(context).show_address_book_popup,
|
|
value: _displaySettingsViewModel.showAddressBookPopup,
|
|
onChanged: (val) {
|
|
_displaySettingsViewModel.setShowAddressBookPopup(val);
|
|
}),
|
|
ListItemToggle(
|
|
keyValue: "display_settings_disable_buy_button",
|
|
label: S.of(context).disable_buy,
|
|
value: _displaySettingsViewModel.disableTradeOption,
|
|
onChanged: (val) {
|
|
_displaySettingsViewModel.setDisableTradeOption(val);
|
|
}),
|
|
ListItemSelector(
|
|
keyValue: "display_settings_sync_status_display",
|
|
label: S.of(context).sync_status_display_mode,
|
|
options: [_displaySettingsViewModel.syncStatusDisplayMode.title],
|
|
onTap: () async {
|
|
final items = SyncStatusDisplayMode.values.toList();
|
|
final selectedAtIndex =
|
|
items.indexOf(_displaySettingsViewModel.syncStatusDisplayMode);
|
|
|
|
await showPopUp<void>(
|
|
context: context,
|
|
builder: (_) => Picker(
|
|
items: items,
|
|
selectedAtIndex: selectedAtIndex,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
onItemSelected: (SyncStatusDisplayMode mode) {
|
|
_displaySettingsViewModel.setSyncStatusDisplayMode(mode);
|
|
},
|
|
displayItem: (SyncStatusDisplayMode mode) => mode.title,
|
|
isSeparated: false,
|
|
),
|
|
);
|
|
}),
|
|
if (_displaySettingsViewModel.showDisplayAmountsInSatoshiSetting)
|
|
ListItemRegularRow(
|
|
keyValue: "display_settings_bitcoin_amount_display",
|
|
label: S.of(context).bitcoin_amount_display,
|
|
trailingText: _displaySettingsViewModel.displayAmountsInSatoshi.title,
|
|
onTap: () async {
|
|
final items = BitcoinAmountDisplayMode.all;
|
|
|
|
final selectedAtIndex =
|
|
items.indexOf(_displaySettingsViewModel.displayAmountsInSatoshi);
|
|
|
|
await showPopUp<void>(
|
|
context: context,
|
|
builder: (_) => Picker(
|
|
items: items,
|
|
selectedAtIndex: selectedAtIndex,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
onItemSelected: _displaySettingsViewModel.setDisplayAmountsInSatoshi,
|
|
displayItem: (BitcoinAmountDisplayMode mode) => mode.title,
|
|
isSeparated: false,
|
|
),
|
|
);
|
|
}),
|
|
if (!_displaySettingsViewModel.disabledFiatApiMode)
|
|
ListItemSelector(
|
|
keyValue: "display_settings_fiat_currency",
|
|
label: S.of(context).settings_currency,
|
|
options: [_displaySettingsViewModel.fiatCurrency.title],
|
|
onTap: () => FiatCurrencyPickerSheet.show(
|
|
context: context,
|
|
selected: _displaySettingsViewModel.fiatCurrency,
|
|
onSelected: _displaySettingsViewModel.setFiatCurrency,
|
|
)),
|
|
ListItemSelector(
|
|
keyValue: "display_settings_language",
|
|
label: S.of(context).settings_change_language,
|
|
options: [LanguageService.list[_displaySettingsViewModel.languageCode] ?? ''],
|
|
onTap: () async {
|
|
final items = LanguageService.list.keys.toList();
|
|
|
|
final selectedAtIndex =
|
|
items.indexOf(_displaySettingsViewModel.languageCode);
|
|
|
|
await showPopUp<void>(
|
|
context: context,
|
|
builder: (_) => Picker(
|
|
displayItem: (dynamic code) {
|
|
return LanguageService.list[code] ?? '';
|
|
},
|
|
items: items,
|
|
selectedAtIndex: selectedAtIndex,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
onItemSelected: _displaySettingsViewModel.onLanguageSelected,
|
|
images: LanguageService.list.keys
|
|
.map((e) => Image.asset(
|
|
"assets/images/flags/${LanguageService.localeCountryCode[e]}.png"))
|
|
.toList(),
|
|
hintText: S.of(context).search_language,
|
|
matchingCriteria: (String code, String searchText) {
|
|
return LanguageService.list[code]
|
|
?.toLowerCase()
|
|
.contains(searchText) ??
|
|
false;
|
|
},
|
|
isSeparated: true,
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
},
|
|
),
|
|
),
|
|
if (FeatureFlag.customBackgroundEnabled)
|
|
StandardListRow(
|
|
title: "Custom background",
|
|
isSelected: false,
|
|
onTap: (_) => _pickImage(context),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// Function to pick an image from the gallery
|
|
Future<void> _pickImage(BuildContext context) async {
|
|
if (_displaySettingsViewModel.backgroundImage.isNotEmpty) {
|
|
final bool? shouldReplace = await showPopUp<bool>(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertWithTwoActions(
|
|
alertTitle: S.of(context).replace,
|
|
alertContent: S.of(context).customBackgroundDescription,
|
|
rightButtonText: S.of(context).replace,
|
|
leftButtonText: S.of(context).remove,
|
|
actionRightButton: () => Navigator.of(context).pop(true),
|
|
actionLeftButton: () => Navigator.of(context).pop(false));
|
|
});
|
|
|
|
if (shouldReplace == false) {
|
|
// remove the current background by setting it as an empty string
|
|
_displaySettingsViewModel.setBackgroundImage("");
|
|
return;
|
|
} else if (shouldReplace == null) {
|
|
// user didn't choose anything, then just return
|
|
return;
|
|
}
|
|
}
|
|
|
|
final ImagePicker picker = ImagePicker();
|
|
// Pick an image from the gallery
|
|
final XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery);
|
|
|
|
if (pickedFile != null) {
|
|
_displaySettingsViewModel.setBackgroundImage(pickedFile.path);
|
|
}
|
|
}
|
|
}
|