From feef5a6c98239a2c08433aec1bbc4e5510a79e32 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Fri, 27 Jan 2023 22:59:06 +0100 Subject: move app/*.js files into app/js/ --- src/app/js/browser.js | 555 +++++++++++++++++++++++++++++++++++++++++++++++++ src/app/js/launcher.js | 167 +++++++++++++++ src/app/js/settings.js | 137 ++++++++++++ src/app/js/toast.js | 63 ++++++ 4 files changed, 922 insertions(+) create mode 100644 src/app/js/browser.js create mode 100644 src/app/js/launcher.js create mode 100644 src/app/js/settings.js create mode 100644 src/app/js/toast.js (limited to 'src/app/js') diff --git a/src/app/js/browser.js b/src/app/js/browser.js new file mode 100644 index 0000000..dee263b --- /dev/null +++ b/src/app/js/browser.js @@ -0,0 +1,555 @@ +const Fuse = require("fuse.js"); +var fuse; +var packages = []; + +var packagecount = 0; + +var Browser = { + maxentries: 50, + filters: { + getpkgs: () => { + let pkgs = []; + let other = []; + for (let i in packages) { + if (! Browser.filters.isfiltered(packages[i].categories)) { + pkgs.push(packages[i]); + } else { + other.push(packages[i]); + } + } + + return pkgs; + }, + get: () => { + let filtered = []; + let unfiltered = []; + let checks = browser.querySelectorAll("#filters .check"); + + for (let i = 0; i < checks.length; i++) { + if (! checks[i].classList.contains("checked")) { + filtered.push(checks[i].getAttribute("value")); + } else { + unfiltered.push(checks[i].getAttribute("value")); + } + } + + return { + filtered, + unfiltered + }; + }, + isfiltered: (categories) => { + let filtered = Browser.filters.get().filtered; + let unfiltered = Browser.filters.get().unfiltered; + let state = false; + + let filters = [ + "Mods", "Skins", + "Client-side", "Server-side", + ]; + + let newcategories = []; + for (let i = 0; i < categories.length; i++) { + if (filters.includes(categories[i])) { + newcategories.push(categories[i]); + } + }; categories = newcategories; + + if (categories.length == 0) {return true} + for (let i = 0; i < categories.length; i++) { + if (filtered.includes(categories[i])) { + state = true; + continue + } else if (unfiltered.includes(categories[i])) { + state = false; + continue + } + + state = true; + } + + return state; + }, + toggle: (state) => { + if (state == false) { + filters.classList.remove("shown"); + return + } + + filters.classList.toggle("shown"); + let filterRect = filter.getBoundingClientRect(); + let spacing = parseInt(getComputedStyle(filters).getPropertyValue("--spacing")); + + filters.style.top = filterRect.bottom - spacing; + filters.style.right = filterRect.right - filterRect.left + filterRect.width - (spacing / 2); + }, + }, + toggle: (state) => { + if (state) { + browser.scrollTo(0, 0); + overlay.classList.add("shown"); + browser.classList.add("shown"); + + if (browserEntries.querySelectorAll(".el").length == 0) { + Browser.loadfront(); + } + return + } else if (! state) { + if (state != undefined) { + Browser.filters.toggle(false); + overlay.classList.remove("shown"); + browser.classList.remove("shown"); + preview.classList.remove("shown"); + return + } + } + + browser.scrollTo(0, 0); + overlay.classList.toggle("shown"); + browser.classList.toggle("shown"); + }, + loadfront: async () => { + Browser.loading(); + + packagecount = 0; + + if (packages.length < 1) { + packages = await (await fetch("https://northstar.thunderstore.io/api/v1/package/")).json(); + + fuse = new Fuse(packages, { + keys: ["full_name"] + }) + } + + let pkgs = Browser.filters.getpkgs(); + for (let i in pkgs) { + if (packagecount >= Browser.maxentries) { + Browser.endoflist(); + break + } + + new BrowserElFromObj(pkgs[i]); + packagecount++; + } + }, + loading: (string) => { + if (Browser.filters.get().unfiltered.length == 0) { + string = lang("gui.browser.noresults"); + } + + if (string) { + browserEntries.innerHTML = `
${string}
`; + } + + if (! browserEntries.querySelector(".loading")) { + browserEntries.innerHTML = `
${lang('gui.browser.loading')}
`; + } + }, + endoflist: (isEnd) => { + let pkgs = []; + let filtered = Browser.filters.getpkgs(); + for (let i = 0; i < filtered.length; i++) { + if ([packagecount + i]) { + pkgs.push(filtered[packagecount + i]); + } else { + break + } + } + + if (browserEntries.querySelector(".message")) { + browserEntries.querySelector(".message").remove(); + } + + if (pkgs.length == 0 || isEnd) { + Browser.msg(`${lang('gui.browser.endoflist')}`); + return + } + + Browser.msg(``); + loadmore.addEventListener("click", () => { + Browser.loadpkgs(pkgs); + Browser.endoflist(pkgs); + }) + }, + search: (string) => { + Browser.loading(); + let res = fuse.search(string); + + if (res.length < 1) { + Browser.loading(lang("gui.browser.noresults")); + return + } + + packagecount = 0; + + let count = 0; + for (let i = 0; i < res.length; i++) { + if (count >= Browser.maxentries) {break} + if (Browser.filters.isfiltered(res[i].item.categories)) {continue} + new BrowserElFromObj(res[i].item); + count++; + } + + if (count < 1) { + Browser.loading(lang("gui.browser.noresults")); + } + }, + setbutton: (mod, string) => { + mod = normalize(mod); + if (browserEntries.querySelector(`#mod-${mod}`)) { + let elems = browserEntries.querySelectorAll(`.el#mod-${mod}`); + + for (let i = 0; i < elems.length; i++) { + elems[i].querySelector(".text button").innerHTML = string; + } + } else { + let make = (str) => { + if (browserEntries.querySelector(`#mod-${str}`)) { + return Browser.setbutton(str, string); + } else { + return false; + } + } + + setTimeout(() => { + for (let i = 0; i < modsobj.all.length; i++) { + let modname = normalize(modsobj.all[i].Name); + let modfolder = normalize(modsobj.all[i].FolderName); + + if (mod.includes(modname)) { + if (! make(modname)) { + if (modsobj.all[i].ManifestName) { + make(normalize(modsobj.all[i].ManifestName)); + } + } + } + else if (mod.includes(modfolder)) {make(modfolder);break} + } + }, 1501) + } + }, + loadpkgs: (pkgs, clear) => { + if (clear) {packagecount = 0} + + if (browserEntries.querySelector(".message")) { + browserEntries.querySelector(".message").remove(); + } + + let count = 0; + for (let i in pkgs) { + if (count >= Browser.maxentries) { + if (pkgs[i] === undefined) { + Browser.endoflist(true); + } + + Browser.endoflist(); + break + } + + try { + new BrowserElFromObj(pkgs[i]); + }catch(e) {} + + count++; + packagecount++; + } + }, + msg: (html) => { + let msg = document.createElement("div"); + msg.classList.add("message"); + msg.innerHTML = html; + + browserEntries.appendChild(msg); + } +} + +function openExternal(url) { + require("electron").shell.openExternal(url); +} + +var view = document.querySelector(".popup#preview webview"); +var Preview = { + show: () => { + preview.classList.add("shown"); + }, + hide: () => { + preview.classList.remove("shown"); + }, + set: (url, autoshow) => { + if (autoshow != false) {Preview.show()} + view.src = url; + document.querySelector("#preview #external").setAttribute("onclick", `openExternal("${url}")`); + } +} + +function BrowserElFromObj(obj) { + let pkg = {...obj, ...obj.versions[0]}; + + new BrowserEl({ + title: pkg.name, + image: pkg.icon, + author: pkg.owner, + url: pkg.package_url, + download: pkg.download_url, + version: pkg.version_number, + categories: pkg.categories, + description: pkg.description, + dependencies: pkg.dependencies, + }) +} + +function BrowserEl(properties) { + if (Browser.filters.isfiltered(properties.categories)) {return} + + properties = { + title: "No name", + version: "1.0.0", + image: "icons/no-image.png", + author: "Unnamed Pilot", + description: "No description", + ...properties + } + + if (properties.version[0] != "v") { + properties.version = "v" + properties.version; + } + + if (browserEntries.querySelector(".loading")) { + browserEntries.innerHTML = ""; + } + + let installstr = lang("gui.browser.install"); + + if (normalize(modsdiv.innerText.split("\n")).includes(normalize(properties.title))) { + installstr = lang("gui.browser.reinstall"); + + for (let i = 0; i < modsobj.all.length; i++) { + if (normalize(modsobj.all[i].Name) == normalize(properties.title) + && "v" + modsobj.all[i].Version != properties.version) { + + installstr = lang("gui.browser.update"); + } + } + } else { + for (let i = 0; i < modsobj.all.length; i++) { + let title = normalize(properties.title); + let folder = normalize(modsobj.all[i].FolderName); + let manifestname = null; + if (modsobj.all[i].ManifestName) { + manifestname = normalize(modsobj.all[i].ManifestName); + } + + if (title.includes(folder) || title.includes(manifestname)) { + installstr = lang("gui.browser.reinstall"); + + if (folder == title + && "v" + modsobj.all[i].Version != properties.version) { + + installstr = lang("gui.browser.update"); + } + } + } + } + + let entry = document.createElement("div"); + entry.classList.add("el"); + entry.id = `mod-${normalize(properties.title)}`; + + entry.innerHTML = ` +
+ + +
+
+
${properties.title}
+
${properties.description}
+ + + + +
+ ` + + entry.querySelector("button.install").addEventListener("click", () => { + installFromURL( + properties.download, + JSON.stringify(properties.dependencies), + true, properties.author + ) + }) + + browserEntries.appendChild(entry); +} + +let recent_toasts = {}; +function add_recent_toast(name, timeout = 3000) { + if (recent_toasts[name]) {return} + + recent_toasts[name] = true; + + setTimeout(() => { + delete recent_toasts[name]; + }, timeout) +} + +ipcRenderer.on("removed-mod", (event, mod) => { + setButtons(true); + Browser.setbutton(mod.name, lang("gui.browser.install")); + if (mod.manifestname) { + Browser.setbutton(mod.manifestname, lang("gui.browser.install")); + } +}) + +ipcRenderer.on("failed-mod", (event, modname) => { + if (recent_toasts["failed" + modname]) {return} + add_recent_toast("failed" + modname); + + setButtons(true); + new Toast({ + timeout: 10000, + scheme: "error", + title: lang("gui.toast.title.failed"), + description: lang("gui.toast.desc.failed") + }) +}) + +ipcRenderer.on("duped-mod", (event, modname) => { + if (recent_toasts["duped" + modname]) {return} + add_recent_toast("duped" + modname); + + setButtons(true); + new Toast({ + timeout: 10000, + scheme: "warning", + title: lang("gui.toast.title.duped"), + description: modname + " " + lang("gui.toast.desc.duped") + }) +}) + +ipcRenderer.on("no-internet", (event, modname) => { + setButtons(true); + new Toast({ + timeout: 10000, + scheme: "error", + title: lang("gui.toast.noInternet.title"), + description: lang("gui.toast.noInternet.desc") + }) +}) + +ipcRenderer.on("installed-mod", (event, mod) => { + if (recent_toasts["installed" + mod.name]) {return} + add_recent_toast("installed" + mod.name); + + setButtons(true); + Browser.setbutton(mod.name, lang("gui.browser.reinstall")); + + if (mod.malformed) { + new Toast({ + timeout: 8000, + scheme: "warning", + title: lang("gui.toast.title.malformed"), + description: mod.name + " " + lang("gui.toast.desc.malformed") + }) + } + + new Toast({ + scheme: "success", + title: lang("gui.toast.title.installed"), + description: mod.name + " " + lang("gui.toast.desc.installed") + }) + + if (installqueue.length != 0) { + installFromURL( + "https://thunderstore.io/package/download/" + installqueue[0].pkg, + false, false, installqueue[0].author + ) + + installqueue.shift(); + } +}) + +function normalize(items) { + let main = (string) => { + return string.replaceAll(" ", "") + .replaceAll(".", "").replaceAll("-", "") + .replaceAll("_", "").toLowerCase(); + } + if (typeof items == "string") { + return main(items); + } else { + let newArray = []; + for (let i = 0; i < items.length; i++) { + newArray.push(main(items[i])); + } + + return newArray; + } +} + +let searchtimeout; +let searchstr = ""; +search.addEventListener("keyup", () => { + Browser.filters.toggle(false); + clearTimeout(searchtimeout); + + if (searchstr != search.value) { + if (search.value.replaceAll(" ", "") == "") { + searchstr = ""; + Browser.loadfront(); + return + } + + searchtimeout = setTimeout(() => { + Browser.search(search.value); + searchstr = search.value; + }, 500) + } +}) + +let events = ["scroll", "mousedown", "touchdown"]; +events.forEach((event) => { + browser.addEventListener(event, () => { + Preview.hide(); + + let mouseAt = document.elementsFromPoint(mouseX, mouseY); + if (! mouseAt.includes(document.querySelector("#filter")) + && ! mouseAt.includes(document.querySelector(".overlay"))) { + Browser.filters.toggle(false); + } + }) +}); + +view.addEventListener("dom-ready", () => { + let css = [ + fs.readFileSync(__dirname + "/css/theming.css", "utf8"), + fs.readFileSync(__dirname + "/css/webview.css", "utf8") + ] + + view.insertCSS(css.join(" ")); +}) + +view.addEventListener("did-stop-loading", () => { + view.style.display = "flex"; + setTimeout(() => { + view.classList.remove("loading"); + }, 200) +}) + +view.addEventListener("did-start-loading", () => { + view.style.display = "none"; + view.classList.add("loading"); +}) + +let mouseY = 0; +let mouseX = 0; +browser.addEventListener("mousemove", (event) => { + mouseY = event.clientY; + mouseX = event.clientX; +}) + +let checks = document.querySelectorAll(".check"); +for (let i = 0; i < checks.length; i++) { + checks[i].setAttribute("onclick", + "this.classList.toggle('checked');Browser.loadfront();search.value = ''" + ) +} diff --git a/src/app/js/launcher.js b/src/app/js/launcher.js new file mode 100644 index 0000000..5330b7a --- /dev/null +++ b/src/app/js/launcher.js @@ -0,0 +1,167 @@ +const markdown = require("marked").parse; + +var servercount; +var playercount; +var masterserver; + +// Changes the main page +// This is the tabs in the sidebar +function page(page) { + let btns = document.querySelectorAll(".gamesContainer button"); + let pages = document.querySelectorAll(".mainContainer .contentContainer"); + + for (let i = 0; i < pages.length; i++) { + pages[i].classList.add("hidden"); + } + + for (let i = 0; i < btns.length; i++) { + btns[i].classList.add("inactive"); + } + + pages[page].classList.remove("hidden"); + btns[page].classList.remove("inactive"); + bgHolder.setAttribute("bg", page); +}; page(1) + +function formatRelease(notes) { + let content = ""; + + if (notes.length === 1) { + content = notes[0]; + } else { + for (let release of notes) { + if (release.prerelease) {continue} + let new_content = "# " + release.name + "\n\n" + release.body + "\n\n\n"; + content += + "
\n" + + markdown(new_content, {breaks: true}) + "\n" + + "
"; + } + + content = content.replaceAll(/\@(\S+)/g, `@$1`); + } + + return markdown(content, { + breaks: true + }); +} + +// Updates the Viper release notes +ipcRenderer.on("vp-notes", (event, response) => { + vpReleaseNotes.innerHTML = formatRelease(response); +}); + +// Updates the Northstar release notes +ipcRenderer.on("ns-notes", (event, response) => { + nsRelease.innerHTML = formatRelease(response); +}); + +async function loadVpReleases() { + ipcRenderer.send("get-vp-notes"); +}; loadVpReleases(); + +async function loadNsReleases() { + ipcRenderer.send("get-ns-notes"); +}; loadNsReleases(); + +// TODO: We gotta make this more automatic instead of switch statements +// it's both not pretty, but adding more sections requires way too much +// effort, compared to how it should be. +function showVpSection(section) { + if (!["main", "release", "info", "credits"].includes(section)) throw new Error("unknown vp section"); + vpMainBtn.removeAttribute("active"); + vpReleaseBtn.removeAttribute("active"); + vpInfoBtn.removeAttribute("active"); + + vpMain.classList.add("hidden"); + vpReleaseNotes.classList.add("hidden"); + vpInfo.classList.add("hidden"); + + switch(section) { + case "main": + vpMainBtn.setAttribute("active", ""); + vpMain.classList.remove("hidden"); + break; + case "release": + vpReleaseBtn.setAttribute("active", ""); + vpReleaseNotes.classList.remove("hidden"); + break; + case "info": + vpInfoBtn.setAttribute("active", ""); + vpInfo.classList.remove("hidden"); + break; + } +} + +function showNsSection(section) { + if (!["main", "release", "mods"].includes(section)) { + throw new Error("unknown ns section"); + } + + nsMainBtn.removeAttribute("active"); + nsModsBtn.removeAttribute("active"); + nsReleaseBtn.removeAttribute("active"); + + nsMain.classList.add("hidden"); + nsMods.classList.add("hidden"); + nsRelease.classList.add("hidden"); + + switch(section) { + case "main": + nsMainBtn.setAttribute("active", ""); + nsMain.classList.remove("hidden"); + break; + case "mods": + nsModsBtn.setAttribute("active", ""); + nsMods.style.display = "block"; + nsMods.classList.remove("hidden"); + break; + case "release": + nsReleaseBtn.setAttribute("active", ""); + nsRelease.classList.remove("hidden"); + break; + } +} + +async function loadServers() { + serverstatus.classList.add("checking"); + + try { + let servers = await (await fetch("https://northstar.tf/client/servers")).json(); + masterserver = true; + + playercount = 0; + servercount = servers.length; + + for (let i = 0; i < servers.length; i++) { + playercount += servers[i].playerCount + } + }catch (err) { + playercount = 0; + servercount = 0; + masterserver = false; + } + + serverstatus.classList.remove("checking"); + + if (servercount == 0 || ! servercount || ! playercount) {masterserver = false} + + let playerstr = lang("gui.server.players"); + if (playercount == 1) { + playerstr = lang("gui.server.player"); + } + + if (masterserver) { + serverstatus.classList.add("up"); + serverstatus.innerHTML = `${servercount} ${lang("gui.server.servers")} - ${playercount} ${playerstr}`; + } else { + serverstatus.classList.add("down"); + serverstatus.innerHTML = lang("gui.server.offline"); + + } +}; loadServers() + +// Refreshes every 5 minutes +setInterval(() => { + loadServers(); +}, 300000) diff --git a/src/app/js/settings.js b/src/app/js/settings.js new file mode 100644 index 0000000..63b4b99 --- /dev/null +++ b/src/app/js/settings.js @@ -0,0 +1,137 @@ +var Settings = { + toggle: (state) => { + if (state) { + Settings.load(); + options.scrollTo(0, 0); + overlay.classList.add("shown"); + options.classList.add("shown"); + + return + } else if (! state) { + if (state != undefined) { + overlay.classList.remove("shown"); + options.classList.remove("shown"); + return + } + } + + Settings.load(); + options.scrollTo(0, 0); + overlay.classList.toggle("shown"); + options.classList.toggle("shown"); + }, + apply: () => { + settings = {...settings, ...Settings.get()}; + ipcRenderer.send("save-settings", Settings.get()); + }, + reloadSwitches: () => { + let switches = document.querySelectorAll(".switch"); + + for (let i = 0; i < switches.length; i++) { + switches[i].setAttribute("onclick", `Settings.switch(${i})`); + } + }, + switch: (element, state) => { + let switches = document.querySelectorAll(".switch"); + if (switches[element]) { + element = switches[element]; + } + + let on = () => { + element.classList.add("on"); + element.classList.remove("off"); + } + + let off = () => { + element.classList.add("off"); + element.classList.remove("on"); + } + + if (state != undefined) { + if (state) {on()} else {off()} + } else { + if (element.classList.contains("on")) {off()} else {on()} + } + + Settings.reloadSwitches(); + }, + get: () => { + let opts = {}; + let options = document.querySelectorAll(".option"); + + for (let i = 0; i < options.length; i++) { + let optName = options[i].getAttribute("name"); + if (options[i].querySelector(".actions input")) { + let input = options[i].querySelector(".actions input").value; + if (options[i].getAttribute("type")) { + opts[optName] = input.split(" "); + } else { + opts[optName] = input; + } + } else if (options[i].querySelector(".actions select")) { + opts[optName] = options[i].querySelector(".actions select").value; + } else if (options[i].querySelector(".actions .switch")) { + if (options[i].querySelector(".actions .switch.on")) { + opts[optName] = true; + } else { + opts[optName] = false; + } + } + } + + return opts; + }, + load: () => { + let options = document.querySelectorAll(".option"); + + for (let i = 0; i < options.length; i++) { + let optName = options[i].getAttribute("name"); + if (optName == "forcedlang") { + let div = options[i].querySelector("select"); + + div.innerHTML = ""; + let langs = fs.readdirSync(__dirname + "/../lang"); + for (let i in langs) { + title = JSON.parse(fs.readFileSync(__dirname + `/../lang/${langs[i]}`, "utf8"))["lang.title"]; + if (title) { + div.innerHTML += `` + } + + } + + div.value = settings.forcedlang; + continue; + } + + if (settings[optName] != undefined) { + switch(typeof settings[optName]) { + case "string": + options[i].querySelector(".actions input").value = settings[optName]; + break + case "object": + options[i].querySelector(".actions input").value = settings[optName].join(" "); + break + case "boolean": + let switchDiv = options[i].querySelector(".actions .switch"); + if (settings[optName]) { + switchDiv.classList.add("on"); + switchDiv.classList.remove("off"); + } else { + switchDiv.classList.add("off"); + switchDiv.classList.remove("on"); + } + break + + } + } + } + + ipcRenderer.send("can-autoupdate"); + ipcRenderer.on("cant-autoupdate", () => { + document.querySelector(".option[name=autoupdate]").style.display = "none"; + }) + } +} + +Settings.reloadSwitches(); +Settings.load(); diff --git a/src/app/js/toast.js b/src/app/js/toast.js new file mode 100644 index 0000000..3bc1745 --- /dev/null +++ b/src/app/js/toast.js @@ -0,0 +1,63 @@ +function Toast(properties) { + let toast = { + fg: "#000000", + bg: "#FFFFFF", + timeout: 3000, + callback: () => {}, + title: "Untitled Toast", + description: "No description provided for toast", + ...properties + } + + switch(toast.scheme) { + case "error": + toast.fg = "#FFFFFF"; + toast.bg = "rgb(var(--red))"; + break + case "success": + toast.fg = "#FFFFFF"; + toast.bg = "#60D394"; + break + case "warning": + toast.fg = "#FFFFFF"; + toast.bg = "#FF9B85"; + break + } + + + let id = Date.now(); + if (document.getElementById(id)) {id = id + 1} + let el = document.createElement("div"); + + el.classList.add("toast"); + + el.style.color = toast.fg; + el.style.background = toast.bg; + + el.id = id; + el.addEventListener("click", () => { + dismissToast(id); + toast.callback(); + }) + + el.innerHTML = ` +
${toast.title}
+
${toast.description}
+ ` + + toasts.appendChild(el); + + setTimeout(() => { + dismissToast(id); + }, toast.timeout) +} + +function dismissToast(id) { + id = document.getElementById(id); + if (id) { + id.classList.add("hidden"); + setTimeout(() => { + id.remove(); + }, 500) + } +} -- cgit v1.2.3 From 7fea75e8b064e5ca241a044e6efb3a315b6cd882 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Fri, 27 Jan 2023 11:54:36 +0100 Subject: initial draft for redesign of the mod list The current design for the installed mods is not exactly the best. And it has been due for a redesign for quite a while, I'm finally starting work on this. --- src/app/css/grid.css | 148 +++++++++++++++++++++++++++++++++++++++++++++ src/app/css/launcher.css | 12 +--- src/app/css/popups.css | 153 ----------------------------------------------- src/app/css/theming.css | 9 +++ src/app/index.html | 16 ++--- src/app/js/browser.js | 7 ++- src/app/js/mods.js | 81 +++++++++++++++++++++++++ src/app/main.css | 9 +++ src/app/main.js | 87 ++------------------------- src/lang/de.json | 2 - src/lang/en.json | 2 - src/lang/es.json | 2 - src/lang/fr.json | 2 - 13 files changed, 268 insertions(+), 262 deletions(-) create mode 100644 src/app/css/grid.css create mode 100644 src/app/js/mods.js (limited to 'src/app/js') diff --git a/src/app/css/grid.css b/src/app/css/grid.css new file mode 100644 index 0000000..5c4019c --- /dev/null +++ b/src/app/css/grid.css @@ -0,0 +1,148 @@ +.grid .el, .popup .misc, .popup .loading { + --spacing: calc(var(--padding) / 2); + --height: calc(var(--padding) * 3.5); + --mischeight: calc(var(--padding) * 1.5); + + animation-duration: 0.15s; + animation-iteration-count: 1; + animation-name: fadein; + animation-fill-mode: forwards; + animation-timing-function: ease-in-out; + + opacity: 0.0; + transition: 0.15s ease-in-out; +} + +.grid .el, .popup #search, +.popup #close, .popup .misc button, +.option .actions select, .option .actions input { + color: white; + display: flex; + align-items: center; + height: var(--height); + margin: var(--spacing); + padding: var(--spacing); + background: var(--selbg); + border-radius: var(--spacing); + width: calc(50% - var(--spacing) * 4); +} + +.popup .misc, .popup #search, .option .actions input { + --height: var(--mischeight); +} + +.popup .misc { + display: flex; +} + +.popup .misc.vertical { + display: block; +} + +.popup .misc.fixed { + width: 100%; + position: fixed; +} + +.popup #search, +.option .actions input, +.option .actions select { + border: none; + outline: none; + transition: filter 0.15s ease-in-out; + width: calc(100% - var(--spacing) * 2); +} + +.popup #search:focus, +.option .actions input:focus, +.option .actions button:active { + filter: brightness(1.5); +} + +.popup .misc button { + --height: calc(var(--padding) * 1.5); + + padding: 0px; + margin-left: 0px; + padding: 0px !important; + width: var(--height) !important; +} + +.popup .misc button img { + opacity: 0.6; + width: var(--height); + transform: scale(0.5); + height: var(--height) !important; +} + +.popup .misc button:last-child { + margin-left: 0px !important; +} + +.popup#preview #close, +.popup .misc.vertical button { + margin: var(--spacing) var(--spacing) 0 auto !important; +} + +.popup .loading { + width: 100%; + color: white; + display: flex; + position: absolute; + text-align: center; + align-items: center; + justify-content: center; + height: calc(100% - var(--mischeight) - var(--height)); +} + +.popup .message { + color: white; + text-align: center; + margin: var(--padding); + width: calc(100% - var(--padding)); +} + +.grid .el .image, .grid .el .image img { + width: var(--height); + height: var(--height); + margin-right: var(--spacing); + border-radius: var(--spacing); +} + +.grid .el .image img.blur { + z-index: -1; + position: relative; + filter: blur(10px); + top: calc(var(--height) * -1 + 5px); +} + +.grid .el .text { + overflow: hidden; +} + +.grid .el .title, .grid .el .description { + height: 1.2em; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.grid .el .title { + font-size: 1.2em; + font-weight: 700; +} + +.popup .message #loadmore { + background: rgb(var(--blue2)); +} + +.grid .el .description {font-size: 0.8em} +.grid .el button { + background: rgb(var(--blue)); + margin-top: var(--spacing); +} + +.grid .el button.info { + background: rgb(var(--blue2)); +} + diff --git a/src/app/css/launcher.css b/src/app/css/launcher.css index 6e7b578..5698179 100644 --- a/src/app/css/launcher.css +++ b/src/app/css/launcher.css @@ -242,14 +242,6 @@ background: rgb(var(--red)) !important; } -#installmod {background: rgb(var(--blue))} -#findmod {background: rgb(var(--blue2))} - -#togglemod {background: rgb(var(--orange))} -#toggleall {background: rgb(var(--orange2))} - -#removemod {background: rgb(var(--red))} -#removeall {background: rgb(var(--red2))} button:disabled { opacity: 0.5; pointer-events: none; @@ -270,13 +262,15 @@ code { } #nsMods .line { + width: 100%; display: flex; align-items: center; + margin: calc(var(--padding) / 2); margin-top: calc(var(--padding) / 2); } #modsdiv { - height: 50vh; + height: 80vh; overflow-y: scroll; border-radius: 5px; background: var(--bg); diff --git a/src/app/css/popups.css b/src/app/css/popups.css index 826955b..70a39a6 100644 --- a/src/app/css/popups.css +++ b/src/app/css/popups.css @@ -64,11 +64,6 @@ 100% {opacity: 1.0} } -#browserEntries { - display: flex; - flex-wrap: wrap; -} - .popup webview { width: 78%; margin: 0 auto; @@ -82,154 +77,6 @@ filter: opacity(0.0); pointer-events: none; } - -.popup .el, .popup .misc, .popup .loading { - --spacing: calc(var(--padding) / 2); - --height: calc(var(--padding) * 3.5); - --mischeight: calc(var(--padding) * 1.5); - - animation-duration: 0.15s; - animation-iteration-count: 1; - animation-name: fadein; - animation-fill-mode: forwards; - animation-timing-function: ease-in-out; - - opacity: 0.0; - transition: 0.15s ease-in-out; -} - -.popup .el, .popup #search, -.popup #close, .popup .misc button, -.option .actions select, .option .actions input { - color: white; - display: flex; - align-items: center; - height: var(--height); - margin: var(--spacing); - padding: var(--spacing); - background: var(--selbg); - border-radius: var(--spacing); - width: calc(50% - var(--spacing) * 4); -} - -.popup .misc, .popup #search, .option .actions input { - --height: var(--mischeight); -} - -.popup .misc { - display: flex; -} - -.popup .misc.vertical { - display: block; -} - -.popup .misc.fixed { - width: 100%; - position: fixed; -} - -.popup #search, -.option .actions input, -.option .actions select { - border: none; - outline: none; - transition: filter 0.15s ease-in-out; - width: calc(100% - var(--spacing) * 2); -} - -.popup #search:focus, -.option .actions input:focus, -.option .actions button:active { - filter: brightness(1.5); -} - -.popup .misc button { - --height: calc(var(--padding) * 1.5); - - padding: 0px; - margin-left: 0px; - padding: 0px !important; - width: var(--height) !important; -} - -.popup .misc button img { - opacity: 0.6; - width: var(--height); - transform: scale(0.5); - height: var(--height) !important; -} - -.popup .misc button:last-child { - margin-left: 0px !important; -} - -.popup#preview #close, -.popup .misc.vertical button { - margin: var(--spacing) var(--spacing) 0 auto !important; -} - -.popup .loading { - width: 100%; - color: white; - display: flex; - position: absolute; - text-align: center; - align-items: center; - justify-content: center; - height: calc(100% - var(--mischeight) - var(--height)); -} - -.popup .message { - color: white; - text-align: center; - margin: var(--padding); - width: calc(100% - var(--padding)); -} - -.popup .el .image, .popup .el .image img { - width: var(--height); - height: var(--height); - margin-right: var(--spacing); - border-radius: var(--spacing); -} - -.popup .el .image img.blur { - z-index: -1; - position: relative; - filter: blur(10px); - top: calc(var(--height) * -1 + 5px); -} - -.popup .el .text { - overflow: hidden; -} - -.popup .el .title, .popup .el .description { - height: 1.2em; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.popup .el .title { - font-size: 1.2em; - font-weight: 700; -} - -.popup .message #loadmore { - background: rgb(var(--blue2)); -} - -.popup .el .description {font-size: 0.8em} -.popup .el button { - background: rgb(var(--blue)); - margin-top: var(--spacing); -} - -.popup .el button.info { - background: rgb(var(--blue2)); -} /* } */ /* settings popup { */ diff --git a/src/app/css/theming.css b/src/app/css/theming.css index 2d45b1d..6d7e223 100644 --- a/src/app/css/theming.css +++ b/src/app/css/theming.css @@ -60,3 +60,12 @@ a:hover { color: black !important; background: rgb(var(--red)) !important; } + +.blue {background: rgb(var(--blue)) !important} +.blue2 {background: rgb(var(--blue2)) !important} + +.orange {background: rgb(var(--orange)) !important} +.orange2 {background: rgb(var(--orange2)) !important} + +.red {background: rgb(var(--red)) !important} +.red2 {background: rgb(var(--red2)) !important} diff --git a/src/app/index.html b/src/app/index.html index d2d70e1..9a571f0 100644 --- a/src/app/index.html +++ b/src/app/index.html @@ -226,19 +226,18 @@ @@ -260,6 +259,7 @@ + diff --git a/src/app/js/browser.js b/src/app/js/browser.js index dee263b..cfd0c03 100644 --- a/src/app/js/browser.js +++ b/src/app/js/browser.js @@ -319,8 +319,13 @@ function BrowserEl(properties) { } let installstr = lang("gui.browser.install"); + let normalized_mods = []; - if (normalize(modsdiv.innerText.split("\n")).includes(normalize(properties.title))) { + for (let i = 0; i < modsobj.all; i++) { + normalized_mods.push(normalize(mods_list[i].Name)); + } + + if (normalized_mods.includes(normalize(properties.title))) { installstr = lang("gui.browser.reinstall"); for (let i = 0; i < modsobj.all.length; i++) { diff --git a/src/app/js/mods.js b/src/app/js/mods.js new file mode 100644 index 0000000..39e904e --- /dev/null +++ b/src/app/js/mods.js @@ -0,0 +1,81 @@ +var mods = {}; + +mods.load = (mods_obj) => { + modcount.innerHTML = `${lang("gui.mods.count")} ${mods_obj.all.length}`; + + let normalized_names = []; + + let set_mod = (mod) => { + let image_url = ""; + let normalized_name = "mod-list-" + normalize(mod.Name); + + normalized_names.push(normalized_name); + + if (document.getElementById(normalized_name)) { + return; + } + + let div = document.createElement("div"); + div.classList.add("el"); + div.id = normalized_name; + + div.innerHTML += ` +
+ + +
+
+
${mod.Name}
+
${mod.Description}
+ + + +
+ `; + + if (! image_url) { + div.querySelector(".image").remove(); + } + + modsdiv.append(div); + } + + for (let i = 0; i < mods_obj.all.length; i++) { + set_mod(mods_obj.all[i]); + } + + let mod_els = document.querySelectorAll("#modsdiv .el"); + for (let i = 0; i < mod_els.length; i++) { + if (! normalized_names.includes(mod_els[i].id)) { + mod_els[i].remove(); + } + } +} + +mods.remove = (mod) => { + if (mod.match(/^northstar\./)) { + if (! confirm(lang("gui.mods.required.confirm"))) { + return; + } + } else if (mod == "allmods") { + if (! confirm(lang("gui.mods.removeall.confirm"))) { + return; + } + } + + ipcRenderer.send("remove-mod", mod); +} + +mods.toggle = (mod) => { + if (mod.match(/^Northstar\./)) { + if (! confirm(lang("gui.mods.required.confirm"))) { + return; + } + } else if (mod == "allmods") { + if (! confirm(lang("gui.mods.toggleall.confirm"))) { + return; + } + } + + ipcRenderer.send("toggle-mod", mod); +} diff --git a/src/app/main.css b/src/app/main.css index 2750094..17ed3ee 100644 --- a/src/app/main.css +++ b/src/app/main.css @@ -1,3 +1,4 @@ +@import "css/grid.css"; @import "css/dragui.css"; @import "css/toasts.css"; @import "css/popups.css"; @@ -105,3 +106,11 @@ a, button, #close, #nsRelease, #vpReleaseNotes, .mod, #overlay, #modsdiv, #winbtns, .contentMenu { -webkit-app-region: no-drag; } + +/* grids */ + +.grid { + display: flex; + flex-wrap: wrap; +} + diff --git a/src/app/main.js b/src/app/main.js index 50160d0..5ac07de 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -157,69 +157,6 @@ ipcRenderer.on("unknown-error", (event, err) => { console.error(err.stack) }) -let lastselected = ""; -function select(entry) { - let entries = document.querySelectorAll("#modsdiv .mod .modtext"); - - for (let i = 0; i < entries.length; i++) { - if (entries[i].innerHTML == entry) { - lastselected = entry; - entries[i].parentElement.classList.add("selected"); - } else { - entries[i].parentElement.classList.remove("selected"); - } - } -} - -// Mod selection -function selected(all) { - let selected = ""; - if (all) { - selected = "allmods" - } else { - selected = document.querySelector(".mod.selected .modtext"); - if (selected != null) { - selected = selected.innerHTML; - } else { - alert(lang("gui.mods.nothingselected")); - return { - remove: () => {}, - toggle: () => {}, - } - } - } - - return { - remove: () => { - - if (selected.match(/^Northstar\./)) { - if (! confirm(lang("gui.mods.required.confirm"))) { - return; - } - } else if (selected == "allmods") { - if (! confirm(lang("gui.mods.removeall.confirm"))) { - return; - } - } - - ipcRenderer.send("remove-mod", selected); - }, - toggle: () => { - if (selected.match(/^Northstar\./)) { - if (! confirm(lang("gui.mods.required.confirm"))) { - return; - } - } else if (selected == "allmods") { - if (! confirm(lang("gui.mods.toggleall.confirm"))) { - return; - } - } - - ipcRenderer.send("toggle-mod", selected); - } - } -} - let installqueue = []; // Tells the main process to install a mod through the file selector @@ -302,27 +239,11 @@ ipcRenderer.on("log", (event, msg) => {log(msg)}) ipcRenderer.on("alert", (event, msg) => {alert(msg)}) // Updates the installed mods -ipcRenderer.on("mods", (event, mods) => { - modsobj = mods; - if (! mods) {return} - - modcount.innerHTML = `${lang("gui.mods.count")} ${mods.all.length}`; - modsdiv.innerHTML = ""; - - let newmod = (name, disabled) => { - if (disabled) { - disabled = `${lang("gui.mods.disabledtag")}` - } else { - disabled = "" - } - - modsdiv.innerHTML += `
${name}${disabled}
`; - } - - for (let i = 0; i < mods.enabled.length; i++) {newmod(mods.enabled[i].Name)} - for (let i = 0; i < mods.disabled.length; i++) {newmod(mods.disabled[i].Name, " - Disabled")} +ipcRenderer.on("mods", (event, mods_obj) => { + modsobj = mods_obj; + if (! mods_obj) {return} - select(lastselected); + mods.load(mods_obj); }) // Updates version numbers diff --git a/src/lang/de.json b/src/lang/de.json index 305176b..e281ff5 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -56,9 +56,7 @@ "gui.mods.disabledtag": "Deaktiviert", "gui.mods.install": "Installiere den Mod", "gui.mods.find": "Suche nach Mods", - "gui.mods.toggle": "Aktiviere/Deaktiviere den Mod", "gui.mods.toggleall": "Aktiviere/Deaktiviere alle Mods", - "gui.mods.remove": "Entferne den Mod", "gui.mods.removeall": "Entferne alle Mods", "gui.mods.nothingselected": "Es wurde kein Mod ausgewählt.", "gui.mods.toggleall.confirm": "Das Deaktivieren aller Mods kann zum Deaktiveren von Mods führen die von Northstar benötigt werden. Bist du dir sicher das du diese Aktion durchführen willst?", diff --git a/src/lang/en.json b/src/lang/en.json index e383249..cfb294a 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -56,9 +56,7 @@ "gui.mods.disabledtag": "Disabled", "gui.mods.install": "Install Mod", "gui.mods.find": "Find Mods", - "gui.mods.toggle": "Toggle Mod", "gui.mods.toggleall": "Toggle All", - "gui.mods.remove": "Remove Mod", "gui.mods.removeall": "Remove All", "gui.mods.nothingselected": "You've not selected a mod.", "gui.mods.toggleall.confirm": "Toggling all mods could disable mods required for Northstar to function. Are you sure?", diff --git a/src/lang/es.json b/src/lang/es.json index b8b406b..64c3f01 100644 --- a/src/lang/es.json +++ b/src/lang/es.json @@ -56,9 +56,7 @@ "gui.mods.disabledtag": "Deshabilitado", "gui.mods.install": "Instalar modificación", "gui.mods.find": "Encontrar modificaciones", - "gui.mods.toggle": "Alternar modificación", "gui.mods.toggleall": "Alternar todo", - "gui.mods.remove": "Remover modificación", "gui.mods.removeall": "Remover todo", "gui.mods.nothingselected": "No has seleccionado una modificación.", "gui.mods.toggleall.confirm": "Alternar todo podría deshabilitar las modificaciones requeridas para que Northstar funcione. ¿Está seguro?", diff --git a/src/lang/fr.json b/src/lang/fr.json index a1f27a4..94c31c8 100644 --- a/src/lang/fr.json +++ b/src/lang/fr.json @@ -56,9 +56,7 @@ "gui.mods.disabledtag": "Désactivé", "gui.mods.install": "Installer le mod", "gui.mods.find": "Chercher des mods", - "gui.mods.toggle": "Activer/désactiver le mod", "gui.mods.toggleall": "Activer/désactiver tous les mods", - "gui.mods.remove": "Supprimer le mod", "gui.mods.removeall": "Tout supprimer", "gui.mods.nothingselected": "Aucun mod n'est sélectionné.", "gui.mods.toggleall.confirm": "Cette action pourrait désactiver des mods nécessaires au bon fonctionnement de Northstar. Souhaitez-vous faire cela ?", -- cgit v1.2.3 From b4615926e88562461aad1a94520919a48de934e9 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Sun, 29 Jan 2023 00:30:13 +0100 Subject: simplify and fix checking package versions Previously some outdated packages would just say "Re-Install" and not "Update", now that's fixed, along with making the logic easier for use in the mod list. --- src/app/index.html | 1 + src/app/js/browser.js | 12 +++--------- src/app/js/misc.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 src/app/js/misc.js (limited to 'src/app/js') diff --git a/src/app/index.html b/src/app/index.html index 9a571f0..d9e8e14 100644 --- a/src/app/index.html +++ b/src/app/index.html @@ -259,6 +259,7 @@ + diff --git a/src/app/js/browser.js b/src/app/js/browser.js index cfd0c03..fcb79a2 100644 --- a/src/app/js/browser.js +++ b/src/app/js/browser.js @@ -328,12 +328,8 @@ function BrowserEl(properties) { if (normalized_mods.includes(normalize(properties.title))) { installstr = lang("gui.browser.reinstall"); - for (let i = 0; i < modsobj.all.length; i++) { - if (normalize(modsobj.all[i].Name) == normalize(properties.title) - && "v" + modsobj.all[i].Version != properties.version) { - - installstr = lang("gui.browser.update"); - } + if (version.is_newer(properties.version, modsobj.all[i].Version)) { + installstr = lang("gui.browser.update"); } } else { for (let i = 0; i < modsobj.all.length; i++) { @@ -347,9 +343,7 @@ function BrowserEl(properties) { if (title.includes(folder) || title.includes(manifestname)) { installstr = lang("gui.browser.reinstall"); - if (folder == title - && "v" + modsobj.all[i].Version != properties.version) { - + if (version.is_newer(properties.version, modsobj.all[i].Version)) { installstr = lang("gui.browser.update"); } } diff --git a/src/app/js/misc.js b/src/app/js/misc.js new file mode 100644 index 0000000..b35f239 --- /dev/null +++ b/src/app/js/misc.js @@ -0,0 +1,44 @@ +version = { + is_newer: (version1, version2) => { + version1 = version.format(version1, true).split("."); + version2 = version.format(version2, true).split("."); + + for (let i = 0; i < version1.length; i++) { + + + let nums = [ + parseInt(version1[i]) || 0, + parseInt(version2[i]) || 0 + ]; + if (nums[0] > nums[1]) { + return true; + } else if (nums[0] < nums[1]) { + return false; + } + } + + return false; + }, + format: (version_number, no_leading_v) => { + version_number = version_number.trim(); + + if (no_leading_v) { + if (version_number[0] == "v") { + return version_number.slice(1, version_number.length); + } + + return version_number; + if (no_leading_v) { + return version_number + } + + return "v" + version_number; + } else { + if (version_number[0] != "v") { + return "v" + version_number; + } + } + + return version_number; + } +} -- cgit v1.2.3