Skip to content

Commit 76797cf

Browse files
authored
🎨 Improve opening bazaar package readme via URI #17938 (#18068)
1 parent adaabd0 commit 76797cf

3 files changed

Lines changed: 152 additions & 51 deletions

File tree

app/src/config/bazaar.ts

Lines changed: 121 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const renderReadme = (bazaarType: TBazaarType, from: "downloaded" | "upda
6161
bazaar._renderReadme(bazaarType, from, data);
6262
};
6363

64-
const bazaar = {
64+
export const bazaar = {
6565
element: undefined as Element,
6666
genHTML() {
6767
if (!window.siyuan.config.bazaar.trust) {
@@ -400,8 +400,10 @@ const bazaar = {
400400
this._getUpdate();
401401
}
402402
const contentElement = bazaar.element.querySelector("#configBazaarDownloaded");
403+
const myType = bazaar._type2myType(bazaarType);
404+
const typeBtn = contentElement.previousElementSibling.querySelector(`[data-type="${myType}"]`) as HTMLElement;
403405
if (contentElement.getAttribute("data-loading") === "true" ||
404-
contentElement.previousElementSibling.querySelector(`[data-type="my${bazaarType.replace(bazaarType[0], bazaarType[0].toUpperCase()).substring(0, bazaarType.length - 1)}"]`).classList.contains("b3-button--outline")) {
406+
typeBtn?.classList.contains("b3-button--outline")) {
405407
return;
406408
}
407409
contentElement.setAttribute("data-loading", "true");
@@ -421,6 +423,10 @@ const bazaar = {
421423
keyword: (contentElement.previousElementSibling.querySelector(".b3-text-field") as HTMLInputElement)?.value || "",
422424
}, response => {
423425
contentElement.removeAttribute("data-loading");
426+
const activeBtn = contentElement.previousElementSibling.querySelector(".b3-button:not(.b3-button--outline)") as HTMLElement;
427+
if (activeBtn?.getAttribute("data-type") !== myType) {
428+
return;
429+
}
424430
let html = "";
425431
const counterElement = contentElement.previousElementSibling.querySelector(".counter");
426432
if (response.data.packages.length === 0) {
@@ -483,8 +489,10 @@ type="checkbox">
483489
checkElement.classList.add("fn__none");
484490
}
485491
contentElement.innerHTML = html ? html : `<ul class="b3-list b3-list--background"><li class="b3-list--empty">${window.siyuan.languages.emptyContent}</li></ul>`;
486-
if (bazaar.element.querySelector("#configBazaarReadme").classList.contains("config-bazaar__readme--show")) {
487-
const repoURL = bazaar.element.querySelector("#configBazaarReadme .item__side")?.getAttribute("data-repourl");
492+
const sideElement = bazaar.element.querySelector("#configBazaarReadme.config-bazaar__readme--show .item__side");
493+
// 仅刷新「已下载」详情,避免通过 URI 打开的在线详情被本地数据覆盖
494+
if (sideElement?.getAttribute("data-from") === "downloaded") {
495+
const repoURL = sideElement.getAttribute("data-repourl");
488496
bazaar._data.downloaded.find((i) => {
489497
if (i.repoURL === repoURL) {
490498
bazaar._renderReadme(bazaarType, "downloaded", i);
@@ -509,6 +517,23 @@ type="checkbox">
509517
plugins: [] as IBazaarItem[],
510518
}
511519
},
520+
_upsertReadmeData(bazaarType: TBazaarType, from: "downloaded" | "updated" | "bazaar", data: IBazaarItem) {
521+
const upsert = (list: IBazaarItem[]) => {
522+
const index = list.findIndex((item) => item.repoURL === data.repoURL);
523+
if (index >= 0) {
524+
list[index] = data;
525+
} else {
526+
list.push(data);
527+
}
528+
};
529+
if (from === "downloaded") {
530+
upsert(bazaar._data.downloaded);
531+
} else if (from === "updated") {
532+
upsert(bazaar._data.update[bazaarType]);
533+
} else {
534+
upsert(bazaar._data[bazaarType]);
535+
}
536+
},
512537
_renderReadme(bazaarType: TBazaarType, from: "downloaded" | "updated" | "bazaar", data: IBazaarItem) {
513538
const readmeElement = bazaar.element.querySelector("#configBazaarReadme") as HTMLElement;
514539
const urls = data.repoURL.split("/");
@@ -523,8 +548,9 @@ type="checkbox">
523548
if (!(bazaarType in navTitles)) {
524549
return;
525550
}
551+
bazaar._upsertReadmeData(bazaarType, from, data);
526552
const isDownload = from === "downloaded";
527-
readmeElement.innerHTML = ` <div class="item__side" data-from="${from}" data-repourl="${escapeAttr(data.repoURL)}">
553+
readmeElement.innerHTML = ` <div class="item__side" data-from="${from}" data-package-type="${bazaarType}" data-repourl="${escapeAttr(data.repoURL)}">
528554
<div class="fn__flex">
529555
<div style="padding-right: 8px" class="block__icon block__icon--show ariaLabel" data-position="north" data-type="goBack" aria-label="${window.siyuan.languages.back}">
530556
<svg><use xlink:href="#iconLeft"></use></svg>
@@ -631,6 +657,80 @@ type="checkbox">
631657
_type2tabType(type: TBazaarType) {
632658
return type.slice(0, -1);
633659
},
660+
_type2myType(type: TBazaarType) {
661+
const tab = bazaar._type2tabType(type);
662+
return "my" + tab.charAt(0).toUpperCase() + tab.slice(1);
663+
},
664+
_initBazaarPanel(app: App, bazaarType: TBazaarType, panel: HTMLElement) {
665+
if (panel.getAttribute("data-init")) {
666+
return;
667+
}
668+
switch (bazaar._type2tabType(bazaarType)) {
669+
case "template":
670+
fetchPost("/api/bazaar/getBazaarTemplate", {}, response => {
671+
bazaar._onBazaar(response, "templates");
672+
bazaar._data.templates = response.data.packages;
673+
});
674+
break;
675+
case "icon":
676+
fetchPost("/api/bazaar/getBazaarIcon", {}, response => {
677+
bazaar._onBazaar(response, "icons");
678+
bazaar._data.icons = response.data.packages;
679+
});
680+
break;
681+
case "widget":
682+
fetchPost("/api/bazaar/getBazaarWidget", {}, response => {
683+
bazaar._onBazaar(response, "widgets");
684+
bazaar._data.widgets = response.data.packages;
685+
});
686+
break;
687+
case "theme":
688+
fetchPost("/api/bazaar/getBazaarTheme", {}, response => {
689+
bazaar._onBazaar(response, "themes");
690+
bazaar._data.themes = response.data.packages;
691+
});
692+
break;
693+
case "plugin":
694+
fetchPost("/api/bazaar/getBazaarPlugin", {
695+
frontend: getFrontend()
696+
}, response => {
697+
bazaar._onBazaar(response, "plugins");
698+
bazaar._data.plugins = response.data.packages;
699+
});
700+
break;
701+
}
702+
panel.setAttribute("data-init", "true");
703+
},
704+
/** 切换集市顶部 Tab */
705+
switchBazaarTab(app: App, bazaarType: TBazaarType, from: "downloaded" | "updated" | "bazaar") {
706+
if (!bazaar.element) {
707+
return;
708+
}
709+
const layoutTabType = from === "bazaar" ? bazaar._type2tabType(bazaarType) : "downloaded";
710+
const focusItem = bazaar.element.querySelector(`.layout-tab-bar .item[data-type="${layoutTabType}"]`);
711+
const currentFocus = bazaar.element.querySelector(".layout-tab-bar .item--focus");
712+
if (focusItem && focusItem !== currentFocus) {
713+
currentFocus?.classList.remove("item--focus");
714+
focusItem.classList.add("item--focus");
715+
}
716+
bazaar.element.querySelectorAll(".config-bazaar__panel").forEach((panel) => {
717+
const panelType = panel.getAttribute("data-type");
718+
const isActive = panelType === layoutTabType;
719+
panel.classList.toggle("fn__none", !isActive);
720+
if (isActive && from === "bazaar") {
721+
bazaar._initBazaarPanel(app, bazaarType, panel as HTMLElement);
722+
}
723+
});
724+
if (from === "downloaded") {
725+
const myType = bazaar._type2myType(bazaarType);
726+
const titleBar = bazaar.element.querySelector('.config-bazaar__panel[data-type="downloaded"] .config-bazaar__title');
727+
titleBar?.querySelectorAll(".b3-button").forEach((btn) => {
728+
btn.classList.toggle("b3-button--outline", btn.getAttribute("data-type") !== myType);
729+
});
730+
bazaar.element.querySelector("#configBazaarDownloaded")?.removeAttribute("data-loading");
731+
bazaar._genMyHTML(bazaarType, app, false);
732+
}
733+
},
634734
bindEvent(app: App) {
635735
if (!window.siyuan.config.bazaar.trust) {
636736
bazaar.element.querySelector("button").addEventListener("click", () => {
@@ -653,11 +753,20 @@ type="checkbox">
653753
let pkgItem: IBazaarItem;
654754
if (repoElement) {
655755
const repo = repoElement.getAttribute("data-repourl");
656-
let sideForm;
657756
if (repoElement.classList.contains("item__side")) {
658-
sideForm = repoElement.getAttribute("data-from");
659-
}
660-
if (hasClosestByAttribute(repoElement, "data-type", "downloaded-update") || sideForm === "updated") {
757+
const sideForm = repoElement.getAttribute("data-from");
758+
const sidePackageType = repoElement.getAttribute("data-package-type") as TBazaarType;
759+
if (sidePackageType && sideForm === "downloaded") {
760+
pkgType = sidePackageType;
761+
pkgItem = bazaar._data.downloaded.find((i) => i.repoURL === repo);
762+
} else if (sidePackageType && sideForm === "updated") {
763+
pkgType = sidePackageType;
764+
pkgItem = bazaar._data.update[sidePackageType]?.find((i) => i.repoURL === repo);
765+
} else if (sidePackageType && sideForm === "bazaar") {
766+
pkgType = sidePackageType;
767+
pkgItem = bazaar._data[sidePackageType]?.find((i) => i.repoURL === repo);
768+
}
769+
} else if (hasClosestByAttribute(repoElement, "data-type", "downloaded-update")) {
661770
for (const bazaarType of ["plugins", "themes", "icons", "templates", "widgets"] as TBazaarType[]) {
662771
const item = bazaar._data.update[bazaarType]?.find((i) => i.repoURL === repo);
663772
if (item) {
@@ -666,7 +775,7 @@ type="checkbox">
666775
break;
667776
}
668777
}
669-
} else if (hasClosestByAttribute(repoElement, "id", "configBazaarDownloaded") || sideForm === "downloaded") {
778+
} else if (hasClosestByAttribute(repoElement, "id", "configBazaarDownloaded")) {
670779
const activeBtn = bazaar.element.querySelector("#configBazaarDownloaded")?.previousElementSibling?.querySelector(".b3-button:not(.b3-button--outline)") as HTMLElement;
671780
if (activeBtn?.getAttribute("data-type")) {
672781
const activeBazaarType = bazaar._myType2Type(activeBtn.getAttribute("data-type"));
@@ -972,36 +1081,8 @@ type="checkbox">
9721081
bazaar.element.querySelectorAll(".config-bazaar__panel").forEach(item => {
9731082
if (type === item.getAttribute("data-type")) {
9741083
item.classList.remove("fn__none");
975-
if (!item.getAttribute("data-init")) {
976-
if (type === "template") {
977-
fetchPost("/api/bazaar/getBazaarTemplate", {}, response => {
978-
bazaar._onBazaar(response, "templates");
979-
bazaar._data.templates = response.data.packages;
980-
});
981-
} else if (type === "icon") {
982-
fetchPost("/api/bazaar/getBazaarIcon", {}, response => {
983-
bazaar._onBazaar(response, "icons");
984-
bazaar._data.icons = response.data.packages;
985-
});
986-
} else if (type === "widget") {
987-
fetchPost("/api/bazaar/getBazaarWidget", {}, response => {
988-
bazaar._onBazaar(response, "widgets");
989-
bazaar._data.widgets = response.data.packages;
990-
});
991-
} else if (type === "theme") {
992-
fetchPost("/api/bazaar/getBazaarTheme", {}, response => {
993-
bazaar._onBazaar(response, "themes");
994-
bazaar._data.themes = response.data.packages;
995-
});
996-
} else if (type === "plugin") {
997-
fetchPost("/api/bazaar/getBazaarPlugin", {
998-
frontend: getFrontend()
999-
}, response => {
1000-
bazaar._onBazaar(response, "plugins");
1001-
bazaar._data.plugins = response.data.packages;
1002-
});
1003-
}
1004-
item.setAttribute("data-init", "true");
1084+
if (type !== "downloaded") {
1085+
bazaar._initBazaarPanel(app, (type + "s") as TBazaarType, item as HTMLElement);
10051086
}
10061087
} else {
10071088
item.classList.add("fn__none");

app/src/config/index.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {bindSettingSaveDelegation} from "./setting/save";
66
import {Dialog} from "../dialog";
77
import {Constants} from "../constants";
88
import {focusByRange} from "../protyle/util/selection";
9-
import {renderReadme} from "./bazaar";
9+
import {bazaar, renderReadme} from "./bazaar";
1010
import {fetchSyncPost} from "../util/fetch";
11+
import {getFrontend} from "../util/functions";
12+
import {showMessage} from "../dialog/message";
1113
/// #endif
1214
import {getSettingTabDefs} from "./setting/tabs";
1315
import {clearAccessTabElement} from "./tabs/accessRuntime";
@@ -83,36 +85,50 @@ export const openSetting = (app: App, tab?: TSettingTab) => {
8385
/// #endif
8486
};
8587

86-
export const openBazaarReadme = async (app: App, bazaarType: TBazaarType, itemName: string) => {
88+
export const openBazaarReadme = async (app: App, bazaarType: TBazaarType, itemName: string, from: "bazaar" | "downloaded") => {
8789
/// #if !MOBILE
90+
if (!window.siyuan.config.bazaar.trust) {
91+
openSettingDialog(app, "bazaar");
92+
return;
93+
}
94+
95+
const isDownloaded = from === "downloaded";
8896
let getResourcesUrl: string;
8997
switch (bazaarType) {
9098
case "templates":
91-
getResourcesUrl = "/api/bazaar/getBazaarTemplate";
99+
getResourcesUrl = isDownloaded ? "/api/bazaar/getInstalledTemplate" : "/api/bazaar/getBazaarTemplate";
92100
break;
93101
case "icons":
94-
getResourcesUrl = "/api/bazaar/getBazaarIcon";
102+
getResourcesUrl = isDownloaded ? "/api/bazaar/getInstalledIcon" : "/api/bazaar/getBazaarIcon";
95103
break;
96104
case "widgets":
97-
getResourcesUrl = "/api/bazaar/getBazaarWidget";
105+
getResourcesUrl = isDownloaded ? "/api/bazaar/getInstalledWidget" : "/api/bazaar/getBazaarWidget";
98106
break;
99107
case "themes":
100-
getResourcesUrl = "/api/bazaar/getBazaarTheme";
108+
getResourcesUrl = isDownloaded ? "/api/bazaar/getInstalledTheme" : "/api/bazaar/getBazaarTheme";
101109
break;
102110
case "plugins":
103-
getResourcesUrl = "/api/bazaar/getBazaarPlugin";
111+
getResourcesUrl = isDownloaded ? "/api/bazaar/getInstalledPlugin" : "/api/bazaar/getBazaarPlugin";
104112
break;
105113
default:
106114
return;
107115
}
108116

109-
const response = await fetchSyncPost(getResourcesUrl, {frontend: "all", keyword: itemName});
117+
const response = await fetchSyncPost(getResourcesUrl, {
118+
frontend: getFrontend(),
119+
// 完整包名作 keyword 可缩小请求响应列表;最终仍按 name 精确匹配
120+
keyword: itemName,
121+
});
110122
if (response.code !== 0) return;
111123

112124
const resource = (response.data.packages as IBazaarItem[]).find((item: IBazaarItem) => item.name === itemName);
113-
if (!resource) return;
125+
if (!resource) {
126+
showMessage(`Package not found: ${itemName}`);
127+
return;
128+
}
114129

115130
openSettingDialog(app, "bazaar");
116-
renderReadme(bazaarType, "bazaar", resource);
131+
bazaar.switchBazaarTab(app, bazaarType, from);
132+
renderReadme(bazaarType, from, resource);
117133
/// #endif
118134
};

app/src/util/uri.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,16 @@ const processSiYuanUriBazaar = (app: App, uriObj: URL): boolean => {
110110
}
111111
switch (target) {
112112
case "readme":
113+
case "readme-installed": {
113114
// siyuan://bazaar/plugins/plugin-sample/readme
115+
// siyuan://bazaar/plugins/plugin-sample/readme-installed
116+
const from = target === "readme-installed" ? "downloaded" : "bazaar";
114117
(async () => {
115118
const {openBazaarReadme} = await import("../config");
116-
openBazaarReadme(app, resourceType, resourceName);
119+
openBazaarReadme(app, resourceType, resourceName, from);
117120
})();
118121
return true;
122+
}
119123
default:
120124
break;
121125
}

0 commit comments

Comments
 (0)