Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernize storage list #831

Merged
merged 22 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions cozy/app_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from cozy.view_model.search_view_model import SearchViewModel
from cozy.view_model.settings_view_model import SettingsViewModel
from cozy.view_model.sleep_timer_view_model import SleepTimerViewModel
from cozy.view_model.storages_view_model import StoragesViewModel


class AppController(metaclass=Singleton):
Expand Down Expand Up @@ -75,7 +76,7 @@ def __init__(self, gtk_app, main_window_builder, main_window):
self.library_view_model.add_listener(self._on_open_view)
self.library_view_model.add_listener(self._on_library_view_event)
self.playback_control_view_model.add_listener(self._on_open_view)
self.headerbar_view_model.add_listener(self._on_open_view)
self.headerbar_view_model.add_listener(self._on_working_event)
self.app_view_model.add_listener(self._on_app_view_event)

self.main_window.add_listener(self._on_main_window_event)
Expand Down Expand Up @@ -108,6 +109,7 @@ def configure_inject(self, binder):
binder.bind_to_constructor(ToastNotifier, lambda: ToastNotifier())
binder.bind_to_constructor(AppViewModel, lambda: AppViewModel())
binder.bind_to_constructor(SettingsViewModel, lambda: SettingsViewModel())
binder.bind_to_constructor(StoragesViewModel, lambda: StoragesViewModel())

def open_author(self, author: str):
self.library_view_model.library_view_mode = LibraryViewMode.AUTHOR
Expand Down Expand Up @@ -146,10 +148,12 @@ def _on_app_view_event(self, event: str, data):
if event == "view":
self.headerbar_view_model.set_view(data)

def _on_main_window_event(self, event: str, data):
def _on_working_event(self, event: str, data) -> None:
if event == "working":
self.book_detail_view_model.lock_ui = data
self.settings_view_model.lock_ui = data

def _on_main_window_event(self, event: str, data):
if event == "open_view":
self._on_open_view(data, None)

Expand Down
17 changes: 5 additions & 12 deletions cozy/model/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


class Settings:
_storages: List[Storage] = []
_storages: list[Storage] = []
_db = cache = inject.attr(SqliteDatabase)

def __init__(self):
Expand Down Expand Up @@ -49,10 +49,7 @@ def last_played_book(self, new_value):

@property
def default_location(self):
return next(location
for location
in self.storage_locations
if location.default)
return next(location for location in self.storage_locations if location.default)

@property
def storage_locations(self):
Expand All @@ -69,10 +66,10 @@ def external_storage_locations(self):
return [storage for storage in self._storages if storage.external]

def invalidate(self):
self._storages = []
self._storages.clear()

def _load_all_storage_locations(self):
self._storages = []
self.invalidate()

for storage_db_obj in StorageModel.select(StorageModel.id):
try:
Expand All @@ -83,9 +80,5 @@ def _load_all_storage_locations(self):
self._ensure_default_storage_present()

def _ensure_default_storage_present(self):
default_storage_present = any(storage.default
for storage
in self._storages)

if not default_storage_present and len(self._storages) > 0:
if self._storages and not any(storage.default for storage in self._storages):
self._storages[0].default = True
14 changes: 7 additions & 7 deletions cozy/model/storage.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os
from pathlib import Path

from peewee import SqliteDatabase

Expand All @@ -17,8 +17,8 @@ def __init__(self, db: SqliteDatabase, db_id: int):
self._get_db_object()

@staticmethod
def new(db: SqliteDatabase):
db_obj = StorageModel.create(path="")
def new(db: SqliteDatabase, path: str):
db_obj = StorageModel.create(path=path)
return Storage(db, db_obj.id)

def _get_db_object(self):
Expand All @@ -33,11 +33,11 @@ def path(self):
return self._db_object.path

@path.setter
def path(self, new_path: str):
if not os.path.isabs(new_path):
def path(self, path: str):
if not Path(path).is_absolute():
raise InvalidPath

self._db_object.path = new_path
self._db_object.path = path
self._db_object.save(only=self._db_object.dirty_fields)

@property
Expand Down Expand Up @@ -68,4 +68,4 @@ def external(self, new_external: bool):
self._db_object.save(only=self._db_object.dirty_fields)

def delete(self):
self._db_object.delete_instance(recursive=True, delete_nullable=False)
self._db_object.delete_instance(recursive=True, delete_nullable=False)
8 changes: 4 additions & 4 deletions cozy/ui/main_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from cozy.media.importer import Importer, ScanStatus
from cozy.media.player import Player
from cozy.model.settings import Settings as SettingsModel
from cozy.view_model.settings_view_model import SettingsViewModel
from cozy.view_model.storages_view_model import StoragesViewModel
from cozy.open_view import OpenView
from cozy.ui.library_view import LibraryView
from cozy.ui.preferences_view import PreferencesView
Expand All @@ -38,7 +38,7 @@ class CozyUI(EventSender, metaclass=Singleton):
_settings: SettingsModel = inject.attr(SettingsModel)
_files: Files = inject.attr(Files)
_player: Player = inject.attr(Player)
_settings_view_model: SettingsViewModel = inject.attr(SettingsViewModel)
_storages_view_model: StoragesViewModel = inject.attr(StoragesViewModel)

def __init__(self, pkgdatadir, app, version):
super().__init__()
Expand Down Expand Up @@ -249,9 +249,9 @@ def switch_to_playing(self):
self.block_ui_buttons(False, True)
else:
# we want to only block the player controls
# TODO: rework. this is messy
self.block_ui_buttons(False, True)
self.block_ui_buttons(True, False)
self.emit_event_main_thread("working", False)

def check_for_tracks(self):
"""
Expand Down Expand Up @@ -309,7 +309,7 @@ def _on_drag_data_received(self, widget, value, *_):
return True

def _set_audiobook_path(self, path):
self._settings_view_model.add_first_storage_location(path)
self._storages_view_model.add_first_storage_location(path)
self.main_stack.props.visible_child_name = "import"
self.scan(None, None)
self.fs_monitor.init_offline_mode()
Expand Down
204 changes: 72 additions & 132 deletions cozy/ui/preferences_view.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from gi.repository import Gtk
from cozy.view_model.settings_view_model import SettingsViewModel
import gi
from gi.repository import Adw, Gio
from typing import Any

from gi.repository import Adw, Gio, Gtk

from cozy.ext import inject
from cozy.ui.widgets.error_reporting import ErrorReporting
from cozy.ui.widgets.storage_list_box_row import StorageListBoxRow
from cozy.ui.widgets.storages import StorageLocations
from cozy.view_model.settings_view_model import SettingsViewModel


@Gtk.Template.from_resource('/com/github/geigi/cozy/preferences.ui')
@Gtk.Template.from_resource("/com/github/geigi/cozy/preferences.ui")
class PreferencesView(Adw.PreferencesWindow):
__gtype_name__ = "PreferencesWindow"

Expand All @@ -17,149 +17,89 @@ class PreferencesView(Adw.PreferencesWindow):
_glib_settings: Gio.Settings = inject.attr(Gio.Settings)
_view_model: SettingsViewModel = inject.attr(SettingsViewModel)

dark_mode_switch: Gtk.Switch = Gtk.Template.Child()
swap_author_reader_switch: Gtk.Switch = Gtk.Template.Child()
replay_switch: Gtk.Switch = Gtk.Template.Child()
storages_page: Adw.PreferencesPage = Gtk.Template.Child()
user_feedback_preference_group: Adw.PreferencesGroup = Gtk.Template.Child()

dark_mode_switch: Adw.SwitchRow = Gtk.Template.Child()
swap_author_reader_switch: Adw.SwitchRow = Gtk.Template.Child()
replay_switch: Adw.SwitchRow = Gtk.Template.Child()
sleep_timer_fadeout_switch: Adw.SwitchRow = Gtk.Template.Child()
fadeout_duration_spin_button: Adw.SpinRow = Gtk.Template.Child()
artwork_prefer_external_switch: Gtk.Switch = Gtk.Template.Child()
artwork_prefer_external_switch: Adw.SwitchRow = Gtk.Template.Child()

rewind_duration_adjustment: Gtk.Adjustment = Gtk.Template.Child()
forward_duration_adjustment: Gtk.Adjustment = Gtk.Template.Child()
fadeout_duration_adjustment: Gtk.Adjustment = Gtk.Template.Child()

storage_list_box: Gtk.ListBox = Gtk.Template.Child()
add_storage_button: Gtk.Button = Gtk.Template.Child()
remove_storage_button: Gtk.Button = Gtk.Template.Child()
external_storage_toggle_button: Gtk.ToggleButton = Gtk.Template.Child()
default_storage_button: Gtk.ToggleButton = Gtk.Template.Child()

user_feedback_preference_group: Adw.PreferencesRow = Gtk.Template.Child()

def __init__(self, **kwargs):
super().__init__(**kwargs)
def __init__(self, **kwargs: Any) -> None:
super().__init__(transient_for=self.main_window.window, **kwargs)

error_reporting = ErrorReporting()
error_reporting.show_header(False)
self.user_feedback_preference_group.add(error_reporting)

self.storage_locations_view = StorageLocations()
self.storages_page.add(self.storage_locations_view)

self._bind_settings()
self._bind_view_model()

self._view_model.bind_to("lock_ui", self._on_lock_ui_changed)

self.connect("close-request", self._hide_window)

self.sleep_timer_fadeout_switch.connect("notify::active", self._on_sleep_fadeout_switch_changed)
self.fadeout_duration_spin_button.set_sensitive(self.sleep_timer_fadeout_switch.props.active)

self.storage_list_box.connect("row-selected", self._on_storage_box_changed)

self.add_storage_button.connect("clicked", self._on_add_storage_clicked)
self.remove_storage_button.connect("clicked", self._on_remove_storage_clicked)
self.external_button_handle_id = self.external_storage_toggle_button.connect("clicked", self._on_external_clicked)
self.default_storage_button.connect("clicked", self._on_default_storage_clicked)

self.set_transient_for(self.main_window.window)

self._init_storage_box()

def _bind_view_model(self):
self._view_model.bind_to("storage_locations", self._init_storage_box)
self._view_model.bind_to("storage_attributes", self._refresh_storage_rows)

def _bind_settings(self):
self._glib_settings.bind("dark-mode", self.dark_mode_switch, "active",
Gio.SettingsBindFlags.DEFAULT)

self._glib_settings.bind("swap-author-reader", self.swap_author_reader_switch, "active",
Gio.SettingsBindFlags.DEFAULT)

self._glib_settings.bind("replay", self.replay_switch, "active", Gio.SettingsBindFlags.DEFAULT)
self._glib_settings.bind("rewind-duration", self.rewind_duration_adjustment, "value",
Gio.SettingsBindFlags.DEFAULT)
self._glib_settings.bind("forward-duration", self.forward_duration_adjustment, "value",
Gio.SettingsBindFlags.DEFAULT)

self._glib_settings.bind("sleep-timer-fadeout", self.sleep_timer_fadeout_switch, "active",
Gio.SettingsBindFlags.DEFAULT)

self._glib_settings.bind("sleep-timer-fadeout-duration", self.fadeout_duration_adjustment,
"value", Gio.SettingsBindFlags.DEFAULT)

self._glib_settings.bind("prefer-external-cover", self.artwork_prefer_external_switch, "active",
Gio.SettingsBindFlags.DEFAULT)

def _on_sleep_fadeout_switch_changed(self, widget, param):
state = widget.get_property(param.name)
self.fadeout_duration_spin_button.set_sensitive(state)

def _init_storage_box(self):
self.storage_list_box.remove_all_children()

for storage in self._view_model.storage_locations:
row = StorageListBoxRow(storage)
row.connect("location-changed", self._on_storage_location_changed)
self.storage_list_box.append(row)

def _on_add_storage_clicked(self, _):
self._view_model.add_storage_location()

def _on_remove_storage_clicked(self, _):
row = self.storage_list_box.get_selected_row()
self._view_model.remove_storage_location(row.model)

def _on_default_storage_clicked(self, _):
row = self.storage_list_box.get_selected_row()
self._view_model.set_default_storage(row.model)
self._on_storage_box_changed(None, row)

def _on_storage_box_changed(self, _, row):
row = self.storage_list_box.get_selected_row()
if row is None:
sensitive = False
default_sensitive = False
remove_sensitive = False
else:
sensitive = True
remove_sensitive = True
if row.model.default or not row.model.path:
default_sensitive = remove_sensitive = False
else:
default_sensitive = True

if not row.model.path:
remove_sensitive = True

self.external_storage_toggle_button.handler_block(self.external_button_handle_id)
self.external_storage_toggle_button.set_active(row.model.external)
self.external_storage_toggle_button.handler_unblock(self.external_button_handle_id)

self.remove_storage_button.set_sensitive(remove_sensitive)
self.external_storage_toggle_button.set_sensitive(sensitive)
self.default_storage_button.set_sensitive(default_sensitive)

def _on_external_clicked(self, _):
external = self.external_storage_toggle_button.get_active()
row = self.storage_list_box.get_selected_row()
self._view_model.set_storage_external(row.model, external)

def _on_storage_location_changed(self, widget, new_location):
self._view_model.change_storage_location(widget.model, new_location)

def _refresh_storage_rows(self):
self._init_storage_box()

self._on_storage_box_changed(None, self.storage_list_box.get_selected_row())

def _on_lock_ui_changed(self):
def _bind_settings(self) -> None:
self._glib_settings.bind(
"dark-mode", self.dark_mode_switch, "active", Gio.SettingsBindFlags.DEFAULT
)

self._glib_settings.bind(
"swap-author-reader",
self.swap_author_reader_switch,
"active",
Gio.SettingsBindFlags.DEFAULT,
)

self._glib_settings.bind(
"replay", self.replay_switch, "active", Gio.SettingsBindFlags.DEFAULT
)
self._glib_settings.bind(
"rewind-duration",
self.rewind_duration_adjustment,
"value",
Gio.SettingsBindFlags.DEFAULT,
)
self._glib_settings.bind(
"forward-duration",
self.forward_duration_adjustment,
"value",
Gio.SettingsBindFlags.DEFAULT,
)

self._glib_settings.bind(
"sleep-timer-fadeout",
self.sleep_timer_fadeout_switch,
"enable-expansion",
Gio.SettingsBindFlags.DEFAULT,
)

self._glib_settings.bind(
"sleep-timer-fadeout-duration",
self.fadeout_duration_adjustment,
"value",
Gio.SettingsBindFlags.DEFAULT,
)

self._glib_settings.bind(
"prefer-external-cover",
self.artwork_prefer_external_switch,
"active",
Gio.SettingsBindFlags.DEFAULT,
)

def _on_lock_ui_changed(self) -> None:
sensitive = not self._view_model.lock_ui

self.storage_list_box.set_sensitive(sensitive)
self.add_storage_button.set_sensitive(sensitive)
self.remove_storage_button.set_sensitive(sensitive)
self.external_storage_toggle_button.set_sensitive(sensitive)
self.default_storage_button.set_sensitive(sensitive)
self._on_storage_box_changed(None, self.storage_list_box.get_selected_row())

self.storage_locations_view.set_sensitive(sensitive)

def _hide_window(self, *_):
self.hide()
return True
Loading