From 6c2f6fdc5fb76b173da96a8ccca0020ae4d50edc Mon Sep 17 00:00:00 2001 From: lideming Date: Tue, 31 Oct 2023 12:53:34 +0800 Subject: [PATCH] wip: content area as popup window --- src/Infra/Router.ts | 49 ++++++++++++++++++++++++++--------------- src/Infra/UI.ts | 23 +++++++++++++++++++ src/Infra/style.ts | 7 ++++++ src/Infra/ui-views.tsx | 2 ++ src/Track/ListIndex.ts | 43 ++++++++++++++++++++++-------------- src/Track/NowPlaying.ts | 23 +++++++++++++++++-- src/main.ts | 8 +------ 7 files changed, 111 insertions(+), 44 deletions(-) create mode 100644 src/Infra/style.ts diff --git a/src/Infra/Router.ts b/src/Infra/Router.ts index cca56a4..69b75c7 100644 --- a/src/Infra/Router.ts +++ b/src/Infra/Router.ts @@ -8,7 +8,11 @@ export interface Route { path: string[]; contentView?: () => ContentView; sidebarItem?: () => SidebarItem; - onNav?: (arg: { path: string[]; remaining: string[] }) => void; + onNav?: (arg: { + path: string[]; + remaining: string[]; + popup: boolean; + }) => void; onLeave?: () => void; } @@ -49,35 +53,44 @@ export const router = new (class { pushState?: boolean | "replace"; evenIsCurrent?: boolean; idx?: number; + popup?: boolean; } ) { + const popup = options?.popup ?? false; if (typeof path === "string") path = parsePath(path); var strPath = path.map((x) => encodeURIComponent(x)).join("/"); if (this.currentStr === strPath && !options?.evenIsCurrent) return; - this.current = path; - this.currentStr = strPath; - const oldIdx = this.currentIdx; - this.currentIdx = options?.idx ?? this.currentIdx + 1; - if (options?.idx || options?.pushState !== false) - this.wasBacked = this.currentIdx < oldIdx; + if (!popup) { + this.current = path; + this.currentStr = strPath; + const oldIdx = this.currentIdx; + this.currentIdx = options?.idx ?? this.currentIdx + 1; + if (options?.idx || options?.pushState !== false) + this.wasBacked = this.currentIdx < oldIdx; - if (options?.pushState !== false) { - const state = { idx: this.currentIdx }; - const url = strPath ? "#" + strPath : undefined; - const args = [state, "", url] as const; - if (options?.pushState == "replace") { - window.history.replaceState(...args); - } else { - window.history.pushState(...args); + if (options?.pushState !== false) { + const state = { idx: this.currentIdx }; + const url = strPath ? "#" + strPath : undefined; + const args = [state, "", url] as const; + if (options?.pushState == "replace") { + window.history.replaceState(...args); + } else { + window.history.pushState(...args); + } } } for (const r of this.routes) { if (match(path, r)) { - if (r.contentView) ui.content.setCurrent(r.contentView()); - if (r.sidebarItem) ui.sidebarList.setActive(r.sidebarItem()); - if (r.onNav) r.onNav({ path, remaining: path.slice(r.path.length) }); + if (!popup) { + if (r.contentView) ui.content.setCurrent(r.contentView()); + if (r.sidebarItem) ui.sidebarList.setActive(r.sidebarItem()); + } else { + if (r.contentView) ui.content.popup(r.contentView()); + } + if (r.onNav) + r.onNav({ path, remaining: path.slice(r.path.length), popup }); break; } } diff --git a/src/Infra/UI.ts b/src/Infra/UI.ts index 1f85ec1..a03b4fc 100644 --- a/src/Infra/UI.ts +++ b/src/Infra/UI.ts @@ -66,6 +66,7 @@ import { user } from "../API/User"; import { playerCore, playingLoopModes } from "../Player/PlayerCore"; import { uploads } from "../Track/Uploads"; import { api } from "../API/Api"; +import { injectStyle } from "./style"; export const ui = new (class { usingKeyboardInput = false; @@ -442,6 +443,28 @@ export const ui = new (class { } this.current = view; } + + async popup(view: views.ContentView) { + if (view === this.current) { + this.removeCurrent(); + } + const win = await this.createPopupWindow(); + view.onShow(); + mountView(win.document.body, view); + view.onDomInserted(); + view.fadeIn(); + } + + private async createPopupWindow() { + console.info("create popup"); + const win = window.open("about:blank", undefined, "width=600,height=400"); + return new Promise((r) => { + win?.addEventListener("load", () => { + injectStyle({ parent: win!.document.head }); + r(win); + }); + }); + } })(); contentBg = new (class { bgView: View | null = null; diff --git a/src/Infra/style.ts b/src/Infra/style.ts new file mode 100644 index 0000000..d88c18d --- /dev/null +++ b/src/Infra/style.ts @@ -0,0 +1,7 @@ +import { getWebfxCss, injectCss, injectWebfxCss } from "./viewlib"; +import style from "../../style.css"; + +export function injectStyle(options?: { parent?: Node }) { + injectCss(getWebfxCss(), { ...options, tag: "style#mcloud-injected-style-webfx" }); + injectCss(style, { ...options, tag: "style#mcloud-injected-style-app" }); +} diff --git a/src/Infra/ui-views.tsx b/src/Infra/ui-views.tsx index b7cc1fa..bf786cb 100644 --- a/src/Infra/ui-views.tsx +++ b/src/Infra/ui-views.tsx @@ -119,6 +119,8 @@ export class ContentView extends View { super.postCreateDom(); this.toggleClass("contentview", true); } + + // ContentView lifecycle methods: onShow() { this._isVisible = true; if (this.domCreated && this._lastRenderedLanguage != ui.lang.curLang) { diff --git a/src/Track/ListIndex.ts b/src/Track/ListIndex.ts index c4134d6..0a0ccfd 100644 --- a/src/Track/ListIndex.ts +++ b/src/Track/ListIndex.ts @@ -17,7 +17,7 @@ import { objectApply, appendView, } from "../Infra/viewlib"; -import { BuildDomExpr, View } from "../Infra/utils"; +import { BuildDomExpr, MessageBox, View } from "../Infra/utils"; import { I } from "../I18n/I18n"; import { TrackList, TrackViewItem, TrackListView } from "./TrackList"; import { user } from "../API/User"; @@ -64,7 +64,7 @@ export class ListIndex { for (const item of arg.sourceItems as TrackViewItem[]) { list.addTrack( item.track.toApiTrack(), - arg.event.altKey ? undefined : 0 + arg.event.altKey ? undefined : 0, ); } return list.put(); @@ -87,7 +87,7 @@ export class ListIndex { await list.fetch(); list.addTrack( track.toApiTrack(), - arg.event.altKey ? undefined : 0 + arg.event.altKey ? undefined : 0, ); await list.put(); }) @@ -161,9 +161,9 @@ export class ListIndex { this.newTracklist(); }, }, - [icon] - ) - ) + [icon], + ), + ), ); ui.sidebarList.container.appendView(this.section); ui.sidebar.dom.addEventListener( @@ -186,11 +186,11 @@ export class ListIndex { secondHeader, secondHeader.offsetTop + secondHeader.offsetHeight - - this.listView.dom.offsetTop + this.listView.dom.offsetTop, ); } }, - { passive: true } + { passive: true }, ); this.listView.scrollBox = ui.sidebar.dom; router.addRoute({ @@ -324,7 +324,7 @@ export class ListIndex { owner: user.id, name: createName( (x) => (x ? I`New Playlist (${x + 1})` : I`New Playlist`), - (x) => !!this.listView.find((l) => l.listInfo.name === x) + (x) => !!this.listView.find((l) => l.listInfo.name === x), ), visibility: 0, version: 0, @@ -338,9 +338,9 @@ export class ListIndex { (err) => { Toast.show( I`Failed to create playlist "${list.name}".` + "\n" + err, - 5000 + 5000, ); - } + }, ); } } @@ -399,7 +399,7 @@ export class ListIndexViewItem extends SidebarItem { new CopyMenuItem({ text: I`Copy link`, textToCopy: api.appBaseUrl + "#list/" + this.listInfo.id, - }) + }), ); } if (this.listInfo.owner == user.id) { @@ -413,24 +413,33 @@ export class ListIndexViewItem extends SidebarItem { list.onInfoChanged(); list.put(); }, - }) + }), ); } m.add( new MenuItem({ text: I`Remove`, cls: "dangerous", - onActive: () => { - this.index.removeList(this.listInfo.id); + onActive: async () => { + if ( + (await new MessageBox() + .setTitle(I`Warning`) + .addText(I`Are you sure to delete the playlist permanently?`) + .addResultBtns(["cancel", "ok"]) + .allowCloseWithResult("cancel") + .showAndWaitResult()) !== "ok" + ) { + this.index.removeList(this.listInfo.id); + } }, - }) + }), ); } if (this.listInfo) m.add( new MenuInfoItem({ text: I`List ID` + ": " + this.listInfo.id, - }) + }), ); if (m.length) { ev.preventDefault(); diff --git a/src/Track/NowPlaying.ts b/src/Track/NowPlaying.ts index 5e633ce..fa12df9 100644 --- a/src/Track/NowPlaying.ts +++ b/src/Track/NowPlaying.ts @@ -1,6 +1,12 @@ import { router } from "../Infra/Router"; import { ui } from "../Infra/UI"; -import { Lazy, BuildDomExpr, SettingItem } from "../Infra/utils"; +import { + Lazy, + BuildDomExpr, + SettingItem, + ContextMenu, + MenuItem, +} from "../Infra/utils"; import { I } from "../I18n/I18n"; import { playerCore } from "../Player/PlayerCore"; import { LyricsView } from "../Lyrics/LyricsView"; @@ -18,7 +24,20 @@ import { Track } from "./Track"; export const nowPlaying = new (class { init() { - var sidebarItem = new SidebarItem({ text: () => I`Now Playing` }); + var sidebarItem = new SidebarItem({ + text: () => I`Now Playing`, + onContextMenu: (item, ev) => { + ev.preventDefault(); + new ContextMenu([ + new MenuItem({ + text: "Open popup", + onActive: () => { + router.nav("nowplaying", { popup: true }); + }, + }), + ]).show(ev); + }, + }); router.addRoute({ path: ["nowplaying"], contentView: () => this.view, diff --git a/src/main.ts b/src/main.ts index 063bbba..65fc74b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -49,11 +49,10 @@ import { serviceWorkerClient } from "./ServiceWorker/client"; import { infoProvider } from "./Overlay/infoProvider"; import { userStore, UserStoreFields } from "./API/UserStore"; import { plugins } from "./Plugins/plugins"; +import { injectStyle } from "./Infra/style"; import * as webfx from "@yuuza/webfx"; -import style from "../style.css"; - function init() { console.time("[Main] app init()"); settings.init(); @@ -86,11 +85,6 @@ function checkMode() { } } -function injectStyle() { - webfx.injectWebfxCss(); - injectCss(style, { tag: "style#mcloud-injected-style" }); -} - export { api, discussion,