diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9e80e7a8..a77f535b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,7 +14,7 @@ + android:icon="@mipmap/ic_launcher"> fetchClientsOverTime( PiholeSettings settings) async { - // TODO implement - throw UnimplementedError(); + return _overTimeDataClientsFromJson(); } @override @@ -236,9 +235,9 @@ class ApiDataSourceDev implements ApiDataSource { return TopSourcesResult( topSources: { PiClient(name: 'localhost', ip: '127.0.0.1'): 3204, - PiClient(name: 'openelec', ip: '10.0.1.2'): 324, - PiClient(ip: '10.0.1.3'): 216, - PiClient(name: 'laptop', ip: '10.0.1.4'): 96, + PiClient(name: 'openelec', ip: '127.0.1.2'): 324, + PiClient(ip: '127.0.1.3'): 216, + PiClient(name: 'laptop', ip: '127.0.1.4'): 96, }, ); } @@ -359,7 +358,7 @@ OverTimeData _overTimeDataFromJson() { "1588227900": 250, "1588228500": 40, "1588229100": 745, - "1588229700": 1151, + "1588229700": 851, "1588230300": 210, "1588230900": 229, "1588231500": 243, @@ -501,17 +500,17 @@ OverTimeData _overTimeDataFromJson() { "1588227300": 0, "1588227900": 0, "1588228500": 0, - "1588229100": 0, - "1588229700": 0, - "1588230300": 0, - "1588230900": 0, - "1588231500": 0, - "1588232100": 0, - "1588232700": 0, - "1588233300": 0, - "1588233900": 0, - "1588234500": 0, - "1588235100": 0, + "1588229100": 400, + "1588229700": 550, + "1588230300": 561, + "1588230900": 212, + "1588231500": 218, + "1588232100": 56, + "1588232700": 123, + "1588233300": 145, + "1588233900": 156, + "1588234500": 130, + "1588235100": 53, "1588235700": 0, "1588236300": 0, "1588236900": 0, @@ -595,3 +594,165 @@ OverTimeData _overTimeDataFromJson() { return OverTimeData.fromJson(json); } + +OverTimeDataClients _overTimeDataClientsFromJson() { + final Map json = { + "clients": [ + {"name": "", "ip": "127.0.1.12"}, + {"name": "", "ip": "127.0.1.2"}, + {"name": "hello", "ip": "127.0.1.15"}, + {"name": "", "ip": "127.0.1.1"}, + {"name": "", "ip": "127.0.1.17"}, + {"name": "localhost", "ip": "127.0.0.1"}, + {"name": "", "ip": "1.2.3.4"}, + {"name": "", "ip": "127.0.1.7"}, + {"name": "", "ip": "127.0.1.18"}, + {"name": "", "ip": "127.0.1.6"}, + {"name": "", "ip": "127.0.1.9"}, + {"name": "and finally", "ip": "127.0.1.13"} + ], + "over_time": { + "1590084300": [9, 90, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0], + "1590084900": [96, 73, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0], + "1590085500": [30, 49, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0], + "1590086100": [19, 58, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0], + "1590086700": [86, 36, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0], + "1590087300": [51, 21, 0, 4, 0, 0, 0, 0, 42, 0, 0, 0], + "1590087900": [12, 8, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0], + "1590088500": [79, 37, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0], + "1590089100": [141, 124, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0], + "1590089700": [27, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + "1590090300": [92, 38, 0, 0, 0, 0, 3, 0, 6, 0, 0, 0], + "1590090900": [94, 31, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0], + "1590091500": [52, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590092100": [61, 39, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0], + "1590092700": [50, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590093300": [58, 34, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0], + "1590093900": [34, 53, 0, 0, 0, 0, 1, 0, 17, 0, 0, 0], + "1590094500": [63, 89, 0, 4, 0, 0, 0, 0, 18, 0, 0, 0], + "1590095100": [64, 50, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0], + "1590095700": [32, 84, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0], + "1590096300": [58, 74, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0], + "1590096900": [45, 42, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0], + "1590097500": [51, 50, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0], + "1590098100": [81, 7, 0, 4, 0, 0, 0, 0, 6, 0, 0, 0], + "1590098700": [38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590099300": [37, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0], + "1590099900": [1, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0], + "1590100500": [5, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0], + "1590101100": [1, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0], + "1590101700": [110, 0, 0, 4, 0, 0, 0, 0, 16, 0, 0, 0], + "1590102300": [119, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0], + "1590102900": [25, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0], + "1590103500": [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590104100": [3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0], + "1590104700": [3, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0], + "1590105300": [24, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0], + "1590105900": [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590106500": [21, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + "1590107100": [0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0], + "1590107700": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590108300": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590108900": [0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0], + "1590109500": [0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0], + "1590110100": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590110700": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590111300": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590111900": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590112500": [0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0], + "1590113100": [0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0], + "1590113700": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590114300": [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + "1590114900": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590115500": [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + "1590116100": [0, 0, 0, 4, 0, 0, 1, 0, 1, 0, 0, 0], + "1590116700": [0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0], + "1590117300": [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + "1590117900": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590118500": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590119100": [15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590119700": [2, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0], + "1590120300": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590120900": [1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0], + "1590121500": [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590122100": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590122700": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590123300": [0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0], + "1590123900": [5, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + "1590124500": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590125100": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590125700": [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + "1590126300": [0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0], + "1590126900": [4, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0], + "1590127500": [2, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0], + "1590128100": [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590128700": [15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590129300": [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590129900": [0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0], + "1590130500": [2, 0, 0, 4, 0, 0, 0, 0, 54, 0, 0, 0], + "1590131100": [1, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0], + "1590131700": [3, 0, 0, 0, 0, 0, 0, 0, 84, 2, 0, 0], + "1590132300": [3, 57, 0, 0, 0, 0, 2, 0, 19, 0, 0, 0], + "1590132900": [88, 93, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0], + "1590133500": [62, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590134100": [48, 19, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0], + "1590134700": [72, 70, 0, 0, 0, 26, 1, 0, 1, 0, 0, 0], + "1590135300": [74, 24, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0], + "1590135900": [82, 31, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0], + "1590136500": [28, 14, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0], + "1590137100": [15, 23, 0, 0, 0, 0, 4, 0, 6, 0, 0, 0], + "1590137700": [5, 8, 0, 4, 0, 0, 0, 0, 6, 0, 0, 0], + "1590138300": [30, 8, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0], + "1590138900": [19, 14, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0], + "1590139500": [75, 34, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0], + "1590140100": [82, 17, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0], + "1590140700": [29, 13, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0], + "1590141300": [3, 12, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0], + "1590141900": [5, 10, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0], + "1590142500": [16, 7, 0, 0, 0, 0, 0, 0, 86, 0, 0, 0], + "1590143100": [7, 18, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0], + "1590143700": [23, 3, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0], + "1590144300": [48, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0], + "1590144900": [30, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0], + "1590145500": [88, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0], + "1590146100": [56, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0], + "1590146700": [69, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0], + "1590147300": [34, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0], + "1590147900": [25, 0, 0, 0, 0, 0, 1, 0, 53, 0, 0, 0], + "1590148500": [99, 0, 0, 4, 0, 0, 0, 0, 35, 0, 0, 0], + "1590149100": [47, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0], + "1590149700": [10, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0], + "1590150300": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590150900": [32, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0], + "1590151500": [0, 81, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0], + "1590152100": [0, 62, 0, 4, 0, 0, 0, 0, 9, 0, 0, 0], + "1590152700": [0, 14, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0], + "1590153300": [0, 38, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0], + "1590153900": [0, 41, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0], + "1590154500": [0, 31, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0], + "1590155100": [0, 15, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0], + "1590155700": [0, 28, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0], + "1590156300": [0, 13, 0, 0, 0, 0, 5, 0, 11, 0, 0, 0], + "1590156900": [0, 12, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0], + "1590157500": [0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + "1590158100": [0, 23, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0], + "1590158700": [0, 17, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0], + "1590159300": [0, 38, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0], + "1590159900": [0, 9, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0], + "1590160500": [0, 22, 0, 0, 0, 6, 0, 0, 2, 0, 0, 0], + "1590161100": [0, 16, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + "1590161700": [0, 9, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0], + "1590162300": [0, 16, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0], + "1590162900": [0, 18, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0], + "1590163500": [0, 7, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0], + "1590164100": [0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590164700": [0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "1590165300": [0, 7, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + "1590165900": [0, 12, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0], + "1590166500": [0, 28, 0, 4, 0, 0, 0, 0, 6, 0, 0, 0], + "1590167100": [0, 20, 0, 0, 0, 0, 2, 0, 5, 0, 0, 0] + } + }; + + return OverTimeDataClients.fromJson(json); +} diff --git a/lib/features/home/presentation/pages/summary/widgets/clients_over_day_tile.dart b/lib/features/home/presentation/pages/summary/widgets/clients_over_day_tile.dart index 4b36e220..6afbb9ee 100644 --- a/lib/features/home/presentation/pages/summary/widgets/clients_over_day_tile.dart +++ b/lib/features/home/presentation/pages/summary/widgets/clients_over_day_tile.dart @@ -18,7 +18,7 @@ class ClientsOverDayTile extends StatelessWidget { Widget build(BuildContext context) { return Card( child: LineChartScaffold( - title: 'Total queries over last 24 hours', + title: 'Client activity over last 24 hours', lineChart: clientsOverTimeResult.fold( (failure) => CenteredFailureIndicator(failure), (overTimeData) { diff --git a/lib/features/home/presentation/pages/summary/widgets/graph_legend_item.dart b/lib/features/home/presentation/pages/summary/widgets/graph_legend_item.dart index 9eeab515..985062a3 100644 --- a/lib/features/home/presentation/pages/summary/widgets/graph_legend_item.dart +++ b/lib/features/home/presentation/pages/summary/widgets/graph_legend_item.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutterhole/features/home/presentation/pages/summary/notifiers/pie_chart_notifier.dart'; import 'package:provider/provider.dart'; diff --git a/lib/features/routing/presentation/pages/about_page.dart b/lib/features/routing/presentation/pages/about_page.dart index 0e6f06cd..4f5f292b 100644 --- a/lib/features/routing/presentation/pages/about_page.dart +++ b/lib/features/routing/presentation/pages/about_page.dart @@ -7,10 +7,183 @@ import 'package:flutterhole/features/routing/presentation/pages/privacy_page.dar import 'package:flutterhole/features/settings/services/package_info_service.dart'; import 'package:flutterhole/widgets/layout/animations/animated_opener.dart'; import 'package:flutterhole/widgets/layout/lists/list_title.dart'; +import 'package:flutterhole/widgets/layout/lists/open_url_tile.dart'; import 'package:flutterhole/widgets/layout/notifications/dialogs.dart'; import 'package:package_info/package_info.dart'; import 'package:share/share.dart'; +const Color _jpegBackgroundColor = Colors.white; +final Color _iconColor = Colors.green.shade900; + +const int _subTileCount = 2; + +class _Spacer extends StatelessWidget { + const _Spacer({ + @required this.width, + @required this.color, + Key key, + }) : super(key: key); + + final double width; + final Color color; + + @override + Widget build(BuildContext context) { + return Container( + width: width, + decoration: BoxDecoration( + color: color, + ), + ); + } +} + +class LogoInspector extends StatefulWidget { + const LogoInspector({ + Key key, + @required this.screenWidth, + }) : super(key: key); + + final double screenWidth; + + @override + _LogoInspectorState createState() => _LogoInspectorState(); +} + +class _LogoInspectorState extends State { + Color _color; + ScrollController _scrollController; + + @override + void initState() { + super.initState(); + _color = _iconColor; + + _scrollController = ScrollController( + initialScrollOffset: widget.screenWidth * 3, + ); + + _scrollController.addListener(() { + final offset = _scrollController.offset; + + final pageOffset = (offset / widget.screenWidth) - 3; + + if (pageOffset <= -1.5) { + setState(() { + _color = ThemeData.dark().cardColor; + }); + } else if (pageOffset >= -0.2 && pageOffset <= 0.2) { + setState(() { + _color = _iconColor; + }); + } else if (pageOffset >= 3) { + setState(() { + _color = Colors.purple; + }); + } + }); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + List _buildSpacers(double screenWidth) { + return [ + _Spacer( + width: screenWidth / _subTileCount, + color: _jpegBackgroundColor.withOpacity(.2), + ), + _Spacer( + width: screenWidth / _subTileCount, + color: _jpegBackgroundColor.withOpacity(.4), + ), + _Spacer( + width: screenWidth / _subTileCount, + color: _jpegBackgroundColor.withOpacity(.6), + ), + _Spacer( + width: screenWidth / _subTileCount, + color: _jpegBackgroundColor.withOpacity(.8), + ), + ]; + } + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + duration: Duration(seconds: 1), + decoration: BoxDecoration( + // TODO perhaps not white if we make this image a png + color: _color, + ), + curve: Curves.easeInOut, + width: widget.screenWidth * 4, + child: ListView( + scrollDirection: Axis.horizontal, + controller: _scrollController, + physics: BouncingScrollPhysics(), + children: [ + Container( + width: widget.screenWidth, + child: Stack( + children: [ + Center( + child: Image( + image: AssetImage('assets/icon/old_icon.png'), + width: widget.screenWidth / 2, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: ListTile( + leading: Icon(KIcons.info), + title: Text('Original logo design'), + ), + ), + ], + ), + ), + ..._buildSpacers(widget.screenWidth / 2), + ..._buildSpacers(widget.screenWidth / 2).reversed.toList(), + Container( + width: widget.screenWidth, + child: Stack( + children: [ + Center( + child: Image( + image: AssetImage('assets/icon/logo.png'), + width: widget.screenWidth / 2, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: OpenUrlTile( + url: KStrings.logoDesignerUrl, + leading: Icon(KIcons.info), + title: Text('Logo design by Mathijs Sterrenburg'), + ), + ), + ], + ), + ), + ..._buildSpacers(widget.screenWidth), + Container( + decoration: BoxDecoration(color: Colors.white), + child: Image( + image: AssetImage('assets/images/logos.jpeg'), + width: widget.screenWidth * 2, + ), + ), + ..._buildSpacers(widget.screenWidth / 2).reversed.toList(), + ], + ), + ); + } +} + class AboutPage extends StatelessWidget { @override Widget build(BuildContext context) { @@ -29,16 +202,36 @@ class AboutPage extends StatelessWidget { children: [ ListTile( contentPadding: EdgeInsets.all(16), - title: Row( - children: [ - Text('${packageInfo?.appName}', - style: Theme.of(context).textTheme.headline4), - ], - ), + title: Text('${packageInfo?.appName}', + style: Theme + .of(context) + .textTheme + .headline4), subtitle: Text( 'Made by Sterrenburg', style: Theme.of(context).textTheme.caption, ), + trailing: SizedBox( + width: 56, + child: Ink.image( + image: AssetImage('assets/icon/icon.png'), + child: InkWell( + onTap: () { + showModalBottomSheet( + context: context, + builder: (sheetContext) { + final screenWidth = + MediaQuery + .of(context) + .size + .width; + + return LogoInspector(screenWidth: screenWidth); + }); + }, + ), + ), + ), ), ListTile( leading: Icon( diff --git a/lib/features/routing/presentation/widgets/default_drawer.dart b/lib/features/routing/presentation/widgets/default_drawer.dart index 7aea81d9..d311c768 100644 --- a/lib/features/routing/presentation/widgets/default_drawer.dart +++ b/lib/features/routing/presentation/widgets/default_drawer.dart @@ -79,19 +79,24 @@ class _Footer extends StatelessWidget { final String footerMessage = getIt().footerMessage; final PackageInfo packageInfo = getIt().packageInfo; + final textStyle = Theme.of(context).textTheme.caption; + return ListTile( title: Text( '${packageInfo.appName} ${packageInfo.versionAndBuildString}', - style: Theme.of(context).textTheme.caption, + style: textStyle, + ), + leading: Image( + image: AssetImage('assets/icon/logo.png'), + width: 50, + height: 50, + color: textStyle.color, ), subtitle: footerMessage.isEmpty ? null : Text( '$footerMessage', - style: Theme - .of(context) - .textTheme - .caption, + style: textStyle, ), onLongPress: () { showAppDetailsDialog(context, packageInfo); diff --git a/lib/features/settings/data/datasources/settings_data_source_hive.dart b/lib/features/settings/data/datasources/settings_data_source_hive.dart index 6903e8ec..0df5e66c 100644 --- a/lib/features/settings/data/datasources/settings_data_source_hive.dart +++ b/lib/features/settings/data/datasources/settings_data_source_hive.dart @@ -128,7 +128,7 @@ class SettingsDataSourceHive implements SettingsDataSource { final box = await _piholeBox; final index = await _activeIndex; - if (index < 0) { + if (index < 0 || box.isEmpty) { print('no active pihole found in storage, returning default'); return PiholeSettings(); } diff --git a/lib/features/settings/presentation/pages/add_pihole_page.dart b/lib/features/settings/presentation/pages/add_pihole_page.dart index 7453ec27..6c06a506 100644 --- a/lib/features/settings/presentation/pages/add_pihole_page.dart +++ b/lib/features/settings/presentation/pages/add_pihole_page.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; diff --git a/lib/features/settings/presentation/widgets/pihole_theme_builder.dart b/lib/features/settings/presentation/widgets/pihole_theme_builder.dart index b3e7d3f5..caa3741a 100644 --- a/lib/features/settings/presentation/widgets/pihole_theme_builder.dart +++ b/lib/features/settings/presentation/widgets/pihole_theme_builder.dart @@ -5,7 +5,7 @@ import 'package:flutterhole/features/settings/presentation/widgets/settings_bloc // Color to MaterialColor: // https://medium.com/py-bits/turn-any-color-to-material-color-for-flutter-d8e8e037a837 -Map _materialColorMap = { +Map materialColorMap = { 50: Color.fromRGBO(136, 14, 79, .1), 100: Color.fromRGBO(136, 14, 79, .2), 200: Color.fromRGBO(136, 14, 79, .3), @@ -56,7 +56,7 @@ class PiholeThemeBuilder extends StatelessWidget { ThemeData _buildTheme(BuildContext context, PiholeSettings settings) { final MaterialColor color = MaterialColor( settings.primaryColor.value, - _materialColorMap, + materialColorMap, ); return Theme.of(context).copyWith( diff --git a/lib/features/settings/services/package_info_service_impl.dart b/lib/features/settings/services/package_info_service_impl.dart index 5c59d61b..8d707845 100644 --- a/lib/features/settings/services/package_info_service_impl.dart +++ b/lib/features/settings/services/package_info_service_impl.dart @@ -2,7 +2,6 @@ import 'package:flutterhole/features/settings/services/package_info_service.dart import 'package:injectable/injectable.dart'; import 'package:package_info/package_info.dart'; -@prod @singleton @RegisterAs(PackageInfoService) class PackageInfoServiceImpl implements PackageInfoService { diff --git a/lib/main.dart b/lib/main.dart index 8bfc6fc1..f2283ebf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,7 +10,7 @@ import 'package:flutterhole/features/settings/presentation/notifiers/theme_mode_ import 'package:injectable/injectable.dart'; import 'package:provider/provider.dart'; -void main([List arguments = const [Environment.prod]]) { +void main([List arguments = const []]) { // wait for flutter initialization WidgetsFlutterBinding.ensureInitialized(); @@ -18,6 +18,7 @@ void main([List arguments = const [Environment.prod]]) { // Configure service injection List args = List.from(arguments) ?? []; + if (args.isEmpty) args.add(Environment.prod); configure(args.first).then((_) { if (foundation.kReleaseMode) { diff --git a/lib/widgets/layout/notifications/dialogs.dart b/lib/widgets/layout/notifications/dialogs.dart index afd91b1b..d4886e5c 100644 --- a/lib/widgets/layout/notifications/dialogs.dart +++ b/lib/widgets/layout/notifications/dialogs.dart @@ -130,8 +130,7 @@ void showAppDetailsDialog(BuildContext context, PackageInfo packageInfo) { 'can view the code that runs your app. ' 'You can find the repository on '), TextSpan( - style: Theme - .of(context) + style: Theme.of(context) .textTheme .bodyText2 .apply(color: KColors.link), @@ -140,10 +139,42 @@ void showAppDetailsDialog(BuildContext context, PackageInfo packageInfo) { ..onTap = () => getIt().launchUrl(KStrings.githubHomeUrl), ), - TextSpan(text: '.'), + TextSpan( + text: '.' + '\n\n' + 'Logo design by '), + TextSpan( + style: Theme + .of(context) + .textTheme + .bodyText2 + .apply(color: KColors.link), + text: 'Mathijs Sterrenburg', + recognizer: TapGestureRecognizer() + ..onTap = () => + getIt().launchUrl(KStrings.logoDesignerUrl), + ), + TextSpan(text: ', an amazing designer.'), ], ), ), ], ); } + +class ImageDialog extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Dialog( + child: Container( +// width: 200, +// height: 200, + decoration: BoxDecoration( + image: DecorationImage( + image: ExactAssetImage('assets/icon/icon.png'), + fit: BoxFit.cover, + )), + ), + ); + } +} diff --git a/lib/widgets/layout/notifications/snackbars.dart b/lib/widgets/layout/notifications/snackbars.dart index 0b209f7f..4411c437 100644 --- a/lib/widgets/layout/notifications/snackbars.dart +++ b/lib/widgets/layout/notifications/snackbars.dart @@ -1,5 +1,4 @@ import 'package:flushbar/flushbar.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutterhole/constants.dart'; @@ -7,8 +6,8 @@ void showInfoSnackBar( BuildContext context, String message, { String title, - Duration duration = const Duration(seconds: 5), - }) { + Duration duration = const Duration(seconds: 5), +}) { Flushbar( title: title, message: message ?? 'Message', diff --git a/pubspec.yaml b/pubspec.yaml index dc3c079a..9fe1316a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -91,12 +91,15 @@ dev_dependencies: # icon generation flutter_launcher_icons: ^0.7.5 # flutter_driver extras - ozzie: ^2.2.3 + # TODO fixed version until this pull merges: + # https://github.com/bmw-tech/ozzie.flutter/pull/49/commits/88685857e966ea6743fac3134c50b071f740dd67 + ozzie: 2.2.3 flutter: uses-material-design: true -flutter_icons: - android: "launcher_icon" - ios: true - image_path: "assets/icon/icon.png" \ No newline at end of file + assets: + - assets/icon/icon.png + - assets/icon/old_icon.png + - assets/icon/logo.png + - assets/images/logos.jpeg \ No newline at end of file diff --git a/release_notes.txt b/release_notes.txt index 27cb3452..e01a3091 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -1,9 +1,21 @@ -* Fix: Pi-holes without a password and API token can now toggle off the API token requirement. +* Adding new logo with an easter egg 🥚 -* Feature: the queries/clients over time charts are back. +* Adding apiTokenRequired field, fixes #79 -* Feature: errors are now more verbose. +* Remove index shift from QueryStatus toJson/fromJson, fixes #82 -* Feature: query logs are searchable and refreshable. +* Adding query log page -* Feature: the query log is back. I intend to make some kind of live query viewer separately, this page does _not_ automatically listen for new data. \ No newline at end of file +* Adding queries over time chart + +* Adding clients over time chart + +* Searchable & refreshable query log/client/domain pages + +* Status indicators in Pi-hole forms can be tapped to show error details + +* More verbose error logging + +* Adding drawer footer and better about dialog + +* Adding DoubleBackToClose \ No newline at end of file diff --git a/test_driver/app_test.dart b/test_driver/app_test.dart index 67ef200f..b72ecc85 100644 --- a/test_driver/app_test.dart +++ b/test_driver/app_test.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter_driver/flutter_driver.dart'; import 'package:ozzie/ozzie.dart'; import 'package:test/test.dart'; @@ -42,14 +40,14 @@ main() { await driver.scrollIntoView(find.byType('TotalQueriesOverDayTile')); + await driver.scrollIntoView(find.byType('ClientsOverDayTile')); + await ozzie.takeScreenshot('summary_middle_1'); await driver.scrollIntoView(find.byType('QueryTypesTile')); await driver.waitFor(find.text('AAAA (IPv6)')); - await ozzie.takeScreenshot('summary_middle_2'); - await driver.scrollIntoView(find.byType('ForwardDestinationsTile')); await ozzie.takeScreenshot('summary_bottom'); @@ -59,15 +57,21 @@ main() { await ozzie.takeScreenshot('clients_all'); - await driver.tap(find.text('10.0.1.2 (openelec)')); + await driver.tap(find.text('127.0.1.2 (openelec)')); await ozzie.takeScreenshot('client_single'); await driver.tap(find.byTooltip('Back')); + await driver.tap(find.byTooltip('Open navigation menu')); + + await ozzie.takeScreenshot('navigation_menu'); + + await driver.tap(find.pageBack()); + print('done with screenshots'); - exit(0); +// exit(0); }); }); }