2023-11-09 06:56:06 +00:00
|
|
|
|
import { useThemeStore } from "@console/stores/theme";
|
2024-06-19 10:55:00 +00:00
|
|
|
|
import type { Theme } from "@halo-dev/api-client";
|
2024-06-25 04:31:44 +00:00
|
|
|
|
import { consoleApiClient } from "@halo-dev/api-client";
|
2024-06-19 10:55:00 +00:00
|
|
|
|
import { Dialog, Toast } from "@halo-dev/components";
|
|
|
|
|
import { useFileDialog } from "@vueuse/core";
|
2022-11-02 06:40:16 +00:00
|
|
|
|
import { storeToRefs } from "pinia";
|
2024-06-19 10:55:00 +00:00
|
|
|
|
import type { ComputedRef, Ref } from "vue";
|
|
|
|
|
import { computed, ref } from "vue";
|
2023-03-23 08:54:33 +00:00
|
|
|
|
import { useI18n } from "vue-i18n";
|
2022-08-03 03:30:13 +00:00
|
|
|
|
|
|
|
|
|
interface useThemeLifeCycleReturn {
|
2022-08-22 08:49:07 +00:00
|
|
|
|
loading: Ref<boolean>;
|
2022-08-03 03:30:13 +00:00
|
|
|
|
isActivated: ComputedRef<boolean>;
|
2023-01-19 02:58:14 +00:00
|
|
|
|
getFailedMessage: () => string | undefined;
|
2023-04-19 02:23:00 +00:00
|
|
|
|
handleActiveTheme: (reload?: boolean) => void;
|
2022-12-19 08:25:47 +00:00
|
|
|
|
handleResetSettingConfig: () => void;
|
2022-08-03 03:30:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-10 15:11:15 +00:00
|
|
|
|
export function useThemeLifeCycle(
|
|
|
|
|
theme: Ref<Theme | undefined>
|
|
|
|
|
): useThemeLifeCycleReturn {
|
2023-03-23 08:54:33 +00:00
|
|
|
|
const { t } = useI18n();
|
|
|
|
|
|
2022-08-22 08:49:07 +00:00
|
|
|
|
const loading = ref(false);
|
2022-08-03 03:30:13 +00:00
|
|
|
|
|
2022-11-02 06:40:16 +00:00
|
|
|
|
const themeStore = useThemeStore();
|
2022-08-03 03:30:13 +00:00
|
|
|
|
|
2022-11-02 06:40:16 +00:00
|
|
|
|
const { activatedTheme } = storeToRefs(themeStore);
|
2022-08-03 03:30:13 +00:00
|
|
|
|
|
2022-11-02 06:40:16 +00:00
|
|
|
|
const isActivated = computed(() => {
|
|
|
|
|
return activatedTheme?.value?.metadata.name === theme.value?.metadata.name;
|
|
|
|
|
});
|
2022-08-03 03:30:13 +00:00
|
|
|
|
|
2023-01-19 02:58:14 +00:00
|
|
|
|
const getFailedMessage = (): string | undefined => {
|
|
|
|
|
if (!(theme.value?.status?.phase === "FAILED")) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const condition = theme.value.status.conditions?.[0];
|
|
|
|
|
|
|
|
|
|
if (condition) {
|
|
|
|
|
return [condition.type, condition.message].join(":");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-19 02:23:00 +00:00
|
|
|
|
const handleActiveTheme = async (reload?: boolean) => {
|
2022-10-18 01:58:09 +00:00
|
|
|
|
Dialog.info({
|
2023-03-23 08:54:33 +00:00
|
|
|
|
title: t("core.theme.operations.active.title"),
|
2022-09-10 15:11:15 +00:00
|
|
|
|
description: theme.value?.spec.displayName,
|
2023-03-23 08:54:33 +00:00
|
|
|
|
confirmText: t("core.common.buttons.confirm"),
|
|
|
|
|
cancelText: t("core.common.buttons.cancel"),
|
2022-08-03 03:30:13 +00:00
|
|
|
|
onConfirm: async () => {
|
|
|
|
|
try {
|
2023-01-30 06:36:10 +00:00
|
|
|
|
if (!theme.value) return;
|
2022-08-03 03:30:13 +00:00
|
|
|
|
|
2024-06-25 04:31:44 +00:00
|
|
|
|
await consoleApiClient.theme.theme.activateTheme({
|
2023-01-30 06:36:10 +00:00
|
|
|
|
name: theme.value?.metadata.name,
|
|
|
|
|
});
|
2022-12-20 11:04:29 +00:00
|
|
|
|
|
2023-03-23 08:54:33 +00:00
|
|
|
|
Toast.success(t("core.theme.operations.active.toast_success"));
|
2023-04-19 02:23:00 +00:00
|
|
|
|
|
|
|
|
|
if (reload) {
|
|
|
|
|
window.location.reload();
|
|
|
|
|
}
|
2022-08-03 03:30:13 +00:00
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Failed to active theme", e);
|
|
|
|
|
} finally {
|
2022-11-02 06:40:16 +00:00
|
|
|
|
themeStore.fetchActivatedTheme();
|
2022-08-03 03:30:13 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-19 08:25:47 +00:00
|
|
|
|
const handleResetSettingConfig = async () => {
|
|
|
|
|
Dialog.warning({
|
2023-03-23 08:54:33 +00:00
|
|
|
|
title: t("core.theme.operations.reset.title"),
|
|
|
|
|
description: t("core.theme.operations.reset.description"),
|
2022-12-19 08:25:47 +00:00
|
|
|
|
confirmType: "danger",
|
2023-03-23 08:54:33 +00:00
|
|
|
|
confirmText: t("core.common.buttons.confirm"),
|
|
|
|
|
cancelText: t("core.common.buttons.cancel"),
|
2022-12-19 08:25:47 +00:00
|
|
|
|
onConfirm: async () => {
|
|
|
|
|
try {
|
|
|
|
|
if (!theme?.value) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-25 04:31:44 +00:00
|
|
|
|
await consoleApiClient.theme.theme.resetThemeConfig({
|
2022-12-19 08:25:47 +00:00
|
|
|
|
name: theme.value.metadata.name as string,
|
|
|
|
|
});
|
|
|
|
|
|
2023-03-23 08:54:33 +00:00
|
|
|
|
Toast.success(t("core.theme.operations.reset.toast_success"));
|
2022-12-19 08:25:47 +00:00
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Failed to reset theme setting config", e);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2022-08-03 03:30:13 +00:00
|
|
|
|
return {
|
2022-08-22 08:49:07 +00:00
|
|
|
|
loading,
|
2022-08-03 03:30:13 +00:00
|
|
|
|
isActivated,
|
2023-01-19 02:58:14 +00:00
|
|
|
|
getFailedMessage,
|
2022-08-03 03:30:13 +00:00
|
|
|
|
handleActiveTheme,
|
2022-12-19 08:25:47 +00:00
|
|
|
|
handleResetSettingConfig,
|
2022-08-03 03:30:13 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
2022-11-02 06:40:16 +00:00
|
|
|
|
|
|
|
|
|
export function useThemeCustomTemplates(type: "post" | "page" | "category") {
|
|
|
|
|
const themeStore = useThemeStore();
|
2023-03-23 08:54:33 +00:00
|
|
|
|
const { t } = useI18n();
|
|
|
|
|
|
2022-11-02 06:40:16 +00:00
|
|
|
|
const templates = computed(() => {
|
|
|
|
|
const defaultTemplate = [
|
|
|
|
|
{
|
2023-03-23 08:54:33 +00:00
|
|
|
|
label: t("core.theme.custom_templates.default"),
|
2022-11-02 06:40:16 +00:00
|
|
|
|
value: "",
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (!themeStore.activatedTheme) {
|
|
|
|
|
return defaultTemplate;
|
|
|
|
|
}
|
|
|
|
|
const { customTemplates } = themeStore.activatedTheme.spec;
|
|
|
|
|
if (!customTemplates?.[type]) {
|
|
|
|
|
return defaultTemplate;
|
|
|
|
|
}
|
|
|
|
|
return [
|
|
|
|
|
...defaultTemplate,
|
|
|
|
|
...(customTemplates[type]?.map((template) => {
|
|
|
|
|
return {
|
|
|
|
|
value: template.file,
|
|
|
|
|
label: template.name || template.file,
|
|
|
|
|
};
|
|
|
|
|
}) || []),
|
|
|
|
|
];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
templates,
|
|
|
|
|
};
|
|
|
|
|
}
|
2024-06-19 10:55:00 +00:00
|
|
|
|
|
|
|
|
|
interface ExportData {
|
|
|
|
|
themeName: string;
|
|
|
|
|
version: string;
|
|
|
|
|
settingName: string;
|
|
|
|
|
configMapName: string;
|
|
|
|
|
configs: { [key: string]: string };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useThemeConfigFile(theme: Ref<Theme | undefined>) {
|
|
|
|
|
const { t } = useI18n();
|
|
|
|
|
|
|
|
|
|
const handleExportThemeConfiguration = async () => {
|
|
|
|
|
if (!theme.value) {
|
|
|
|
|
console.error("No selected or activated theme");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-25 04:31:44 +00:00
|
|
|
|
const { data } = await consoleApiClient.theme.theme.fetchThemeConfig({
|
2024-06-19 10:55:00 +00:00
|
|
|
|
name: theme?.value?.metadata.name as string,
|
|
|
|
|
});
|
|
|
|
|
if (!data) {
|
|
|
|
|
console.error("Failed to fetch theme config");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const themeName = theme.value.metadata.name;
|
|
|
|
|
const exportData = {
|
|
|
|
|
themeName: themeName,
|
|
|
|
|
version: theme.value.spec.version,
|
|
|
|
|
settingName: theme.value.spec.settingName,
|
|
|
|
|
configMapName: theme.value.spec.configMapName,
|
|
|
|
|
configs: data.data,
|
|
|
|
|
} as ExportData;
|
|
|
|
|
const exportStr = JSON.stringify(exportData, null, 2);
|
|
|
|
|
const blob = new Blob([exportStr], { type: "application/json" });
|
|
|
|
|
const temporaryExportUrl = URL.createObjectURL(blob);
|
|
|
|
|
const temporaryLinkTag = document.createElement("a");
|
|
|
|
|
|
|
|
|
|
temporaryLinkTag.href = temporaryExportUrl;
|
|
|
|
|
temporaryLinkTag.download = `export-${themeName}-config-${Date.now().toString()}.json`;
|
|
|
|
|
|
|
|
|
|
document.body.appendChild(temporaryLinkTag);
|
|
|
|
|
temporaryLinkTag.click();
|
|
|
|
|
|
|
|
|
|
document.body.removeChild(temporaryLinkTag);
|
|
|
|
|
URL.revokeObjectURL(temporaryExportUrl);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
open: openSelectImportFileDialog,
|
|
|
|
|
onChange: handleImportThemeConfiguration,
|
|
|
|
|
} = useFileDialog({
|
|
|
|
|
accept: "application/json",
|
|
|
|
|
multiple: false,
|
|
|
|
|
directory: false,
|
|
|
|
|
reset: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
handleImportThemeConfiguration(async (files) => {
|
|
|
|
|
if (files === null || files.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const configText = await files[0].text();
|
|
|
|
|
const configJson = JSON.parse(configText || "{}");
|
|
|
|
|
if (!configJson.configs) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!configJson.themeName || !configJson.version) {
|
|
|
|
|
Toast.error(
|
|
|
|
|
t("core.theme.operations.import_configuration.invalid_format")
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!theme.value) {
|
|
|
|
|
console.error("No selected or activated theme");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (configJson.themeName !== theme.value.metadata.name) {
|
|
|
|
|
Toast.error(
|
|
|
|
|
t("core.theme.operations.import_configuration.mismatched_theme")
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (configJson.version !== theme.value.spec.version) {
|
|
|
|
|
Dialog.warning({
|
|
|
|
|
title: t(
|
|
|
|
|
"core.theme.operations.import_configuration.version_mismatch.title"
|
|
|
|
|
),
|
|
|
|
|
description: t(
|
|
|
|
|
"core.theme.operations.import_configuration.version_mismatch.description"
|
|
|
|
|
),
|
|
|
|
|
confirmText: t("core.common.buttons.confirm"),
|
|
|
|
|
cancelText: t("core.common.buttons.cancel"),
|
|
|
|
|
onConfirm: () => {
|
|
|
|
|
handleSaveConfigMap(configJson.configs);
|
|
|
|
|
},
|
|
|
|
|
onCancel() {
|
|
|
|
|
return;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
handleSaveConfigMap(configJson.configs);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleSaveConfigMap = async (importData: Record<string, string>) => {
|
|
|
|
|
if (!theme.value) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-06-25 04:31:44 +00:00
|
|
|
|
const { data } = await consoleApiClient.theme.theme.fetchThemeConfig({
|
2024-06-19 10:55:00 +00:00
|
|
|
|
name: theme.value.metadata.name as string,
|
|
|
|
|
});
|
|
|
|
|
if (!data || !data.data) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const combinedConfigData = combinedConfigMap(data.data, importData);
|
2024-06-25 04:31:44 +00:00
|
|
|
|
await consoleApiClient.theme.theme.updateThemeConfig({
|
2024-06-19 10:55:00 +00:00
|
|
|
|
name: theme.value.metadata.name,
|
|
|
|
|
configMap: {
|
|
|
|
|
...data,
|
|
|
|
|
data: combinedConfigData,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
Toast.success(t("core.common.toast.save_success"));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* combined benchmark configuration and import configuration
|
|
|
|
|
*
|
|
|
|
|
* benchmark: { a: "{\"a\": 1}", b: "{\"b\": 2}" }
|
|
|
|
|
* expand: { a: "{\"c\": 3}", b: "{\"d\": 4}" }
|
|
|
|
|
* => { a: "{\"a\": 1, \"c\": 3}", b: "{\"b\": 2, \"d\": 4}" }
|
|
|
|
|
*
|
|
|
|
|
* benchmark: { a: "{\"a\": 1}", b: "{\"b\": 2}", d: "{\"d\": 4}"
|
|
|
|
|
* expand: { a: "{\"a\": 2}", b: "{\"b\": 3, \"d\": 4}", c: "{\"c\": 5}" }
|
|
|
|
|
* => { a: "{\"a\": 2}", b: "{\"b\": 3, \"d\": 4}", d: "{\"d\": 4}" }
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const combinedConfigMap = (
|
|
|
|
|
benchmarkConfigMap: { [key: string]: string },
|
|
|
|
|
importConfigMap: { [key: string]: string }
|
|
|
|
|
): { [key: string]: string } => {
|
|
|
|
|
const result = benchmarkConfigMap;
|
|
|
|
|
|
|
|
|
|
for (const key in result) {
|
|
|
|
|
const benchmarkValueJson = JSON.parse(benchmarkConfigMap[key] || "{}");
|
|
|
|
|
const expandValueJson = JSON.parse(importConfigMap[key] || "{}");
|
|
|
|
|
const combinedValue = {
|
|
|
|
|
...benchmarkValueJson,
|
|
|
|
|
...expandValueJson,
|
|
|
|
|
};
|
|
|
|
|
result[key] = JSON.stringify(combinedValue);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
handleExportThemeConfiguration,
|
|
|
|
|
openSelectImportFileDialog,
|
|
|
|
|
};
|
|
|
|
|
}
|