Skip to content

Commit

Permalink
feat: enable asynchronous resolving for UI extension points (#6018)
Browse files Browse the repository at this point in the history
#### What type of PR is this?

/area ui
/kind feature
/milestone 2.16.x

#### What this PR does / why we need it:

优化 UI 部分的扩展点获取实现,让部分扩展点支持异步获取,之前的实现与文档不符。

比如:

```ts
import { definePlugin } from "@halo-dev/console-shared";
import axios from "axios";

export default definePlugin({
  components: {},
  routes: [],
  extensionPoints: {
    "attachment:selector:create": async () => {
      const { data } = await axios.get(
        "/apis/v1alpha1/fake.halo.run/attachments/selectors"
      );

      return data;
    },
  },
});
```

#### Which issue(s) this PR fixes:

Fixes #6008

#### Does this PR introduce a user-facing change?

```release-note
优化 UI 部分的扩展点获取实现,让部分扩展点支持异步获取。
```
  • Loading branch information
ruibaby committed May 30, 2024
1 parent bf75a36 commit 4c6abdc
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 272 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<script lang="ts" setup>
import { usePluginModuleStore } from "@/stores/plugin";
import { VButton, VModal, VSpace, VTabbar } from "@halo-dev/components";
import { ref, markRaw, onMounted, computed } from "vue";
import CoreSelectorProvider from "./selector-providers/CoreSelectorProvider.vue";
import type {
AttachmentLike,
AttachmentSelectProvider,
PluginModule,
} from "@halo-dev/console-shared";
import { usePluginModuleStore } from "@/stores/plugin";
import { computed, markRaw, onMounted, ref } from "vue";
import { useI18n } from "vue-i18n";
import CoreSelectorProvider from "./selector-providers/CoreSelectorProvider.vue";
const { t } = useI18n();
Expand Down Expand Up @@ -46,23 +45,22 @@ const attachmentSelectProviders = ref<AttachmentSelectProvider[]>([
// resolve plugin extension points
const { pluginModules } = usePluginModuleStore();
onMounted(() => {
pluginModules.forEach((pluginModule: PluginModule) => {
const { extensionPoints } = pluginModule;
if (!extensionPoints?.["attachment:selector:create"]) {
return;
}
onMounted(async () => {
for (const pluginModule of pluginModules) {
try {
const callbackFunction =
pluginModule?.extensionPoints?.["attachment:selector:create"];
const providers = extensionPoints[
"attachment:selector:create"
]() as AttachmentSelectProvider[];
if (typeof callbackFunction !== "function") {
continue;
}
if (providers) {
providers.forEach((provider) => {
attachmentSelectProviders.value.push(provider);
});
const providers = await callbackFunction();
attachmentSelectProviders.value.push(...providers);
} catch (error) {
console.error(`Error processing plugin module:`, pluginModule, error);
}
});
}
});
const activeId = ref(attachmentSelectProviders.value[0].id);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
<script lang="ts" setup>
import { usePluginModuleStore } from "@/stores/plugin";
import { apiClient } from "@/utils/api-client";
import { formatDatetime } from "@/utils/date";
import { usePermission } from "@/utils/permission";
import type {
Extension,
ListedComment,
ListedReply,
Post,
SinglePage,
} from "@halo-dev/api-client";
import {
Dialog,
IconAddCircle,
Expand All @@ -15,28 +26,16 @@ import {
VStatusDot,
VTag,
} from "@halo-dev/components";
import ReplyCreationModal from "./ReplyCreationModal.vue";
import type {
Extension,
ListedComment,
ListedReply,
Post,
SinglePage,
} from "@halo-dev/api-client";
import { formatDatetime } from "@/utils/date";
import { computed, onMounted, provide, ref, type Ref } from "vue";
import ReplyListItem from "./ReplyListItem.vue";
import { apiClient } from "@/utils/api-client";
import { cloneDeep } from "lodash-es";
import { usePermission } from "@/utils/permission";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import { useI18n } from "vue-i18n";
import { usePluginModuleStore } from "@/stores/plugin";
import type {
CommentSubjectRefProvider,
CommentSubjectRefResult,
PluginModule,
} from "@halo-dev/console-shared";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import { cloneDeep } from "lodash-es";
import { computed, onMounted, provide, ref, type Ref } from "vue";
import { useI18n } from "vue-i18n";
import ReplyCreationModal from "./ReplyCreationModal.vue";
import ReplyListItem from "./ReplyListItem.vue";
const { currentUserHasPermission } = usePermission();
const { t } = useI18n();
Expand Down Expand Up @@ -250,25 +249,21 @@ const SubjectRefProviders = ref<CommentSubjectRefProvider[]>([
},
]);
const { pluginModules } = usePluginModuleStore();
onMounted(() => {
const { pluginModules } = usePluginModuleStore();
for (const pluginModule of pluginModules) {
const callbackFunction =
pluginModule?.extensionPoints?.["comment:subject-ref:create"];
pluginModules.forEach((pluginModule: PluginModule) => {
const { extensionPoints } = pluginModule;
if (!extensionPoints?.["comment:subject-ref:create"]) {
return;
if (typeof callbackFunction !== "function") {
continue;
}
const providers = extensionPoints[
"comment:subject-ref:create"
]() as CommentSubjectRefProvider[];
const providers = callbackFunction();
if (providers) {
providers.forEach((provider) => {
SubjectRefProviders.value.push(provider);
});
}
});
SubjectRefProviders.value.push(...providers);
}
});
const subjectRefResult = computed(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<script lang="ts" setup>
import { usePluginModuleStore } from "@/stores/plugin";
import { usePermission } from "@/utils/permission";
import type { Theme } from "@halo-dev/api-client";
import { VButton, VModal, VTabbar } from "@halo-dev/components";
import type { ThemeListTab } from "@halo-dev/console-shared";
import { useRouteQuery } from "@vueuse/router";
import {
computed,
inject,
Expand All @@ -8,19 +13,14 @@ import {
onMounted,
provide,
ref,
type Ref,
watch,
type Ref,
} from "vue";
import type { Theme } from "@halo-dev/api-client";
import { useI18n } from "vue-i18n";
import { useRouteQuery } from "@vueuse/router";
import InstalledThemes from "./list-tabs/InstalledThemes.vue";
import NotInstalledThemes from "./list-tabs/NotInstalledThemes.vue";
import LocalUpload from "./list-tabs/LocalUpload.vue";
import NotInstalledThemes from "./list-tabs/NotInstalledThemes.vue";
import RemoteDownload from "./list-tabs/RemoteDownload.vue";
import { usePluginModuleStore } from "@/stores/plugin";
import type { PluginModule, ThemeListTab } from "@halo-dev/console-shared";
import { usePermission } from "@/utils/permission";
const { t } = useI18n();
const { currentUserHasPermission } = usePermission();
Expand Down Expand Up @@ -92,22 +92,30 @@ onMounted(() => {
});
const { pluginModules } = usePluginModuleStore();
onMounted(() => {
onMounted(async () => {
const tabsFromPlugins: ThemeListTab[] = [];
pluginModules.forEach((pluginModule: PluginModule) => {
const { extensionPoints } = pluginModule;
if (!extensionPoints?.["theme:list:tabs:create"]) {
return;
}
let items = extensionPoints["theme:list:tabs:create"]() as ThemeListTab[];
for (const pluginModule of pluginModules) {
try {
const callbackFunction =
pluginModule?.extensionPoints?.["theme:list:tabs:create"];
items = items.filter((item) => {
return currentUserHasPermission(item.permissions);
});
if (typeof callbackFunction !== "function") {
continue;
}
tabsFromPlugins.push(...items);
});
const items = await callbackFunction();
tabsFromPlugins.push(
...items.filter((item) => {
return currentUserHasPermission(item.permissions);
})
);
} catch (error) {
console.error(`Error processing plugin module:`, pluginModule, error);
}
}
tabs.value = tabs.value.concat(tabsFromPlugins).sort((a, b) => {
return a.priority - b.priority;
Expand Down
47 changes: 26 additions & 21 deletions ui/console-src/modules/system/backup/Backups.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
<script lang="ts" setup>
import {
VPageHeader,
VCard,
VButton,
VTabbar,
IconAddCircle,
IconServerLine,
VButton,
VCard,
VPageHeader,
VTabbar,
} from "@halo-dev/components";
import { onMounted, shallowRef } from "vue";
import ListTab from "./tabs/List.vue";
import RestoreTab from "./tabs/Restore.vue";
import { usePluginModuleStore } from "@/stores/plugin";
import type { BackupTab } from "@halo-dev/console-shared";
import { useRouteQuery } from "@vueuse/router";
import { markRaw } from "vue";
import { markRaw, onMounted, shallowRef } from "vue";
import { useI18n } from "vue-i18n";
import { useBackup } from "./composables/use-backup";
import { usePluginModuleStore } from "@/stores/plugin";
import type { BackupTab } from "@halo-dev/console-shared";
import ListTab from "./tabs/List.vue";
import RestoreTab from "./tabs/Restore.vue";
const { t } = useI18n();
Expand All @@ -37,21 +36,27 @@ const activeTab = useRouteQuery<string>("tab", tabs.value[0].id);
const { handleCreate } = useBackup();
onMounted(() => {
const { pluginModules } = usePluginModuleStore();
const { pluginModules } = usePluginModuleStore();
pluginModules.forEach((pluginModule) => {
const { extensionPoints } = pluginModule;
if (!extensionPoints?.["backup:tabs:create"]) {
return;
}
onMounted(async () => {
for (const pluginModule of pluginModules) {
try {
const callbackFunction =
pluginModule?.extensionPoints?.["backup:tabs:create"];
if (typeof callbackFunction !== "function") {
continue;
}
const backupTabs = extensionPoints["backup:tabs:create"]() as BackupTab[];
const backupTabs = await callbackFunction();
if (backupTabs) {
tabs.value = tabs.value.concat(backupTabs);
if (backupTabs) {
tabs.value = tabs.value.concat(backupTabs);
}
} catch (error) {
console.error(`Error processing plugin module:`, pluginModule, error);
}
});
}
});
</script>

Expand Down
35 changes: 17 additions & 18 deletions ui/console-src/modules/system/plugins/PluginDetail.vue
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<script lang="ts" setup>
// core libs
import { provide, ref, computed } from "vue";
import { useRoute } from "vue-router";
import { apiClient } from "@/utils/api-client";
import { computed, provide, ref } from "vue";
import { useRoute } from "vue-router";
// libs
import { cloneDeep } from "lodash-es";
// components
import { VCard, VPageHeader, VTabbar, VAvatar } from "@halo-dev/components";
import { VAvatar, VCard, VPageHeader, VTabbar } from "@halo-dev/components";
// types
import type { Ref } from "vue";
import type { Plugin, Setting, SettingForm } from "@halo-dev/api-client";
import { usePluginModuleStore } from "@/stores/plugin";
import { usePermission } from "@/utils/permission";
import { useI18n } from "vue-i18n";
import { useQuery } from "@tanstack/vue-query";
import type { Plugin, Setting, SettingForm } from "@halo-dev/api-client";
import type { PluginTab } from "@halo-dev/console-shared";
import { useQuery } from "@tanstack/vue-query";
import { useRouteQuery } from "@vueuse/router";
import type { Ref } from "vue";
import { markRaw } from "vue";
import { useI18n } from "vue-i18n";
import DetailTab from "./tabs/Detail.vue";
import SettingTab from "./tabs/Setting.vue";
import { useRouteQuery } from "@vueuse/router";
import { usePluginModuleStore } from "@/stores/plugin";
const { currentUserHasPermission } = usePermission();
const { t } = useI18n();
Expand Down Expand Up @@ -50,12 +50,12 @@ const { data: plugin } = useQuery({
});
return data;
},
onSuccess(data) {
async onSuccess(data) {
if (
!data.spec.settingName ||
!currentUserHasPermission(["system:plugins:manage"])
) {
tabs.value = [...initialTabs.value, ...getTabsFromExtensions()];
tabs.value = [...initialTabs.value, ...(await getTabsFromExtensions())];
}
},
});
Expand All @@ -82,7 +82,7 @@ const { data: setting } = useQuery({
const { forms } = data.spec;
tabs.value = [
...initialTabs.value,
...getTabsFromExtensions(),
...(await getTabsFromExtensions()),
...forms.map((item: SettingForm) => {
return {
id: item.group,
Expand All @@ -97,7 +97,7 @@ const { data: setting } = useQuery({
provide<Ref<Setting | undefined>>("setting", setting);
function getTabsFromExtensions(): PluginTab[] {
async function getTabsFromExtensions() {
const { pluginModuleMap } = usePluginModuleStore();
const currentPluginModule = pluginModuleMap[route.params.name as string];
Expand All @@ -106,15 +106,14 @@ function getTabsFromExtensions(): PluginTab[] {
return [];
}
const { extensionPoints } = currentPluginModule;
const callbackFunction =
currentPluginModule?.extensionPoints?.["plugin:self:tabs:create"];
if (!extensionPoints?.["plugin:self:tabs:create"]) {
if (typeof callbackFunction !== "function") {
return [];
}
const pluginTabs = extensionPoints[
"plugin:self:tabs:create"
]() as PluginTab[];
const pluginTabs = await callbackFunction();
return pluginTabs.filter((tab) => {
return currentUserHasPermission(tab.permissions);
Expand Down
Loading

0 comments on commit 4c6abdc

Please sign in to comment.