diff --git a/src/components/buttons/Screen.vue b/src/components/buttons/Screen.vue index 24d8920e..2a226419 100644 --- a/src/components/buttons/Screen.vue +++ b/src/components/buttons/Screen.vue @@ -48,12 +48,22 @@ export default { return this.$appdata.get("is_mobile"); }, is_popup_opened: function () { - return !!this.$appdata.get("popup"); + const multiplePopups = this.$userdata.get("multiple_popups", false); + if (multiplePopups) { + const popups = this.$appdata.get("popups") || {}; + return !!(popups[this.module] && !popups[this.module].closed); + } + const popup = this.$appdata.get("popup_instance"); + return !!(popup && !popup.closed); }, popup_module: function () { return this.$appdata.get("popup_module"); }, is_selected: function () { + const multiplePopups = this.$userdata.get("multiple_popups", false); + if (multiplePopups) { + return this.is_popup_opened; + } return this.is_popup_opened && this.popup_module == this.module; }, }, @@ -66,7 +76,7 @@ export default { } }, close: function () { - this.$popup.close(); + this.$popup.close(this.module); }, }, }; diff --git a/src/helpers/AppData.js b/src/helpers/AppData.js index a375de9a..48003b4a 100644 --- a/src/helpers/AppData.js +++ b/src/helpers/AppData.js @@ -4,23 +4,32 @@ export default { set(param, value) { store.commit("setData", [param, value]); - const popup = this.get("popup"); + const popup_instance = this.get("popup_instance"); + const popups = this.get("popups") || {}; + + const activePopups = []; + if (popup_instance) activePopups.push({ key: "popup_instance", win: popup_instance }); + Object.keys(popups).forEach((key) => { + activePopups.push({ key: `popups.${key}`, win: popups[key] }); + }); + if ( - popup && - param != "popup" && + activePopups.length > 0 && + param != "popups" && + param != "popup_instance" && param != "is_popup" && param != "is_fullscreen" ) { - if (popup.closed) { - this.set("popup", null); - //this.set("popup_module", null); - } else { - try { - popup.postMessage({ param, value }, window.location.origin); - } catch (e) { - console.log(e); + activePopups.forEach((item) => { + const popup = item.win; + if (popup && !popup.closed) { + try { + popup.postMessage({ param, value }, window.location.origin); + } catch (e) { + console.log(e); + } } - } + }); } }, @@ -35,6 +44,8 @@ export default { getFlatten() { let data = Object.assign({}, this.get()); delete data.popup; + delete data.popups; + delete data.popup_instance; delete data.is_popup; data = JSON.parse(JSON.stringify(data)); return this.flatten(data); diff --git a/src/helpers/Modules.js b/src/helpers/Modules.js index 3afa19ee..8df1ff51 100644 --- a/src/helpers/Modules.js +++ b/src/helpers/Modules.js @@ -1,5 +1,7 @@ import $dev from "@/helpers/Dev"; import $appdata from "@/helpers/AppData"; +import $popup from "@/helpers/Popup"; +import $userdata from "@/helpers/UserData"; export default { open(id) { @@ -20,6 +22,23 @@ export default { //Remove da TrayArea this.removeTray(id); + + // Auto-closing popup logic + const multiplePopups = $userdata.get("multiple_popups", false); + + if (multiplePopups) { + $popup.close(id); + } else { + // Check if any module is still active or in the tray + const modules = $appdata.get("modules") || {}; + const trayModules = $appdata.get("tray_area.modules") || []; + const anyActive = Object.values(modules).some((m) => m.show); + const anyInTray = trayModules.length > 0; + + if (!anyActive && !anyInTray) { + $popup.close(); + } + } }, minimize(id) { if (!this.check(id)) { diff --git a/src/helpers/Popup.js b/src/helpers/Popup.js index c5551bb9..89466a06 100644 --- a/src/helpers/Popup.js +++ b/src/helpers/Popup.js @@ -1,7 +1,6 @@ import $appdata from "@/helpers/AppData"; import $window from "@/helpers/Window"; - -let popup = null; +import $userdata from "@/helpers/UserData"; export default { async open(params) { @@ -9,21 +8,65 @@ export default { params = { module: params }; } - popup = $appdata.get("popup"); - if (popup && !popup.closed) { - popup.focus(); + const module = params.module; + const multiplePopups = $userdata.get("multiple_popups", false); + const popups = $appdata.get("popups") || {}; + + if (multiplePopups) { + let popup = popups[module]; + if (popup && !popup.closed) { + popup.focus(); + } else { + popup = $window.open( + `/popup?module=${module}`, + `Popup_${module}`, + "width=800,height=600" + ); + popups[module] = popup; + $appdata.set("popups", popups); + } } else { - popup = $window.open("/popup", "PopupWindow", "width=800,height=600"); + // Single popup mode (current behavior) + let popup = $appdata.get("popup_instance"); // Using a separate key for the instance to avoid confusion with popups object + if (popup && !popup.closed) { + popup.focus(); + } else { + popup = $window.open("/popup", "PopupWindow", "width=800,height=600"); + $appdata.set("popup_instance", popup); + } } - $appdata.set("popup_module", params.module); - $appdata.set("popup", popup); + + $appdata.set("popup_module", module); }, async exit() { $appdata.set("popup_module", ""); }, - async close() { - popup.close(); - await this.exit(); - $appdata.set("popup", null); + async close(module = null) { + const multiplePopups = $userdata.get("multiple_popups", false); + if (multiplePopups && module) { + const popups = $appdata.get("popups") || {}; + if (popups[module]) { + popups[module].close(); + delete popups[module]; + $appdata.set("popups", popups); + } + } else if (!multiplePopups) { + const popup = $appdata.get("popup_instance"); + if (popup) { + popup.close(); + $appdata.set("popup_instance", null); + } + await this.exit(); + } else { + // Close all if multiple and no module specified + const popups = $appdata.get("popups") || {}; + Object.keys(popups).forEach((key) => { + if (popups[key] && !popups[key].closed) { + popups[key].close(); + } + }); + $appdata.set("popups", {}); + await this.exit(); + } }, }; diff --git a/src/modules/core/config/index.js b/src/modules/core/config/index.js new file mode 100644 index 00000000..9a8b10c3 --- /dev/null +++ b/src/modules/core/config/index.js @@ -0,0 +1,14 @@ +import BaseModule from "../../BaseModule"; +import es from "./lang/es.json"; +import pt from "./lang/pt.json"; +import manifest from "./manifest.json"; + +export default class extends BaseModule { + constructor() { + // Load translations + manifest.translations = { pt, es }; + + // Load manifest + super(manifest); + } +} diff --git a/src/modules/core/config/interface/Index.vue b/src/modules/core/config/interface/Index.vue new file mode 100644 index 00000000..cfd6cff1 --- /dev/null +++ b/src/modules/core/config/interface/Index.vue @@ -0,0 +1,45 @@ + + + + + + + + diff --git a/src/modules/core/config/lang/es.json b/src/modules/core/config/lang/es.json new file mode 100644 index 00000000..50ff59ef --- /dev/null +++ b/src/modules/core/config/lang/es.json @@ -0,0 +1,8 @@ +{ + "title": "Configuraciones", + "settings": { + "popup_title": "Pantalla Auxiliar (Popup)", + "multiple_popups": "Permitir múltiplos popups", + "multiple_popups_help": "Si está activado, cada módulo se abrirá en una ventana separada. Si está desactivado, el nuevo popup reemplazará al actual." + } +} diff --git a/src/modules/core/config/lang/pt.json b/src/modules/core/config/lang/pt.json new file mode 100644 index 00000000..1588af02 --- /dev/null +++ b/src/modules/core/config/lang/pt.json @@ -0,0 +1,8 @@ +{ + "title": "Configurações", + "settings": { + "popup_title": "Tela Auxiliar (Popup)", + "multiple_popups": "Permitir múltiplos popups", + "multiple_popups_help": "Se ativado, cada módulo abrirá em uma janela separada. Se desativado, o novo popup substituirá o atual." + } +} diff --git a/src/modules/core/config/manifest.json b/src/modules/core/config/manifest.json new file mode 100644 index 00000000..9aeb7f90 --- /dev/null +++ b/src/modules/core/config/manifest.json @@ -0,0 +1,12 @@ +{ + "id": "config", + "name": "Configurações", + "active": true, + "showInMainMenu": true, + "icon": "mdi-cog", + "category": null, + "development": false, + "moduleOptions": { + "size": "small" + } +} diff --git a/src/store/state.js b/src/store/state.js index c8fde8c6..19ca9e92 100644 --- a/src/store/state.js +++ b/src/store/state.js @@ -5,7 +5,7 @@ export default { is_mobile: false, is_desktop: false, is_online: false, - popup: null, + popups: {}, popup_module: null, import_modules: false, loading: false, @@ -48,6 +48,7 @@ export default { user_data: { theme: "", language: "", + multiple_popups: false, layout: "apps", modules: { musics: { diff --git a/src/views/Main.vue b/src/views/Main.vue index adc8ec56..bd09950c 100644 --- a/src/views/Main.vue +++ b/src/views/Main.vue @@ -98,8 +98,8 @@ export default { window.addEventListener("message", (event) => { if (event.origin === window.location.origin) { - if (event.data == "mounted") { - const popup = this.$appdata.get("popup"); + if (event.data === "mounted" || (event.data && event.data.type === "mounted")) { + const popup = event.source; if (popup) { const data = this.$appdata.getFlatten(); Object.keys(data).map((item) => { @@ -108,10 +108,10 @@ export default { window.location.origin, ); }); - //popup.postMessage({ all: data }, window.location.origin); } - } else if (event.data == "closed") { - this.$popup.close(); + } else if (event.data === "closed" || (event.data && event.data.type === "closed")) { + const module = event.data.module || null; + this.$popup.close(module); } } }); diff --git a/src/views/Popup.vue b/src/views/Popup.vue index 8bf5a574..f4b81332 100644 --- a/src/views/Popup.vue +++ b/src/views/Popup.vue @@ -14,6 +14,11 @@ export default { }), computed: { module() { + const urlParams = new URLSearchParams(window.location.search); + const module = urlParams.get("module"); + if (module) { + return module; + } return this.$appdata.get("popup_module"); }, }, @@ -50,10 +55,16 @@ export default { } }); - window.opener.postMessage("mounted", window.location.origin); + window.opener.postMessage( + { type: "mounted", module: this.module }, + window.location.origin + ); window.addEventListener("beforeunload", () => { - window.opener?.postMessage("closed", window.location.origin); + window.opener?.postMessage( + { type: "closed", module: this.module }, + window.location.origin + ); }); }, };