From 4b5f2b8b3abc26c974ddb0024b9a2502c4fa3824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9v=C3=A9nyi=20Benedek?= Date: Mon, 2 Dec 2024 19:40:03 +0100 Subject: [PATCH 1/7] First two pages done --- cozy/ui/main_view.py | 3 + cozy/ui/preferences_window.py | 1 - cozy/ui/widgets/error_reporting.py | 78 ++++-------- cozy/ui/widgets/welcome_dialog.py | 33 +++++ data/gresource.xml | 1 + data/ui/error_reporting.blp | 191 +++++------------------------ data/ui/meson.build | 1 + data/ui/welcome_dialog.blp | 51 ++++++++ 8 files changed, 149 insertions(+), 210 deletions(-) create mode 100644 cozy/ui/widgets/welcome_dialog.py create mode 100644 data/ui/welcome_dialog.blp diff --git a/cozy/ui/main_view.py b/cozy/ui/main_view.py index d5ca71c4..b73d9bad 100644 --- a/cozy/ui/main_view.py +++ b/cozy/ui/main_view.py @@ -20,6 +20,7 @@ from cozy.ui.library_view import LibraryView from cozy.ui.preferences_window import PreferencesWindow from cozy.ui.widgets.first_import_button import FirstImportButton +from cozy.ui.widgets.welcome_dialog import WelcomeDialog from cozy.view_model.playback_control_view_model import PlaybackControlViewModel from cozy.view_model.playback_speed_view_model import PlaybackSpeedViewModel from cozy.view_model.storages_view_model import StoragesViewModel @@ -92,6 +93,8 @@ def __init_window(self): self.navigation_view.add(BookDetailView()) + WelcomeDialog().present(self.window) + self.window.present() def __init_actions(self): diff --git a/cozy/ui/preferences_window.py b/cozy/ui/preferences_window.py index 6c9bc157..8ce105b0 100644 --- a/cozy/ui/preferences_window.py +++ b/cozy/ui/preferences_window.py @@ -29,7 +29,6 @@ def __init__(self) -> None: super().__init__() error_reporting = ErrorReporting() - error_reporting.show_header(False) self.user_feedback_preference_group.add(error_reporting) self.storage_locations_view = StorageLocations() diff --git a/cozy/ui/widgets/error_reporting.py b/cozy/ui/widgets/error_reporting.py index 44177562..880c75ec 100644 --- a/cozy/ui/widgets/error_reporting.py +++ b/cozy/ui/widgets/error_reporting.py @@ -1,7 +1,7 @@ -from gettext import gettext as _ +from itertools import chain import inject -from gi.repository import Gtk +from gi.repository import Adw, Gtk from cozy.settings import ApplicationSettings @@ -9,7 +9,7 @@ _("Disabled"), _("Basic error reporting"), _("Detailed error reporting"), - _("Detailed error reporting with import errors") + _("Detailed, with media types") ] LEVEL_DESCRIPTION = [ @@ -17,76 +17,52 @@ _("The following information will be sent in case of an error or crash:") ] -LEVEL_DETAILS = { - 0: [], - 1: [_("Which type of error occurred"), +LEVEL_DETAILS = [ + [], + [_("Which type of error occurred"), _("Line of code where an error occurred"), _("Cozy's version"), ], - 2: [_("Linux distribution"), + [_("Linux distribution"), _("Desktop environment")], - 3: [_("Media type of files that Cozy couldn't import")] -} + [_("Media type of files that Cozy couldn't import")] +] @Gtk.Template.from_resource('/com/github/geigi/cozy/ui/error_reporting.ui') class ErrorReporting(Gtk.Box): __gtype_name__ = 'ErrorReporting' - level_label: Gtk.Label = Gtk.Template.Child() - description_label: Gtk.Label = Gtk.Template.Child() - details_label: Gtk.Label = Gtk.Template.Child() - header_box: Gtk.Box = Gtk.Template.Child() - - verbose_adjustment: Gtk.Adjustment = Gtk.Template.Child() - verbose_scale: Gtk.Scale = Gtk.Template.Child() + description: Adw.ActionRow = Gtk.Template.Child() + detail_combo: Adw.ComboRow = Gtk.Template.Child() app_settings: ApplicationSettings = inject.attr(ApplicationSettings) def __init__(self, **kwargs): super().__init__(**kwargs) - self.__init_scale() + levels_list = Gtk.StringList(strings=LEVELS) + self.detail_combo.props.model = levels_list + self.detail_combo.connect("notify::selected-item", self._level_selected) + self._load_report_level() - self.__connect() self.app_settings.add_listener(self._on_app_setting_changed) - def show_header(self, show: bool): - self.header_box.set_visible(show) - def _load_report_level(self): level = self.app_settings.report_level - self.verbose_adjustment.set_value(level + 1) - self._update_ui_texts(level) - - def __init_scale(self): - for i in range(1, 5): - self.verbose_scale.add_mark(i, Gtk.PositionType.RIGHT, None) - self.verbose_scale.set_round_digits(0) - - def __connect(self): - self.verbose_adjustment.connect("value-changed", self._adjustment_changed) - - def _adjustment_changed(self, adjustment: Gtk.Adjustment): - level = int(adjustment.get_value()) - 1 - self.app_settings.report_level = level - self._update_ui_texts(level) - - def _update_ui_texts(self, level: int): - self.level_label.set_text(LEVELS[level]) self._update_description(level) - self._update_details(level) - - def _update_description(self, value): - detail_index = min(value, 1) - self.description_label.set_text(LEVEL_DESCRIPTION[detail_index]) - - def _update_details(self, value): - details = "" - for i in range(value + 1): - for line in LEVEL_DETAILS[i]: - details += f"• {line}\n" - self.details_label.set_text(details) + self.detail_combo.set_selected(level) + + def _level_selected(self, obj, param) -> None: + selected = obj.get_property(param.name).get_string() + index = LEVELS.index(selected) + self.app_settings.report_level = index + self._update_description(index) + + def _update_description(self, level: int): + self.description.set_title(LEVEL_DESCRIPTION[min(level, 1)]) + details = "\n".join(["• " + i for i in chain(*LEVEL_DETAILS[:level+1])]) + self.description.set_subtitle(details) def _on_app_setting_changed(self, event, _): if event == "report-level": diff --git a/cozy/ui/widgets/welcome_dialog.py b/cozy/ui/widgets/welcome_dialog.py new file mode 100644 index 00000000..95885259 --- /dev/null +++ b/cozy/ui/widgets/welcome_dialog.py @@ -0,0 +1,33 @@ +import inject +from gi.repository import Adw, Gtk + +from cozy.view_model.playback_speed_view_model import PlaybackSpeedViewModel +from cozy.ui.widgets.error_reporting import ErrorReporting +from cozy.settings import ApplicationSettings + + +@Gtk.Template.from_resource('/com/github/geigi/cozy/ui/welcome_dialog.ui') +class WelcomeDialog(Adw.Dialog): + __gtype_name__ = "WelcomeDialog" + + app_settings: ApplicationSettings = inject.attr(ApplicationSettings) + + carousel: Adw.Carousel = Gtk.Template.Child() + welcome_page: Adw.StatusPage = Gtk.Template.Child() + reporting_page: Gtk.Box = Gtk.Template.Child() + + def __init__(self): + super().__init__() + self.order = [self.welcome_page, self.reporting_page, self.welcome_page] + + @Gtk.Template.Callback() + def deny_reporting(self, _): + self.app_settings.report_level = 0 + self.advance() + + @Gtk.Template.Callback() + def accept_reporting(self, _): + self.advance() + + def advance(self): + self.carousel.scroll_to(self.order[int(self.carousel.get_position()) + 1], True) diff --git a/data/gresource.xml b/data/gresource.xml index 79f0f758..131384ab 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -18,6 +18,7 @@ ui/storage_locations.ui ui/storage_row.ui ui/timer_popover.ui + ui/welcome_dialog.ui ../AUTHORS.md diff --git a/data/ui/error_reporting.blp b/data/ui/error_reporting.blp index 6f59a85d..3494e89d 100644 --- a/data/ui/error_reporting.blp +++ b/data/ui/error_reporting.blp @@ -1,174 +1,49 @@ using Gtk 4.0; +using Adw 1; -Adjustment verbose_adjustment { - lower: 1; - upper: 5; - value: 3; - step-increment: 1; - page-increment: 1; - page-size: 1; -} template $ErrorReporting: Box { orientation: vertical; - spacing: 10; - - styles [ - "card", - ] - - Box header_box { - halign: center; - spacing: 15; + spacing: 12; + + Label { + halign: fill; + hexpand: true; + label: C_("Error and crash reporting dialog", "You can help us improve Cozy by contributing information in case of errors and crashes."); + wrap: true; + xalign: 0; + } - Label { - label: _("User feedback"); - wrap: true; + Label { + halign: fill; + hexpand: true; + label: C_("Error and crash reporting dialog", "Contributing this information is optional and completely anonymous. We will never collect personal data, files you import or any information that could identify you."); + wrap: true; + xalign: 0; + } - styles [ - "title-3", - ] - } + Label { + halign: fill; + hexpand: true; + label: C_("Error and crash reporting dialog", "Cozy is developed in the open, and the error reporting code can be inspected here:"); + wrap: true; + xalign: 0; + } - Image { - pixel-size: 32; - icon-name: 'book-alert-symbolic'; - } + LinkButton { + label: _("Sourcecode on GitHub"); + uri: "https://github.com/geigi/cozy/tree/master/cozy/report"; + margin-bottom: 6; } ListBox { + styles ["boxed-list"] selection-mode: none; - activate-on-single-click: false; - - styles [ - "transparent_bg", - ] - ListBoxRow { - activatable: false; - selectable: false; - - child: Box { - margin-start: 10; - margin-end: 10; - margin-top: 10; - margin-bottom: 10; - orientation: vertical; - spacing: 10; - - Box { - valign: center; - orientation: vertical; - spacing: 10; - - Label { - halign: start; - valign: center; - label: C_("Error and crash reporting dialog", "You can help improve Cozy by contributing information in case of errors and crashes. "); - wrap: true; - xalign: 0; - } - - Label { - valign: center; - label: C_("Error and crash reporting dialog", "Contributing this information is optional and completely anonymous. We will never collect personal data, files you import or any information that could identify you."); - wrap: true; - xalign: 0; - } - - Label { - valign: center; - halign: start; - label: C_("Error and crash reporting dialog", "Cozy is opensource and the user feedback source code can be inspected here: "); - wrap: true; - xalign: 0; - } - } - - Label { - label: _("Sourcecode on GitHub"); - use-markup: true; - } - }; + Adw.ComboRow detail_combo { + title: "Detail Level"; } - ListBoxRow { - activatable: false; - selectable: false; - - child: Separator { - margin-start: 5; - margin-end: 5; - }; - } - - ListBoxRow { - activatable: false; - selectable: false; - - child: Scale verbose_scale { - focusable: true; - margin-start: 20; - margin-end: 20; - margin-top: 10; - margin-bottom: 10; - adjustment: verbose_adjustment; - restrict-to-fill-level: false; - fill-level: 0; - round-digits: 0; - }; - } - - ListBoxRow { - activatable: false; - selectable: false; - - child: Separator { - margin-start: 5; - margin-end: 5; - }; - } - - ListBoxRow { - activatable: false; - selectable: false; - - child: Box { - halign: start; - valign: center; - margin-start: 10; - margin-end: 10; - margin-top: 10; - margin-bottom: 10; - orientation: vertical; - spacing: 10; - - Label level_label { - halign: start; - label: _("Detailed error reporting with import errors"); - wrap: true; - xalign: 0; - - styles [ - "heading", - ] - } - - Label description_label { - halign: start; - valign: start; - label: _("The following information will be sent in case of an error or crash:"); - wrap: true; - xalign: 0; - } - - Label details_label { - vexpand: true; - halign: start; - valign: start; - wrap: true; - } - }; - } + Adw.ActionRow description {} } -} +} \ No newline at end of file diff --git a/data/ui/meson.build b/data/ui/meson.build index 12bfd3b0..37763e49 100644 --- a/data/ui/meson.build +++ b/data/ui/meson.build @@ -19,6 +19,7 @@ blueprints = custom_target('blueprints', 'storage_locations.blp', 'storage_row.blp', 'timer_popover.blp', + 'welcome_dialog.blp', ), output: '.', command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'], diff --git a/data/ui/welcome_dialog.blp b/data/ui/welcome_dialog.blp new file mode 100644 index 00000000..981329b7 --- /dev/null +++ b/data/ui/welcome_dialog.blp @@ -0,0 +1,51 @@ +using Gtk 4.0; +using Adw 1; + +template $WelcomeDialog: Adw.Dialog { + width-request: 360; + content-width: 450; + content-height: 600; + + Adw.Carousel carousel { + Adw.StatusPage welcome_page { + icon-name: 'com.github.geigi.cozy'; + title: "Let's Get Cozy!"; + hexpand: true; + } + + Box reporting_page{ + orientation: vertical; + spacing: 18; + + Adw.StatusPage { + valign: start; + margin-start: 12; + margin-end: 12; + title: "Error Reporting"; + + styles ["compact"] + + child: $ErrorReporting {}; + } + + CenterBox { + valign: end; + vexpand: true; + + [center] + Box { + spacing: 12; + margin-bottom: 18; + Button { + label: "I Don't Want This"; + clicked => $deny_reporting(); + } + Button { + label: "It's Fine With Me"; + clicked => $accept_reporting(); + } + } + } + } + } +} \ No newline at end of file From 05a5979ca1fa69ac4be21d228137de8376b91aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9v=C3=A9nyi=20Benedek?= Date: Mon, 2 Dec 2024 19:41:50 +0100 Subject: [PATCH 2/7] Call it error reporting in the preferences dialog --- data/ui/preferences.blp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/ui/preferences.blp b/data/ui/preferences.blp index 312dcb74..e608c13a 100644 --- a/data/ui/preferences.blp +++ b/data/ui/preferences.blp @@ -100,7 +100,7 @@ template $PreferencesWindow: Adw.PreferencesDialog { title: _("Feedback"); Adw.PreferencesGroup user_feedback_preference_group { - title: _("User Feedback"); + title: _("Error Reporting"); } } } From daa957610107e1434cb28fb7f5f020fb1e69f7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9v=C3=A9nyi=20Benedek?= Date: Mon, 2 Dec 2024 21:41:44 +0100 Subject: [PATCH 3/7] Add initial storage location selection page --- cozy/settings.py | 8 +++++ cozy/ui/main_view.py | 4 +-- cozy/ui/widgets/welcome_dialog.py | 15 ++++++++-- data/com.github.geigi.cozy.gschema.xml | 5 ++++ data/ui/welcome_dialog.blp | 41 ++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 4 deletions(-) diff --git a/cozy/settings.py b/cozy/settings.py index d98123c2..1f582452 100644 --- a/cozy/settings.py +++ b/cozy/settings.py @@ -94,6 +94,14 @@ def last_launched_version(self) -> str: def last_launched_version(self, new_value: str): self._settings.set_string("last-launched-version", new_value) + @property + def first_launch(self) -> bool: + return self._settings.get_boolean("first-launch") + + @first_launch.setter + def first_launch(self, new_value: bool): + self._settings.set_boolean("first-launch", new_value) + @property def rewind_duration(self) -> int: return self._settings.get_int("rewind-duration") diff --git a/cozy/ui/main_view.py b/cozy/ui/main_view.py index b73d9bad..4274947d 100644 --- a/cozy/ui/main_view.py +++ b/cozy/ui/main_view.py @@ -92,8 +92,8 @@ def __init_window(self): self.drop_revealer: Gtk.Revealer = self.window_builder.get_object("drop_revealer") self.navigation_view.add(BookDetailView()) - - WelcomeDialog().present(self.window) + if self.application_settings.first_launch: + WelcomeDialog().present(self.window) self.window.present() diff --git a/cozy/ui/widgets/welcome_dialog.py b/cozy/ui/widgets/welcome_dialog.py index 95885259..096354d8 100644 --- a/cozy/ui/widgets/welcome_dialog.py +++ b/cozy/ui/widgets/welcome_dialog.py @@ -5,6 +5,8 @@ from cozy.ui.widgets.error_reporting import ErrorReporting from cozy.settings import ApplicationSettings +from cozy.ui.widgets.storages import ask_storage_location + @Gtk.Template.from_resource('/com/github/geigi/cozy/ui/welcome_dialog.ui') class WelcomeDialog(Adw.Dialog): @@ -15,19 +17,28 @@ class WelcomeDialog(Adw.Dialog): carousel: Adw.Carousel = Gtk.Template.Child() welcome_page: Adw.StatusPage = Gtk.Template.Child() reporting_page: Gtk.Box = Gtk.Template.Child() + locations_page: Adw.StatusPage = Gtk.Template.Child() def __init__(self): super().__init__() - self.order = [self.welcome_page, self.reporting_page, self.welcome_page] + self.order = [self.welcome_page, self.reporting_page, self.locations_page] @Gtk.Template.Callback() def deny_reporting(self, _): - self.app_settings.report_level = 0 self.advance() + self.app_settings.report_level = 0 @Gtk.Template.Callback() def accept_reporting(self, _): self.advance() + @Gtk.Template.Callback() + def done(self, _): + self.close() + + @Gtk.Template.Callback() + def choose_directory(self, _): + ask_storage_location(inject.instance("MainWindow")._set_audiobook_path, None) + def advance(self): self.carousel.scroll_to(self.order[int(self.carousel.get_position()) + 1], True) diff --git a/data/com.github.geigi.cozy.gschema.xml b/data/com.github.geigi.cozy.gschema.xml index b8ddeec9..c940ad6b 100644 --- a/data/com.github.geigi.cozy.gschema.xml +++ b/data/com.github.geigi.cozy.gschema.xml @@ -1,6 +1,11 @@ + + true + Whether to show the welcome dialog + + false Follow symlinks diff --git a/data/ui/welcome_dialog.blp b/data/ui/welcome_dialog.blp index 981329b7..cee6aae7 100644 --- a/data/ui/welcome_dialog.blp +++ b/data/ui/welcome_dialog.blp @@ -47,5 +47,46 @@ template $WelcomeDialog: Adw.Dialog { } } } + Adw.StatusPage locations_page { + title: _("Add Audiobooks"); + margin-start: 12; + margin-end: 12; + + child: Box { + orientation: vertical; + spacing: 18; + + Adw.PreferencesGroup { + Adw.SwitchRow { + title: _("Create Default Audiobooks Directory"); + subtitle: _("This will create a dedicated directory for audiobooks in your home directory"); + } + + Adw.ActionRow { + selectable: false; + activatable: true; + title: _("Audiobooks Directory"); + subtitle: _("You can add more locations in the settings"); + + activated => $choose_directory(); + + [suffix] + Button { + icon-name: "folder-open-symbolic"; + valign: center; + } + } + } + + Button { + label: _("Done"); + halign: center; + + styles ["pill", "suggested-action"] + + clicked => $done(); + } + }; + } } } \ No newline at end of file From 59c7fd5628123f25df088fde85fd3cfbdc89ba15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9v=C3=A9nyi=20Benedek?= Date: Tue, 3 Dec 2024 17:01:53 +0100 Subject: [PATCH 4/7] Hope I'm not doing anything stupid with the database --- cozy/application.py | 10 ---------- cozy/ui/main_view.py | 14 ++++++++------ cozy/ui/widgets/first_import_button.py | 23 ----------------------- cozy/ui/widgets/welcome_dialog.py | 20 ++++++++++++++++---- cozy/view_model/storages_view_model.py | 12 ++---------- data/gresource.xml | 1 - data/ui/first_import_button.blp | 23 ----------------------- data/ui/main_window.blp | 16 ++++++++++++++-- data/ui/meson.build | 1 - data/ui/welcome_dialog.blp | 5 +++-- 10 files changed, 43 insertions(+), 82 deletions(-) delete mode 100644 cozy/ui/widgets/first_import_button.py delete mode 100644 data/ui/first_import_button.blp diff --git a/cozy/application.py b/cozy/application.py index 4fa083ad..cd85e7ff 100644 --- a/cozy/application.py +++ b/cozy/application.py @@ -12,8 +12,6 @@ from cozy.app_controller import AppController from cozy.control.db import init_db from cozy.control.mpris import MPRIS -from cozy.db.settings import Settings -from cozy.db.storage import Storage from cozy.report import reporter from cozy.ui.main_view import CozyUI from cozy.ui.widgets.filter_list_box import FilterListBox @@ -52,14 +50,6 @@ def do_activate(self): self.app_controller = AppController(self, main_window_builder, self.ui) self.ui.activate(self.app_controller.library_view) - - if Settings.get().first_start: - Settings.update(first_start=False).execute() - - audiobooks_path = Path.home() / _("Audiobooks") - audiobooks_path.mkdir(exist_ok=True) - Storage.create(path=str(audiobooks_path), default=True) - self.add_window(self.ui.window) if platform.system().lower() == "linux": diff --git a/cozy/ui/main_view.py b/cozy/ui/main_view.py index 4274947d..271c6b91 100644 --- a/cozy/ui/main_view.py +++ b/cozy/ui/main_view.py @@ -19,11 +19,11 @@ from cozy.ui.book_detail_view import BookDetailView from cozy.ui.library_view import LibraryView from cozy.ui.preferences_window import PreferencesWindow -from cozy.ui.widgets.first_import_button import FirstImportButton from cozy.ui.widgets.welcome_dialog import WelcomeDialog from cozy.view_model.playback_control_view_model import PlaybackControlViewModel from cozy.view_model.playback_speed_view_model import PlaybackSpeedViewModel from cozy.view_model.storages_view_model import StoragesViewModel +from cozy.ui.widgets.storages import ask_storage_location log = logging.getLogger("ui") @@ -124,15 +124,17 @@ def set_hotkeys_enabled(self, enabled: bool) -> None: action.set_enabled(enabled) def __init_components(self): - path = self._settings.default_location.path if self._settings.storage_locations else None - self.import_button = FirstImportButton(self._set_audiobook_path, path) - self.get_object("welcome_status_page").set_child(self.import_button) + self.get_object("first_import_button").connect("clicked", self._on_choose_location_clicked) if not self._player.loaded_book: self.block_ui_buttons(True) self._importer.add_listener(self._on_importer_event) + def _on_choose_location_clicked(self, _): + initial_path = self._settings.default_location.path if self._settings.storage_locations else None + ask_storage_location(self._set_audiobook_path, initial_path) + def create_action( self, name: str, @@ -273,8 +275,8 @@ def _set_audiobook_path(self, path: str | None) -> None: if path is None: return - self.import_button.disable() - self._storages_view_model.add_first_storage_location(path) + self.main_stack.set_visible_child_name("import") + self._storages_view_model.add_storage_location(path, default=True) self.scan(None, None) self.fs_monitor.init_offline_mode() diff --git a/cozy/ui/widgets/first_import_button.py b/cozy/ui/widgets/first_import_button.py deleted file mode 100644 index 8cc2269e..00000000 --- a/cozy/ui/widgets/first_import_button.py +++ /dev/null @@ -1,23 +0,0 @@ -from typing import Callable - -from gi.repository import Adw, Gtk - -from .storages import ask_storage_location - - -@Gtk.Template.from_resource('/com/github/geigi/cozy/ui/first_import_button.ui') -class FirstImportButton(Gtk.Button): - __gtype_name__ = "FirstImportButton" - - stack: Gtk.Stack = Gtk.Template.Child() - label: Adw.ButtonContent = Gtk.Template.Child() - spinner: Adw.Spinner = Gtk.Template.Child() - - def __init__(self, callback: Callable[[str], None], initial_folder: str) -> None: - super().__init__() - - self.connect("clicked", lambda *_: ask_storage_location(callback, initial_folder)) - - def disable(self) -> None: - self.set_sensitive(False) - self.stack.set_visible_child(self.spinner) diff --git a/cozy/ui/widgets/welcome_dialog.py b/cozy/ui/widgets/welcome_dialog.py index 096354d8..842dbf86 100644 --- a/cozy/ui/widgets/welcome_dialog.py +++ b/cozy/ui/widgets/welcome_dialog.py @@ -1,9 +1,11 @@ +from pathlib import Path import inject from gi.repository import Adw, Gtk from cozy.view_model.playback_speed_view_model import PlaybackSpeedViewModel from cozy.ui.widgets.error_reporting import ErrorReporting from cozy.settings import ApplicationSettings +from cozy.view_model.storages_view_model import StoragesViewModel from cozy.ui.widgets.storages import ask_storage_location @@ -13,15 +15,18 @@ class WelcomeDialog(Adw.Dialog): __gtype_name__ = "WelcomeDialog" app_settings: ApplicationSettings = inject.attr(ApplicationSettings) + _storages_view_model: StoragesViewModel = inject.attr(StoragesViewModel) carousel: Adw.Carousel = Gtk.Template.Child() welcome_page: Adw.StatusPage = Gtk.Template.Child() reporting_page: Gtk.Box = Gtk.Template.Child() locations_page: Adw.StatusPage = Gtk.Template.Child() + create_directory_switch: Adw.SwitchRow = Gtk.Template.Child() def __init__(self): super().__init__() - self.order = [self.welcome_page, self.reporting_page, self.locations_page] + self._path = None + self._order = [self.welcome_page, self.reporting_page, self.locations_page] @Gtk.Template.Callback() def deny_reporting(self, _): @@ -33,12 +38,19 @@ def accept_reporting(self, _): self.advance() @Gtk.Template.Callback() - def done(self, _): + def done(self, obj): self.close() + inject.instance("MainWindow")._set_audiobook_path(self._path) + + if self.create_directory_switch.props.active: + audiobooks_dir = Path.home() / _("Audiobooks") + audiobooks_dir.mkdir(exist_ok=True) + self._storages_view_model.add_storage_location(str(audiobooks_dir)) + @Gtk.Template.Callback() def choose_directory(self, _): - ask_storage_location(inject.instance("MainWindow")._set_audiobook_path, None) + ask_storage_location(lambda path: setattr(self, "_path", path), None) def advance(self): - self.carousel.scroll_to(self.order[int(self.carousel.get_position()) + 1], True) + self.carousel.scroll_to(self._order[int(self.carousel.get_position()) + 1], True) diff --git a/cozy/view_model/storages_view_model.py b/cozy/view_model/storages_view_model.py index dbc083f3..7f38581d 100644 --- a/cozy/view_model/storages_view_model.py +++ b/cozy/view_model/storages_view_model.py @@ -44,11 +44,12 @@ def _rebase_storage_location(self, model: Storage, old_path: str) -> None: name="RebaseStorageLocationThread", ).start() - def add_storage_location(self, path: str | None) -> None: + def add_storage_location(self, path: str | None, default: bool = False) -> None: if path is None: return model = Storage.new(self._db, path) + model.default = default model.external = self._fs_monitor.is_external(path) self._model.invalidate() @@ -56,15 +57,6 @@ def add_storage_location(self, path: str | None) -> None: self._scan_new_storage(model) - def add_first_storage_location(self, path: str) -> None: - storage = self.storages[0] - storage.path = path - storage.external = self._fs_monitor.is_external(path) - assert storage.default - - self._model.invalidate() - self._notify("storage_locations") - def change_storage_location(self, model: Storage, new_path: str) -> None: old_path = model.path model.path = new_path diff --git a/data/gresource.xml b/data/gresource.xml index 131384ab..3097e36b 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -6,7 +6,6 @@ ui/book_detail.ui ui/chapter_element.ui ui/error_reporting.ui - ui/first_import_button.ui ui/headerbar.ui ui/main_window.ui ui/media_controller.ui diff --git a/data/ui/first_import_button.blp b/data/ui/first_import_button.blp deleted file mode 100644 index 2a941968..00000000 --- a/data/ui/first_import_button.blp +++ /dev/null @@ -1,23 +0,0 @@ -using Gtk 4.0; -using Adw 1; - -template $FirstImportButton: Button { - halign: center; - - Stack stack { - hhomogeneous: false; - - Adw.ButtonContent label { - halign: center; - label: _("Select Folder"); - icon-name: 'folder-open-symbolic'; - } - - Adw.Spinner spinner {} - } - - styles [ - "suggested-action", - "pill", - ] -} diff --git a/data/ui/main_window.blp b/data/ui/main_window.blp index f4765d94..d60f3335 100644 --- a/data/ui/main_window.blp +++ b/data/ui/main_window.blp @@ -216,8 +216,10 @@ Adw.ApplicationWindow app_window { show-title: false; } - content: Adw.StatusPage { - icon-name: 'com.github.geigi.cozy'; + content: Adw.StatusPage import_status_page { + paintable: Adw.SpinnerPaintable { + widget: import_status_page; + }; title: _("Importing"); description: _("Stay tuned while Cozy is preparing your library…"); }; @@ -237,6 +239,16 @@ Adw.ApplicationWindow app_window { icon-name: 'com.github.geigi.cozy'; title: _("Let's Get Cozy"); description: _("Select a folder, or drop audiobooks here to add them to your library"); + child: Button first_import_button { + halign: center; + styles ["pill", "suggested-action"] + + Adw.ButtonContent content { + halign: center; + label: _("Select Folder"); + icon-name: 'folder-open-symbolic'; + } + }; }; }; } diff --git a/data/ui/meson.build b/data/ui/meson.build index 37763e49..98c40f21 100644 --- a/data/ui/meson.build +++ b/data/ui/meson.build @@ -7,7 +7,6 @@ blueprints = custom_target('blueprints', 'book_detail.blp', 'chapter_element.blp', 'error_reporting.blp', - 'first_import_button.blp', 'headerbar.blp', 'main_window.blp', 'media_controller.blp', diff --git a/data/ui/welcome_dialog.blp b/data/ui/welcome_dialog.blp index cee6aae7..9b63c9e4 100644 --- a/data/ui/welcome_dialog.blp +++ b/data/ui/welcome_dialog.blp @@ -57,9 +57,10 @@ template $WelcomeDialog: Adw.Dialog { spacing: 18; Adw.PreferencesGroup { - Adw.SwitchRow { + Adw.SwitchRow create_directory_switch { title: _("Create Default Audiobooks Directory"); subtitle: _("This will create a dedicated directory for audiobooks in your home directory"); + active: true; } Adw.ActionRow { @@ -79,8 +80,8 @@ template $WelcomeDialog: Adw.Dialog { } Button { - label: _("Done"); halign: center; + label: _("Done"); styles ["pill", "suggested-action"] From fa75c7ac7ebc2c700d6bb463a6ee575be23954df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9v=C3=A9nyi=20Benedek?= Date: Tue, 3 Dec 2024 17:38:03 +0100 Subject: [PATCH 5/7] Fix database errors with new default sotarge location logic --- cozy/ui/main_view.py | 9 ++++----- cozy/ui/widgets/book_card.py | 3 ++- cozy/ui/widgets/welcome_dialog.py | 10 ++++++---- cozy/view_model/settings_view_model.py | 3 --- cozy/view_model/storages_view_model.py | 5 ++++- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/cozy/ui/main_view.py b/cozy/ui/main_view.py index 271c6b91..87bb92eb 100644 --- a/cozy/ui/main_view.py +++ b/cozy/ui/main_view.py @@ -241,13 +241,13 @@ def check_for_tracks(self): if books().count() < 1: self.block_ui_buttons(True) - def scan(self, _, __): + def scan(self, *_): thread = Thread(target=self._importer.scan, name="ScanMediaThread") thread.start() def auto_import(self): if self.application_settings.autoscan: - self.scan(None, None) + self.scan() def __on_hide_offline(self, action, value): """ @@ -271,13 +271,12 @@ def _on_drag_data_received(self, widget, value, *_): thread.start() return True - def _set_audiobook_path(self, path: str | None) -> None: + def _set_audiobook_path(self, path: str | None, default: bool = True) -> None: if path is None: return self.main_stack.set_visible_child_name("import") - self._storages_view_model.add_storage_location(path, default=True) - self.scan(None, None) + self._storages_view_model.add_storage_location(path, default=default) self.fs_monitor.init_offline_mode() def on_close(self, widget, data=None): diff --git a/cozy/ui/widgets/book_card.py b/cozy/ui/widgets/book_card.py index 92fc0438..5d56f343 100644 --- a/cozy/ui/widgets/book_card.py +++ b/cozy/ui/widgets/book_card.py @@ -112,7 +112,8 @@ def set_playing(self, is_playing): self.play_button.set_playing(is_playing) def update_progress(self): - self.play_button.progress = self.book.progress / self.book.duration + if self.book.duration: + self.play_button.progress = self.book.progress / self.book.duration def reset(self) -> None: self.book.last_played = 0 diff --git a/cozy/ui/widgets/welcome_dialog.py b/cozy/ui/widgets/welcome_dialog.py index 842dbf86..83b921dc 100644 --- a/cozy/ui/widgets/welcome_dialog.py +++ b/cozy/ui/widgets/welcome_dialog.py @@ -38,15 +38,17 @@ def accept_reporting(self, _): self.advance() @Gtk.Template.Callback() - def done(self, obj): + def done(self, _): self.close() - inject.instance("MainWindow")._set_audiobook_path(self._path) - if self.create_directory_switch.props.active: audiobooks_dir = Path.home() / _("Audiobooks") audiobooks_dir.mkdir(exist_ok=True) - self._storages_view_model.add_storage_location(str(audiobooks_dir)) + self._storages_view_model.add_storage_location(str(audiobooks_dir), default=True) + + inject.instance("MainWindow")._set_audiobook_path(self._path, default=False) + else: + inject.instance("MainWindow")._set_audiobook_path(self._path) @Gtk.Template.Callback() def choose_directory(self, _): diff --git a/cozy/view_model/settings_view_model.py b/cozy/view_model/settings_view_model.py index 4a8e9a93..f1066d60 100644 --- a/cozy/view_model/settings_view_model.py +++ b/cozy/view_model/settings_view_model.py @@ -22,9 +22,6 @@ def __init__(self): self._lock_ui: bool = False - if self._model.first_start: - self._importer.scan() - @property def lock_ui(self) -> bool: return self._lock_ui diff --git a/cozy/view_model/storages_view_model.py b/cozy/view_model/storages_view_model.py index 7f38581d..9c25fade 100644 --- a/cozy/view_model/storages_view_model.py +++ b/cozy/view_model/storages_view_model.py @@ -1,4 +1,5 @@ import logging +from pathlib import Path from threading import Thread import inject @@ -55,7 +56,9 @@ def add_storage_location(self, path: str | None, default: bool = False) -> None: self._model.invalidate() self._notify("storage_locations") - self._scan_new_storage(model) + if any(Path(path).iterdir()): + # Do this check here, because importer has an entirely different mechanism + self._scan_new_storage(model) def change_storage_location(self, model: Storage, new_path: str) -> None: old_path = model.path From 72155759b6144c3826d853bf148e9c9f653c0e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9v=C3=A9nyi=20Benedek?= Date: Tue, 3 Dec 2024 19:51:50 +0100 Subject: [PATCH 6/7] Finish up welcome dialog, correct some strings --- cozy/ui/widgets/welcome_dialog.py | 26 +++++++++++++++++--------- data/ui/error_reporting.blp | 4 ++-- data/ui/main_window.blp | 4 ++-- data/ui/welcome_dialog.blp | 26 ++++++++++++++++++++++---- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/cozy/ui/widgets/welcome_dialog.py b/cozy/ui/widgets/welcome_dialog.py index 83b921dc..155bf5f8 100644 --- a/cozy/ui/widgets/welcome_dialog.py +++ b/cozy/ui/widgets/welcome_dialog.py @@ -1,5 +1,6 @@ from pathlib import Path import inject +import os.path from gi.repository import Adw, Gtk from cozy.view_model.playback_speed_view_model import PlaybackSpeedViewModel @@ -22,24 +23,38 @@ class WelcomeDialog(Adw.Dialog): reporting_page: Gtk.Box = Gtk.Template.Child() locations_page: Adw.StatusPage = Gtk.Template.Child() create_directory_switch: Adw.SwitchRow = Gtk.Template.Child() + chooser_button_label: Adw.ButtonContent = Gtk.Template.Child() def __init__(self): super().__init__() self._path = None self._order = [self.welcome_page, self.reporting_page, self.locations_page] + @Gtk.Template.Callback() + def advance(self, *_): + self.carousel.scroll_to(self._order[int(self.carousel.get_position()) + 1], True) + @Gtk.Template.Callback() def deny_reporting(self, _): - self.advance() self.app_settings.report_level = 0 + self.advance() @Gtk.Template.Callback() def accept_reporting(self, _): self.advance() @Gtk.Template.Callback() - def done(self, _): + def choose_directory(self, _): + ask_storage_location(self._ask_storage_location_callback, None) + + def _ask_storage_location_callback(self, path): + self._path = path + self.chooser_button_label.set_label(os.path.basename(path)) + + @Gtk.Template.Callback() + def done(self, __chooser_button_label): self.close() + self.app_settings.first_launch = False if self.create_directory_switch.props.active: audiobooks_dir = Path.home() / _("Audiobooks") @@ -49,10 +64,3 @@ def done(self, _): inject.instance("MainWindow")._set_audiobook_path(self._path, default=False) else: inject.instance("MainWindow")._set_audiobook_path(self._path) - - @Gtk.Template.Callback() - def choose_directory(self, _): - ask_storage_location(lambda path: setattr(self, "_path", path), None) - - def advance(self): - self.carousel.scroll_to(self._order[int(self.carousel.get_position()) + 1], True) diff --git a/data/ui/error_reporting.blp b/data/ui/error_reporting.blp index 3494e89d..3ffa8024 100644 --- a/data/ui/error_reporting.blp +++ b/data/ui/error_reporting.blp @@ -4,7 +4,7 @@ using Adw 1; template $ErrorReporting: Box { orientation: vertical; - spacing: 12; + spacing: 8; Label { halign: fill; @@ -41,7 +41,7 @@ template $ErrorReporting: Box { selection-mode: none; Adw.ComboRow detail_combo { - title: "Detail Level"; + title: _("Detail Level"); } Adw.ActionRow description {} diff --git a/data/ui/main_window.blp b/data/ui/main_window.blp index d60f3335..0f59af78 100644 --- a/data/ui/main_window.blp +++ b/data/ui/main_window.blp @@ -178,7 +178,7 @@ Adw.ApplicationWindow app_window { child: Adw.StatusPage { icon-name: 'library-symbolic'; - title: _("No Recent Books yet"); + title: _("No Recent Books Yet"); description: _("Explore your library by switching to the Author or Reader view"); }; } @@ -237,7 +237,7 @@ Adw.ApplicationWindow app_window { content: Adw.StatusPage welcome_status_page { icon-name: 'com.github.geigi.cozy'; - title: _("Let's Get Cozy"); + title: _("Let's Get Cozy!"); description: _("Select a folder, or drop audiobooks here to add them to your library"); child: Button first_import_button { halign: center; diff --git a/data/ui/welcome_dialog.blp b/data/ui/welcome_dialog.blp index 9b63c9e4..10836da9 100644 --- a/data/ui/welcome_dialog.blp +++ b/data/ui/welcome_dialog.blp @@ -7,15 +7,26 @@ template $WelcomeDialog: Adw.Dialog { content-height: 600; Adw.Carousel carousel { + interactive: false; + Adw.StatusPage welcome_page { icon-name: 'com.github.geigi.cozy'; title: "Let's Get Cozy!"; hexpand: true; + + Button { + halign: center; + label: _("Start!"); + + styles ["pill", "suggested-action"] + + clicked => $advance(); + } } Box reporting_page{ orientation: vertical; - spacing: 18; + spacing: 12; Adw.StatusPage { valign: start; @@ -35,13 +46,15 @@ template $WelcomeDialog: Adw.Dialog { [center] Box { spacing: 12; - margin-bottom: 18; + margin-bottom: 12; + homogeneous: true; + Button { label: "I Don't Want This"; clicked => $deny_reporting(); } Button { - label: "It's Fine With Me"; + label: "It's Fine by Me"; clicked => $accept_reporting(); } } @@ -73,8 +86,13 @@ template $WelcomeDialog: Adw.Dialog { [suffix] Button { - icon-name: "folder-open-symbolic"; valign: center; + clicked => $choose_directory(); + + Adw.ButtonContent chooser_button_label { + icon-name: "folder-open-symbolic"; + can-shrink: true; + } } } } From 69a2e3b09a4115ee998d636981da6b29775220b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9v=C3=A9nyi=20Benedek?= Date: Tue, 3 Dec 2024 20:00:44 +0100 Subject: [PATCH 7/7] Format changed files --- cozy/application.py | 3 +-- cozy/ui/main_view.py | 2 +- cozy/ui/widgets/book_card.py | 2 +- cozy/ui/widgets/error_reporting.py | 22 +++++++++++----------- cozy/ui/widgets/welcome_dialog.py | 10 ++++------ 5 files changed, 18 insertions(+), 21 deletions(-) diff --git a/cozy/application.py b/cozy/application.py index cd85e7ff..45768e30 100644 --- a/cozy/application.py +++ b/cozy/application.py @@ -2,7 +2,6 @@ import platform import sys import threading -from pathlib import Path from traceback import format_exception import distro @@ -27,7 +26,7 @@ class Application(Adw.Application): def __init__(self, pkgdatadir: str): self.pkgdatadir = pkgdatadir - super().__init__(application_id='com.github.geigi.cozy') + super().__init__(application_id="com.github.geigi.cozy") self.init_custom_widgets() GLib.setenv("PULSE_PROP_media.role", "music", True) diff --git a/cozy/ui/main_view.py b/cozy/ui/main_view.py index 87bb92eb..076664b9 100644 --- a/cozy/ui/main_view.py +++ b/cozy/ui/main_view.py @@ -19,11 +19,11 @@ from cozy.ui.book_detail_view import BookDetailView from cozy.ui.library_view import LibraryView from cozy.ui.preferences_window import PreferencesWindow +from cozy.ui.widgets.storages import ask_storage_location from cozy.ui.widgets.welcome_dialog import WelcomeDialog from cozy.view_model.playback_control_view_model import PlaybackControlViewModel from cozy.view_model.playback_speed_view_model import PlaybackSpeedViewModel from cozy.view_model.storages_view_model import StoragesViewModel -from cozy.ui.widgets.storages import ask_storage_location log = logging.getLogger("ui") diff --git a/cozy/ui/widgets/book_card.py b/cozy/ui/widgets/book_card.py index 5d56f343..d8a6ad58 100644 --- a/cozy/ui/widgets/book_card.py +++ b/cozy/ui/widgets/book_card.py @@ -49,7 +49,7 @@ def do_snapshot(self, snapshot: Gtk.Snapshot) -> None: context.stroke() -@Gtk.Template.from_resource('/com/github/geigi/cozy/ui/book_card.ui') +@Gtk.Template.from_resource("/com/github/geigi/cozy/ui/book_card.ui") class BookCard(Gtk.FlowBoxChild): __gtype_name__ = "BookCard" diff --git a/cozy/ui/widgets/error_reporting.py b/cozy/ui/widgets/error_reporting.py index 880c75ec..1be4a51e 100644 --- a/cozy/ui/widgets/error_reporting.py +++ b/cozy/ui/widgets/error_reporting.py @@ -9,28 +9,29 @@ _("Disabled"), _("Basic error reporting"), _("Detailed error reporting"), - _("Detailed, with media types") + _("Detailed, with media types"), ] LEVEL_DESCRIPTION = [ _("No error or crash reporting."), - _("The following information will be sent in case of an error or crash:") + _("The following information will be sent in case of an error or crash:"), ] LEVEL_DETAILS = [ [], - [_("Which type of error occurred"), + [ + _("Which type of error occurred"), _("Line of code where an error occurred"), - _("Cozy's version"), ], - [_("Linux distribution"), - _("Desktop environment")], - [_("Media type of files that Cozy couldn't import")] + _("Cozy's version"), + ], + [_("Linux distribution"), _("Desktop environment")], + [_("Media type of files that Cozy couldn't import")], ] -@Gtk.Template.from_resource('/com/github/geigi/cozy/ui/error_reporting.ui') +@Gtk.Template.from_resource("/com/github/geigi/cozy/ui/error_reporting.ui") class ErrorReporting(Gtk.Box): - __gtype_name__ = 'ErrorReporting' + __gtype_name__ = "ErrorReporting" description: Adw.ActionRow = Gtk.Template.Child() detail_combo: Adw.ComboRow = Gtk.Template.Child() @@ -61,10 +62,9 @@ def _level_selected(self, obj, param) -> None: def _update_description(self, level: int): self.description.set_title(LEVEL_DESCRIPTION[min(level, 1)]) - details = "\n".join(["• " + i for i in chain(*LEVEL_DETAILS[:level+1])]) + details = "\n".join(["• " + i for i in chain(*LEVEL_DETAILS[: level + 1])]) self.description.set_subtitle(details) def _on_app_setting_changed(self, event, _): if event == "report-level": self._load_report_level() - diff --git a/cozy/ui/widgets/welcome_dialog.py b/cozy/ui/widgets/welcome_dialog.py index 155bf5f8..8a273c08 100644 --- a/cozy/ui/widgets/welcome_dialog.py +++ b/cozy/ui/widgets/welcome_dialog.py @@ -1,17 +1,15 @@ +import os.path from pathlib import Path + import inject -import os.path from gi.repository import Adw, Gtk -from cozy.view_model.playback_speed_view_model import PlaybackSpeedViewModel -from cozy.ui.widgets.error_reporting import ErrorReporting from cozy.settings import ApplicationSettings -from cozy.view_model.storages_view_model import StoragesViewModel - from cozy.ui.widgets.storages import ask_storage_location +from cozy.view_model.storages_view_model import StoragesViewModel -@Gtk.Template.from_resource('/com/github/geigi/cozy/ui/welcome_dialog.ui') +@Gtk.Template.from_resource("/com/github/geigi/cozy/ui/welcome_dialog.ui") class WelcomeDialog(Adw.Dialog): __gtype_name__ = "WelcomeDialog"