refactor(frontend): Project page composition API + TS (#3245)

* refactor(frontend): move project description to composition API + TS

* refactor(frontend): rename to `patchRequestPayload` for consistency

* chore: lint
This commit is contained in:
Erb3
2025-02-19 19:49:07 +01:00
committed by GitHub
parent f6d64e8fde
commit 9c2cd868a7

View File

@@ -8,21 +8,25 @@
<span class="label__subdescription"> <span class="label__subdescription">
The description must clearly and honestly describe the purpose and function of the The description must clearly and honestly describe the purpose and function of the
project. See section 2.1 of the project. See section 2.1 of the
<nuxt-link to="/legal/rules" class="text-link" target="_blank">Content Rules</nuxt-link> <nuxt-link class="text-link" target="_blank" to="/legal/rules">Content Rules</nuxt-link>
for the full requirements. for the full requirements.
</span> </span>
</span> </span>
</div> </div>
<MarkdownEditor <MarkdownEditor
v-model="description" v-model="description"
:disabled="
!currentMember ||
(currentMember.permissions & TeamMemberPermission.EDIT_BODY) !==
TeamMemberPermission.EDIT_BODY
"
:on-image-upload="onUploadHandler" :on-image-upload="onUploadHandler"
:disabled="(currentMember.permissions & EDIT_BODY) !== EDIT_BODY"
/> />
<div class="input-group markdown-disclaimer"> <div class="input-group markdown-disclaimer">
<button <button
type="button"
class="iconified-button brand-button"
:disabled="!hasChanges" :disabled="!hasChanges"
class="iconified-button brand-button"
type="button"
@click="saveChanges()" @click="saveChanges()"
> >
<SaveIcon /> <SaveIcon />
@@ -33,91 +37,50 @@
</div> </div>
</template> </template>
<script> <script lang="ts" setup>
import { SaveIcon } from "@modrinth/assets";
import { MarkdownEditor } from "@modrinth/ui"; import { MarkdownEditor } from "@modrinth/ui";
import Chips from "~/components/ui/Chips.vue"; import { type Project, type TeamMember, TeamMemberPermission } from "@modrinth/utils";
import SaveIcon from "~/assets/images/utils/save.svg?component"; import { computed, ref } from "vue";
import { renderHighlightedString } from "~/helpers/highlight.js";
import { useImageUpload } from "~/composables/image-upload.ts"; import { useImageUpload } from "~/composables/image-upload.ts";
export default defineNuxtComponent({ const props = defineProps<{
components: { project: Project;
Chips, allMembers: TeamMember[];
SaveIcon, currentMember: TeamMember | undefined;
MarkdownEditor, patchProject: (payload: object, quiet?: boolean) => object;
}, }>();
props: {
project: {
type: Object,
default() {
return {};
},
},
allMembers: {
type: Array,
default() {
return [];
},
},
currentMember: {
type: Object,
default() {
return null;
},
},
patchProject: {
type: Function,
default() {
return () => {
this.$notify({
group: "main",
title: "An error occurred",
text: "Patch project function not found",
type: "error",
});
};
},
},
},
data() {
return {
description: this.project.body,
bodyViewMode: "source",
};
},
computed: {
patchData() {
const data = {};
if (this.description !== this.project.body) { const description = ref(props.project.body);
data.body = this.description;
}
return data; const patchRequestPayload = computed(() => {
}, const payload: {
hasChanges() { body?: string;
return Object.keys(this.patchData).length > 0; } = {};
},
}, if (description.value !== props.project.body) {
created() { payload.body = description.value;
this.EDIT_BODY = 1 << 3; }
},
methods: { return payload;
renderHighlightedString,
saveChanges() {
if (this.hasChanges) {
this.patchProject(this.patchData);
}
},
async onUploadHandler(file) {
const response = await useImageUpload(file, {
context: "project",
projectID: this.project.id,
});
return response.url;
},
},
}); });
const hasChanges = computed(() => {
return Object.keys(patchRequestPayload.value).length > 0;
});
function saveChanges() {
props.patchProject(patchRequestPayload.value);
}
async function onUploadHandler(file: File) {
const response = await useImageUpload(file, {
context: "project",
projectID: props.project.id,
});
return response.url;
}
</script> </script>
<style scoped> <style scoped>