delete custom epicbox impl and some other minimal working changes
What changed, and why it matters
This commit removes a custom Epicbox server implementation from the Stack Wallet app and switches the UI to use a built-in list of default Epicbox servers. It also adds delete buttons for custom servers and changes how default servers are stored. There is no clear security fix here; it looks like a feature refactor or cleanup that may have minor side effects on how server settings are saved and restored.
Treat as a routine refactor. Review whether disabling default epicbox persistence could cause wallets to fall back to unexpected servers or lose user preferences. Verify DefaultEpicBoxes.all IDs are stable and that firstWhere fallbacks cannot be triggered by attacker-controlled IDs. No immediate security patch appears required based solely on this diff.
Security signals we found
Default server records are no longer persisted, which could affect integrity of user-selected defaults across app restarts or updates
Fallback to DefaultEpicBoxes.all by ID in multiple UI locations assumes IDs exist and are unique; firstWhere will throw if a stale/unknown ID is referenced
Delete action navigates before awaiting deletion, which is a UX/race issue but not a clear security flaw
No explicit security framing, CVE, or advisory in commit message or diff
Evidence from the diff
The patch deletes the custom epicbox implementation and replaces it with references to DefaultEpicBoxes.all. Default epicbox records are no longer persisted/updated in the local database (updateDefaultEpicBoxes() is mostly commented out), while custom boxes remain stored. UI views now fall back to DefaultEpicBoxes.all when an ID is not found in the node service. Add/edit views gain delete affordances and stricter add/edit assertions. The change is structural rather than a targeted vulnerability patch.
Changed components
lib/services/node_service.dartlib/widgets/epicbox_card.dartlib/pages/settings_views/wallet_settings_view/epicbox_settings/manage_epicbox_view.dartlib/pages_desktop_specific/settings/settings_menu/epicbox_settings/desktop_manage_epicbox_dialog.dartlib/pages/settings_views/wallet_settings_view/epicbox_settings/add_edit_epicbox_mobile_view.dartlib/pages_desktop_specific/settings/settings_menu/epicbox_settings/add_edit_epicbox_view.dartlib/route_generator.dartlib/main.dartInspect captured patch +388 / −303
diff --git a/lib/main.dart b/lib/main.dart
index 1d8f992..ea6880a 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -33,13 +33,13 @@ import 'db/hive/db.dart';
import 'db/isar/main_db.dart';
import 'db/special_migrations.dart';
import 'db/sqlite/firo_cache.dart';
+import 'models/epicbox_server_model.dart';
import 'models/exchange/change_now/exchange_transaction.dart';
import 'models/exchange/change_now/exchange_transaction_status.dart';
import 'models/exchange/response_objects/trade.dart';
import 'models/models.dart';
import 'models/node_model.dart';
import 'models/notification_model.dart';
-import 'models/epicbox_server_model.dart';
import 'models/trade_wallet_lookup.dart';
import 'pages/campfire_migrate_view.dart';
import 'pages/home_view/home_view.dart';
diff --git a/lib/pages/settings_views/wallet_settings_view/epicbox_settings/add_edit_epicbox_mobile_view.dart b/lib/pages/settings_views/wallet_settings_view/epicbox_settings/add_edit_epicbox_mobile_view.dart
index 9208387..907aaaf 100644
--- a/lib/pages/settings_views/wallet_settings_view/epicbox_settings/add_edit_epicbox_mobile_view.dart
+++ b/lib/pages/settings_views/wallet_settings_view/epicbox_settings/add_edit_epicbox_mobile_view.dart
@@ -3,18 +3,21 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_svg/svg.dart';
import 'package:uuid/uuid.dart';
import '../../../../models/epicbox_server_model.dart';
import '../../../../notifications/show_flush_bar.dart';
import '../../../../providers/global/node_service_provider.dart';
import '../../../../themes/stack_colors.dart';
+import '../../../../utilities/assets.dart';
import '../../../../utilities/constants.dart';
import '../../../../utilities/test_epicbox_server_connection.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../widgets/background.dart';
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
-import '../../../../widgets/custom_buttons/blue_text_button.dart';
+import '../../../../widgets/desktop/primary_button.dart';
+import '../../../../widgets/desktop/secondary_button.dart';
import '../../../../widgets/icon_widgets/x_icon.dart';
import '../../../../widgets/stack_text_field.dart';
import '../../../../widgets/textfield_icon_button.dart';
@@ -26,12 +29,17 @@ class AddEditEpicboxMobileView extends ConsumerStatefulWidget {
super.key,
required this.viewType,
this.epicBoxId,
- });
+ required this.routeOnSuccessOrDelete,
+ }) : assert(
+ (viewType == .edit && epicBoxId != null) ||
+ viewType == .add && epicBoxId == null,
+ );
static const routeName = "/addEditEpicboxMobile";
final AddEditEpicboxMobileViewType viewType;
final String? epicBoxId;
+ final String routeOnSuccessOrDelete;
@override
ConsumerState<AddEditEpicboxMobileView> createState() =>
@@ -111,14 +119,15 @@ class _AddEditEpicboxMobileViewState
title: const Text("Server currently unreachable"),
content: const Text("Would you like to save this server anyways?"),
actions: [
+ // todo both pop until routeOnSuccessOrDelete ?
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
- color: Theme.of(context)
- .extension<StackColors>()!
- .accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
),
),
),
@@ -127,9 +136,9 @@ class _AddEditEpicboxMobileViewState
child: Text(
"Save",
style: STextStyles.button(context).copyWith(
- color: Theme.of(context)
- .extension<StackColors>()!
- .accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
),
),
),
@@ -162,6 +171,8 @@ class _AddEditEpicboxMobileViewState
}
}
+ late final bool canDelete;
+
@override
void initState() {
super.initState();
@@ -169,20 +180,25 @@ class _AddEditEpicboxMobileViewState
_hostController = TextEditingController();
_portController = TextEditingController();
- if (widget.epicBoxId != null) {
- final epicBox = ref
- .read(nodeServiceChangeNotifierProvider)
- .getEpicBoxById(id: widget.epicBoxId!);
- if (epicBox != null) {
+ switch (widget.viewType) {
+ case .add:
+ _portController.text = "443";
+ port = 443;
+ canDelete = false;
+ break;
+
+ case .edit:
+ final epicBox = ref
+ .read(nodeServiceChangeNotifierProvider)
+ .getEpicBoxById(id: widget.epicBoxId!)!;
+
_nameController.text = epicBox.name;
_hostController.text = epicBox.host;
_portController.text = (epicBox.port ?? 443).toString();
_useSSL = epicBox.useSSL ?? true;
port = epicBox.port ?? 443;
- }
- } else {
- _portController.text = "443";
- port = 443;
+ canDelete = !epicBox.isDefault;
+ break;
}
}
@@ -214,196 +230,231 @@ class _AddEditEpicboxMobileViewState
: "Edit Epicbox Server",
style: STextStyles.navBarTitle(context),
),
+ actions: [
+ if (canDelete)
+ Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 10, right: 10),
+ child: AspectRatio(
+ aspectRatio: 1,
+ child: AppBarIconButton(
+ key: const Key("deleteNodeAppBarButtonKey"),
+ size: 36,
+ shadows: const [],
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
+ icon: SvgPicture.asset(
+ Assets.svg.trash,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
+ width: 20,
+ height: 20,
+ ),
+ onPressed: () async {
+ Navigator.popUntil(
+ context,
+ ModalRoute.withName(widget.routeOnSuccessOrDelete),
+ );
+ await ref
+ .read(nodeServiceChangeNotifierProvider)
+ .deleteEpicBox(widget.epicBoxId!, true);
+ },
+ ),
+ ),
+ ),
+ ],
),
- body: Padding(
- padding: const EdgeInsets.all(16),
- child: Column(
- children: [
- Expanded(
- child: SingleChildScrollView(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: false,
- enableSuggestions: false,
- controller: _nameController,
- focusNode: _nameFocusNode,
- style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Server name",
- _nameFocusNode,
- context,
- ).copyWith(
- suffixIcon: _nameController.text.isNotEmpty
- ? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: TextFieldIconButton(
- child: const XIcon(),
- onTap: () {
- _nameController.clear();
- setState(() {});
- },
- ),
- ),
- )
- : null,
- ),
- onChanged: (_) => setState(() {}),
- ),
- ),
- const SizedBox(height: 12),
- ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: false,
- enableSuggestions: false,
- controller: _hostController,
- focusNode: _hostFocusNode,
- style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Host",
- _hostFocusNode,
- context,
- ).copyWith(
- suffixIcon: _hostController.text.isNotEmpty
- ? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: TextFieldIconButton(
- child: const XIcon(),
- onTap: () {
- _hostController.clear();
- setState(() {});
- },
- ),
- ),
- )
- : null,
+ body: SafeArea(
+ child: LayoutBuilder(
+ builder: (builderContext, constraints) {
+ return SingleChildScrollView(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(minHeight: constraints.maxHeight),
+ child: IntrinsicHeight(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ children: [
+ ClipRRect(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ child: TextField(
+ autocorrect: false,
+ enableSuggestions: false,
+ controller: _nameController,
+ focusNode: _nameFocusNode,
+ style: STextStyles.field(context),
+ decoration:
+ standardInputDecoration(
+ "Server name",
+ _nameFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon: _nameController.text.isNotEmpty
+ ? Padding(
+ padding: const EdgeInsets.only(
+ right: 0,
+ ),
+ child: UnconstrainedBox(
+ child: TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () {
+ _nameController.clear();
+ setState(() {});
+ },
+ ),
+ ),
+ )
+ : null,
+ ),
+ onChanged: (_) => setState(() {}),
+ ),
),
- onChanged: (_) => setState(() {}),
- ),
- ),
- const SizedBox(height: 12),
- ClipRRect(
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- child: TextField(
- autocorrect: false,
- enableSuggestions: false,
- controller: _portController,
- focusNode: _portFocusNode,
- inputFormatters: [
- FilteringTextInputFormatter.digitsOnly,
- ],
- keyboardType: TextInputType.number,
- style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Port",
- _portFocusNode,
- context,
- ).copyWith(
- suffixIcon: _portController.text.isNotEmpty
- ? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: TextFieldIconButton(
- child: const XIcon(),
- onTap: () {
- _portController.clear();
- setState(() {});
- },
- ),
- ),
- )
- : null,
+ const SizedBox(height: 12),
+ ClipRRect(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ child: TextField(
+ autocorrect: false,
+ enableSuggestions: false,
+ controller: _hostController,
+ focusNode: _hostFocusNode,
+ style: STextStyles.field(context),
+ decoration:
+ standardInputDecoration(
+ "Host",
+ _hostFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon: _hostController.text.isNotEmpty
+ ? Padding(
+ padding: const EdgeInsets.only(
+ right: 0,
+ ),
+ child: UnconstrainedBox(
+ child: TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () {
+ _hostController.clear();
+ setState(() {});
+ },
+ ),
+ ),
+ )
+ : null,
+ ),
+ onChanged: (_) => setState(() {}),
+ ),
),
- onChanged: (value) {
- port = int.tryParse(value);
- setState(() {});
- },
- ),
- ),
- const SizedBox(height: 12),
- Row(
- children: [
- GestureDetector(
- onTap: () {
- setState(() {
- _useSSL = !_useSSL;
- });
- },
- child: Container(
- color: Colors.transparent,
- child: Row(
- children: [
- SizedBox(
- width: 20,
- height: 20,
- child: Checkbox(
- materialTapTargetSize:
- MaterialTapTargetSize.shrinkWrap,
- value: _useSSL,
- onChanged: (newValue) {
- setState(() {
- _useSSL = newValue!;
- });
- },
- ),
+ const SizedBox(height: 12),
+ ClipRRect(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ child: TextField(
+ autocorrect: false,
+ enableSuggestions: false,
+ controller: _portController,
+ focusNode: _portFocusNode,
+ inputFormatters: [
+ FilteringTextInputFormatter.digitsOnly,
+ ],
+ keyboardType: TextInputType.number,
+ style: STextStyles.field(context),
+ decoration:
+ standardInputDecoration(
+ "Port",
+ _portFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon: _portController.text.isNotEmpty
+ ? Padding(
+ padding: const EdgeInsets.only(
+ right: 0,
+ ),
+ child: UnconstrainedBox(
+ child: TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () {
+ _portController.clear();
+ setState(() {});
+ },
+ ),
+ ),
+ )
+ : null,
),
- const SizedBox(width: 12),
- Text(
- "Use SSL",
- style: STextStyles.itemSubtitle12(context),
+ onChanged: (value) {
+ port = int.tryParse(value);
+ setState(() {});
+ },
+ ),
+ ),
+ const SizedBox(height: 12),
+ Row(
+ children: [
+ GestureDetector(
+ onTap: () {
+ setState(() {
+ _useSSL = !_useSSL;
+ });
+ },
+ child: Container(
+ color: Colors.transparent,
+ child: Row(
+ children: [
+ SizedBox(
+ width: 20,
+ height: 20,
+ child: Checkbox(
+ materialTapTargetSize:
+ MaterialTapTargetSize.shrinkWrap,
+ value: _useSSL,
+ onChanged: (newValue) {
+ setState(() {
+ _useSSL = newValue!;
+ });
+ },
+ ),
+ ),
+ const SizedBox(width: 12),
+ Text(
+ "Use SSL",
+ style: STextStyles.itemSubtitle12(
+ context,
+ ),
+ ),
+ ],
),
- ],
+ ),
),
- ),
+ ],
+ ),
+
+ const Spacer(),
+ const SizedBox(height: 16),
+ SecondaryButton(
+ label: "Test connection",
+ enabled: canTestConnection,
+ onPressed: canTestConnection
+ ? _testConnection
+ : null,
+ ),
+ const SizedBox(height: 16),
+ PrimaryButton(
+ label: "Save",
+ onPressed: canSave ? _attemptSave : null,
),
],
),
- ],
- ),
- ),
- ),
- const SizedBox(height: 16),
- Row(
- children: [
- Expanded(
- child: CustomTextButton(
- text: "Test connection",
- enabled: canTestConnection,
- onTap: canTestConnection ? _testConnection : null,
),
),
- const SizedBox(width: 16),
- Expanded(
- child: TextButton(
- onPressed: canSave ? _attemptSave : null,
- style: canSave
- ? Theme.of(context)
- .extension<StackColors>()!
- .getPrimaryEnabledButtonStyle(context)
- : Theme.of(context)
- .extension<StackColors>()!
- .getPrimaryDisabledButtonStyle(context),
- child: Text(
- "Save",
- style: STextStyles.button(context),
- ),
- ),
- ),
- ],
- ),
- ],
+ ),
+ );
+ },
),
),
),
diff --git a/lib/pages/settings_views/wallet_settings_view/epicbox_settings/manage_epicbox_view.dart b/lib/pages/settings_views/wallet_settings_view/epicbox_settings/manage_epicbox_view.dart
index 46765b2..797ce8e 100644
--- a/lib/pages/settings_views/wallet_settings_view/epicbox_settings/manage_epicbox_view.dart
+++ b/lib/pages/settings_views/wallet_settings_view/epicbox_settings/manage_epicbox_view.dart
@@ -7,6 +7,7 @@ import '../../../../notifications/show_flush_bar.dart';
import '../../../../providers/providers.dart';
import '../../../../themes/stack_colors.dart';
import '../../../../utilities/assets.dart';
+import '../../../../utilities/default_epicboxes.dart';
import '../../../../utilities/test_epicbox_server_connection.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../wallets/wallet/impl/epiccash_wallet.dart';
@@ -28,11 +29,11 @@ class ManageEpicboxView extends ConsumerStatefulWidget {
class _ManageEpicboxViewState extends ConsumerState<ManageEpicboxView> {
Future<void> _onConnect(String epicBoxId) async {
- final epicBox = ref
- .read(nodeServiceChangeNotifierProvider)
- .getEpicBoxById(id: epicBoxId);
-
- if (epicBox == null) return;
+ final epicBox =
+ ref
+ .read(nodeServiceChangeNotifierProvider)
+ .getEpicBoxById(id: epicBoxId) ??
+ DefaultEpicBoxes.all.firstWhere((e) => e.id == epicBoxId);
final data = EpicBoxFormData()
..host = epicBox.host
@@ -79,6 +80,7 @@ class _ManageEpicboxViewState extends ConsumerState<ManageEpicboxView> {
arguments: (
viewType: AddEditEpicboxMobileViewType.edit,
epicBoxId: epicBoxId,
+ routeOnSuccessOrDelete: ManageEpicboxView.routeName,
),
);
}
@@ -89,6 +91,7 @@ class _ManageEpicboxViewState extends ConsumerState<ManageEpicboxView> {
arguments: (
viewType: AddEditEpicboxMobileViewType.add,
epicBoxId: null,
+ routeOnSuccessOrDelete: ManageEpicboxView.routeName,
),
);
}
@@ -104,9 +107,6 @@ class _ManageEpicboxViewState extends ConsumerState<ManageEpicboxView> {
),
);
- final defaultBoxes = epicBoxes.where((e) => e.isDefault).toList();
- final customBoxes = epicBoxes.where((e) => !e.isDefault).toList();
-
return Background(
child: Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
@@ -132,9 +132,9 @@ class _ManageEpicboxViewState extends ConsumerState<ManageEpicboxView> {
child: Center(
child: Icon(
Icons.add,
- color: Theme.of(context)
- .extension<StackColors>()!
- .topNavIconPrimary,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.topNavIconPrimary,
size: 20,
),
),
@@ -151,7 +151,7 @@ class _ManageEpicboxViewState extends ConsumerState<ManageEpicboxView> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- if (defaultBoxes.isNotEmpty) ...[
+ ...[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
@@ -166,20 +166,20 @@ class _ManageEpicboxViewState extends ConsumerState<ManageEpicboxView> {
),
),
),
- ...defaultBoxes.map(
+ ...DefaultEpicBoxes.all.map(
(epicBox) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: EpicBoxCard(
key: Key("${epicBox.id}_card_key"),
epicBoxId: epicBox.id,
onConnect: () => _onConnect(epicBox.id),
- onEdit: () => _onEdit(epicBox.id),
+ onEdit: () {},
testOnInit: primaryEpicBox?.id == epicBox.id,
),
),
),
],
- if (customBoxes.isNotEmpty) ...[
+ if (epicBoxes.isNotEmpty) ...[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
@@ -194,7 +194,7 @@ class _ManageEpicboxViewState extends ConsumerState<ManageEpicboxView> {
),
),
),
- ...customBoxes.map(
+ ...epicBoxes.map(
(epicBox) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: EpicBoxCard(
diff --git a/lib/pages_desktop_specific/settings/settings_menu/epicbox_settings/add_edit_epicbox_view.dart b/lib/pages_desktop_specific/settings/settings_menu/epicbox_settings/add_edit_epicbox_view.dart
index 2a091b6..07f5046 100644
--- a/lib/pages_desktop_specific/settings/settings_menu/epicbox_settings/add_edit_epicbox_view.dart
+++ b/lib/pages_desktop_specific/settings/settings_menu/epicbox_settings/add_edit_epicbox_view.dart
@@ -12,6 +12,7 @@ import '../../../../utilities/constants.dart';
import '../../../../utilities/test_epicbox_server_connection.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
+import '../../../../widgets/desktop/delete_button.dart';
import '../../../../widgets/desktop/desktop_dialog.dart';
import '../../../../widgets/desktop/primary_button.dart';
import '../../../../widgets/desktop/secondary_button.dart';
@@ -27,7 +28,10 @@ class AddEditEpicBoxView extends ConsumerStatefulWidget {
required this.viewType,
this.epicBoxId,
required this.onSave,
- });
+ }) : assert(
+ (viewType == .edit && epicBoxId != null) ||
+ viewType == .add && epicBoxId == null,
+ );
final AddEditEpicBoxViewType viewType;
final String? epicBoxId;
@@ -198,6 +202,8 @@ class _AddEditEpicBoxViewState extends ConsumerState<AddEditEpicBoxView> {
}
}
+ late final bool canDelete;
+
@override
void initState() {
super.initState();
@@ -205,20 +211,25 @@ class _AddEditEpicBoxViewState extends ConsumerState<AddEditEpicBoxView> {
_hostController = TextEditingController();
_portController = TextEditingController();
- if (widget.epicBoxId != null) {
- final epicBox = ref
- .read(nodeServiceChangeNotifierProvider)
- .getEpicBoxById(id: widget.epicBoxId!);
- if (epicBox != null) {
+ switch (widget.viewType) {
+ case .add:
+ _portController.text = "443";
+ port = 443;
+ canDelete = false;
+ break;
+
+ case .edit:
+ final epicBox = ref
+ .read(nodeServiceChangeNotifierProvider)
+ .getEpicBoxById(id: widget.epicBoxId!)!;
+
_nameController.text = epicBox.name;
_hostController.text = epicBox.host;
_portController.text = (epicBox.port ?? 443).toString();
_useSSL = epicBox.useSSL ?? true;
port = epicBox.port ?? 443;
- }
- } else {
- _portController.text = "443";
- port = 443;
+ canDelete = !epicBox.isDefault;
+ break;
}
}
@@ -416,7 +427,30 @@ class _AddEditEpicBoxViewState extends ConsumerState<AddEditEpicBoxView> {
),
],
),
- const SizedBox(height: 78),
+ const SizedBox(height: 22),
+ if (canDelete)
+ SizedBox(
+ height: 56,
+ child: Row(
+ children: [
+ Expanded(
+ child: DeleteButton(
+ label: "Delete node",
+ desktopMed: true,
+ onPressed: () {
+ Navigator.of(context).pop();
+ ref
+ .read(nodeServiceChangeNotifierProvider)
+ .deleteEpicBox(widget.epicBoxId!, true);
+ },
+ ),
+ ),
+ const SizedBox(width: 16),
+ const Spacer(),
+ ],
+ ),
+ ),
+ if (canDelete) const SizedBox(height: 45),
Row(
children: [
Expanded(
diff --git a/lib/pages_desktop_specific/settings/settings_menu/epicbox_settings/desktop_manage_epicbox_dialog.dart b/lib/pages_desktop_specific/settings/settings_menu/epicbox_settings/desktop_manage_epicbox_dialog.dart
index 886e564..5dab7e2 100644
--- a/lib/pages_desktop_specific/settings/settings_menu/epicbox_settings/desktop_manage_epicbox_dialog.dart
+++ b/lib/pages_desktop_specific/settings/settings_menu/epicbox_settings/desktop_manage_epicbox_dialog.dart
@@ -7,6 +7,7 @@ import '../../../../notifications/show_flush_bar.dart';
import '../../../../providers/providers.dart';
import '../../../../themes/stack_colors.dart';
import '../../../../utilities/assets.dart';
+import '../../../../utilities/default_epicboxes.dart';
import '../../../../utilities/test_epicbox_server_connection.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../wallets/wallet/impl/epiccash_wallet.dart';
@@ -29,11 +30,11 @@ class DesktopManageEpicBoxDialog extends ConsumerStatefulWidget {
class _DesktopManageEpicBoxDialogState
extends ConsumerState<DesktopManageEpicBoxDialog> {
Future<void> _onConnect(String epicBoxId) async {
- final epicBox = ref
- .read(nodeServiceChangeNotifierProvider)
- .getEpicBoxById(id: epicBoxId);
-
- if (epicBox == null) return;
+ final epicBox =
+ ref
+ .read(nodeServiceChangeNotifierProvider)
+ .getEpicBoxById(id: epicBoxId) ??
+ DefaultEpicBoxes.all.firstWhere((e) => e.id == epicBoxId);
final data = EpicBoxFormData()
..host = epicBox.host
@@ -106,9 +107,6 @@ class _DesktopManageEpicBoxDialogState
),
);
- final defaultBoxes = epicBoxes.where((e) => e.isDefault).toList();
- final customBoxes = epicBoxes.where((e) => !e.isDefault).toList();
-
return DesktopDialog(
maxHeight: double.infinity,
maxWidth: 580,
@@ -146,35 +144,33 @@ class _DesktopManageEpicBoxDialogState
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- if (defaultBoxes.isNotEmpty) ...[
- Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 12,
- vertical: 8,
- ),
- child: Text(
- "Default servers",
- style: STextStyles.smallMed12(context).copyWith(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textDark3,
- ),
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 12,
+ vertical: 8,
+ ),
+ child: Text(
+ "Default servers",
+ style: STextStyles.smallMed12(context).copyWith(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textDark3,
),
),
- ...defaultBoxes.map(
- (epicBox) => Padding(
- padding: const EdgeInsets.symmetric(vertical: 4),
- child: EpicBoxCard(
- key: Key("${epicBox.id}_card_key"),
- epicBoxId: epicBox.id,
- onConnect: () => _onConnect(epicBox.id),
- onEdit: () => _onEdit(epicBox.id),
- testOnInit: primaryEpicBox?.id == epicBox.id,
- ),
+ ),
+ ...DefaultEpicBoxes.all.map(
+ (epicBox) => Padding(
+ padding: const EdgeInsets.symmetric(vertical: 4),
+ child: EpicBoxCard(
+ key: Key("${epicBox.id}_card_key"),
+ epicBoxId: epicBox.id,
+ onConnect: () => _onConnect(epicBox.id),
+ onEdit: () {}, // do nothing for defaults
+ testOnInit: primaryEpicBox?.id == epicBox.id,
),
),
- ],
- if (customBoxes.isNotEmpty) ...[
+ ),
+ if (epicBoxes.isNotEmpty) ...[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
@@ -189,7 +185,7 @@ class _DesktopManageEpicBoxDialogState
),
),
),
- ...customBoxes.map(
+ ...epicBoxes.map(
(epicBox) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: EpicBoxCard(
diff --git a/lib/route_generator.dart b/lib/route_generator.dart
index fcf57da..cad05cb 100644
--- a/lib/route_generator.dart
+++ b/lib/route_generator.dart
@@ -1444,9 +1444,7 @@ class RouteGenerator {
if (args is String) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
- builder: (_) => ManageEpicboxView(
- walletId: args,
- ),
+ builder: (_) => ManageEpicboxView(walletId: args),
settings: RouteSettings(name: settings.name),
);
}
@@ -1457,12 +1455,14 @@ class RouteGenerator {
is ({
AddEditEpicboxMobileViewType viewType,
String? epicBoxId,
+ String routeOnSuccessOrDelete,
})) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => AddEditEpicboxMobileView(
viewType: args.viewType,
epicBoxId: args.epicBoxId,
+ routeOnSuccessOrDelete: args.routeOnSuccessOrDelete,
),
settings: RouteSettings(name: settings.name),
);
diff --git a/lib/services/node_service.dart b/lib/services/node_service.dart
index 487e60d..6a83299 100644
--- a/lib/services/node_service.dart
+++ b/lib/services/node_service.dart
@@ -294,33 +294,33 @@ class NodeService extends ChangeNotifier {
//============================================================================
Future<void> updateDefaultEpicBoxes() async {
- final primaryEpicBox = getPrimaryEpicBox();
-
- for (final defaultEpicBox in DefaultEpicBoxes.all) {
- final savedEpicBox = DB.instance.get<EpicBoxServerModel>(
- boxName: DB.boxNameEpicBoxModels,
- key: defaultEpicBox.id,
- );
- if (savedEpicBox == null) {
- await DB.instance.put<EpicBoxServerModel>(
- boxName: DB.boxNameEpicBoxModels,
- key: defaultEpicBox.id,
- value: defaultEpicBox,
- );
- } else {
- await DB.instance.put<EpicBoxServerModel>(
- boxName: DB.boxNameEpicBoxModels,
- key: savedEpicBox.id,
- value: defaultEpicBox.copyWith(enabled: savedEpicBox.enabled),
- );
- }
-
- if (primaryEpicBox != null && primaryEpicBox.id == defaultEpicBox.id) {
- await setPrimaryEpicBox(
- epicBox: defaultEpicBox.copyWith(enabled: primaryEpicBox.enabled),
- );
- }
- }
+ // final primaryEpicBox = getPrimaryEpicBox();
+ //
+ // for (final defaultEpicBox in DefaultEpicBoxes.all) {
+ // final savedEpicBox = DB.instance.get<EpicBoxServerModel>(
+ // boxName: DB.boxNameEpicBoxModels,
+ // key: defaultEpicBox.id,
+ // );
+ // if (savedEpicBox == null) {
+ // await DB.instance.put<EpicBoxServerModel>(
+ // boxName: DB.boxNameEpicBoxModels,
+ // key: defaultEpicBox.id,
+ // value: defaultEpicBox,
+ // );
+ // } else {
+ // await DB.instance.put<EpicBoxServerModel>(
+ // boxName: DB.boxNameEpicBoxModels,
+ // key: savedEpicBox.id,
+ // value: defaultEpicBox.copyWith(enabled: savedEpicBox.enabled),
+ // );
+ // }
+ //
+ // if (primaryEpicBox != null && primaryEpicBox.id == defaultEpicBox.id) {
+ // await setPrimaryEpicBox(
+ // epicBox: defaultEpicBox.copyWith(enabled: primaryEpicBox.enabled),
+ // );
+ // }
+ // }
// set default primary if none exists
if (getPrimaryEpicBox() == null) {
diff --git a/lib/widgets/epicbox_card.dart b/lib/widgets/epicbox_card.dart
index 3db6ebb..ca80683 100644
--- a/lib/widgets/epicbox_card.dart
+++ b/lib/widgets/epicbox_card.dart
@@ -5,6 +5,7 @@ import 'package:flutter_svg/svg.dart';
import '../providers/global/node_service_provider.dart';
import '../themes/stack_colors.dart';
import '../utilities/assets.dart';
+import '../utilities/default_epicboxes.dart';
import '../utilities/test_epicbox_server_connection.dart';
import '../utilities/text_styles.dart';
import '../utilities/util.dart';
@@ -55,10 +56,11 @@ class _EpicBoxCardState extends ConsumerState<EpicBoxCard> {
}
Future<void> _testConnection() async {
- final epicBox = ref
- .read(nodeServiceChangeNotifierProvider)
- .getEpicBoxById(id: widget.epicBoxId);
- if (epicBox == null) return;
+ final epicBox =
+ ref
+ .read(nodeServiceChangeNotifierProvider)
+ .getEpicBoxById(id: widget.epicBoxId) ??
+ DefaultEpicBoxes.all.firstWhere((e) => e.id == widget.epicBoxId);
setState(() {
_testing = true;
@@ -82,15 +84,13 @@ class _EpicBoxCardState extends ConsumerState<EpicBoxCard> {
@override
Widget build(BuildContext context) {
- final epicBox = ref.watch(
- nodeServiceChangeNotifierProvider.select(
- (value) => value.getEpicBoxById(id: widget.epicBoxId),
- ),
- );
-
- if (epicBox == null) {
- return const SizedBox.shrink();
- }
+ final epicBox =
+ ref.watch(
+ nodeServiceChangeNotifierProvider.select(
+ (value) => value.getEpicBoxById(id: widget.epicBoxId),
+ ),
+ ) ??
+ DefaultEpicBoxes.all.firstWhere((e) => e.id == widget.epicBoxId);
final primaryEpicBox = ref.watch(
nodeServiceChangeNotifierProvider.select(
@@ -107,14 +107,18 @@ class _EpicBoxCardState extends ConsumerState<EpicBoxCard> {
status = "Testing...";
} else if (_testResult == true) {
status = isPrimary ? "Connected" : "Reachable";
- statusColor = Theme.of(context).extension<StackColors>()!.accentColorGreen;
+ statusColor = Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorGreen;
} else if (_testResult == false) {
status = "Unreachable";
statusColor = Theme.of(context).extension<StackColors>()!.accentColorRed;
} else {
status = isPrimary ? "Selected" : "";
if (isPrimary) {
- statusColor = Theme.of(context).extension<StackColors>()!.accentColorBlue;
+ statusColor = Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorBlue;
}
}
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.