Skip to content

Commit

Permalink
Refactoring. Move some common functionality into BaseController.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kern, Thomas committed Jan 12, 2024
1 parent 06e5c1e commit 92c0018
Show file tree
Hide file tree
Showing 41 changed files with 234 additions and 305 deletions.
61 changes: 61 additions & 0 deletions lib/common/base_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2023 Thomas Kern
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

import 'package:get_it/get_it.dart';
import 'package:mopicon/common/selected_item_positions.dart';
import 'package:mopicon/services/mopidy_service.dart';
import 'package:rxdart/rxdart.dart';

abstract class BaseController {
/// Notification to trigger refresh.
Stream<bool> get refresh$ => _refresh$.stream;
final _refresh$ = PublishSubject<bool>();

final selectionModeChanged = SelectionModeChangedNotifier(SelectionMode.off);
final selectionChanged = SelectionChangedNotifier(SelectedItemPositions());

final _mopidyService = GetIt.instance<MopidyService>();

MopidyService get mopidyService => _mopidyService;
bool get isSelectionEmpty => selectionChanged.value.isEmpty;
SelectionMode get selectionMode => selectionModeChanged.value;

void notifyRefresh() {
notifyUnselect();
_refresh$.add(true);
}

void notifyUnselect() {
selectionModeChanged.value = SelectionMode.off;
selectionChanged.value.isNotEmpty ? selectionChanged.value = SelectedItemPositions() : null;
}

void notifySelectAll(int numItems) {
selectionChanged.value = SelectedItemPositions.all(numItems);
selectionModeChanged.value = selectionChanged.value.isNotEmpty ? SelectionMode.on : SelectionMode.off;
}

void notifySelectPositions(SelectedItemPositions positions) {
selectionChanged.value = positions;
selectionModeChanged.value = selectionChanged.value.isNotEmpty ? SelectionMode.on : SelectionMode.off;
}
}
2 changes: 1 addition & 1 deletion lib/utils/globals.dart → lib/common/globals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ library mopicon.globals;

import 'package:flutter/material.dart';
import 'package:mopicon/routes/application_routes.dart';
import 'logging_utils.dart' as lg;
import '../utils/logging_utils.dart' as lg;

class Globals {
static var rootScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>(debugLabel: "rootScaffoldMessengerKey");
Expand Down
File renamed without changes.
5 changes: 2 additions & 3 deletions lib/components/action_buttons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:mopicon/components/selected_item_positions.dart';
import 'package:mopicon/common/selected_item_positions.dart';

/// An IconButton with additional ValueListenable to control whether the button is shown.
///
Expand All @@ -43,8 +43,7 @@ class ActionButton<T> extends StatelessWidget {
/// The [ValueListenable] to control whether the button is displayed.
final ValueListenable<T>? valueListenable;

const ActionButton(this.iconData, this.onPressed,
{this.valueListenable, super.key});
const ActionButton(this.iconData, this.onPressed, {this.valueListenable, super.key});

bool _shouldEnable(T value) {
if (value == null) {
Expand Down
4 changes: 2 additions & 2 deletions lib/components/busy_wrapper.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:mopicon/services/preferences_service.dart';
import 'package:mopicon/pages/settings/preferences_controller.dart';

/// Shows a modal busy indicator.
///
Expand All @@ -18,7 +18,7 @@ class BusyWrapper extends StatefulWidget {
}

class _BusyWrapperState extends State<BusyWrapper> with TickerProviderStateMixin {
final preferences = GetIt.instance<Preferences>();
final preferences = GetIt.instance<PreferencesController>();

late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 4),
Expand Down
6 changes: 3 additions & 3 deletions lib/components/error_snackbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
*/

import 'package:flutter/material.dart';
import 'package:mopicon/utils/globals.dart';
import 'package:mopicon/common/globals.dart';
import 'package:get_it/get_it.dart';
import 'package:mopicon/services/preferences_service.dart';
import 'package:mopicon/pages/settings/preferences_controller.dart';

/// Displays a [SnackBar] with error icon, [title] and [detail].
void showError(String title, String? detail) {
Expand Down Expand Up @@ -62,6 +62,6 @@ class _InfoSnackBar extends SnackBar {
subtitle: detail != null ? Text(detail) : null,
),
duration: const Duration(seconds: 4),
backgroundColor: GetIt.instance<Preferences>().theme.data.colorScheme.onInverseSurface,
backgroundColor: GetIt.instance<PreferencesController>().theme.data.colorScheme.onInverseSurface,
showCloseIcon: false);
}
8 changes: 3 additions & 5 deletions lib/components/item_action_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*/

import 'package:flutter/material.dart';
import 'package:mopicon/utils/globals.dart';
import 'package:mopicon/common/globals.dart';

/// Available options for [showActionDialog].
enum ItemActionOption { play, addToTracklist, addToPlaylist }
Expand All @@ -32,8 +32,7 @@ enum ItemActionOption { play, addToTracklist, addToPlaylist }
/// respective [ItemActionOption].
Future<ItemActionOption?> showActionDialog(List<ItemActionOption> buttons) {
return showDialog<ItemActionOption>(
context: Globals
.applicationRoutes.rootNavigatorKey.currentState!.overlay!.context,
context: Globals.applicationRoutes.rootNavigatorKey.currentState!.overlay!.context,
builder: (BuildContext context) {
var actions = List<Widget>.empty(growable: true);
for (var button in buttons) {
Expand Down Expand Up @@ -66,8 +65,7 @@ Future<ItemActionOption?> showActionDialog(List<ItemActionOption> buttons) {
}

return AlertDialog(
actionsPadding:
const EdgeInsets.only(left: 40, top: 50, bottom: 50, right: 40),
actionsPadding: const EdgeInsets.only(left: 40, top: 50, bottom: 50, right: 40),
actions: actions,
actionsAlignment: MainAxisAlignment.spaceBetween,
);
Expand Down
4 changes: 2 additions & 2 deletions lib/components/menu_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
*/
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:mopicon/utils/globals.dart';
import 'package:mopicon/components/selected_item_positions.dart';
import 'package:mopicon/common/globals.dart';
import 'package:mopicon/common/selected_item_positions.dart';

typedef MenuCallbackFunction<T> = void Function(BuildContext context, T? arg, int? index);

Expand Down
11 changes: 4 additions & 7 deletions lib/components/question_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import 'package:flutter/material.dart';
import 'package:mopicon/components/dialog_button.dart';
import 'package:mopicon/utils/globals.dart';
import 'package:mopicon/common/globals.dart';

/// Confirmation options for question dialogs.
enum DialogButtonOption { yes, no, ok, cancel, retry, abort, submit, save }
Expand All @@ -31,12 +31,10 @@ enum DialogButtonOption { yes, no, ok, cancel, retry, abort, submit, save }
/// If [defaultOption] is provided, the corresponding button gets the autofocus.
/// Upon closing, either the pressed dialog option is returned or `null` if
/// no button was pressed.
Future<DialogButtonOption?> showQuestionDialog(
String title, String message, List<DialogButtonOption> buttons,
Future<DialogButtonOption?> showQuestionDialog(String title, String message, List<DialogButtonOption> buttons,
{DialogButtonOption? defaultOption}) {
return showDialog<DialogButtonOption>(
context: Globals
.applicationRoutes.rootNavigatorKey.currentState!.overlay!.context,
context: Globals.applicationRoutes.rootNavigatorKey.currentState!.overlay!.context,
builder: (BuildContext context) {
var actions = List<Widget>.empty(growable: true);
for (var button in buttons) {
Expand Down Expand Up @@ -84,8 +82,7 @@ Future<DialogButtonOption?> showQuestionDialog(
}
}

return AlertDialog(
title: Text(title), content: Text(message), actions: actions);
return AlertDialog(title: Text(title), content: Text(message), actions: actions);
},
);
}
6 changes: 3 additions & 3 deletions lib/components/reorderable_list_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:mopicon/services/cover_service.dart';
import 'package:mopicon/services/mopidy_service.dart';
import 'package:mopicon/services/preferences_service.dart';
import 'package:mopicon/pages/settings/preferences_controller.dart';
import 'package:mopicon/extensions/mopidy_utils.dart';
import 'selected_item_positions.dart';
import '../common/selected_item_positions.dart';

typedef OnReorderCallback = void Function(int start, int current);
typedef OnTapCallback<T> = void Function(T item, int index);

class ReorderableTrackListView<T extends Object> {
final _preferences = GetIt.instance<Preferences>();
final _preferences = GetIt.instance<PreferencesController>();

// uri/image map
final BuildContext context;
Expand Down
13 changes: 4 additions & 9 deletions lib/components/volume_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*/
import 'package:flutter/material.dart';
import 'package:mopicon/services/mopidy_service.dart';
import 'package:mopicon/utils/globals.dart';
import 'package:mopicon/common/globals.dart';
import 'package:get_it/get_it.dart';

/// Volume control an muting for Mopidy server.
Expand Down Expand Up @@ -93,9 +93,7 @@ class _VolumeControlState extends State<VolumeControl> {
menuChildren: <Widget>[
Row(children: <Widget>[
IconButton(
icon: muted
? const Icon(Icons.volume_off)
: const Icon(Icons.volume_up),
icon: muted ? const Icon(Icons.volume_off) : const Icon(Icons.volume_up),
focusNode: _buttonFocusNode,
onPressed: () async {
var m = !muted;
Expand Down Expand Up @@ -133,12 +131,9 @@ class _VolumeControlState extends State<VolumeControl> {
)
])
],
builder: (BuildContext context, MenuController controller,
Widget? child) {
builder: (BuildContext context, MenuController controller, Widget? child) {
return IconButton(
icon: muted
? const Icon(Icons.volume_off)
: const Icon(Icons.volume_up),
icon: muted ? const Icon(Icons.volume_off) : const Icon(Icons.volume_up),
focusNode: _buttonFocusNode,
onPressed: () {
if (controller.isOpen) {
Expand Down
8 changes: 4 additions & 4 deletions lib/initializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@
import 'dart:async';

import 'package:get_it/get_it.dart';
import 'package:mopicon/services/preferences_service.dart';
import 'package:mopicon/pages/settings/preferences_controller.dart';
import 'package:mopicon/services/mopidy_service.dart';
import 'package:mopicon/services/cover_service.dart';
import 'package:mopicon/pages/connecting_screen/connecting_screen_controller.dart';
import 'package:mopicon/pages/browse/library_browser_controller.dart';
import 'package:mopicon/pages/playlist/playlist_view_controller.dart';
import 'package:mopicon/pages/tracklist/tracklist_view_controller.dart';
import 'package:mopicon/pages/search/search_view_controller.dart';
import 'package:mopicon/utils/globals.dart';
import 'package:mopicon/common/globals.dart';

/// Registers all services and loads preferences.
class Initializer {
Expand All @@ -46,7 +46,7 @@ class Initializer {
static void _registerServices() {
GetIt getIt = GetIt.instance;
Globals.logger.i("starting registering services");
getIt.registerLazySingleton<Preferences>(() => PreferencesServiceImpl());
getIt.registerLazySingleton<PreferencesController>(() => PreferencesControllerImpl());
getIt.registerLazySingleton<MopidyService>(() => MopidyServiceImpl());
getIt.registerLazySingleton<ConnectingScreenController>(() => ConnectingScreenControllerImpl());
getIt.registerLazySingleton<CoverService>(() => CoverServiceImpl());
Expand All @@ -59,7 +59,7 @@ class Initializer {

static Future<void> _loadSettings() async {
Globals.logger.i("starting loading settings");
await GetIt.instance<Preferences>().load();
await GetIt.instance<PreferencesController>().load();
Globals.logger.i("finished loading settings");
}
}
6 changes: 3 additions & 3 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@

import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:mopicon/utils/globals.dart';
import 'package:mopicon/common/globals.dart';
import 'package:mopicon/initializer.dart';
import 'package:mopicon/services/preferences_service.dart';
import 'package:mopicon/pages/settings/preferences_controller.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'generated/l10n.dart';

Expand All @@ -38,7 +38,7 @@ void main() async {
class AppWidget extends StatelessWidget {
AppWidget({super.key});

final preferences = GetIt.instance<Preferences>();
final preferences = GetIt.instance<PreferencesController>();
final applicationRoutes = Globals.applicationRoutes;

@override
Expand Down
4 changes: 2 additions & 2 deletions lib/pages/about/about_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import 'package:mopicon/components/error_snackbar.dart';
import 'package:mopicon/components/material_page_frame.dart';
import 'package:mopicon/components/titled_divider.dart';
import 'package:mopicon/generated/l10n.dart';
import 'package:mopicon/services/preferences_service.dart';
import 'package:mopicon/pages/settings/preferences_controller.dart';
import 'package:url_launcher/url_launcher.dart';

var license = '''
Expand Down Expand Up @@ -55,7 +55,7 @@ DEALINGS IN THE SOFTWARE.
/// Displays information about this program, with links to sourcecode
/// and documentation.
class AboutPage extends StatelessWidget {
final _preferences = GetIt.instance<Preferences>();
final _preferences = GetIt.instance<PreferencesController>();

AboutPage({super.key});

Expand Down
11 changes: 4 additions & 7 deletions lib/pages/browse/library_appbar_menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ import 'package:mopicon/pages/browse/new_playlist_dialog.dart';
import 'package:mopicon/services/mopidy_service.dart';
import 'package:mopicon/components/error_snackbar.dart';
import 'package:mopicon/components/menu_builder.dart';
import 'package:mopicon/components/selected_item_positions.dart';
import 'package:mopicon/utils/globals.dart';
import 'package:mopicon/common/globals.dart';

class LibraryBrowserAppBarMenu extends StatelessWidget {
final mopidyService = GetIt.instance<MopidyService>();
Expand Down Expand Up @@ -59,9 +58,7 @@ class LibraryBrowserAppBarMenu extends StatelessWidget {
}

void _selectAll([BuildContext? context, _, __]) async {
controller.selectionChanged.value = SelectedItemPositions.all(items.length);
controller.selectionModeChanged.value =
controller.selectionChanged.value.isNotEmpty ? SelectionMode.on : SelectionMode.on;
controller.notifySelectAll(items.length);
}

void _newPlayList(BuildContext? context, _, __) {
Expand All @@ -84,7 +81,7 @@ class LibraryBrowserAppBarMenu extends StatelessWidget {
void _renamePlayList(BuildContext context, _, __) async {
if (controller.selectionChanged.value.positions.length == 1) {
var ref = items[controller.selectionChanged.value.positions.first];
controller.unselect();
controller.notifyUnselect();
var name = await renamePlaylistDialog(ref.name);
if (name != null) {
if (context.mounted) {
Expand All @@ -95,6 +92,6 @@ class LibraryBrowserAppBarMenu extends StatelessWidget {
}

void _refresh(BuildContext context, _, __) {
controller.triggerRefresh();
controller.notifyRefresh();
}
}
Loading

0 comments on commit 92c0018

Please sign in to comment.