diff --git a/frontend/lib/components/api/APIBuilder.dart b/frontend/lib/components/api/APIBuilder.dart index 0375bc1..22d0426 100644 --- a/frontend/lib/components/api/APIBuilder.dart +++ b/frontend/lib/components/api/APIBuilder.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../../providers/BaseProvider.dart'; +import '../../providers/api/BaseProvider.dart'; class APIBuilder extends StatefulWidget { const APIBuilder( diff --git a/frontend/lib/components/build_output.dart b/frontend/lib/components/build_output.dart index 237de48..4d514cf 100644 --- a/frontend/lib/components/build_output.dart +++ b/frontend/lib/components/build_output.dart @@ -2,9 +2,11 @@ import 'dart:async'; import 'package:aurcache/api/builds.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import '../api/API.dart'; import '../models/build.dart'; +import '../providers/build_log_provider.dart'; class BuildOutput extends StatefulWidget { const BuildOutput({super.key, required this.build}); @@ -20,14 +22,15 @@ class _BuildOutputState extends State { String output = ""; Timer? outputTimer; - final scrollController = ScrollController(); @override Widget build(BuildContext context) { + final sc = + Provider.of(context, listen: false).scrollController; return Expanded( flex: 1, child: SingleChildScrollView( - controller: scrollController, + controller: sc, scrollDirection: Axis.vertical, //.horizontal child: Padding( padding: const EdgeInsets.only(left: 30, right: 15), @@ -57,38 +60,28 @@ class _BuildOutputState extends State { setState(() { output = value; }); - _scrollToBottom(); + Provider.of(context, listen: false).go_to_bottom(); }); // poll new output only if not finished if (widget.build.status == 0) { outputTimer = Timer.periodic(const Duration(seconds: 3), (Timer t) async { - print("refreshing output"); - final value = await API.getOutput( - buildID: widget.build.id, line: output.split("\n").length); - setState(() { - output += value; - }); + final follow = + Provider.of(context, listen: false).followLog; + if (follow) { + print("refreshing output"); + final value = await API.getOutput( + buildID: widget.build.id, line: output.split("\n").length); + setState(() { + output += value; + }); - _scrollToBottom(); + Provider.of(context, listen: false).go_to_bottom(); + } }); } } - void _scrollToBottom() { - WidgetsBinding.instance.addPostFrameCallback((_) { - // scroll to bottom - final scrollPosition = scrollController.position; - if (scrollPosition.viewportDimension < scrollPosition.maxScrollExtent) { - scrollController.animateTo( - scrollPosition.maxScrollExtent, - duration: const Duration(milliseconds: 200), - curve: Curves.easeOut, - ); - } - }); - } - @override void dispose() { super.dispose(); diff --git a/frontend/lib/components/dashboard/recent_builds.dart b/frontend/lib/components/dashboard/recent_builds.dart index 07dc1ef..3c4bdc3 100644 --- a/frontend/lib/components/dashboard/recent_builds.dart +++ b/frontend/lib/components/dashboard/recent_builds.dart @@ -1,7 +1,7 @@ import 'package:aurcache/components/builds_table.dart'; import 'package:aurcache/models/build.dart'; import 'package:aurcache/components/api/APIBuilder.dart'; -import 'package:aurcache/providers/builds_provider.dart'; +import 'package:aurcache/providers/api/builds_provider.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../../constants/color_constants.dart'; diff --git a/frontend/lib/components/dashboard/your_packages.dart b/frontend/lib/components/dashboard/your_packages.dart index 6dd0251..cc11b1a 100644 --- a/frontend/lib/components/dashboard/your_packages.dart +++ b/frontend/lib/components/dashboard/your_packages.dart @@ -1,6 +1,6 @@ import 'package:aurcache/components/api/APIBuilder.dart'; import 'package:aurcache/components/packages_table.dart'; -import 'package:aurcache/providers/packages_provider.dart'; +import 'package:aurcache/providers/api/packages_provider.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../../constants/color_constants.dart'; diff --git a/frontend/lib/components/packages_table.dart b/frontend/lib/components/packages_table.dart index a901049..34fae93 100644 --- a/frontend/lib/components/packages_table.dart +++ b/frontend/lib/components/packages_table.dart @@ -6,9 +6,9 @@ import 'package:provider/provider.dart'; import '../api/API.dart'; import '../constants/color_constants.dart'; import '../models/package.dart'; -import '../providers/builds_provider.dart'; -import '../providers/packages_provider.dart'; -import '../providers/stats_provider.dart'; +import '../providers/api/builds_provider.dart'; +import '../providers/api/packages_provider.dart'; +import '../providers/api/stats_provider.dart'; import 'confirm_popup.dart'; import 'dashboard/your_packages.dart'; diff --git a/frontend/lib/providers/BaseProvider.dart b/frontend/lib/providers/api/BaseProvider.dart similarity index 100% rename from frontend/lib/providers/BaseProvider.dart rename to frontend/lib/providers/api/BaseProvider.dart diff --git a/frontend/lib/providers/aur_search_provider.dart b/frontend/lib/providers/api/aur_search_provider.dart similarity index 93% rename from frontend/lib/providers/aur_search_provider.dart rename to frontend/lib/providers/api/aur_search_provider.dart index da8a87e..d36e871 100644 --- a/frontend/lib/providers/aur_search_provider.dart +++ b/frontend/lib/providers/api/aur_search_provider.dart @@ -1,7 +1,7 @@ import 'package:aurcache/api/aur.dart'; import 'package:aurcache/models/aur_package.dart'; -import '../api/API.dart'; +import '../../api/API.dart'; import 'BaseProvider.dart'; class AurSearchDTO { diff --git a/frontend/lib/providers/build_provider.dart b/frontend/lib/providers/api/build_provider.dart similarity index 85% rename from frontend/lib/providers/build_provider.dart rename to frontend/lib/providers/api/build_provider.dart index 6800bf5..e2946d9 100644 --- a/frontend/lib/providers/build_provider.dart +++ b/frontend/lib/providers/api/build_provider.dart @@ -1,7 +1,7 @@ import 'package:aurcache/api/builds.dart'; -import '../api/API.dart'; -import '../models/build.dart'; +import '../../api/API.dart'; +import '../../models/build.dart'; import 'BaseProvider.dart'; class BuildDTO { diff --git a/frontend/lib/providers/builds_provider.dart b/frontend/lib/providers/api/builds_provider.dart similarity index 87% rename from frontend/lib/providers/builds_provider.dart rename to frontend/lib/providers/api/builds_provider.dart index 7d7cd49..4a9ca07 100644 --- a/frontend/lib/providers/builds_provider.dart +++ b/frontend/lib/providers/api/builds_provider.dart @@ -1,7 +1,7 @@ import 'package:aurcache/api/builds.dart'; -import '../api/API.dart'; -import '../models/build.dart'; +import '../../api/API.dart'; +import '../../models/build.dart'; import 'BaseProvider.dart'; class BuildsDTO { diff --git a/frontend/lib/providers/package_provider.dart b/frontend/lib/providers/api/package_provider.dart similarity index 85% rename from frontend/lib/providers/package_provider.dart rename to frontend/lib/providers/api/package_provider.dart index d78892d..6aaf29a 100644 --- a/frontend/lib/providers/package_provider.dart +++ b/frontend/lib/providers/api/package_provider.dart @@ -1,7 +1,7 @@ import 'package:aurcache/api/packages.dart'; -import '../api/API.dart'; -import '../models/package.dart'; +import '../../api/API.dart'; +import '../../models/package.dart'; import 'BaseProvider.dart'; class PackageDTO { diff --git a/frontend/lib/providers/packages_provider.dart b/frontend/lib/providers/api/packages_provider.dart similarity index 72% rename from frontend/lib/providers/packages_provider.dart rename to frontend/lib/providers/api/packages_provider.dart index ac54229..4dee760 100644 --- a/frontend/lib/providers/packages_provider.dart +++ b/frontend/lib/providers/api/packages_provider.dart @@ -1,8 +1,8 @@ import 'package:aurcache/api/packages.dart'; -import 'package:aurcache/providers/BaseProvider.dart'; +import 'package:aurcache/providers/api/BaseProvider.dart'; -import '../api/API.dart'; -import '../models/package.dart'; +import '../../api/API.dart'; +import '../../models/package.dart'; class PackagesDTO { final int limit; diff --git a/frontend/lib/providers/stats_provider.dart b/frontend/lib/providers/api/stats_provider.dart similarity index 88% rename from frontend/lib/providers/stats_provider.dart rename to frontend/lib/providers/api/stats_provider.dart index 203ea7d..64f8a9e 100644 --- a/frontend/lib/providers/stats_provider.dart +++ b/frontend/lib/providers/api/stats_provider.dart @@ -1,6 +1,6 @@ import 'package:aurcache/api/statistics.dart'; -import '../api/API.dart'; +import '../../api/API.dart'; import 'BaseProvider.dart'; class StatsProvider extends BaseProvider { diff --git a/frontend/lib/providers/build_log_provider.dart b/frontend/lib/providers/build_log_provider.dart new file mode 100644 index 0000000..cb94f57 --- /dev/null +++ b/frontend/lib/providers/build_log_provider.dart @@ -0,0 +1,39 @@ +import 'package:flutter/widgets.dart'; + +class BuildLogProvider with ChangeNotifier { + final scrollController = ScrollController(); + + bool followLog = true; + + set follow_log(bool value) { + followLog = value; + notifyListeners(); + } + + void go_to_bottom() { + _scrollToBottom(); + } + + void go_to_top() { + final scrollPosition = scrollController.position; + scrollController.animateTo( + scrollPosition.minScrollExtent, + duration: const Duration(milliseconds: 200), + curve: Curves.easeOut, + ); + } + + void _scrollToBottom() { + WidgetsBinding.instance.addPostFrameCallback((_) { + // scroll to bottom + final scrollPosition = scrollController.position; + if (scrollPosition.viewportDimension < scrollPosition.maxScrollExtent) { + scrollController.animateTo( + scrollPosition.maxScrollExtent, + duration: const Duration(milliseconds: 200), + curve: Curves.easeOut, + ); + } + }); + } +} diff --git a/frontend/lib/screens/aur_screen.dart b/frontend/lib/screens/aur_screen.dart index 3ed71cc..cc94821 100644 --- a/frontend/lib/screens/aur_screen.dart +++ b/frontend/lib/screens/aur_screen.dart @@ -2,13 +2,13 @@ import 'dart:async'; import 'package:aurcache/components/aur_search_table.dart'; import 'package:aurcache/models/aur_package.dart'; -import 'package:aurcache/providers/aur_search_provider.dart'; +import 'package:aurcache/providers/api/aur_search_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../components/api/APIBuilder.dart'; import '../constants/color_constants.dart'; -import '../providers/packages_provider.dart'; +import '../providers/api/packages_provider.dart'; class AurScreen extends StatefulWidget { const AurScreen({super.key, this.initalQuery}); diff --git a/frontend/lib/screens/build_screen.dart b/frontend/lib/screens/build_screen.dart index 6750a8b..9b4e6ba 100644 --- a/frontend/lib/screens/build_screen.dart +++ b/frontend/lib/screens/build_screen.dart @@ -1,7 +1,7 @@ import 'package:aurcache/components/build_output.dart'; import 'package:aurcache/models/build.dart'; import 'package:aurcache/components/api/APIBuilder.dart'; -import 'package:aurcache/providers/build_provider.dart'; +import 'package:aurcache/providers/build_log_provider.dart'; import 'package:aurcache/utils/time_formatter.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -11,6 +11,7 @@ import '../components/confirm_popup.dart'; import '../components/dashboard/chart_card.dart'; import '../components/dashboard/your_packages.dart'; import '../constants/color_constants.dart'; +import '../providers/api/build_provider.dart'; class BuildScreen extends StatefulWidget { const BuildScreen({super.key, required this.buildID}); @@ -22,44 +23,50 @@ class BuildScreen extends StatefulWidget { } class _BuildScreenState extends State { + bool scrollFollowActive = true; + @override Widget build(BuildContext context) { return Scaffold( body: MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => BuildProvider()), + ChangeNotifierProvider( + create: (_) => BuildLogProvider()), ], - child: APIBuilder( - key: const Key("Build on seperate page"), - dto: BuildDTO(buildID: widget.buildID), - interval: const Duration(seconds: 10), - onLoad: () => const Text("loading"), - onData: (buildData) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - _buildTopBar(buildData), - const SizedBox( - height: 15, - ), - _buildPage(buildData) - ], + builder: (context, child) { + return APIBuilder( + key: const Key("Build on seperate page"), + dto: BuildDTO(buildID: widget.buildID), + interval: const Duration(seconds: 10), + onLoad: () => const Text("loading"), + onData: (buildData) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _buildTopBar(buildData, context), + const SizedBox( + height: 15, + ), + _buildPage(buildData) + ], + ), ), - ), - _buildSideBar(), - ], - ); - }), + _buildSideBar(), + ], + ); + }); + }, ), ); } - Widget _buildTopBar(Build buildData) { + Widget _buildTopBar(Build buildData, BuildContext context) { final start_time = DateTime.fromMillisecondsSinceEpoch((buildData.start_time ?? 0) * 1000); @@ -113,18 +120,32 @@ class _BuildScreenState extends State { Row( children: [ IconButton( - onPressed: () {}, + onPressed: () { + setState(() { + scrollFollowActive = !scrollFollowActive; + Provider.of(context, listen: false) + .followLog = scrollFollowActive; + }); + }, + isSelected: scrollFollowActive, icon: const Icon(Icons.read_more), + selectedIcon: const Icon(Icons.read_more), tooltip: "Follow log", ), IconButton( - onPressed: () {}, + onPressed: () { + Provider.of(context, listen: false) + .go_to_top(); + }, icon: const Icon(Icons.vertical_align_top_rounded), tooltip: "Go to Top", ), IconButton( - onPressed: () {}, - icon: Icon(Icons.vertical_align_bottom_rounded), + onPressed: () { + Provider.of(context, listen: false) + .go_to_bottom(); + }, + icon: const Icon(Icons.vertical_align_bottom_rounded), tooltip: "Go to Bottom", ), const SizedBox( @@ -164,12 +185,11 @@ class _BuildScreenState extends State { ElevatedButton( onPressed: () async { final confirmResult = await showConfirmationDialog( - context, - "Delete Build", - "Are you sure to delete this Package?", - () async {}, - () {}, - ); + context, + "Delete Build", + "Are you sure to delete this Package?", () async { + // todo delete build + }, null); }, child: const Text( "Delete", @@ -181,13 +201,7 @@ class _BuildScreenState extends State { ), ElevatedButton( onPressed: () async { - final confirmResult = await showConfirmationDialog( - context, - "Delete Build", - "Are you sure to delete this Package?", - () async {}, - () {}, - ); + // todo api call and page redirect }, child: const Text( "Retry", diff --git a/frontend/lib/screens/builds_screen.dart b/frontend/lib/screens/builds_screen.dart index 7231e31..19f4768 100644 --- a/frontend/lib/screens/builds_screen.dart +++ b/frontend/lib/screens/builds_screen.dart @@ -1,6 +1,6 @@ import 'package:aurcache/components/builds_table.dart'; import 'package:aurcache/components/api/APIBuilder.dart'; -import 'package:aurcache/providers/builds_provider.dart'; +import 'package:aurcache/providers/api/builds_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../constants/color_constants.dart'; diff --git a/frontend/lib/screens/dashboard_screen.dart b/frontend/lib/screens/dashboard_screen.dart index 246003b..c8e2114 100644 --- a/frontend/lib/screens/dashboard_screen.dart +++ b/frontend/lib/screens/dashboard_screen.dart @@ -1,12 +1,12 @@ import 'package:aurcache/components/api/APIBuilder.dart'; -import 'package:aurcache/providers/stats_provider.dart'; +import 'package:aurcache/providers/api/stats_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../components/dashboard/header.dart'; import '../constants/color_constants.dart'; -import '../providers/builds_provider.dart'; -import '../providers/packages_provider.dart'; +import '../providers/api/builds_provider.dart'; +import '../providers/api/packages_provider.dart'; import '../utils/responsive.dart'; import '../models/stats.dart'; import '../components/dashboard/quick_info_banner.dart'; diff --git a/frontend/lib/screens/package_screen.dart b/frontend/lib/screens/package_screen.dart index eeb4790..1ea1732 100644 --- a/frontend/lib/screens/package_screen.dart +++ b/frontend/lib/screens/package_screen.dart @@ -1,6 +1,6 @@ import 'package:aurcache/api/packages.dart'; import 'package:aurcache/components/api/APIBuilder.dart'; -import 'package:aurcache/providers/builds_provider.dart'; +import 'package:aurcache/providers/api/builds_provider.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; @@ -11,9 +11,9 @@ import '../components/confirm_popup.dart'; import '../constants/color_constants.dart'; import '../models/build.dart'; import '../models/package.dart'; -import '../providers/package_provider.dart'; -import '../providers/packages_provider.dart'; -import '../providers/stats_provider.dart'; +import '../providers/api/package_provider.dart'; +import '../providers/api/packages_provider.dart'; +import '../providers/api/stats_provider.dart'; class PackageScreen extends StatefulWidget { const PackageScreen({super.key, required this.pkgID}); diff --git a/frontend/lib/screens/packages_screen.dart b/frontend/lib/screens/packages_screen.dart index 1c41ea0..b7e118d 100644 --- a/frontend/lib/screens/packages_screen.dart +++ b/frontend/lib/screens/packages_screen.dart @@ -1,5 +1,5 @@ import 'package:aurcache/components/packages_table.dart'; -import 'package:aurcache/providers/packages_provider.dart'; +import 'package:aurcache/providers/api/packages_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart';