import {appearance} from "./appearance"; import {showMessage} from "../dialog/message"; import {fetchPost} from "../util/fetch"; import {hasClosestByClassName} from "../protyle/util/hasClosest"; import {confirmDialog} from "../dialog/confirmDialog"; import {highlightRender} from "../protyle/markdown/highlightRender"; import {exportLayout} from "../layout/util"; import {Constants} from "../constants"; /// #if !BROWSER import {shell} from "electron"; import * as path from "path"; /// #endif import {isBrowser} from "../util/functions"; import {setStorageVal} from "../protyle/util/compatibility"; export const bazaar = { element: undefined as Element, genHTML() { const localSort = window.siyuan.storage[Constants.LOCAL_BAZAAR]; const loadingHTML = `
`; return `
${window.siyuan.languages.theme}
${window.siyuan.languages.template}
${window.siyuan.languages.icon}
${window.siyuan.languages.widget}
${window.siyuan.languages.downloaded}
${loadingHTML}
${loadingHTML}
${loadingHTML}
${loadingHTML}
${loadingHTML}
`; }, _genCardHTML(item: IBazaarItem, bazaarType: TBazaarType) { let hide = false; let themeMode = ""; if (bazaarType === "themes") { const themeValue = (bazaar.element.querySelector("#bazaarSelect") as HTMLSelectElement).value; if ((themeValue === "0" && item.modes.includes("dark")) || themeValue === "1" && item.modes.includes("light")) { hide = true; } themeMode = item.modes.toString(); } let showSwitch = false; if (["icons", "themes"].includes(bazaarType)) { showSwitch = true; } const dataObj = { bazaarType, themeMode: themeMode, updated: item.updated, name: item.name, repoURL: item.repoURL, repoHash: item.repoHash, downloaded: false, } return `
${item.name} ${item.downloads}
`; }, _genMyHTML(bazaarType: TBazaarType) { let url = "/api/bazaar/getInstalledTheme"; if (bazaarType === "icons") { url = "/api/bazaar/getInstalledIcon"; } else if (bazaarType === "widgets") { url = "/api/bazaar/getInstalledWidget"; } else if (bazaarType === "templates") { url = "/api/bazaar/getInstalledTemplate"; } fetchPost(url, {}, response => { let html = ""; let showSwitch = false; if (["icons", "themes"].includes(bazaarType)) { showSwitch = true; } response.data.packages.forEach((item: IBazaarItem) => { const dataObj = { bazaarType, themeMode: item.modes?.toString(), updated: item.updated, name: item.name, repoURL: item.repoURL, repoHash: item.repoHash, downloaded: true } html += `
${item.name}
`; }); bazaar._data.downloaded = response.data.packages; bazaar.element.querySelector("#configBazaarDownloaded").innerHTML = html ? `
${html}
` : `
`; }); }, _data: { themes: [] as IBazaarItem[], templates: [] as IBazaarItem[], icons: [] as IBazaarItem[], widgets: [] as IBazaarItem[], downloaded: [] as IBazaarItem[], }, _renderReadme(cardElement: HTMLElement, bazaarType: TBazaarType) { const dataObj = JSON.parse(cardElement.getAttribute("data-obj")); let data: IBazaarItem; (dataObj.downloaded ? bazaar._data.downloaded : bazaar._data[bazaarType]).find((item: IBazaarItem) => { if (item.repoURL === dataObj.repoURL) { data = item; return true; } }); const readmeElement = bazaar.element.querySelector("#configBazaarReadme") as HTMLElement; const urls = data.repoURL.split("/"); urls.pop(); let navTitle = window.siyuan.languages.icon; if (bazaarType === "themes") { if (data.modes.includes("dark")) { navTitle = window.siyuan.languages.themeDark + " " + window.siyuan.languages.theme; } else { navTitle = window.siyuan.languages.themeLight + " " + window.siyuan.languages.theme; } } else if (bazaarType === "widgets") { navTitle = window.siyuan.languages.widget; } else if (bazaarType === "templates") { navTitle = window.siyuan.languages.template; } const dataObj1 = { bazaarType, themeMode: data.modes?.toString(), name: data.name, repoURL: data.repoURL, repoHash: data.repoHash, downloaded: true } readmeElement.innerHTML = `
${navTitle}
${data.name}
Made with ❤️ by
${data.author}
${window.siyuan.languages.currentVer}
v${data.version}
${dataObj.downloaded ? window.siyuan.languages.installDate : window.siyuan.languages.releaseDate}
${dataObj.downloaded ? data.hInstallDate : data.hUpdated}
${dataObj.downloaded ? window.siyuan.languages.installSize : window.siyuan.languages.pkgSize}
${dataObj.downloaded ? data.hInstallSize : data.hSize}
Repo ${data.stars} ${data.openIssues} ${data.downloads}
`; if (dataObj.downloaded) { const mdElement = readmeElement.querySelector(".item__readme"); mdElement.innerHTML = data.readme; highlightRender(mdElement); } else { fetchPost("/api/bazaar/getBazaarPackageREAME", { repoURL: data.repoURL, repoHash: data.repoHash, }, response => { const mdElement = readmeElement.querySelector(".item__readme"); mdElement.innerHTML = response.data.html; highlightRender(mdElement); }); } readmeElement.style.right = "0"; }, bindEvent() { fetchPost("/api/bazaar/getBazaarTheme", {}, response => { bazaar._onBazaar(response, "themes", false); bazaar._data.themes = response.data.packages; }); bazaar.element.addEventListener("click", (event) => { let target = event.target as HTMLElement; while (target && !target.isEqualNode(bazaar.element)) { const type = target.getAttribute("data-type"); if (type === "open") { /// #if !BROWSER const dataObj = JSON.parse(target.parentElement.parentElement.getAttribute("data-obj")); const dirName = dataObj.bazaarType; if (dirName === "icons" || dirName === "themes") { shell.openPath(path.join(window.siyuan.config.system.confDir, "appearance", dirName, dataObj.name)); } else { shell.openPath(path.join(window.siyuan.config.system.dataDir, dirName, dataObj.name)); } /// #endif event.preventDefault(); event.stopPropagation(); break; } else if (type === "myTheme" || type === "myTemplate" || type === "myIcon" || type === "myWidget") { if (target.classList.contains("b3-button--outline")) { target.parentElement.childNodes.forEach((item: HTMLElement) => { if (item.nodeType !== 3 && item.classList.contains("b3-button")) { item.classList.add("b3-button--outline"); } }); target.classList.remove("b3-button--outline"); this._genMyHTML(type.replace("my", "").toLowerCase() + "s" as TBazaarType); } event.preventDefault(); event.stopPropagation(); break; } else if (type === "goBack") { const readmeElement = bazaar.element.querySelector("#configBazaarReadme") as HTMLElement; readmeElement.style.right = "-100%"; event.preventDefault(); event.stopPropagation(); break; } else if (type === "install") { if (!target.classList.contains("b3-button--progress")) { const dataObj = JSON.parse(target.parentElement.parentElement.getAttribute("data-obj")); const bazaarType = dataObj.bazaarType as TBazaarType; let url = "/api/bazaar/installBazaarTemplate"; if (bazaarType === "themes") { url = "/api/bazaar/installBazaarTheme"; } else if (bazaarType === "icons") { url = "/api/bazaar/installBazaarIcon"; } else if (bazaarType === "widgets") { url = "/api/bazaar/installBazaarWidget"; } fetchPost(url, { repoURL: dataObj.repoURL, packageName: dataObj.name, repoHash: dataObj.repoHash, mode: dataObj.themeMode === "dark" ? 1 : 0, }, response => { if (window.siyuan.config.appearance.themeJS && bazaarType === "themes") { exportLayout(true); return; } bazaar._genMyHTML(bazaarType); bazaar._onBazaar(response, bazaarType, ["themes", "icons"].includes(bazaarType)); }); } event.preventDefault(); event.stopPropagation(); break; } else if (type === "install-t") { if (!target.classList.contains("b3-button--progress")) { confirmDialog(window.siyuan.languages.update, window.siyuan.languages.exportTplTip, () => { const dataObj = JSON.parse(target.parentElement.parentElement.getAttribute("data-obj")) const bazaarType: TBazaarType = dataObj.bazaarType; let url = "/api/bazaar/installBazaarTemplate"; if (bazaarType === "themes") { url = "/api/bazaar/installBazaarTheme"; } else if (bazaarType === "icons") { url = "/api/bazaar/installBazaarIcon"; } else if (bazaarType === "widgets") { url = "/api/bazaar/installBazaarWidget"; } if (!target.classList.contains("b3-button")) { target.parentElement.insertAdjacentHTML("afterend", ''); } fetchPost(url, { repoURL: dataObj.repoURL, packageName: dataObj.name, repoHash: dataObj.repoHash, mode: dataObj.themeMode === "dark" ? 1 : 0, update: true, }, response => { // 更新主题后不需要对该主题进行切换 https://github.com/siyuan-note/siyuan/issues/4966 this._genMyHTML(bazaarType); bazaar._onBazaar(response, bazaarType, ["icons"].includes(bazaarType)); // https://github.com/siyuan-note/siyuan/issues/5411 if (bazaarType === "themes" && ( (window.siyuan.config.appearance.mode === 0 && window.siyuan.config.appearance.themeLight === dataObj.name) || (window.siyuan.config.appearance.mode === 1 && window.siyuan.config.appearance.themeDark === dataObj.name) )) { if (window.siyuan.config.appearance.themeJS) { exportLayout(true); } else { const linkElement = (document.getElementById("themeDefaultStyle") as HTMLLinkElement); linkElement.href = linkElement.href + "1"; } } }); }); } event.preventDefault(); event.stopPropagation(); break; } else if (type === "uninstall") { const dataObj = JSON.parse(target.parentElement.parentElement.getAttribute("data-obj")) const bazaarType: TBazaarType = dataObj.bazaarType; let url = "/api/bazaar/uninstallBazaarTemplate"; if (bazaarType === "themes") { url = "/api/bazaar/uninstallBazaarTheme"; } else if (bazaarType === "icons") { url = "/api/bazaar/uninstallBazaarIcon"; } else if (bazaarType === "widgets") { url = "/api/bazaar/uninstallBazaarWidget"; } const packageName = dataObj.name; if (window.siyuan.config.appearance.themeDark === packageName || window.siyuan.config.appearance.themeLight === packageName || window.siyuan.config.appearance.icon === packageName) { showMessage(window.siyuan.languages.uninstallTip); } else { fetchPost(url, { packageName }, response => { this._genMyHTML(bazaarType); bazaar._onBazaar(response, bazaarType, ["themes", "icons"].includes(bazaarType)); }); } event.preventDefault(); event.stopPropagation(); break; } else if (type === "switch") { const dataObj = JSON.parse(target.parentElement.parentElement.getAttribute("data-obj")) const bazaarType: TBazaarType = dataObj.bazaarType; const packageName = dataObj.name; const mode = dataObj.themeMode === "dark" ? 1 : 0; if (bazaarType === "icons") { fetchPost("/api/setting/setAppearance", Object.assign({}, window.siyuan.config.appearance, { icon: packageName, }), (appearanceResponse) => { this._genMyHTML(bazaarType); fetchPost("/api/bazaar/getBazaarIcon", {}, response => { response.data.appearance = appearanceResponse.data bazaar._onBazaar(response, "icons", true); bazaar._data.icons = response.data.packages; }); }); } else if (bazaarType === "themes") { fetchPost("/api/setting/setAppearance", Object.assign({}, window.siyuan.config.appearance, { mode, modeOS: false, themeDark: mode === 1 ? packageName : window.siyuan.config.appearance.themeDark, themeLight: mode === 0 ? packageName : window.siyuan.config.appearance.themeLight, }), (appearanceResponse) => { if ((mode !== window.siyuan.config.appearance.mode || (mode === 1 && window.siyuan.config.appearance.themeDark !== packageName) || (mode === 0 && window.siyuan.config.appearance.themeLight !== packageName)) && window.siyuan.config.appearance.themeJS) { exportLayout(true); } else { this._genMyHTML("themes"); fetchPost("/api/bazaar/getBazaarTheme", {}, response => { response.data.appearance = appearanceResponse.data bazaar._onBazaar(response, "themes", true); bazaar._data.themes = response.data.packages; }); } }); } event.preventDefault(); event.stopPropagation(); break; } else if (target.classList.contains("b3-card")) { bazaar._renderReadme(target, (JSON.parse(target.getAttribute("data-obj")).bazaarType) as TBazaarType); event.preventDefault(); event.stopPropagation(); break; } else if (target.classList.contains("item") && !target.classList.contains("item--focus")) { // switch tab bazaar.element.querySelector(".layout-tab-bar .item--focus").classList.remove("item--focus"); target.classList.add("item--focus"); bazaar.element.querySelectorAll(".bazaarPanel").forEach(item => { if (type === item.getAttribute("data-type")) { item.classList.remove("fn__none"); if (!item.getAttribute("data-init")) { if (type === "template") { fetchPost("/api/bazaar/getBazaarTemplate", {}, response => { bazaar._onBazaar(response, "templates", false); bazaar._data.templates = response.data.packages; }); } else if (type === "icon") { fetchPost("/api/bazaar/getBazaarIcon", {}, response => { bazaar._onBazaar(response, "icons", false); bazaar._data.icons = response.data.packages; }); } else if (type === "widget") { fetchPost("/api/bazaar/getBazaarWidget", {}, response => { bazaar._onBazaar(response, "widgets", false); bazaar._data.widgets = response.data.packages; }); } else if (type === "downloaded") { this._genMyHTML("themes"); } item.setAttribute("data-init", "true"); } } else { item.classList.add("fn__none"); } }); event.preventDefault(); event.stopPropagation(); break; } else if (target.classList.contains("item__preview")) { target.classList.toggle("item__preview--fullscreen"); event.preventDefault(); event.stopPropagation(); break; } target = target.parentElement; } }); bazaar.element.querySelectorAll(".b3-select").forEach((selectElement: HTMLSelectElement) => { selectElement.addEventListener("change", () => { if (selectElement.id === "bazaarSelect") { // theme select bazaar.element.querySelectorAll("#configBazaarTheme .b3-card").forEach((item) => { const dataObj = JSON.parse(item.getAttribute("data-obj")); if (selectElement.value === "0") { if (dataObj.themeMode.indexOf("light") > -1) { item.classList.remove("fn__none"); } else { item.classList.add("fn__none"); } } else if (selectElement.value === "1") { if (dataObj.themeMode.indexOf("dark") > -1) { item.classList.remove("fn__none"); } else { item.classList.add("fn__none"); } } else { item.classList.remove("fn__none"); } }); } else { // sort const localSort = window.siyuan.storage[Constants.LOCAL_BAZAAR]; const panelElement = selectElement.parentElement.parentElement; let html = ""; if (selectElement.value === "0") { // 更新时间降序 Array.from(panelElement.querySelectorAll(".b3-card")).sort((a, b) => { return JSON.parse(b.getAttribute("data-obj")).updated < JSON.parse(a.getAttribute("data-obj")).updated ? -1 : 1; }).forEach((item) => { html += item.outerHTML; }); } else if (selectElement.value === "1") { // 更新时间升序 Array.from(panelElement.querySelectorAll(".b3-card")).sort((a, b) => { return JSON.parse(b.getAttribute("data-obj")).updated < JSON.parse(a.getAttribute("data-obj")).updated ? 1 : -1; }).forEach((item) => { html += item.outerHTML; }); } else if (selectElement.value === "2") { // 下载次数降序 Array.from(panelElement.querySelectorAll(".b3-card")).sort((a, b) => { return parseInt(b.querySelector(".b3-card__info").lastElementChild.textContent) < parseInt(a.querySelector(".b3-card__info").lastElementChild.textContent) ? -1 : 1; }).forEach((item) => { html += item.outerHTML; }); } else if (selectElement.value === "3") { // 下载次数升序 Array.from(panelElement.querySelectorAll(".b3-card")).sort((a, b) => { return parseInt(b.querySelector(".b3-card__info").lastElementChild.textContent) < parseInt(a.querySelector(".b3-card__info").lastElementChild.textContent) ? 1 : -1; }).forEach((item) => { html += item.outerHTML; }); } localSort[selectElement.parentElement.parentElement.getAttribute("data-type")] = selectElement.value; setStorageVal(Constants.LOCAL_BAZAAR, window.siyuan.storage[Constants.LOCAL_BAZAAR]); panelElement.querySelector(".b3-cards").innerHTML = html; } }); }); }, _onBazaar(response: IWebSocketData, bazaarType: TBazaarType, reload: boolean) { let id = "#configBazaarTemplate"; if (bazaarType === "themes") { id = "#configBazaarTheme"; } else if (bazaarType === "icons") { id = "#configBazaarIcon"; } else if (bazaarType === "widgets") { id = "#configBazaarWidget"; } const element = bazaar.element.querySelector(id); if (response.code === 1) { showMessage(response.msg); element.querySelectorAll("img[data-type='img-loading']").forEach((item) => { item.remove(); }); } let html = ""; response.data.packages.forEach((item: IBazaarItem) => { html += this._genCardHTML(item, bazaarType); }); bazaar._data[bazaarType] = response.data.packages; element.innerHTML = `
${html}
`; const localSort = window.siyuan.storage[Constants.LOCAL_BAZAAR]; if (localSort[bazaarType.replace("s", "")] === "1") { html = ""; Array.from(element.querySelectorAll(".b3-card")).sort((a, b) => { return JSON.parse(b.getAttribute("data-obj")).updated < JSON.parse(a.getAttribute("data-obj")).updated ? 1 : -1; }).forEach((item) => { html += item.outerHTML; }); } else if (localSort[bazaarType.replace("s", "")] === "2") { // 下载次数降序 html = ""; Array.from(element.querySelectorAll(".b3-card")).sort((a, b) => { return parseInt(b.querySelector(".b3-card__info").lastElementChild.textContent) < parseInt(a.querySelector(".b3-card__info").lastElementChild.textContent) ? -1 : 1; }).forEach((item) => { html += item.outerHTML; }); } else if (localSort[bazaarType.replace("s", "")] === "3") { // 下载次数升序 html = ""; Array.from(element.querySelectorAll(".b3-card")).sort((a, b) => { return parseInt(b.querySelector(".b3-card__info").lastElementChild.textContent) < parseInt(a.querySelector(".b3-card__info").lastElementChild.textContent) ? 1 : -1; }).forEach((item) => { html += item.outerHTML; }); } element.innerHTML = `
${html}
`; if (reload) { appearance.onSetappearance(response.data.appearance); } } };