mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-11-21 01:21:54 +08:00
project pages
This commit is contained in:
parent
3cb54729de
commit
089a3e396e
@ -3,17 +3,23 @@ import type { ExtendedProjectPage, HangarProject, HangarProjectPage } from "~/ty
|
||||
|
||||
const props = defineProps<{
|
||||
project?: HangarProject;
|
||||
page?: ExtendedProjectPage;
|
||||
mainPage: boolean;
|
||||
}>();
|
||||
|
||||
const route = useRoute("user-project-pages-all");
|
||||
const route = useRoute("user-project-pages-page");
|
||||
const router = useRouter();
|
||||
|
||||
const updateProjectPages = inject<(pages: HangarProjectPage[]) => void>("updateProjectPages");
|
||||
|
||||
const { editingPage, changeEditingPage, page, savePage, deletePage } = await useProjectPage(route, router, props.project, props.mainPage);
|
||||
const { editingPage, changeEditingPage, savePage, deletePage } = useProjectPage(
|
||||
route,
|
||||
router,
|
||||
props.project,
|
||||
props.mainPage ? props.project?.mainPage : props.page
|
||||
);
|
||||
if (!props.mainPage) {
|
||||
useHead(useSeo(page.value?.name + " | " + props.project?.name, props.project?.description, route, props.project?.avatarUrl));
|
||||
useHead(useSeo(props.page?.name + " | " + props.project?.name, props.project?.description, route, props.project?.avatarUrl));
|
||||
}
|
||||
|
||||
async function deletePageAndUpdateProject() {
|
||||
@ -29,22 +35,10 @@ async function deletePageAndUpdateProject() {
|
||||
}
|
||||
|
||||
defineSlots<{
|
||||
default: (props: {
|
||||
page?: ExtendedProjectPage | null;
|
||||
editingPage: boolean;
|
||||
changeEditingPage: (editing: boolean) => void;
|
||||
savePage: (content: string) => void;
|
||||
deletePage: () => void;
|
||||
}) => any;
|
||||
default: (props: { editingPage: boolean; changeEditingPage: (editing: boolean) => void; savePage: (content: string) => void; deletePage: () => void }) => any;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot
|
||||
:page="page"
|
||||
:save-page="savePage"
|
||||
:delete-page="deletePageAndUpdateProject"
|
||||
:editing-page="editingPage"
|
||||
:change-editing-page="changeEditingPage"
|
||||
></slot>
|
||||
<slot :save-page="savePage" :delete-page="deletePageAndUpdateProject" :editing-page="editingPage" :change-editing-page="changeEditingPage"></slot>
|
||||
</template>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { RouteLocationNormalized } from "vue-router";
|
||||
|
||||
type dataLoaders = "user" | "project" | "version" | "organization";
|
||||
type routeParams = "user" | "project" | "version" | "organization";
|
||||
type dataLoaders = "user" | "project" | "version" | "organization" | "page";
|
||||
type routeParams = "user" | "project" | "version" | "organization" | "page";
|
||||
|
||||
// TODO check every handling of the reject stuff (for both composables)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { HangarProject } from "~/types/backend";
|
||||
import type { RouteLocationTyped, RouteMapGeneric } from "vue-router";
|
||||
|
||||
export function useOpenProjectPages(route: RouteLocationTyped<RouteMapGeneric, "user-project-pages-all">, project?: HangarProject) {
|
||||
export function useOpenProjectPages(route: RouteLocationTyped<RouteMapGeneric, "user-project-pages-page">, project?: HangarProject) {
|
||||
const open = ref<string[]>([]);
|
||||
|
||||
watch(
|
||||
|
@ -1,14 +1,8 @@
|
||||
import type { HangarProject } from "~/types/backend";
|
||||
import type { ExtendedProjectPage, HangarProject } from "~/types/backend";
|
||||
import type { Router } from "vue-router";
|
||||
import type { RouteLocationNormalized } from "vue-router/auto";
|
||||
|
||||
export async function useProjectPage(route: RouteLocationNormalized<"user-project-pages-all">, router: Router, project?: HangarProject, mainPage?: boolean) {
|
||||
const page = mainPage ? computed(() => project?.mainPage) : usePage(() => ({ project: route.params.project, path: route.params.all?.toString() })).page;
|
||||
// TODO fix this
|
||||
// if (!page?.value) {
|
||||
// throw useErrorRedirect(404, "Not found");
|
||||
// }
|
||||
|
||||
export function useProjectPage(route: RouteLocationNormalized<"user-project-pages-page">, router: Router, project?: HangarProject, page?: ExtendedProjectPage) {
|
||||
const editingPage = ref<boolean>(false);
|
||||
|
||||
// Helper setter function, v-model cannot directly edit from inside a slot.
|
||||
@ -17,21 +11,21 @@ export async function useProjectPage(route: RouteLocationNormalized<"user-projec
|
||||
}
|
||||
|
||||
async function savePage(content: string) {
|
||||
if (!page?.value) return;
|
||||
await useInternalApi(`pages/save/${project?.id}/${page.value?.id}`, "post", {
|
||||
if (!page) return;
|
||||
await useInternalApi(`pages/save/${project?.id}/${page?.id}`, "post", {
|
||||
content,
|
||||
}).catch((e) => handleRequestError(e, "page.new.error.save"));
|
||||
if (page.value && "contents" in page.value) {
|
||||
page.value.contents = content;
|
||||
if (page && "contents" in page) {
|
||||
page.contents = content;
|
||||
}
|
||||
editingPage.value = false;
|
||||
}
|
||||
|
||||
async function deletePage() {
|
||||
if (!page?.value) return;
|
||||
await useInternalApi(`pages/delete/${project?.id}/${page.value?.id}`, "post").catch((e) => handleRequestError(e, "page.new.error.save"));
|
||||
if (!page) return;
|
||||
await useInternalApi(`pages/delete/${project?.id}/${page?.id}`, "post").catch((e) => handleRequestError(e, "page.new.error.save"));
|
||||
await router.replace(`/${route.params.user}/${route.params.project}`);
|
||||
}
|
||||
|
||||
return { editingPage, changeEditingPage, page, savePage, deletePage };
|
||||
return { editingPage, changeEditingPage, savePage, deletePage };
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { HangarOrganization, HangarProject, HangarVersion, User } from "~/types/backend";
|
||||
import type { ExtendedProjectPage, HangarOrganization, HangarProject, HangarVersion, User } from "~/types/backend";
|
||||
import { useDataLoader } from "~/composables/useDataLoader";
|
||||
import { isAxiosError } from "axios";
|
||||
|
||||
@ -44,6 +44,20 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||
promises
|
||||
);
|
||||
|
||||
const { loader: pageLoader, data: page } = useDataLoader<ExtendedProjectPage>("page");
|
||||
const pageName = pageLoader(
|
||||
"page",
|
||||
to,
|
||||
from,
|
||||
async (pagePath) => {
|
||||
if ("project" in to.params) {
|
||||
return useInternalApi<ExtendedProjectPage>(`pages/page/${to.params.project}/` + pagePath.toString().replaceAll(",", "/"));
|
||||
}
|
||||
throw createError({ statusCode: 500, statusMessage: "No project param?!" });
|
||||
},
|
||||
promises
|
||||
) as string[] | undefined;
|
||||
|
||||
if (import.meta.server && promises?.length) {
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
@ -60,17 +74,23 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||
let newPath = to.fullPath;
|
||||
if (userName) {
|
||||
if (user.value && user.value.name !== userName) {
|
||||
newPath = newPath.replace(userName, user.value?.name);
|
||||
newPath = newPath.replace(userName, user.value.name);
|
||||
}
|
||||
}
|
||||
if (projectName) {
|
||||
if (project.value && project.value.name !== projectName) {
|
||||
newPath = newPath.replace(projectName, project.value?.name);
|
||||
newPath = newPath.replace(projectName, project.value.name);
|
||||
}
|
||||
}
|
||||
if (versionName) {
|
||||
if (version.value && version.value.name !== versionName) {
|
||||
newPath = newPath.replace(versionName, version.value?.name);
|
||||
newPath = newPath.replace(versionName, version.value.name);
|
||||
}
|
||||
}
|
||||
if (pageName) {
|
||||
const pageSlug = pageName.join("/");
|
||||
if (page.value && page.value.slug !== pageSlug) {
|
||||
newPath = newPath.replace(pageSlug, page.value.slug);
|
||||
}
|
||||
}
|
||||
if (newPath != to.fullPath) {
|
||||
|
@ -9,7 +9,7 @@ const props = defineProps<{
|
||||
|
||||
const config = useConfig();
|
||||
const i18n = useI18n();
|
||||
const route = useRoute("user-project-pages-all");
|
||||
const route = useRoute("user-project-pages-page");
|
||||
const openProjectPages = useOpenProjectPages(route, props.project);
|
||||
|
||||
const sponsors = ref(props.project?.settings?.sponsors);
|
||||
@ -65,12 +65,12 @@ useHead(
|
||||
<template>
|
||||
<div class="flex flex-wrap md:flex-nowrap gap-4">
|
||||
<section class="basis-full md:basis-11/15 flex-grow overflow-auto">
|
||||
<ProjectPageMarkdown v-slot="{ page, editingPage, changeEditingPage, savePage }" :project="props.project" main-page>
|
||||
<Card v-if="page?.contents" class="pb-0 overflow-clip overflow-hidden">
|
||||
<ProjectPageMarkdown v-slot="{ editingPage, changeEditingPage, savePage }" :project="props.project" :page="props.project?.mainPage" main-page>
|
||||
<Card v-if="project?.mainPage?.contents" class="pb-0 overflow-clip overflow-hidden">
|
||||
<ClientOnly v-if="hasPerms(NamedPermission.EditPage)">
|
||||
<MarkdownEditor
|
||||
:editing="editingPage"
|
||||
:raw="page.contents"
|
||||
:raw="project?.mainPage.contents"
|
||||
:deletable="false"
|
||||
:saveable="true"
|
||||
:cancellable="true"
|
||||
@ -80,11 +80,11 @@ useHead(
|
||||
@save="savePage"
|
||||
/>
|
||||
<template #fallback>
|
||||
<Markdown :raw="page.contents" />
|
||||
<Markdown :raw="project?.mainPage.contents" />
|
||||
</template>
|
||||
</ClientOnly>
|
||||
<!--We have to blow up v-model:editing into :editing and @update:editing as we are inside a scope--->
|
||||
<Markdown v-else :raw="page.contents" />
|
||||
<Markdown v-else :raw="project?.mainPage.contents" />
|
||||
</Card>
|
||||
</ProjectPageMarkdown>
|
||||
<Card v-if="sponsors || hasPerms(NamedPermission.EditSubjectSettings)" class="mt-2 pb-0 overflow-clip overflow-visible">
|
||||
|
@ -1,12 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import { type HangarProject, NamedPermission, type User } from "~/types/backend";
|
||||
import { type ExtendedProjectPage, type HangarProject, NamedPermission, type User } from "~/types/backend";
|
||||
import { useDataLoader } from "~/composables/useDataLoader";
|
||||
|
||||
const props = defineProps<{
|
||||
user?: User;
|
||||
project?: HangarProject;
|
||||
}>();
|
||||
|
||||
const route = useRoute("user-project-pages-all");
|
||||
const route = useRoute("user-project-pages-page");
|
||||
|
||||
const { data: page } = useDataLoader<ExtendedProjectPage>("page");
|
||||
|
||||
definePageMeta({
|
||||
dataLoader_page: true,
|
||||
});
|
||||
|
||||
const open = useOpenProjectPages(route, props.project);
|
||||
// useSeo is in ProjectPageMarkdown
|
||||
@ -17,8 +24,9 @@ const open = useOpenProjectPages(route, props.project);
|
||||
<section class="basis-full md:basis-9/12 flex-grow overflow-auto">
|
||||
<ProjectPageMarkdown
|
||||
:key="route.fullPath"
|
||||
v-slot="{ page, editingPage, changeEditingPage, savePage, deletePage }"
|
||||
v-slot="{ editingPage, changeEditingPage, savePage, deletePage }"
|
||||
:project="props.project"
|
||||
:page="page"
|
||||
:main-page="false"
|
||||
>
|
||||
<Card v-if="page" class="pb-0 overflow-clip overflow-hidden">
|
Loading…
Reference in New Issue
Block a user