Skip to content
Merged

2.2.0 #194

Show file tree
Hide file tree
Changes from 3 commits
Commits
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
7 changes: 3 additions & 4 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,9 @@
"coinHomeReceived": "Received",
"coinHomeSent": "Sent",
"receiveTitle": "Receive",
"receivePrimaryAddressWarn": "Warning: Unless you know what you're doing, please use subaddresses for better privacy.",
"receiveServerNoSubaddressesWarn": "Warning: This server does not support subaddresses. For better privacy, consider using a server that supports them.",
"receiveSubaddressUnavailableWarn": "Warning: You need to configure Monero to enable subaddresses, which better protect your privacy.",
"receivePrimaryAddressWarn": "Unless you know what you're doing, please use subaddresses for better privacy.",
"receiveServerNoSubaddressesWarn": "This server does not support subaddresses. For better privacy, consider using a server that supports them.",
"receiveSubaddressUnavailableWarn": "You need to configure Monero to enable subaddresses, which better protect your privacy.",
"receiveMaxSubaddressesReachedWarn": "You have reached the maximum number of subaddresses supported by this server. This is a used address.",
"receiveSubaddressTab": "Subaddress",
"receivePrimaryTab": "Primary address",
Expand Down Expand Up @@ -248,7 +248,6 @@
"settingsNotifyNewTxsDescription": "Show a notification when you receive a transaction. Increase the sync frequency setting to see them sooner.",
"settingsSyncFrequencyLabel": "Sync Frequency",
"syncFrequencySheetSubtitle": "How often Monero syncs while the app is closed.",
"syncFrequencyModeLabel": "Frequency",
"syncFrequencyContinuous": "Continuous",
"syncFrequencyContinuousDesc": "Continuously sync Monero in the background so it's always up to date when you open the app. Uses more battery and requires a persistent notification.",
"syncFrequencyPeriodic": "Periodic",
Expand Down
12 changes: 3 additions & 9 deletions lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1146,19 +1146,19 @@ abstract class AppLocalizations {
/// No description provided for @receivePrimaryAddressWarn.
///
/// In en, this message translates to:
/// **'Warning: Unless you know what you\'re doing, please use subaddresses for better privacy.'**
/// **'Unless you know what you\'re doing, please use subaddresses for better privacy.'**
String get receivePrimaryAddressWarn;

/// No description provided for @receiveServerNoSubaddressesWarn.
///
/// In en, this message translates to:
/// **'Warning: This server does not support subaddresses. For better privacy, consider using a server that supports them.'**
/// **'This server does not support subaddresses. For better privacy, consider using a server that supports them.'**
String get receiveServerNoSubaddressesWarn;

/// No description provided for @receiveSubaddressUnavailableWarn.
///
/// In en, this message translates to:
/// **'Warning: You need to configure Monero to enable subaddresses, which better protect your privacy.'**
/// **'You need to configure Monero to enable subaddresses, which better protect your privacy.'**
String get receiveSubaddressUnavailableWarn;

/// No description provided for @receiveMaxSubaddressesReachedWarn.
Expand Down Expand Up @@ -1329,12 +1329,6 @@ abstract class AppLocalizations {
/// **'How often Monero syncs while the app is closed.'**
String get syncFrequencySheetSubtitle;

/// No description provided for @syncFrequencyModeLabel.
///
/// In en, this message translates to:
/// **'Frequency'**
String get syncFrequencyModeLabel;

/// No description provided for @syncFrequencyContinuous.
///
/// In en, this message translates to:
Expand Down
10 changes: 3 additions & 7 deletions lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;

import 'app_localizations.dart';

// ignore_for_file: type=lint
Expand Down Expand Up @@ -584,15 +583,15 @@ class AppLocalizationsEn extends AppLocalizations {

@override
String get receivePrimaryAddressWarn =>
'Warning: Unless you know what you\'re doing, please use subaddresses for better privacy.';
'Unless you know what you\'re doing, please use subaddresses for better privacy.';

@override
String get receiveServerNoSubaddressesWarn =>
'Warning: This server does not support subaddresses. For better privacy, consider using a server that supports them.';
'This server does not support subaddresses. For better privacy, consider using a server that supports them.';

@override
String get receiveSubaddressUnavailableWarn =>
'Warning: You need to configure Monero to enable subaddresses, which better protect your privacy.';
'You need to configure Monero to enable subaddresses, which better protect your privacy.';

@override
String get receiveMaxSubaddressesReachedWarn =>
Expand Down Expand Up @@ -683,9 +682,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get syncFrequencySheetSubtitle => 'How often Monero syncs while the app is closed.';

@override
String get syncFrequencyModeLabel => 'Frequency';

@override
String get syncFrequencyContinuous => 'Continuous';

Expand Down
10 changes: 3 additions & 7 deletions lib/l10n/app_localizations_pt.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;

import 'app_localizations.dart';

// ignore_for_file: type=lint
Expand Down Expand Up @@ -587,15 +586,15 @@ class AppLocalizationsPt extends AppLocalizations {

@override
String get receivePrimaryAddressWarn =>
'Aviso: A menos que saiba o que está fazendo, por favor use subendereços para melhor privacidade.';
'A menos que saiba o que está fazendo, por favor use subendereços para melhor privacidade.';

@override
String get receiveServerNoSubaddressesWarn =>
'Aviso: Este servidor não suporta subendereços. Para melhor privacidade, considere usar um servidor que os suporte.';
'Este servidor não suporta subendereços. Para melhor privacidade, considere usar um servidor que os suporte.';

@override
String get receiveSubaddressUnavailableWarn =>
'Aviso: Você precisa configurar o Monero para ativar subendereços, que protegem melhor sua privacidade.';
'Você precisa configurar o Monero para ativar subendereços, que protegem melhor sua privacidade.';

@override
String get receiveMaxSubaddressesReachedWarn =>
Expand Down Expand Up @@ -687,9 +686,6 @@ class AppLocalizationsPt extends AppLocalizations {
String get syncFrequencySheetSubtitle =>
'Com que frequência o Monero sincroniza enquanto o app está fechado.';

@override
String get syncFrequencyModeLabel => 'Frequência';

@override
String get syncFrequencyContinuous => 'Contínua';

Expand Down
7 changes: 3 additions & 4 deletions lib/l10n/app_pt.arb
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@
"coinHomeReceived": "Recebido",
"coinHomeSent": "Enviado",
"receiveTitle": "Receber",
"receivePrimaryAddressWarn": "Aviso: A menos que saiba o que está fazendo, por favor use subendereços para melhor privacidade.",
"receiveServerNoSubaddressesWarn": "Aviso: Este servidor não suporta subendereços. Para melhor privacidade, considere usar um servidor que os suporte.",
"receiveSubaddressUnavailableWarn": "Aviso: Você precisa configurar o Monero para ativar subendereços, que protegem melhor sua privacidade.",
"receivePrimaryAddressWarn": "A menos que saiba o que está fazendo, por favor use subendereços para melhor privacidade.",
"receiveServerNoSubaddressesWarn": "Este servidor não suporta subendereços. Para melhor privacidade, considere usar um servidor que os suporte.",
"receiveSubaddressUnavailableWarn": "Você precisa configurar o Monero para ativar subendereços, que protegem melhor sua privacidade.",
"receiveMaxSubaddressesReachedWarn": "Você atingiu o número máximo de subendereços suportados por este servidor. Este é um endereço já usado.",
"receiveSubaddressTab": "Subendereço",
"receivePrimaryTab": "Endereço primário",
Expand Down Expand Up @@ -242,7 +242,6 @@
"settingsNotifyNewTxsDescription": "Mostrar uma notificação quando você receber uma transação. Aumente a frequência de sincronização para vê-las mais cedo.",
"settingsSyncFrequencyLabel": "Frequência de sincronização",
"syncFrequencySheetSubtitle": "Com que frequência o Monero sincroniza enquanto o app está fechado.",
"syncFrequencyModeLabel": "Frequência",
"syncFrequencyContinuous": "Contínua",
"syncFrequencyContinuousDesc": "Sincroniza o Monero continuamente em segundo plano para que esteja sempre atualizado ao abrir o app. Usa mais bateria e exige uma notificação persistente.",
"syncFrequencyPeriodic": "Periódica",
Expand Down
84 changes: 24 additions & 60 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@ import 'package:shared_preferences/shared_preferences.dart';

import 'package:skylight_wallet/models/fiat_rate_model.dart';
import 'package:skylight_wallet/models/contact_model.dart';
import 'package:skylight_wallet/services/tor_settings_service.dart';
import 'package:skylight_wallet/screens/lws_details.dart';
import 'package:skylight_wallet/screens/lws_keys.dart';
import 'package:skylight_wallet/screens/scan_qr.dart';
import 'package:skylight_wallet/screens/secret_keys.dart';
import 'package:skylight_wallet/services/tor_service.dart';
import 'package:skylight_wallet/models/language_model.dart';
import 'package:skylight_wallet/models/theme_model.dart';
import 'package:skylight_wallet/l10n/app_localizations.dart';
import 'package:skylight_wallet/screens/settings.dart';
import 'package:skylight_wallet/models/app_wallet.dart';
import 'package:skylight_wallet/screens/connection_setup.dart';
import 'package:skylight_wallet/screens/fiat_api_setup_screen.dart';
import 'package:skylight_wallet/screens/generate_seed.dart';
Expand All @@ -32,9 +29,8 @@ import 'package:skylight_wallet/screens/wallet_home.dart';
import 'package:skylight_wallet/screens/welcome.dart';
import 'package:skylight_wallet/screens/tor_settings.dart';
import 'package:skylight_wallet/screens/address_book.dart';
import 'package:skylight_wallet/screens/desktop/home_shell.dart' show subPageChrome;
import 'package:skylight_wallet/screens/history.dart';
import 'package:skylight_wallet/screens/privacy_policy.dart';
import 'package:skylight_wallet/screens/terms_of_service.dart';
import 'package:skylight_wallet/screens/unlock.dart';
import 'package:skylight_wallet/services/notifications_service.dart';
import 'package:skylight_wallet/services/shared_preferences_service.dart';
Expand Down Expand Up @@ -142,17 +138,12 @@ class _AppRoot extends StatefulWidget {
}

class _AppRootState extends State<_AppRoot>
with WidgetsBindingObserver, AppLockLifecycle<_AppRoot> {
with WidgetsBindingObserver, AppLockLifecycle<_AppRoot>, WalletAppServices<_AppRoot> {
// Started once, on the first build. Building it inside the builder would
// re-run it on every theme/language change, opening the wallet again — a
// second wallet on the same file, with its own sync loop, while the first is
// left running.
Future<List<Object>>? _startup;
// Services that must fire once the startup work is done, not on every build.
var _startedServices = false;
// Desktop-only foreground announce: listens for tx-history growth (see below).
AppWallet? _announceWallet;
int _lastAnnouncedTxCount = 0;

// Brightness-flip repaint: screens read BrandColors globally (not via
// Theme.of), so a theme change doesn't dirty cached routes on its own. On an
Expand Down Expand Up @@ -208,8 +199,18 @@ class _AppRootState extends State<_AppRoot>
'/receive': (context) => ReceiveScreen(),
'/address_book': (context) => AddressBookScreen(),
'/history': (context) => const HistoryScreen(),
'/terms_of_service': (context) => TermsOfService(),
'/privacy_policy': (context) => PrivacyPolicy(),
'/terms_of_service': (context) => TermsOfServiceScreen(
title: AppLocalizations.of(context)!.welcomeTermsLink,
chrome: subPageChrome(context),
),
'/privacy_policy': (context) => PrivacyPolicyScreen(
title: AppLocalizations.of(context)!.welcomePrivacyLink,
chrome: subPageChrome(context),
),
'/open_source_licenses': (context) => OpenSourceLicensesScreen(
title: AppLocalizations.of(context)!.settingsLicensesLabel,
chrome: subPageChrome(context),
),
};

Route<dynamic>? _onGenerateRoute(RouteSettings settings) {
Expand Down Expand Up @@ -256,10 +257,16 @@ class _AppRootState extends State<_AppRoot>
WidgetsBinding.instance.removeObserver(this);
WidgetsBinding.instance.removeObserver(_routeLinkBlocker);
_paymentLinks.dispose();
_announceWallet?.removeListener(_announceNewTxsOnGrowth);
super.dispose();
}

// The services beside the screens (WalletAppServices) use the app's own.
@override
WalletManager get appWalletManager => Provider.of<WalletManager>(context, listen: false);

@override
FiatRateModel get appFiatRates => Provider.of<FiatRateModel>(context, listen: false);

/// Opens the Send form for a payment link the gate let through: the user is
/// past every lock and on a screen behind them. It only pre-fills the form,
/// which reviews and confirms the payment as always.
Expand Down Expand Up @@ -310,23 +317,7 @@ class _AppRootState extends State<_AppRoot>
/// announce. Marks only synced history (hash-based), so an unsynced
/// receipt is still announced later.
@override
void onMobilePaused() {
unawaited(appWalletOf(context, listen: false).notifyNewIncomingTxs(announce: false));
}

// Desktop has no background isolate to announce incoming txs, so the
// foreground announces when the wallet's history grows. notifyNewIncomingTxs
// is the decider (hash-based, net-receipt only, respects the notifications
// toggle); the count is a cheap gate so unrelated notifications (connectivity,
// balance) don't hit the keystore.
void _announceNewTxsOnGrowth() {
final wallet = _announceWallet;
if (wallet == null) return;
final count = wallet.txHistory.length;
if (count <= _lastAnnouncedTxCount) return;
_lastAnnouncedTxCount = count;
unawaited(wallet.notifyNewIncomingTxs());
}
void onMobilePaused() => markIncomingTxsSeen();

Future<List<Object>> _runStartup() {
// Wallet existence drives the initial route; done quickly without a full load.
Expand All @@ -340,8 +331,6 @@ class _AppRootState extends State<_AppRoot>

return Consumer2<LanguageModel, ThemeModel>(
builder: (context, languageProvider, themeProvider, child) {
final fiatRate = Provider.of<FiatRateModel>(context, listen: false);

return FutureBuilder(
future: _startup,
builder: (context, snapshot) {
Expand All @@ -355,33 +344,8 @@ class _AppRootState extends State<_AppRoot>
// Linux and Windows). With security keys on, a key comes next.
final initialRoute = walletExists ? '/unlock' : '/welcome';

if (!_startedServices) {
_startedServices = true;
// Only the built-in Tor is ours to start; external/disabled must
// not spin up an embedded Tor, whose status would then misreport
// the external proxy.
unawaited(() async {
await TorSettingsService.sharedInstance.loadSettings();
if (TorSettingsService.sharedInstance.torMode == TorMode.builtIn) {
await TorService.sharedInstance.start();
}
}());

// Attach the manager once so the (multicoin) fiat model knows to
// fetch XMR; every startService() afterwards can stay argless.
attachFiatWalletManager(context);
if (walletExists) {
fiatRate.startService();
}

// Desktop has no background isolate to announce incoming txs, so
// the foreground announces on tx-history growth. Mobile announces
// from its background isolates.
if (isDesktopOS) {
_announceWallet = appWalletOf(context, listen: false)
..addListener(_announceNewTxsOnGrowth);
}
}
// Tor, fiat rates and desktop announcements, once (WalletAppServices).
startWalletServices(walletExists: walletExists);

return MaterialApp(
navigatorKey: _navigatorKey,
Expand Down
4 changes: 2 additions & 2 deletions lib/models/fiat_rate_model.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// FiatRateModel lives in wallet-core (`wallet_fiat`, multicoin — skylight uses
// the XMR result via rateFor('XMR')). Kept under the same import path so call
// sites are unchanged; the app supplies the Tor proxy via FiatRates.install and
// attaches the WalletManager via attachFiatWalletManager in wallet_core_glue.dart.
// sites are unchanged; the app supplies the Tor proxy via FiatRates.install in
// wallet_core_glue.dart, and WalletAppServices attaches the WalletManager at launch.
// The fiatAutoDisabledByTor auto-disable/restore stays app-side (tor_settings_form).
export 'package:wallet_fiat/wallet_fiat.dart'
show FiatRateModel, FiatApiMode, FiatCurrency, FiatQuote;
1 change: 1 addition & 0 deletions lib/route_policy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ const routePolicy = RoutePolicy({
'/advanced_security': RouteAccess.unlocked,
'/metadata_backup': RouteAccess.unlocked,
'/verify_seed': RouteAccess.unlocked,
'/open_source_licenses': RouteAccess.unlocked,
});
59 changes: 9 additions & 50 deletions lib/screens/desktop/home_shell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,15 @@ import 'package:skylight_wallet/widgets/ui/ui.dart';
/// The top-level desktop destinations, mirroring the mobile navigation bar.
enum DesktopNav { home, history, addressBook, settings }

/// Shared style for a desktop screen's large title (Settings, Contacts, …).
TextStyle get desktopTitleStyle => TextStyle(
fontFamily: 'Ubuntu',
fontSize: 26,
height: 1.2,
fontWeight: FontWeight.w700,
color: BrandColors.ink,
);

/// The "‹ label" back link at the top of a desktop sub-screen (receive, send,
/// ToS, …) — the shared back affordance in place of a mobile app bar.
class DesktopBackLink extends StatelessWidget {
final String label;
final VoidCallback onTap;

const DesktopBackLink({super.key, required this.label, required this.onTap});

@override
Widget build(BuildContext context) {
// Sized to its content (left-aligned) so the hover/ripple doesn't span the
// full parent width; a little right padding keeps the ripple off the label.
return Align(
alignment: Alignment.centerLeft,
child: InkWell(
mouseCursor: WidgetStateMouseCursor.clickable,
onTap: onTap,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 4, 4, 4),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.chevron_left, size: 18, color: BrandColors.primary),
const SizedBox(width: 2),
Text(
label,
style: TextStyle(
fontFamily: 'Ubuntu',
fontSize: 12.5,
height: 1,
fontWeight: FontWeight.w500,
color: BrandColors.primary,
),
),
],
),
),
),
);
}
/// The chrome for wallet_ui's shared sub-pages (the legal pages): the back
/// links' strings, and Settings' place in the sidebar when opened from there.
SubPageChrome subPageChrome(BuildContext context) {
final i18n = AppLocalizations.of(context)!;
return SubPageChrome(
back: i18n.back,
parent: i18n.navigationBarSettings,
desktopShell: (page) => DesktopShell(active: DesktopNav.settings, child: page),
);
}

/// Two-pane desktop chrome: a fixed left sidebar (brand, nav, Tor status,
Expand Down
Loading
Loading