/* Copyright 2019 fuwa This file is part of CyberWOW. CyberWOW is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. CyberWOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with CyberWOW. If not, see . */ import 'package:flutter/material.dart'; import 'dart:io'; import 'dart:developer'; import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; import 'controller/helper.dart'; import 'controller/rpc.dart' as rpc; import 'controller/daemon.dart' as daemon; import 'controller/refresh.dart' as refresh; import 'config.dart'; abstract class AppState { T use ( T Function(BlankState) useBlankState, T Function(LoadingState) useLoadingState, T Function(SyncingState) useSyncingState, T Function(SyncedState) useSyncedState, T Function(ReSyncingState) useReSyncingState, ) { if (this is BlankState) { return useBlankState(this); } if (this is LoadingState) { return useLoadingState(this); } if (this is SyncingState) { return useSyncingState(this); } if (this is SyncedState) { return useSyncedState(this); } if (this is ReSyncingState) { return useReSyncingState(this); } throw Exception('Invalid state'); } } typedef SetStateFunc = void Function(AppState); typedef GetNotificationFunc = AppLifecycleState Function(); class HookedState extends AppState { final SetStateFunc setState; final GetNotificationFunc getNotification; HookedState(this.setState, this.getNotification); } class BlankState extends HookedState { BlankState(f, s) : super (f, s); Future next(String status) async { LoadingState _next = LoadingState(setState, getNotification, status); setState(_next); return _next; } } class LoadingState extends HookedState { String banner; String status = ''; LoadingState(f, s, this.banner) : super (f, s); void append(String msg) { this.status += msg; setState(this); } Future next(Stream loadingProgress, String status) async { Future showBanner() async { var chars = []; banner.runes.forEach((int rune) { final c = new String.fromCharCode(rune); chars.add(c); }); for (String char in chars) { append(char); await Future.delayed(Duration(milliseconds: config.splashDelay), () => "1"); } await Future.delayed(const Duration(seconds: 2), () => "1"); } Future load() async { print("Loading next"); await for (var line in loadingProgress) { // append(line); print(line); } } final outputBinExists = await binaryExists(config.outputBin); if (outputBinExists) { await load(); } else { await Future.wait([load(), showBanner()]); } SyncingState _next = SyncingState(setState, getNotification, status); setState(_next); return _next; } } class SyncingState extends HookedState { String stdout; bool synced = false; SyncingState(f, s, this.stdout) : super (f, s); void append(String msg) { this.stdout += msg; setState(this); } Future next(Stream processOutput) async { print("Syncing next"); Future printStdout() async { await for (var line in processOutput) { if (synced) break; // print('syncing: print stdout loop'); append(line); print(line); } } Future checkSync() async { await for (var _null in refresh.pull(getNotification)) { // print('syncing: target height ${_targetHeight}'); // here doc is wrong, targetHeight could match height when synced // potential bug, targetHeight could be smaller then height final _isConnected = await daemon.isConnected(); final _isSynced = await daemon.isSynced(); if (_isConnected && _isSynced) { synced = true; break; } // print('syncing: checkSync loop'); } } printStdout(); await checkSync(); print('syncing: loop exit'); SyncedState _next = SyncedState(setState, getNotification, stdout, processOutput); _next.height = await rpc.height(); setState(_next); return _next; } } class SyncedState extends HookedState { String stdout; int height; Stream processOutput; bool synced = true; bool connected = true; SyncedState(f, s, this.stdout, this.processOutput) : super (f, s); void updateHeight(int h) { if (height != h) { height = h; setState(this); } } void updateConnected(bool c) { if (connected != c) { // print('update connected: connected: ${connected}, c: ${c}'); connected = c; setState(this); } } Future next() async { print("Synced next"); Future logStdout() async { await for (var line in processOutput) { if (!synced) break; // print('synced: print stdout loop'); stdout += line; print(line); } } logStdout(); Future checkSync() async { await for (var _null in refresh.pull(getNotification)) { if (await daemon.isNotSynced()) { synced = false; break; } // print('synced loop'); updateHeight(await rpc.height()); updateConnected(await daemon.isConnected()); } } await checkSync(); print('synced: loop exit'); ReSyncingState _next = ReSyncingState(setState, getNotification, stdout, processOutput); setState(_next); return _next; } } class ReSyncingState extends HookedState { String stdout; Stream processOutput; bool synced = false; ReSyncingState(f, s, this.stdout, this.processOutput) : super (f, s); void append(String msg) { this.stdout += msg; setState(this); } Future next() async { print("ReSyncing next"); Future printStdout() async { await for (var line in processOutput) { if (synced) break; // print('re-syncing: print stdout loop'); append(line); print(line); } } Future checkSync() async { await for (var _null in refresh.pull(getNotification)) { if (await daemon.isSynced()) { synced = true; break; } // print('re-syncing: checkSync loop'); } } printStdout(); await checkSync(); print('resync: await exit'); SyncedState _next = SyncedState(setState, getNotification, stdout, processOutput); _next.height = await rpc.height(); setState(_next); return _next; } }