diff --git a/data/com.vixalien.muzika.data.gresource.xml b/data/com.vixalien.muzika.data.gresource.xml index 8cf1de99..feddf39f 100644 --- a/data/com.vixalien.muzika.data.gresource.xml +++ b/data/com.vixalien.muzika.data.gresource.xml @@ -86,6 +86,7 @@ icons/scalable/actions/playlist2-symbolic.svg icons/scalable/actions/profit-symbolic.svg icons/scalable/actions/progress-symbolic.svg + icons/scalable/actions/refresh-symbolic.svg icons/scalable/actions/sentiment-satisfied-symbolic.svg icons/scalable/actions/skip-backwards-10-symbolic.svg icons/scalable/actions/skip-forward-10-symbolic.svg diff --git a/data/icons/scalable/actions/refresh-symbolic.svg b/data/icons/scalable/actions/refresh-symbolic.svg new file mode 100644 index 00000000..485f106a --- /dev/null +++ b/data/icons/scalable/actions/refresh-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/data/ui/components/library/history.blp b/data/ui/components/library/history.blp index dce8bf4a..1bb1b455 100644 --- a/data/ui/components/library/history.blp +++ b/data/ui/components/library/history.blp @@ -4,7 +4,13 @@ using Adw 1; template $HistoryPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: Adw.BreakpointBin { width-request: 200; diff --git a/data/ui/components/library/songs.blp b/data/ui/components/library/songs.blp index c1357d3c..40c50661 100644 --- a/data/ui/components/library/songs.blp +++ b/data/ui/components/library/songs.blp @@ -4,7 +4,13 @@ using Adw 1; template $LibrarySongsPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: Adw.BreakpointBin { width-request: 200; diff --git a/data/ui/components/nav/page.blp b/data/ui/components/nav/page.blp index 45bd5cb7..9e7da09f 100644 --- a/data/ui/components/nav/page.blp +++ b/data/ui/components/nav/page.blp @@ -4,6 +4,7 @@ using Adw 1; template $Page : $AdwNavigationPage { Stack stack { vhomogeneous: false; + transition-type: crossfade; Adw.Bin loading { Adw.ToolbarView { diff --git a/data/ui/pages/album.blp b/data/ui/pages/album.blp index efd10b5e..4f1fde2a 100644 --- a/data/ui/pages/album.blp +++ b/data/ui/pages/album.blp @@ -5,6 +5,12 @@ template $AlbumPage : Adw.Bin { Adw.ToolbarView { [top] Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + [end] MenuButton menu { icon-name: "view-more-symbolic"; diff --git a/data/ui/pages/artist-albums.blp b/data/ui/pages/artist-albums.blp index 84eac13c..2189748f 100644 --- a/data/ui/pages/artist-albums.blp +++ b/data/ui/pages/artist-albums.blp @@ -4,7 +4,13 @@ using Adw 1; template $ArtistAlbumsPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: ScrolledWindow scrolled { hexpand: true; diff --git a/data/ui/pages/artist.blp b/data/ui/pages/artist.blp index 9105d032..f4f569be 100644 --- a/data/ui/pages/artist.blp +++ b/data/ui/pages/artist.blp @@ -5,6 +5,12 @@ template $ArtistPage : Adw.Bin { Adw.ToolbarView { [top] Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + [end] MenuButton menu { icon-name: "view-more-symbolic"; diff --git a/data/ui/pages/authentication-error.blp b/data/ui/pages/authentication-error.blp index d17013be..25239103 100644 --- a/data/ui/pages/authentication-error.blp +++ b/data/ui/pages/authentication-error.blp @@ -1,74 +1,85 @@ using Gtk 4.0; using Adw 1; -template $AuthenticationErrorPage : Box { - Adw.StatusPage status { - hexpand: true; - icon-name: "system-lock-screen-symbolic"; - title: _("Authentication is required"); +template $AuthenticationErrorPage : Adw.Bin { + Adw.ToolbarView { + [top] + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } - Box more { - orientation: vertical; - spacing: 6; + Adw.StatusPage status { + hexpand: true; + icon-name: 'system-lock-screen-symbolic'; + title: _('Authentication is required'); - Box buttons { - halign: center; - margin-bottom: 12; + Box more { + orientation: vertical; spacing: 6; - Button { - label: _("Log in"); - action-name: "win.login"; + Box buttons { + halign: center; + margin-bottom: 12; + spacing: 6; - styles [ - "pill", - "suggested-action", - ] - } + Button { + label: _('Log in'); + action-name: 'win.login'; - Button home_button { - label: _("Go to Home"); - - styles [ - "pill", - ] - } - } + styles [ + "pill", + "suggested-action", + ] + } - Expander expander { - label: _("Error Details"); - halign: center; - } + Button home_button { + label: _('Go to Home'); - Revealer { - reveal-child: bind expander.expanded; + styles [ + "pill", + ] + } + } - Adw.Clamp { - maximum-size: 1000; - tightening-threshold: 600; + Expander expander { + label: _('Error Details'); + halign: center; + } - Box { - margin-top: 6; - margin-bottom: 6; - margin-end: 6; - margin-start: 6; + Revealer { + reveal-child: bind-property expander.expanded; - styles [ - "card", - ] + Adw.Clamp { + maximum-size: 1000; + tightening-threshold: 600; - TextView text_view { - hexpand: true; - top-margin: 12; - bottom-margin: 12; - left-margin: 12; - right-margin: 12; - wrap-mode: word_char; - editable: false; + Box { + margin-top: 6; + margin-bottom: 6; + margin-end: 6; + margin-start: 6; styles [ - "transparent", + "card", ] + + TextView text_view { + hexpand: true; + top-margin: 12; + bottom-margin: 12; + left-margin: 12; + right-margin: 12; + wrap-mode: word_char; + editable: false; + + styles [ + "transparent", + ] + } } } } diff --git a/data/ui/pages/channel-playlists.blp b/data/ui/pages/channel-playlists.blp index e3da460d..f7f33667 100644 --- a/data/ui/pages/channel-playlists.blp +++ b/data/ui/pages/channel-playlists.blp @@ -4,7 +4,13 @@ using Adw 1; template $ChannelPlaylistsPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: ScrolledWindow scrolled { hexpand: true; diff --git a/data/ui/pages/channel.blp b/data/ui/pages/channel.blp index 559136b2..1e99d74d 100644 --- a/data/ui/pages/channel.blp +++ b/data/ui/pages/channel.blp @@ -5,6 +5,12 @@ template $ChannelPage : Adw.Bin { Adw.ToolbarView { [top] Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + [end] MenuButton menu { icon-name: "view-more-symbolic"; diff --git a/data/ui/pages/charts.blp b/data/ui/pages/charts.blp index 10755483..e3b13dff 100644 --- a/data/ui/pages/charts.blp +++ b/data/ui/pages/charts.blp @@ -4,7 +4,13 @@ using Adw 1; template $ChartsPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: ScrolledWindow scrolled { vexpand: true; diff --git a/data/ui/pages/error.blp b/data/ui/pages/error.blp index 4def4b13..b430403a 100644 --- a/data/ui/pages/error.blp +++ b/data/ui/pages/error.blp @@ -1,51 +1,62 @@ using Gtk 4.0; using Adw 1; -template $ErrorPage : Box { - Adw.StatusPage status { - hexpand: true; - icon-name: "dialog-question-symbolic"; - title: _("An error occurred"); - description: ""; - - Box more { - orientation: vertical; - spacing: 6; - - Expander expander { - label: _("Error Details"); - halign: center; +template $ErrorPage : Adw.Bin { + Adw.ToolbarView { + [top] + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; } + } + + Adw.StatusPage status { + hexpand: true; + icon-name: 'dialog-question-symbolic'; + title: _('An error occurred'); + description: ''; - Revealer { - reveal-child: bind expander.expanded; + Box more { + orientation: vertical; + spacing: 6; - Adw.Clamp { - maximum-size: 1000; - tightening-threshold: 600; + Expander expander { + label: _('Error Details'); + halign: center; + } - Box { - margin-top: 6; - margin-bottom: 6; - margin-end: 6; - margin-start: 6; + Revealer { + reveal-child: bind expander.expanded; - styles [ - "card", - ] + Adw.Clamp { + maximum-size: 1000; + tightening-threshold: 600; - TextView text_view { - hexpand: true; - top-margin: 12; - bottom-margin: 12; - left-margin: 12; - right-margin: 12; - wrap-mode: word_char; - editable: false; + Box { + margin-top: 6; + margin-bottom: 6; + margin-end: 6; + margin-start: 6; styles [ - "transparent", + "card", ] + + TextView text_view { + hexpand: true; + top-margin: 12; + bottom-margin: 12; + left-margin: 12; + right-margin: 12; + wrap-mode: word_char; + editable: false; + + styles [ + "transparent", + ] + } } } } diff --git a/data/ui/pages/explore.blp b/data/ui/pages/explore.blp index 61de58cf..05f766e6 100644 --- a/data/ui/pages/explore.blp +++ b/data/ui/pages/explore.blp @@ -4,7 +4,13 @@ using Adw 1; template $ExplorePage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: ScrolledWindow scrolled { vexpand: true; diff --git a/data/ui/pages/home.blp b/data/ui/pages/home.blp index 3583b413..07da0033 100644 --- a/data/ui/pages/home.blp +++ b/data/ui/pages/home.blp @@ -4,7 +4,13 @@ using Adw 1; template $HomePage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: ScrolledWindow scrolled { vexpand: true; diff --git a/data/ui/pages/login.blp b/data/ui/pages/login.blp index b70e3ff3..94afb54a 100644 --- a/data/ui/pages/login.blp +++ b/data/ui/pages/login.blp @@ -97,6 +97,14 @@ template $LoginPage : Adw.Window { } } } + + Button { + label: _("Get a new code"); + halign: center; + clicked => $refresh_cb(); + + styles ["pill"] + } } } } diff --git a/data/ui/pages/mood-playlists.blp b/data/ui/pages/mood-playlists.blp index daf0ea69..e262aab2 100644 --- a/data/ui/pages/mood-playlists.blp +++ b/data/ui/pages/mood-playlists.blp @@ -4,7 +4,13 @@ using Adw 1; template $MoodPlaylistsPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: ScrolledWindow scrolled { vexpand: true; diff --git a/data/ui/pages/moods.blp b/data/ui/pages/moods.blp index 27b24846..53346c3f 100644 --- a/data/ui/pages/moods.blp +++ b/data/ui/pages/moods.blp @@ -4,7 +4,13 @@ using Adw 1; template $MoodsPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: ScrolledWindow scrolled { vexpand: true; diff --git a/data/ui/pages/new-releases.blp b/data/ui/pages/new-releases.blp index 54efdd59..8b1215fc 100644 --- a/data/ui/pages/new-releases.blp +++ b/data/ui/pages/new-releases.blp @@ -4,7 +4,13 @@ using Adw 1; template $NewReleasesPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } content: ScrolledWindow scrolled { vexpand: true; diff --git a/data/ui/pages/playlist.blp b/data/ui/pages/playlist.blp index f7eb9827..a9413389 100644 --- a/data/ui/pages/playlist.blp +++ b/data/ui/pages/playlist.blp @@ -8,14 +8,20 @@ template $PlaylistPage : Adw.Bin { [top] Adw.HeaderBar { [start] - ToggleButton select { - icon-name: "selection-mode-symbolic"; + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; } [end] MenuButton menu { icon-name: "view-more-symbolic"; } + + [end] + ToggleButton select { + icon-name: "selection-mode-symbolic"; + } } content: Adw.BreakpointBin { diff --git a/data/ui/pages/search.blp b/data/ui/pages/search.blp index 819a00c8..7c7424ed 100644 --- a/data/ui/pages/search.blp +++ b/data/ui/pages/search.blp @@ -4,7 +4,13 @@ using Adw 1; template $SearchPage : Adw.Bin { Adw.ToolbarView { [top] - Adw.HeaderBar {} + Adw.HeaderBar { + [start] + Button { + icon-name: "refresh"; + action-name: "navigator.reload"; + } + } [top] Adw.Clamp { diff --git a/gi-types b/gi-types index eb2a87a2..94acb630 160000 --- a/gi-types +++ b/gi-types @@ -1 +1 @@ -Subproject commit eb2a87a25c5e2fb580b605fbec0bd312fe34c492 +Subproject commit 94acb6307e8d467cd9b3e340a18431496636b8f6 diff --git a/src/components/inline-tab-switcher.ts b/src/components/inline-tab-switcher.ts index d709357b..3d05bdc2 100644 --- a/src/components/inline-tab-switcher.ts +++ b/src/components/inline-tab-switcher.ts @@ -306,7 +306,7 @@ export class InlineTabSwitcher extends Gtk.Widget { } if (tab.navigate != null) { - button.action_name = "navigator.visit"; + button.action_name = "navigator.replace"; button.action_target = GLib.Variant.new_string(tab.navigate); } diff --git a/src/components/nav/page.ts b/src/components/nav/page.ts index aed30b59..5712fced 100644 --- a/src/components/nav/page.ts +++ b/src/components/nav/page.ts @@ -3,10 +3,11 @@ import Adw from "gi://Adw"; import GObject from "gi://GObject"; import { Loading } from "../loading"; -import { Endpoint, MuzikaComponent } from "src/navigation"; +import { MuzikaPageMeta, MuzikaPageWidget } from "src/navigation"; import { ERROR_CODE, MuseError } from "src/muse"; import { AuthenticationErrorPage } from "src/pages/authentication-error"; import { ErrorPage } from "src/pages/error"; +import { MatchResult } from "path-to-regexp"; export interface PageState { state: State; @@ -49,8 +50,6 @@ export class Page private _loading!: Loading; private _content!: Adw.Bin; - uri: string; - get loading() { return this._stack.visible_child === this._loading; } @@ -72,43 +71,31 @@ export class Page } } - page: MuzikaComponent; - endpoint: Endpoint>; + uri: string; + page?: MuzikaPageWidget; + meta: MuzikaPageMeta; + last_match?: MatchResult>; private __state: PageState | null = null; private __error: any; constructor( - uri: string, - endpoint: Endpoint>, - page: MuzikaComponent, + meta: MuzikaPageMeta, ) { super(); - this.uri = uri; + this.uri = meta.uri; - this.endpoint = endpoint; - this.page = page; - this.title = endpoint.title; + this.meta = meta; + this.title = meta.title; this.loading = true; } - loaded(data: Data) { - const page = this.endpoint.component(); - - page.present(data); - - this.loading = false; - - this.page = page; - this._content.child = page; - } - vfunc_hidden(): void { if (this.__error) { this.page = this._content.child = null as any; - } else { + } else if (this.page) { this.__state = { state: this.page.get_state(), }; @@ -118,34 +105,84 @@ export class Page vfunc_showing(): void { if (this.__state) { - const page = this.endpoint.component(); + const page = this.meta.build(); page.restore_state(this.__state.state); this.page = page; this._content.child = page; this.__state = null; } else if (this.__error) { this.show_error(this.__error); + } else if (this.last_match) { + // page never got to load + this.reload(); } } show_error(error: any) { this.__error = error; - let error_widget: Gtk.Widget; + let error_page: Gtk.Widget; if (error instanceof MuseError && error.code === ERROR_CODE.AUTH_REQUIRED) { - error_widget = new AuthenticationErrorPage({ error }); - this.title = _("Authentication Required"); + error_page = new AuthenticationErrorPage({ error }); } else { - error_widget = new ErrorPage({ error }); - this.title = _("Error"); + error_page = new ErrorPage({ error }); } - const toolbar_view = new Adw.ToolbarView(); - toolbar_view.add_top_bar(Adw.HeaderBar.new()); - toolbar_view.content = error_widget; - this.loading = false; - this._content.child = toolbar_view; + this._content.child = error_page; + } + + async load( + uri: string, + match: MatchResult>, + signal: AbortSignal, + ) { + this.loading = true; + this.uri = uri; + this.last_match = match; + + try { + const result = await this.meta.load({ + match, + set_title: this.set_title.bind(this), + signal, + url: new URL("muzika:" + uri), + })?.catch((error) => { + throw error; + }); + + this.loading = false; + this.__error = null; + + const page = this.meta.build(); + page.present(result!); + + this._content.child = this.page = page; + } catch (error) { + this._handle_error(error); + } + } + + private _handle_error(error: unknown) { + if (error instanceof DOMException && error.name == "AbortError") { + // do nothing + // TODO: maybe + // this._view.remove(page); + return; + } + + this.show_error(error); + } + + reload(_signal?: AbortSignal) { + const signal = _signal ?? new AbortController().signal; + + if (!this.last_match) { + console.error("trying to reload a page that never loaded"); + return; + } + + return this.load(this.uri, this.last_match, signal); } } diff --git a/src/navigation.ts b/src/navigation.ts index ddaddb5c..79dd19ad 100644 --- a/src/navigation.ts +++ b/src/navigation.ts @@ -6,56 +6,37 @@ import Adw from "gi://Adw"; import { match, MatchFunction, MatchResult } from "path-to-regexp"; import { Window } from "./window.js"; -import { endpoints } from "./endpoints.js"; +import { pageMetas } from "./pages.js"; import { AddActionEntries } from "./util/action.js"; import { Page } from "./components/nav/page.js"; import { list_model_to_array } from "./util/list.js"; -export type EndpointResponse = { - title?: string; - data: Data; -}; - -export interface ShowPageOptions { - title: string; - page: Gtk.Widget; - endpoint: Endpoint; - uri: string; -} - -export interface MuzikaComponent< - Data extends any, - State extends any, -> { +export interface MuzikaPageWidget + extends Gtk.Widget { __page_state?: State; present(data: Data): void; get_state(): State; restore_state(state: State): void; } -export interface EndpointContext { +export interface PageLoadContext { match: MatchResult>; url: URL; signal: AbortSignal; set_title(title: string): void; } -export type Endpoint> = - Page extends MuzikaComponent ? { - title: string; - uri: string; - component(): MuzikaComponent & Gtk.Widget; - load(context: EndpointContext): void | Promise; - } - : never; +export type MuzikaPageMeta = { + title: string; + uri: string; + build(): MuzikaPageWidget; + load(context: PageLoadContext): void | Promise; +}; export class Navigator extends GObject.Object { private _view: Adw.NavigationView; - private match_map: Map< - MatchFunction, - Endpoint> - > = new Map(); + private match_map: Map = new Map(); private stacks: Map = new Map(); @@ -97,9 +78,9 @@ export class Navigator extends GObject.Object { this.match_map = new Map(); - for (const endpoint of endpoints) { - const fn = match(endpoint.uri, {}); - this.match_map.set(fn, endpoint); + for (const page of pageMetas) { + const fn = match(page.uri, {}); + this.match_map.set(fn, page); } } @@ -122,71 +103,78 @@ export class Navigator extends GObject.Object { } }, }, + { + name: "replace", + parameter_type: "s", + activate: (_action, param) => { + if (!param) return; + + const [url] = param.get_string(); + + if (url) { + if (url.startsWith("muzika:")) { + this.replace(url.slice(7)); + } + } + }, + }, { name: "back", activate: (_) => { this.back(); }, }, + { + name: "reload", + activate: () => { + this.reload(); + }, + }, ]); return action_group; } - last_controller: AbortController | null = null; - - private show_page( - uri: string, - match: MatchResult>, - endpoint: Endpoint>, - ) { - const url = new URL("muzika:" + uri); - const component = endpoint.component(); - const page = new Page(uri, endpoint, component); + private abort_controller: AbortController | null = null; - if (this.last_controller) { - this.last_controller.abort(); + private abort_current() { + if (this.abort_controller) { + this.abort_controller.abort(); } - this.last_controller = new AbortController(); - - const response = endpoint.load({ - match, - url, - signal: this.last_controller.signal, - set_title(title) { - page.title = title; - }, - }); - - this.loading = true; + this.abort_controller = new AbortController(); + } - this._view.push(page); + private reset_abort_controller() { + this.abort_controller = null; + } - const handle_error = (error: any) => { - // TODO: handle this better + reload() { + const page = this._view.get_last_child() as Page | null; - if (error instanceof DOMException && error.name == "AbortError") { - this._view.remove(page); - return; - } + if (!page || page.loading || !(page instanceof Page)) return; - this.loading = false; - this.last_controller = null; + this.abort_current(); - page.show_error(error); - }; + return page.reload(this.abort_controller!.signal) + ?.then(() => { + this.reset_abort_controller(); + }); + } - Promise.resolve(response).then( - (data) => { - this.loading = false; - this.last_controller = null; + private show_page( + uri: string, + match: MatchResult>, + meta: MuzikaPageMeta, + ) { + this.abort_current(); - if (data != null) { - page.loaded(data); - } - }, - ).catch(handle_error); + const page = new Page(meta); + this._view.push(page); + page.load(uri, match, this.abort_controller!.signal) + .then(() => { + this.reset_abort_controller(); + }); } get current_uri(): string | null { @@ -215,12 +203,6 @@ export class Navigator extends GObject.Object { this._view.pop(); } - // reload(navigate?: boolean) { - // this.go(0, navigate); - // } - reload() { - } - private match_uri(uri: string) { // muzika:home to // muzika/home @@ -238,13 +220,13 @@ export class Navigator extends GObject.Object { ); } - for (const [fn, endpoint] of this.match_map) { + for (const [fn, meta] of this.match_map) { const match = fn(path); if (match) { return { match: match as MatchResult>, - endpoint, + meta, }; } } @@ -254,11 +236,45 @@ export class Navigator extends GObject.Object { const match = this.match_uri(uri); if (match) { - this.show_page(uri, match.match, match.endpoint); + this.show_page(uri, match.match, match.meta); this.emit("navigate"); } } + replace(uri: string) { + const page = this._view.get_last_child() as Page | null; + + if (!page || !(page instanceof Page)) return; + + const match = this.match_uri(uri); + + if (!match) { + console.error(`Tried to replace a page with an invalid uri: ${uri}`); + return; + } + + if (match.meta !== page.meta) { + console.error( + `Tried to replace a page with a uri that renders a different page. Expected: ${page.meta.uri}, got: ${match.meta.uri}`, + ); + return; + } + + this.abort_current(); + + if (match) { + page.load; + return page.load( + uri, + match.match, + this.abort_controller!.signal, + ) + ?.then(() => { + this.reset_abort_controller(); + }); + } + } + switch_stack(uri: string) { const stack = this._view.navigation_stack as Gio.ListModel< Page diff --git a/src/endpoints.ts b/src/pages.ts similarity index 75% rename from src/endpoints.ts rename to src/pages.ts index 90150747..e37bce42 100644 --- a/src/endpoints.ts +++ b/src/pages.ts @@ -1,4 +1,4 @@ -import { Endpoint, MuzikaComponent } from "./navigation.js"; +import { MuzikaPageMeta } from "./navigation.js"; import { LibraryPage } from "./pages/library/index.js"; import { HomePage } from "./pages/home.js"; @@ -21,125 +21,125 @@ import { MoodsPage } from "./pages/moods.js"; import { MoodPlaylistsPage } from "./pages/mood-playlists.js"; import { NewReleasesPage } from "./pages/new-releases.js"; -export const endpoints: Endpoint>[] = [ +export const pageMetas: MuzikaPageMeta[] = [ { uri: "home", title: _("Home"), - component: () => new HomePage(), + build: () => new HomePage(), load: HomePage.load, }, { uri: "playlist/:playlistId", title: _("Playlist"), - component: () => new PlaylistPage(), + build: () => new PlaylistPage(), load: PlaylistPage.load, }, { uri: "album/:albumId", title: _("Album"), - component: () => new AlbumPage(), + build: () => new AlbumPage(), load: AlbumPage.load, }, { uri: "artist/:channelId", title: _("Artist"), - component: () => new ArtistPage(), + build: () => new ArtistPage(), load: ArtistPage.load, }, { uri: "search/:query", title: _("Search Results"), - component: () => new SearchPage(), + build: () => new SearchPage(), load: SearchPage.load, }, { uri: "library", title: _("Library"), - component: () => new LibraryPage(), + build: () => new LibraryPage(), load: LibraryPage.load, }, { uri: "library/playlists", title: _("Library Playlists"), - component: () => new LibraryPlaylistsPage(), + build: () => new LibraryPlaylistsPage(), load: LibraryPlaylistsPage.load, }, { uri: "library/albums", title: _("Library Albums"), - component: () => new LibraryAlbumsPage(), + build: () => new LibraryAlbumsPage(), load: LibraryAlbumsPage.load, }, { uri: "library/artists", title: _("Library Artists"), - component: () => new LibraryArtistsPage(), + build: () => new LibraryArtistsPage(), load: LibraryArtistsPage.load, }, { uri: "library/subscriptions", title: _("Library Subscriptions"), - component: () => new LibrarySubscriptionsPage(), + build: () => new LibrarySubscriptionsPage(), load: LibrarySubscriptionsPage.load, }, { uri: "library/songs", title: _("Library Songs"), - component: () => new LibrarySongsPage(), + build: () => new LibrarySongsPage(), load: LibrarySongsPage.load, }, { uri: "history", title: _("History"), - component: () => new HistoryPage(), + build: () => new HistoryPage(), load: HistoryPage.load, }, { uri: "artist-albums/:channelId/:params", title: _("Artist Albums"), - component: () => new ArtistAlbumsPage(), + build: () => new ArtistAlbumsPage(), load: ArtistAlbumsPage.load, }, { uri: "channel/:channelId", title: _("Channel"), - component: () => new ChannelPage(), + build: () => new ChannelPage(), load: ChannelPage.load, }, { uri: "channel-playlists/:channelId/:params", title: _("Channel Playlists"), - component: () => new ChannelPlaylistsPage(), + build: () => new ChannelPlaylistsPage(), load: ChannelPlaylistsPage.load, }, { uri: "explore", title: _("Explore"), - component: () => new ExplorePage(), + build: () => new ExplorePage(), load: ExplorePage.load, }, { uri: "charts", title: _("Charts"), - component: () => new ChartsPage(), + build: () => new ChartsPage(), load: ChartsPage.load, }, { uri: "moods-and-genres", title: _("Moods and genres"), - component: () => new MoodsPage(), + build: () => new MoodsPage(), load: MoodsPage.load, }, { uri: "mood-playlists/:params", title: _("Mood"), - component: () => new MoodPlaylistsPage(), + build: () => new MoodPlaylistsPage(), load: MoodPlaylistsPage.load, }, { uri: "new-releases", title: _("New Releases"), - component: () => new NewReleasesPage(), + build: () => new NewReleasesPage(), load: NewReleasesPage.load, }, ]; diff --git a/src/pages/album.ts b/src/pages/album.ts index b4fb93d3..5355e335 100644 --- a/src/pages/album.ts +++ b/src/pages/album.ts @@ -13,7 +13,7 @@ import { } from "../muse.js"; import { Carousel } from "../components/carousel/index.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { MuzikaPageWidget, PageLoadContext } from "src/navigation.js"; import { PlaylistItemView } from "src/components/playlist/itemview.js"; import { PlayableContainer, PlayableList } from "src/util/playablelist.js"; import { @@ -22,11 +22,6 @@ import { } from "src/util/scrolled.js"; import { PlaylistHeader } from "src/components/playlist/header.js"; -interface AlbumState extends VScrollState { - album: AlbumResult; - track?: string; -} - GObject.type_ensure(PlaylistHeader.$gtype); GObject.type_ensure(PlaylistItemView.$gtype); @@ -35,8 +30,10 @@ interface AlbumProps { track?: string; } +interface AlbumState extends VScrollState, AlbumProps {} + export class AlbumPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "AlbumPage", @@ -217,7 +214,7 @@ export class AlbumPage extends Adw.Bin no_more = false; - static async load(context: EndpointContext) { + static async load(context: PageLoadContext) { const album = await get_album(context.match.params.albumId, { signal: context.signal, }); diff --git a/src/pages/artist-albums.ts b/src/pages/artist-albums.ts index b16197a7..697209a4 100644 --- a/src/pages/artist-albums.ts +++ b/src/pages/artist-albums.ts @@ -5,7 +5,7 @@ import Gtk from "gi://Gtk?version=4.0"; import { ArtistAlbums, get_artist_albums } from "src/muse.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { CarouselGridView } from "src/components/carousel/view/grid"; import { PlayableContainer } from "src/util/playablelist"; import { MixedCardItem } from "src/components/library/mixedcard"; @@ -21,7 +21,7 @@ interface ArtistAlbumsState extends VScrollState { GObject.type_ensure(CarouselGridView.$gtype); export class ArtistAlbumsPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "ArtistAlbumsPage", @@ -113,7 +113,7 @@ export class ArtistAlbumsPage extends Adw.Bin ); } - static async load(context: EndpointContext) { + static async load(context: PageLoadContext) { const results = await get_artist_albums( context.match.params.channelId, context.match.params.params, diff --git a/src/pages/artist.ts b/src/pages/artist.ts index 32d13a5d..16232ac9 100644 --- a/src/pages/artist.ts +++ b/src/pages/artist.ts @@ -7,7 +7,7 @@ import Gio from "gi://Gio"; import { Artist, Category, get_artist, MixedItem } from "../muse.js"; import { Carousel } from "../components/carousel/index.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { PlaylistListView } from "src/components/playlist/listview.js"; import { PlaylistItemView } from "src/components/playlist/itemview.js"; import { PlayableContainer, PlayableList } from "src/util/playablelist.js"; @@ -26,7 +26,7 @@ GObject.type_ensure(PlaylistHeader.$gtype); GObject.type_ensure(PlaylistListView.$gtype); export class ArtistPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "ArtistPage", @@ -177,7 +177,7 @@ export class ArtistPage extends Adw.Bin this._carousels.append(carousel); } - static async load(context: EndpointContext) { + static async load(context: PageLoadContext) { const artist = await get_artist(context.match.params.channelId, { signal: context.signal, }); diff --git a/src/pages/authentication-error.ts b/src/pages/authentication-error.ts index d7570e71..79eaea81 100644 --- a/src/pages/authentication-error.ts +++ b/src/pages/authentication-error.ts @@ -6,7 +6,7 @@ import Adw from "gi://Adw"; import { get_option } from "../muse.js"; import { error_to_string, ErrorPageOptions } from "./error.js"; -export class AuthenticationErrorPage extends Gtk.Box { +export class AuthenticationErrorPage extends Adw.Bin { static { GObject.registerClass({ GTypeName: "AuthenticationErrorPage", diff --git a/src/pages/channel-playlists.ts b/src/pages/channel-playlists.ts index b1048dcd..763b96d9 100644 --- a/src/pages/channel-playlists.ts +++ b/src/pages/channel-playlists.ts @@ -5,7 +5,7 @@ import Gtk from "gi://Gtk?version=4.0"; import { ChannelPlaylists, get_channel_playlists } from "src/muse.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { CarouselGridView } from "src/components/carousel/view/grid"; import { PlayableContainer } from "src/util/playablelist"; import { MixedCardItem } from "src/components/library/mixedcard"; @@ -21,7 +21,7 @@ interface ChannelPlaylistsState extends VScrollState { GObject.type_ensure(CarouselGridView.$gtype); export class ChannelPlaylistsPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "ChannelPlaylistsPage", @@ -113,7 +113,7 @@ export class ChannelPlaylistsPage extends Adw.Bin ); } - static async load(context: EndpointContext) { + static async load(context: PageLoadContext) { const results = await get_channel_playlists( context.match.params.channelId, context.match.params.params, diff --git a/src/pages/channel.ts b/src/pages/channel.ts index df68bbe6..4efd2cfd 100644 --- a/src/pages/channel.ts +++ b/src/pages/channel.ts @@ -12,7 +12,7 @@ import { } from "src/muse.js"; import { Carousel } from "../components/carousel/index.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { PlaylistListView } from "src/components/playlist/listview.js"; import { PlaylistItemView } from "src/components/playlist/itemview.js"; import { PlayableContainer, PlayableList } from "src/util/playablelist.js"; @@ -30,7 +30,7 @@ GObject.type_ensure(PlaylistHeader.$gtype); GObject.type_ensure(PlaylistListView.$gtype); export class ChannelPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "ChannelPage", @@ -156,7 +156,7 @@ export class ChannelPage extends Adw.Bin this._carousels.append(carousel); } - static async load(context: EndpointContext) { + static async load(context: PageLoadContext) { const artist = await get_channel(context.match.params.channelId, { signal: context.signal, }); diff --git a/src/pages/charts.ts b/src/pages/charts.ts index d6bf8d84..e9bc4624 100644 --- a/src/pages/charts.ts +++ b/src/pages/charts.ts @@ -11,7 +11,7 @@ import type { import { Carousel } from "../components/carousel/index.js"; import { Loading } from "../components/loading.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { MixedCardItem } from "src/components/library/mixedcard.js"; import { set_scrolled_window_initial_vscroll, @@ -25,7 +25,7 @@ export interface ChartsPageState extends VScrollState { } export class ChartsPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "ChartsPage", @@ -51,15 +51,15 @@ export class ChartsPage extends Adw.Bin if (!country || country.selected) return; this.activate_action( - `navigator.visit`, + `navigator.replace`, GLib.Variant.new_string( - `muzika:charts?country=${country.code}&replace=true`, + `muzika:charts?country=${country.code}`, ), ); }); } - static load(ctx: EndpointContext) { + static load(ctx: PageLoadContext) { return get_charts(ctx.url.searchParams.get("country") ?? undefined, { signal: ctx.signal, }); diff --git a/src/pages/error.ts b/src/pages/error.ts index ee61db32..3c15d628 100644 --- a/src/pages/error.ts +++ b/src/pages/error.ts @@ -16,7 +16,7 @@ export interface ErrorPageOptions { error?: any; } -export class ErrorPage extends Gtk.Box { +export class ErrorPage extends Adw.Bin { static { GObject.registerClass({ GTypeName: "ErrorPage", diff --git a/src/pages/explore.ts b/src/pages/explore.ts index bd43a8f2..e8566032 100644 --- a/src/pages/explore.ts +++ b/src/pages/explore.ts @@ -11,7 +11,7 @@ import type { import { Carousel } from "../components/carousel/index.js"; import { Loading } from "../components/loading.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { MixedCardItem } from "src/components/library/mixedcard.js"; import { set_scrolled_window_initial_vscroll, @@ -25,7 +25,7 @@ export interface ExplorePageState extends VScrollState { } export class ExplorePage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "ExplorePage", @@ -39,7 +39,7 @@ export class ExplorePage extends Adw.Bin contents?: ExploreContents; - static load(ctx: EndpointContext) { + static load(ctx: PageLoadContext) { return get_explore({ signal: ctx.signal, }); diff --git a/src/pages/home.ts b/src/pages/home.ts index 1a903b93..b3c1653e 100644 --- a/src/pages/home.ts +++ b/src/pages/home.ts @@ -8,7 +8,7 @@ import { get_home, Home, MixedContent } from "../muse.js"; import { Carousel } from "../components/carousel/index.js"; import { Loading } from "../components/loading.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { set_scrolled_window_initial_vscroll, VScrollState, @@ -22,7 +22,7 @@ export interface HomePageState extends VScrollState { } export class HomePage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "HomePage", @@ -52,7 +52,7 @@ export class HomePage extends Adw.Bin }); } - static load(ctx: EndpointContext) { + static load(ctx: PageLoadContext) { return get_home({ limit: 3, signal: ctx.signal }); } diff --git a/src/pages/library/base.ts b/src/pages/library/base.ts index c3c7a412..462efbd3 100644 --- a/src/pages/library/base.ts +++ b/src/pages/library/base.ts @@ -8,7 +8,7 @@ import { LibraryView } from "../../components/library/view.js"; import type { LibraryOrder, Order } from "libmuse/types/mixins/utils.js"; import { MixedCardItem } from "src/components/library/mixedcard.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { MuzikaPageWidget, PageLoadContext } from "src/navigation.js"; import { set_scrolled_window_initial_vscroll, VScrollState, @@ -56,7 +56,7 @@ interface LibraryState extends VScrollState { export class AbstractLibraryPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "AbstractLibraryPage", @@ -79,15 +79,24 @@ export class AbstractLibraryPage this.uri = options.uri; this.view = new LibraryView({ - filters: Array.from((this.orders).keys()), + filters: Array.from(this.orders.keys()), }); this.view.connect("paginate", () => { this.load_more(); }); + const headerbar = new Adw.HeaderBar(); + + headerbar.pack_start( + new Gtk.Button({ + icon_name: "refresh", + action_name: "navigator.reload", + }), + ); + this.toolbar_view = new Adw.ToolbarView(); - this.toolbar_view.add_top_bar(Adw.HeaderBar.new()); + this.toolbar_view.add_top_bar(headerbar); this.toolbar_view.content = this.view; this.child = this.toolbar_view; @@ -100,9 +109,9 @@ export class AbstractLibraryPage if (!order) return; - const url = `muzika:${this.uri}?replace=true&order=${order}`; + const url = `muzika:${this.uri}?order=${order}`; - this.activate_action("navigator.visit", GLib.Variant.new_string(url)); + this.activate_action("navigator.replace", GLib.Variant.new_string(url)); } show_library(library: LibraryResults) { @@ -170,7 +179,7 @@ export class AbstractLibraryPage static get_loader( loader: LibraryLoader | LibraryLoader, ) { - return function (context: EndpointContext) { + return function (context: PageLoadContext) { return (loader as LibraryLoader)({ signal: context.signal, order: context.url.searchParams.get("order") as LibraryOrder ?? @@ -181,7 +190,7 @@ export class AbstractLibraryPage order: context.url.searchParams.get("order"), }; }); - } as (context: EndpointContext) => Promise; + } as (context: PageLoadContext) => Promise; } static load: ReturnType; diff --git a/src/pages/library/history.ts b/src/pages/library/history.ts index d27e48da..c6652620 100644 --- a/src/pages/library/history.ts +++ b/src/pages/library/history.ts @@ -4,7 +4,7 @@ import Gtk from "gi://Gtk?version=4.0"; import { get_history, History, PlaylistItem } from "../../muse.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { PlaylistItemView } from "src/components/playlist/itemview.js"; import { SectionedPlayableContainer, @@ -63,7 +63,7 @@ interface CategoryMeta { } export class HistoryPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "HistoryPage", @@ -153,7 +153,7 @@ export class HistoryPage extends Adw.Bin this.present(state.results); } - static load(context: EndpointContext) { + static load(context: PageLoadContext) { return get_history(); } } diff --git a/src/pages/library/songs.ts b/src/pages/library/songs.ts index 9faf1521..96a5cd81 100644 --- a/src/pages/library/songs.ts +++ b/src/pages/library/songs.ts @@ -8,7 +8,7 @@ import { get_library_songs, LibrarySongs } from "../../muse.js"; import { alphabetical_orders, order_id_to_name } from "./base.js"; import { Paginator } from "src/components/paginator.js"; import type { Order } from "libmuse/types/mixins/utils.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { PlayableContainer, PlayableList } from "src/util/playablelist.js"; import { PlaylistItemView } from "src/components/playlist/itemview.js"; import { @@ -20,7 +20,7 @@ import { GObject.type_ensure(Paginator.$gtype); export class LibrarySongsPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "LibrarySongsPage", @@ -70,9 +70,9 @@ export class LibrarySongsPage extends Adw.Bin if (!order) return; - let url = `muzika:${this.uri}?replace=true&order=${order}`; + let url = `muzika:${this.uri}?order=${order}`; - this.activate_action("navigator.visit", GLib.Variant.new_string(url)); + this.activate_action("navigator.replace", GLib.Variant.new_string(url)); } loading = false; @@ -148,7 +148,7 @@ export class LibrarySongsPage extends Adw.Bin } } - static load(context: EndpointContext): Promise { + static load(context: PageLoadContext): Promise { return get_library_songs({ signal: context.signal, order: context.url.searchParams.get("order") as Order ?? diff --git a/src/pages/login.ts b/src/pages/login.ts index 9e4c541a..d5cbb236 100644 --- a/src/pages/login.ts +++ b/src/pages/login.ts @@ -74,14 +74,35 @@ export class LoginPage extends Adw.Window { }); } + private _last_signal?: AbortSignal; + async auth_flow(signal?: AbortSignal) { this._stack.visible_child = this._spinner; this._spinner.start(); + this._last_signal = signal; + const login_code = await get_option("auth").get_login_code(); this.show_code(login_code); - await get_option("auth").load_token_with_code(login_code, signal); + await get_option("auth") + .load_token_with_code(login_code, signal) + .then(() => { + this._last_signal = undefined; + }) + .catch((error) => { + if ((error instanceof DOMException) && error.name === "AbortError") { + return; + } else { + throw error; + } + }); + } + + private refresh_cb() { + const signal = this._last_signal; + + this.auth_flow(signal); } } diff --git a/src/pages/mood-playlists.ts b/src/pages/mood-playlists.ts index fd3a4b30..1aea081a 100644 --- a/src/pages/mood-playlists.ts +++ b/src/pages/mood-playlists.ts @@ -6,7 +6,7 @@ import { get_mood_playlists, MoodPlaylists } from "../muse.js"; import { Carousel } from "../components/carousel/index.js"; import { Loading } from "../components/loading.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { set_scrolled_window_initial_vscroll, VScrollState, @@ -19,7 +19,7 @@ export interface MoodPlaylistsPageState extends VScrollState { } export class MoodPlaylistsPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "MoodPlaylistsPage", @@ -33,7 +33,7 @@ export class MoodPlaylistsPage extends Adw.Bin contents?: MoodPlaylists; - static async load(ctx: EndpointContext) { + static async load(ctx: PageLoadContext) { const data = await get_mood_playlists(ctx.match.params.params, { signal: ctx.signal, }); diff --git a/src/pages/moods.ts b/src/pages/moods.ts index 3dba5bc9..8ab25b1b 100644 --- a/src/pages/moods.ts +++ b/src/pages/moods.ts @@ -6,7 +6,7 @@ import { get_mood_categories, MoodCategories } from "../muse.js"; import { Carousel } from "../components/carousel/index.js"; import { Loading } from "../components/loading.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { set_scrolled_window_initial_vscroll, VScrollState, @@ -19,7 +19,7 @@ export interface MoodsPageState extends VScrollState { } export class MoodsPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "MoodsPage", @@ -33,7 +33,7 @@ export class MoodsPage extends Adw.Bin contents?: MoodCategories; - static load(ctx: EndpointContext) { + static load(ctx: PageLoadContext) { return get_mood_categories({ signal: ctx.signal, }); diff --git a/src/pages/new-releases.ts b/src/pages/new-releases.ts index 6f00a551..3d99c512 100644 --- a/src/pages/new-releases.ts +++ b/src/pages/new-releases.ts @@ -6,7 +6,7 @@ import { get_new_releases, NewReleases } from "../muse.js"; import { Carousel } from "../components/carousel/index.js"; import { Loading } from "../components/loading.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { set_scrolled_window_initial_vscroll, VScrollState, @@ -19,7 +19,7 @@ export interface NewReleasesPageState extends VScrollState { } export class NewReleasesPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "NewReleasesPage", @@ -33,7 +33,7 @@ export class NewReleasesPage extends Adw.Bin contents?: NewReleases; - static async load(ctx: EndpointContext) { + static async load(ctx: PageLoadContext) { const data = await get_new_releases({ signal: ctx.signal, }); diff --git a/src/pages/playlist.ts b/src/pages/playlist.ts index 36e1b061..36209aeb 100644 --- a/src/pages/playlist.ts +++ b/src/pages/playlist.ts @@ -22,7 +22,7 @@ import { import { Carousel } from "../components/carousel/index.js"; import { PlaylistHeader } from "../components/playlist/header.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { ObjectContainer } from "src/util/objectcontainer.js"; import { PlaylistItemView } from "src/components/playlist/itemview.js"; import { Paginator } from "src/components/paginator.js"; @@ -51,7 +51,7 @@ GObject.type_ensure(PlaylistItemView.$gtype); GObject.type_ensure(PlaylistBar.$gtype); export class PlaylistPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "PlaylistPage", @@ -528,7 +528,7 @@ export class PlaylistPage extends Adw.Bin } } - static async load(context: EndpointContext) { + static async load(context: PageLoadContext) { const data = await get_playlist(context.match.params.playlistId, { related: true, suggestions_limit: 6, diff --git a/src/pages/search.ts b/src/pages/search.ts index 0aafe682..8e644e07 100644 --- a/src/pages/search.ts +++ b/src/pages/search.ts @@ -16,7 +16,7 @@ import { SearchSection } from "../components/search/section.js"; import { TopResultSection } from "../components/search/topresultsection.js"; import { Paginator } from "../components/paginator.js"; import { InlineTabSwitcher, Tab } from "../components/inline-tab-switcher.js"; -import { EndpointContext, MuzikaComponent } from "src/navigation.js"; +import { PageLoadContext, MuzikaPageWidget } from "src/navigation.js"; import { escape_label } from "src/util/text.js"; import { set_scrolled_window_initial_vscroll, @@ -38,7 +38,7 @@ interface SearchData { GObject.type_ensure(InlineTabSwitcher.$gtype); export class SearchPage extends Adw.Bin - implements MuzikaComponent { + implements MuzikaPageWidget { static { GObject.registerClass({ GTypeName: "SearchPage", @@ -80,7 +80,7 @@ export class SearchPage extends Adw.Bin this._context_label.connect("activate-link", (_, uri) => { if (uri && uri.startsWith("muzika:")) { this.activate_action( - "navigator.visit", + "navigator.replace", GLib.Variant.new_string(uri), ); @@ -162,7 +162,7 @@ export class SearchPage extends Adw.Bin new Gtk.ToggleButton({ label: filter_to_string(filter), css_classes: ["chip"], - action_name: "navigator.visit", + action_name: "navigator.replace", action_target: GLib.Variant.new("s", url), active: selected, }), @@ -269,7 +269,7 @@ export class SearchPage extends Adw.Bin }); } - static load(context: EndpointContext) { + static load(context: PageLoadContext) { const autocorrect = context.url.searchParams.get("autocorrect"); const args = [decodeURIComponent(context.match.params.query), {