/* 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 'package:flutter/services.dart'; import 'dart:collection'; import '../state.dart'; import '../config.dart' as config; import '../helper.dart'; import '../logging.dart'; Widget summary(BuildContext context, SyncedState state) { final height = state.height.toString(); final onFire = state.getTransactionPool.length >= 10; final onFireNotice = onFire ? ' 🔥' : ''; final poolLength = state.getTransactionPool.length; final poolLengthNotice = poolLength > 1 ? '[${poolLength}] ' : ''; final txNotice = state.getTransactionPool.isEmpty ? '' : poolLengthNotice + state.getTransactionPool.first['id_hash'].substring(0, 6) + ' ...'; return Container ( padding: EdgeInsets.only(bottom: 10.0), child: Align ( alignment: Alignment.center, child: Column ( mainAxisAlignment: MainAxisAlignment.center, children: [ Spacer ( flex: 17, ), Image.asset ('assets/wow-sonic.png', height: 220, ), Spacer ( flex: 7, ), Expanded ( flex: 15, child: Row ( children: [ Spacer(), AnimatedSwitcher ( duration: Duration(milliseconds: 500), child: Text ( height, style: Theme.of(context).textTheme.display1, key: ValueKey(state.height), ), ), AnimatedSwitcher ( duration: Duration(milliseconds: 500), child: Text ( onFireNotice, style: TextStyle ( fontSize: 25, ), key: ValueKey(onFire ? 0 : 1), ), ), Spacer(), ] ) ), AnimatedSwitcher ( duration: Duration(milliseconds: 500), child: Text ( txNotice, style: Theme.of(context).textTheme.body2, key: ValueKey(poolLength), ), ), Spacer ( flex: 1, ), SizedBox ( height: 20.0, width: 20.0, child: (state.connected) ? Container() : CircularProgressIndicator ( strokeWidth: 2, ), ), ], ), ), ); } Widget rpcView(BuildContext context, String title, String body) { return Container ( padding: const EdgeInsets.all(10.0), child: Align ( alignment: Alignment.topLeft, child: Column ( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded ( flex: 1, child: SingleChildScrollView ( scrollDirection: Axis.vertical, child: Column ( children: [ Container( height: 0, margin: const EdgeInsets.only(bottom: 15), ), Text ( title, style: Theme.of(context).textTheme.display1, ), Container( height: 1, color: Theme.of(context).primaryColor, margin: const EdgeInsets.only(bottom: 20, top: 20), ), Text ( body, style: Theme.of(context).textTheme.body2, ) ], ) ) ) ], ), ), ); } Widget getInfo(BuildContext context, SyncedState state) => rpcView(context, 'info', state.getInfoCache); Widget syncInfo(BuildContext context, SyncedState state) => rpcView(context, 'sync info', pretty(state.syncInfo)); Widget getTransactionPool(BuildContext context, SyncedState state) { final pool = state.getTransactionPool; const minimalLength = 6; final subTitle = pool.length < minimalLength ? '' : ' ‹${pool.length}›'; return rpcView(context, 'tx pool' + subTitle, state.getTransactionPoolCache); } Widget getConnections(BuildContext context, SyncedState state) { final peers = state.getConnections; const minimalLength = 6; final subTitle = peers.length < minimalLength ? '' : ' ‹${peers.length}›'; return rpcView(context, 'peers' + subTitle, state.getConnectionsCache); } Widget terminalView(BuildContext context, String title, SyncedState state) { final input = TextFormField ( controller: state.textController, textInputAction: TextInputAction.next, autofocus: true, autocorrect: false, enableSuggestions: false, keyboardType: TextInputType.visiblePassword, decoration: InputDecoration ( // border: UnderlineInputBorder // OutlineInputBorder // ( // ), // hintText: 'WOW', enabledBorder: UnderlineInputBorder ( borderSide: BorderSide ( color: Theme.of(context).primaryColor, ), ), border: InputBorder.none, ), onFieldSubmitted: (v) { String autoReplace(final String x) { final words = x.split(' '); if (words.length == 0) { return x; } final head = words.first; final tail = words.sublist(1); final guessHead = head.replaceAll('-', '_'); return [ guessHead, ...tail ].join(' '); } final _text = state.textController.text.trim(); final line = autoReplace(_text); if (line.isNotEmpty) { log.finer('terminal input: ${line}'); state.appendInput(line); state.textController.clear(); } else { state.textController.clear(); SystemChannels.textInput.invokeMethod('TextInput.hide'); } }, ); return Container ( // padding: const EdgeInsets.all(10.0), child: Align ( alignment: Alignment.topLeft, child: Column ( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded ( flex: 1, child: SingleChildScrollView ( scrollDirection: Axis.vertical, reverse: true, child: Column ( children: [ Text ( state.stdout.join('\n'), style: Theme.of(context).textTheme.body2, ) ], ) ) ), Container ( margin: const EdgeInsets.all(10.0), child: input, ), ], ), ), ); } Widget terminal(BuildContext context, SyncedState state) => terminalView(context, 'terminal', state); Widget pageView (BuildContext context, SyncedState state) { void _onPageChanged(int pageIndex) { if (pageIndex != 0) { SystemChannels.textInput.invokeMethod('TextInput.hide'); } state.onPageChanged(pageIndex); } return PageView ( controller: state.pageController, onPageChanged: _onPageChanged, children: [ terminal(context, state), summary(context, state), getTransactionPool(context, state), getConnections(context, state), getInfo(context, state), // syncInfo(state), ], ); } Widget build(BuildContext context, SyncedState state) { return Scaffold ( body: pageView(context, state) ); }