Skip to content
Snippets Groups Projects
Commit c561e9aa authored by vjrj's avatar vjrj
Browse files

More work with node management

parent aa0fc5c7
No related branches found
No related tags found
No related merge requests found
...@@ -11,4 +11,5 @@ CARD_COLOR_TEXT=Ğ1 Wallet Dev ...@@ -11,4 +11,5 @@ CARD_COLOR_TEXT=Ğ1 Wallet Dev
# The duniter nodes are only used at boot time, later it tries to calculate periodically the nodes # The duniter nodes are only used at boot time, later it tries to calculate periodically the nodes
# that are available with the less latency # that are available with the less latency
DUNITER_NODES=https://g1.duniter.fr https://g1.le-sou.org https://g1.cgeek.fr https://g1.monnaielibreoccitanie.org https://g1.duniter.fr https://g1.le-sou.org https://g1.cgeek.fr DUNITER_NODES=https://g1.duniter.fr https://g1.le-sou.org https://g1.cgeek.fr https://g1.monnaielibreoccitanie.org https://g1.duniter.fr https://g1.le-sou.org https://g1.cgeek.fr
CESIUM_PLUS_NODES=https://g1.data.le-sou.org https://g1.data.e-is.pro https://g1.data.presler.fr https://g1.data.mithril.re https://g1.data.adn.life CESIUM_PLUS_NODES=https://g1.data.le-sou.org https://g1.data.e-is.pro https://g1.data.presler.fr https://g1.data.mithril.re
# not updated: https://g1.data.adn.life
...@@ -10,5 +10,6 @@ CARD_COLOR_TEXT=Ğ1 Wallet Cop ...@@ -10,5 +10,6 @@ CARD_COLOR_TEXT=Ğ1 Wallet Cop
# The duniter nodes are only used at boot time, later it tries to calculate periodically the nodes # The duniter nodes are only used at boot time, later it tries to calculate periodically the nodes
# that are available with the less latency # that are available with the less latency
DUNITER_NODES=https://g1.duniter.fr https://g1.le-sou.org https://g1.cgeek.fr https://g1.monnaielibreoccitanie.org https://g1.duniter.fr https://g1.le-sou.org https://g1.cgeek.fr DUNITER_NODES=https://g1.duniter.fr https://g1.le-sou.org https://g1.cgeek.fr https://g1.monnaielibreoccitanie.org https://g1.duniter.fr https://g1.le-sou.org https://g1.cgeek.fr
CESIUM_PLUS_NODES=https://g1.data.le-sou.org https://g1.data.e-is.pro https://g1.data.presler.fr https://g1.data.mithril.re https://g1.data.adn.life CESIUM_PLUS_NODES=https://g1.data.le-sou.org https://g1.data.e-is.pro https://g1.data.presler.fr https://g1.data.mithril.re
# not updated: https://g1.data.adn.life
...@@ -38,14 +38,6 @@ class NodeListCubit extends HydratedCubit<NodeListState> { ...@@ -38,14 +38,6 @@ class NodeListCubit extends HydratedCubit<NodeListState> {
emit(state.copyWith(duniterNodes: nodes)); emit(state.copyWith(duniterNodes: nodes));
} }
void setDuniterFetchTime(DateTime time) {
emit(state.copyWith(lastFetchDuniterNodesTime: time));
}
void setCesiumPluFetchTime(DateTime time) {
emit(state.copyWith(lastFetchCPlusNodesTime: time));
}
void setCesiumPlusNodes(List<Node> nodes) { void setCesiumPlusNodes(List<Node> nodes) {
emit(state.copyWith(cesiumPlusNodes: nodes)); emit(state.copyWith(cesiumPlusNodes: nodes));
} }
...@@ -59,10 +51,6 @@ class NodeListCubit extends HydratedCubit<NodeListState> { ...@@ -59,10 +51,6 @@ class NodeListCubit extends HydratedCubit<NodeListState> {
List<Node> get cesiumPlusNodes => state.cesiumPlusNodes; List<Node> get cesiumPlusNodes => state.cesiumPlusNodes;
DateTime get lastFetchDuniterNodesTime => state.lastFetchDuniterNodesTime;
DateTime get lastFetchCPlusNodesTime => state.lastFetchCPlusNodesTime;
@override @override
NodeListState? fromJson(Map<String, dynamic> json) => NodeListState? fromJson(Map<String, dynamic> json) =>
NodeListState.fromJson(json); NodeListState.fromJson(json);
......
...@@ -9,23 +9,15 @@ part 'node_list_state.g.dart'; ...@@ -9,23 +9,15 @@ part 'node_list_state.g.dart';
@immutable @immutable
@JsonSerializable() @JsonSerializable()
class NodeListState extends Equatable { class NodeListState extends Equatable {
NodeListState( NodeListState({List<Node>? duniterNodes, List<Node>? cesiumPlusNodes})
{List<Node>? duniterNodes,
List<Node>? cesiumPlusNodes,
DateTime? lastFetchDuniterNodesTime,
DateTime? lastFetchCPlusNodesTime})
: duniterNodes = duniterNodes ?? defaultDuniterNodes, : duniterNodes = duniterNodes ?? defaultDuniterNodes,
cesiumPlusNodes = cesiumPlusNodes ?? defaultCesiumPlusNodes, cesiumPlusNodes = cesiumPlusNodes ?? defaultCesiumPlusNodes;
lastFetchDuniterNodesTime = lastFetchDuniterNodesTime ?? DateTime(1970),
lastFetchCPlusNodesTime = lastFetchCPlusNodesTime ?? DateTime(1970);
factory NodeListState.fromJson(Map<String, dynamic> json) => factory NodeListState.fromJson(Map<String, dynamic> json) =>
_$NodeListStateFromJson(json); _$NodeListStateFromJson(json);
final List<Node> duniterNodes; final List<Node> duniterNodes;
final List<Node> cesiumPlusNodes; final List<Node> cesiumPlusNodes;
final DateTime lastFetchDuniterNodesTime;
final DateTime lastFetchCPlusNodesTime;
NodeListState copyWith( NodeListState copyWith(
{List<Node>? duniterNodes, {List<Node>? duniterNodes,
...@@ -35,20 +27,11 @@ class NodeListState extends Equatable { ...@@ -35,20 +27,11 @@ class NodeListState extends Equatable {
return NodeListState( return NodeListState(
duniterNodes: duniterNodes ?? this.duniterNodes, duniterNodes: duniterNodes ?? this.duniterNodes,
cesiumPlusNodes: cesiumPlusNodes ?? this.cesiumPlusNodes, cesiumPlusNodes: cesiumPlusNodes ?? this.cesiumPlusNodes,
lastFetchDuniterNodesTime:
lastFetchDuniterNodesTime ?? lastFetchDuniterNodesTime,
lastFetchCPlusNodesTime:
lastFetchDuniterNodesTime ?? lastFetchCPlusNodesTime,
); );
} }
@override @override
List<Object?> get props => <Object>[ List<Object?> get props => <Object>[duniterNodes, cesiumPlusNodes];
duniterNodes,
cesiumPlusNodes,
lastFetchDuniterNodesTime,
lastFetchCPlusNodesTime
];
Map<String, dynamic> toJson() => _$NodeListStateToJson(this); Map<String, dynamic> toJson() => _$NodeListStateToJson(this);
} }
...@@ -14,20 +14,10 @@ NodeListState _$NodeListStateFromJson(Map<String, dynamic> json) => ...@@ -14,20 +14,10 @@ NodeListState _$NodeListStateFromJson(Map<String, dynamic> json) =>
cesiumPlusNodes: (json['cesiumPlusNodes'] as List<dynamic>?) cesiumPlusNodes: (json['cesiumPlusNodes'] as List<dynamic>?)
?.map((e) => Node.fromJson(e as Map<String, dynamic>)) ?.map((e) => Node.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
lastFetchDuniterNodesTime: json['lastFetchDuniterNodesTime'] == null
? null
: DateTime.parse(json['lastFetchDuniterNodesTime'] as String),
lastFetchCPlusNodesTime: json['lastFetchCPlusNodesTime'] == null
? null
: DateTime.parse(json['lastFetchCPlusNodesTime'] as String),
); );
Map<String, dynamic> _$NodeListStateToJson(NodeListState instance) => Map<String, dynamic> _$NodeListStateToJson(NodeListState instance) =>
<String, dynamic>{ <String, dynamic>{
'duniterNodes': instance.duniterNodes, 'duniterNodes': instance.duniterNodes,
'cesiumPlusNodes': instance.cesiumPlusNodes, 'cesiumPlusNodes': instance.cesiumPlusNodes,
'lastFetchDuniterNodesTime':
instance.lastFetchDuniterNodesTime.toIso8601String(),
'lastFetchCPlusNodesTime':
instance.lastFetchCPlusNodesTime.toIso8601String(),
}; };
...@@ -23,13 +23,9 @@ class NodeManager { ...@@ -23,13 +23,9 @@ class NodeManager {
final List<Node> duniterNodes = <Node>[]; final List<Node> duniterNodes = <Node>[];
final List<Node> cesiumPlusNodes = <Node>[]; final List<Node> cesiumPlusNodes = <Node>[];
DateTime lastDuniterFetchNodesTime = DateTime.now();
DateTime lastCPlusFetchNodesTime = DateTime.now();
void loadFromCubit(NodeListCubit cubit) { void loadFromCubit(NodeListCubit cubit) {
NodeManagerObserver.instance.setCubit(cubit); NodeManagerObserver.instance.cubit = cubit;
lastDuniterFetchNodesTime = cubit.lastFetchDuniterNodesTime;
lastCPlusFetchNodesTime = cubit.lastFetchCPlusNodesTime;
duniterNodes.clear(); duniterNodes.clear();
cesiumPlusNodes.clear(); cesiumPlusNodes.clear();
duniterNodes.addAll(cubit.duniterNodes); duniterNodes.addAll(cubit.duniterNodes);
...@@ -48,16 +44,6 @@ class NodeManager { ...@@ -48,16 +44,6 @@ class NodeManager {
List<Node> _getList(NodeType type) => List<Node> _getList(NodeType type) =>
type == NodeType.duniter ? duniterNodes : cesiumPlusNodes; type == NodeType.duniter ? duniterNodes : cesiumPlusNodes;
void updateLastFetchNodesTime(DateTime time) {
lastDuniterFetchNodesTime = time;
notifyObserver();
}
void updateLastCPlusFetchNodesTime(DateTime time) {
lastCPlusFetchNodesTime = time;
notifyObserver();
}
void addNode(NodeType type, Node node) { void addNode(NodeType type, Node node) {
final List<Node> nodes = _getList(type); final List<Node> nodes = _getList(type);
_addNode(nodes, node); _addNode(nodes, node);
...@@ -97,7 +83,7 @@ class NodeManager { ...@@ -97,7 +83,7 @@ class NodeManager {
void _updateList(List<Node> list, Node updatedNode) { void _updateList(List<Node> list, Node updatedNode) {
final int index = final int index =
list.indexWhere((Node node) => node.url == updatedNode.url); list.indexWhere((Node node) => node.url == updatedNode.url);
if (index != -1) { if (index != -1) {
list.replaceRange(index, index + 1, <Node>[updatedNode]); list.replaceRange(index, index + 1, <Node>[updatedNode]);
} }
...@@ -108,7 +94,7 @@ class NodeManager { ...@@ -108,7 +94,7 @@ class NodeManager {
for (final NodeType type in NodeType.values) { for (final NodeType type in NodeType.values) {
final List<Node> nodes = _getList(type); final List<Node> nodes = _getList(type);
final List<Node> newList = final List<Node> newList =
nodes.map((Node node) => node.copyWith(errors: 0)).toList(); nodes.map((Node node) => node.copyWith(errors: 0)).toList();
nodes.clear(); nodes.clear();
nodes.addAll(newList); nodes.addAll(newList);
} }
...@@ -125,17 +111,10 @@ class NodeManagerObserver { ...@@ -125,17 +111,10 @@ class NodeManagerObserver {
static final NodeManagerObserver instance = NodeManagerObserver._internal(); static final NodeManagerObserver instance = NodeManagerObserver._internal();
late NodeListCubit _cubit; late NodeListCubit cubit;
void setCubit(NodeListCubit cubit) {
_cubit = cubit;
}
void update(NodeManager nodeManager) { void update(NodeManager nodeManager) {
_cubit.setDuniterNodes(nodeManager.duniterNodes); cubit.setDuniterNodes(nodeManager.duniterNodes);
_cubit.setCesiumPlusNodes(nodeManager.cesiumPlusNodes); cubit.setCesiumPlusNodes(nodeManager.cesiumPlusNodes);
_cubit.setDuniterFetchTime(nodeManager.lastDuniterFetchNodesTime);
_cubit.setCesiumPluFetchTime(nodeManager.lastCPlusFetchNodesTime);
} }
} }
...@@ -118,7 +118,7 @@ Future<void> fetchDuniterNodes({bool force = false}) async { ...@@ -118,7 +118,7 @@ Future<void> fetchDuniterNodes({bool force = false}) async {
logger('Fetching nodes forced'); logger('Fetching nodes forced');
} else { } else {
logger( logger(
'Fetching nodes as we did it more than ${minutesToWait}min ago: ${NodeManager().lastDuniterFetchNodesTime.toIso8601String()} and we have only ${duniterNodesWorking()}'); 'Fetching nodes as we did it more than ${minutesToWait}min ago and we have only ${duniterNodesWorking()}');
} }
final List<Node> nodes = await _fetchDuniterNodesFromPeers(); final List<Node> nodes = await _fetchDuniterNodesFromPeers();
NodeManager().updateNodes(type, nodes); NodeManager().updateNodes(type, nodes);
...@@ -310,6 +310,11 @@ Future<http.Response> requestCPlusWithRetry(String path, ...@@ -310,6 +310,11 @@ Future<http.Response> requestCPlusWithRetry(String path,
Future<http.Response> _requestWithRetry( Future<http.Response> _requestWithRetry(
NodeType type, String path, bool dontRecord, bool retryWith404) async { NodeType type, String path, bool dontRecord, bool retryWith404) async {
final List<Node> nodes = NodeManager().nodeList(type); final List<Node> nodes = NodeManager().nodeList(type);
if (nodes.isEmpty) {
nodes.addAll(type == NodeType.duniter
? defaultDuniterNodes
: defaultCesiumPlusNodes);
}
for (int i = 0; i < nodes.length; i++) { for (int i = 0; i < nodes.length; i++) {
final Node node = nodes[i]; final Node node = nodes[i];
if (node.errors >= NodeManager.maxNodeErrors) { if (node.errors >= NodeManager.maxNodeErrors) {
......
...@@ -220,17 +220,21 @@ class _GinkgoAppState extends State<GinkgoApp> { ...@@ -220,17 +220,21 @@ class _GinkgoAppState extends State<GinkgoApp> {
'$prefix with $nDuniterNodes duniter nodes and $nCesiumPlusNodes c+ nodes'); '$prefix with $nDuniterNodes duniter nodes and $nCesiumPlusNodes c+ nodes');
} }
@override
void initState() {
super.initState();
NodeManager().loadFromCubit(context.read<NodeListCubit>());
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<NodeListCubit, NodeListState>( return BlocBuilder<NodeListCubit, NodeListState>(
builder: (BuildContext nodeContext, NodeListState state) { builder: (BuildContext nodeContext, NodeListState state) {
NodeManager().loadFromCubit(nodeContext.read<NodeListCubit>());
Once.runHourly('load_nodes', Once.runHourly('load_nodes',
callback: () => _loadNodes(), callback: () => _loadNodes(),
fallback: () { fallback: () {
_printNodeStatus(prefix: 'After once hourly having'); _printNodeStatus(prefix: 'After once hourly having');
}); });
Once.runCustom('clear_errors', callback: () { Once.runCustom('clear_errors', callback: () {
NodeManager().cleanErrorStats(); NodeManager().cleanErrorStats();
}, duration: const Duration(minutes: 90)); }, duration: const Duration(minutes: 90));
......
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../data/models/node.dart';
import '../../data/models/node_list_cubit.dart';
import '../../data/models/node_list_state.dart';
import '../../data/models/node_manager.dart'; import '../../data/models/node_manager.dart';
import '../../g1/api.dart';
import '../../g1/export_import.dart'; import '../../g1/export_import.dart';
import '../../main.dart';
import '../ui_helpers.dart'; import '../ui_helpers.dart';
import '../widgets/bottom_widget.dart'; import '../widgets/bottom_widget.dart';
import '../widgets/fifth_screen/grid_item.dart'; import '../widgets/fifth_screen/grid_item.dart';
import '../widgets/fifth_screen/info_card.dart';
import '../widgets/fifth_screen/link_card.dart'; import '../widgets/fifth_screen/link_card.dart';
import '../widgets/fifth_screen/node_info.dart';
import '../widgets/fifth_screen/text_divider.dart'; import '../widgets/fifth_screen/text_divider.dart';
import '../widgets/header.dart'; import '../widgets/header.dart';
...@@ -22,84 +15,55 @@ class FifthScreen extends StatelessWidget { ...@@ -22,84 +15,55 @@ class FifthScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<NodeListCubit, NodeListState>( return Material(
builder: (BuildContext nodeContext, NodeListState state) { color: Theme.of(context).colorScheme.background,
return Material( child: ListView(
color: Theme.of(context).colorScheme.background, padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView( physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 16), children: <Widget>[
physics: const BouncingScrollPhysics(), const Header(text: 'bottom_nav_fifth'),
children: <Widget>[ const TextDivider(text: 'key_tools_title'),
const Header(text: 'bottom_nav_fifth'), GridView.count(
const TextDivider(text: 'key_tools_title'), physics: const NeverScrollableScrollPhysics(),
GridView.count( crossAxisCount: 2,
physics: const NeverScrollableScrollPhysics(), childAspectRatio: 2 / 1.15,
crossAxisCount: 2, crossAxisSpacing: 8,
childAspectRatio: 2 / 1.15, mainAxisSpacing: 8,
crossAxisSpacing: 8, shrinkWrap: true,
mainAxisSpacing: 8, padding: EdgeInsets.zero,
shrinkWrap: true, children: <GridItem>[
padding: EdgeInsets.zero, GridItem(
children: <GridItem>[ title: 'export_key',
GridItem( icon: Icons.download,
title: 'export_key', onTap: () {
icon: Icons.download, showDialog(
onTap: () { context: context,
showDialog( builder: (BuildContext context) {
context: context, return const ExportImportPage();
builder: (BuildContext context) { },
return const ExportImportPage(); );
}, }),
); GridItem(
}), title: 'import_key',
GridItem( icon: Icons.upload,
title: 'import_key', onTap: () {
icon: Icons.upload, const ExportImportPage();
onTap: () { }),
const ExportImportPage(); GridItem(
}), title: 'copy_your_key',
GridItem( icon: Icons.copy,
title: 'copy_your_key', onTap: () => copyPublicKeyToClipboard(context),
icon: Icons.copy, )
onTap: () => copyPublicKeyToClipboard(context), ]),
) const TextDivider(text: 'technical_info_title'),
]), const NodeInfoCard(type: NodeType.duniter),
const TextDivider(text: 'technical_info_title'), const NodeInfoCard(type: NodeType.cesiumPlus),
_buildNodeInfo(NodeType.duniter, state.duniterNodes, context), LinkCard(
_buildNodeInfo( title: 'code_card_title',
NodeType.cesiumPlus, state.cesiumPlusNodes, context), icon: Icons.code_rounded,
LinkCard( url: Uri.parse('https://git.duniter.org/vjrj/ginkgo')),
title: 'code_card_title', const BottomWidget()
icon: Icons.code_rounded, ]),
url: Uri.parse('https://git.duniter.org/vjrj/ginkgo')), );
const BottomWidget()
]),
);
});
}
GestureDetector _buildNodeInfo(
NodeType type, List<Node> nodes, BuildContext context) {
return GestureDetector(
onTap: () => showTooltip(context, '', tr('long_press_to_refresh')),
onLongPress: () {
logger('On long press');
if (type == NodeType.duniter) {
fetchDuniterNodes(force: true);
} else {
fetchCesiumPlusNodes(force: true);
}
},
child: InfoCard(
title: tr('using_nodes', namedArgs: <String, String>{
'type': type.name,
'nodes': nodes.length.toString()
}),
translate: false,
subtitle: nodes.isNotEmpty
? tr('using_nodes_first',
namedArgs: <String, String>{'node': nodes.first.url})
: '',
icon: Icons.hub));
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment