237 lines
6.3 KiB
Vue
237 lines
6.3 KiB
Vue
<template>
|
|
<article
|
|
:id="`comment-${comment.id}`"
|
|
class="comment | flow"
|
|
:data-status="getStatus"
|
|
@click="handleClick()"
|
|
@mouseenter="hightlightCorrespondingMarker()"
|
|
@mouseleave="unhightlightCorrespondingMarker()"
|
|
ref="comment-node"
|
|
>
|
|
<header>
|
|
<p>
|
|
<strong>{{ comment.author.name ?? comment.author.email }}</strong>
|
|
<template v-if="commentIndex">
|
|
<span class="comment__id">#{{ commentIndex }}</span>
|
|
•
|
|
</template>
|
|
<span class="comment__page">Page {{ comment.position.pageIndex }}</span>
|
|
<time
|
|
class="comment__date"
|
|
:datetime="dayjs(comment.date).format('YYYY-MM-DD')"
|
|
>{{ formatDate(comment.date) }}</time
|
|
>
|
|
</p>
|
|
</header>
|
|
|
|
<p v-if="!comment.isEditMode" class="comment__body">
|
|
{{ comment.text }}
|
|
</p>
|
|
|
|
<textarea
|
|
v-else
|
|
class="comment__body"
|
|
v-model="draftText"
|
|
:rows="Math.ceil(draftText.length / 32)"
|
|
ref="editField"
|
|
></textarea>
|
|
|
|
<footer v-if="!comment.isEditMode" class="comment__replies">
|
|
<p v-if="comment.replies?.length > 0">
|
|
{{ comment.replies.length }} réponse{{
|
|
comment.replies.length > 1 ? "s" : ""
|
|
}}
|
|
</p>
|
|
<div
|
|
v-if="userStore.canEditComment(comment)"
|
|
class="comment__ctas | mt-8"
|
|
>
|
|
<button
|
|
class="btn btn--transparent btn--icon btn--sm"
|
|
data-icon="edit"
|
|
@click="editComment($event)"
|
|
>
|
|
<span class="sr-only">Éditer</span>
|
|
</button>
|
|
<button
|
|
class="btn btn--transparent btn--icon btn--sm"
|
|
data-icon="delete"
|
|
@click="deleteComment($event)"
|
|
>
|
|
<span class="sr-only">Supprimer</span>
|
|
</button>
|
|
</div>
|
|
</footer>
|
|
|
|
<footer v-else class="comment__edit-ctas">
|
|
<input
|
|
type="submit"
|
|
class="btn btn--tranparent"
|
|
value="Sauvegarder"
|
|
@click="saveEditedComment($event)"
|
|
/>
|
|
<button class="btn btn--white-10" @click="cancelEditComment($event)">
|
|
Annuler
|
|
</button>
|
|
</footer>
|
|
</article>
|
|
</template>
|
|
|
|
<script setup>
|
|
import dayjs from "dayjs";
|
|
import "dayjs/locale/fr";
|
|
import { useUserStore } from "../../stores/user";
|
|
import { useApiStore } from "../../stores/api";
|
|
import { useDialogStore } from "../../stores/dialog";
|
|
import { computed, onMounted, ref, useTemplateRef } from "vue";
|
|
import { storeToRefs } from "pinia";
|
|
import { usePageStore } from "../../stores/page";
|
|
import { useRoute } from "vue-router";
|
|
|
|
dayjs.locale("fr");
|
|
|
|
const { comment, commentIndex } = defineProps({
|
|
comment: Object,
|
|
commentIndex: Number,
|
|
});
|
|
|
|
const emits = defineEmits(["update:file", "close:comment"]);
|
|
|
|
const route = useRoute();
|
|
const userStore = useUserStore();
|
|
const api = useApiStore();
|
|
const dialog = useDialogStore();
|
|
const { activeTracks, openedComment, vpvRef } = storeToRefs(useDialogStore());
|
|
const draftText = ref(comment.text);
|
|
const editField = ref(null);
|
|
const commentNode = useTemplateRef("comment-node");
|
|
const { page } = storeToRefs(usePageStore());
|
|
let correspondingMarker = null;
|
|
|
|
// Functions
|
|
const getStatus = computed(() => {
|
|
const correspondingNotification = userStore.notifications.find(
|
|
(notification) => notification.id === comment.id
|
|
);
|
|
if (correspondingNotification && !correspondingNotification.isRead) {
|
|
return "unread";
|
|
}
|
|
return undefined;
|
|
});
|
|
|
|
function formatDate() {
|
|
const todayNumber = parseInt(dayjs().format("YYMMD"));
|
|
const dateNumber = parseInt(dayjs(comment.date).format("YYMMD"));
|
|
|
|
if (dateNumber === todayNumber) {
|
|
return "Aujourd'hui";
|
|
}
|
|
|
|
if (dateNumber === todayNumber - 1) {
|
|
return "hier";
|
|
}
|
|
|
|
return dayjs(comment.date).format("D MMM YY");
|
|
}
|
|
|
|
function closeAddField() {
|
|
isAddOpen.value = false;
|
|
newCommentText.value = "";
|
|
}
|
|
|
|
function handleClick() {
|
|
read();
|
|
scrollTo();
|
|
}
|
|
|
|
async function read() {
|
|
if (getStatus.value !== "unread") return;
|
|
try {
|
|
const newNotification = await api.readNotification(
|
|
comment.id,
|
|
page.value.uri
|
|
);
|
|
} catch (error) {
|
|
console.log("Erreur lors de la lecture de la notification : ", error);
|
|
}
|
|
}
|
|
|
|
function scrollTo() {
|
|
vpvRef.value.goToPage(comment.position.pageIndex);
|
|
setTimeout(() => {
|
|
const correspondingMarker = document.querySelector(
|
|
`.comment-marker[href="#comment-${comment.id}"]`
|
|
);
|
|
console.log(correspondingMarker);
|
|
if (!correspondingMarker) return;
|
|
correspondingMarker.scrollIntoView();
|
|
}, 1000);
|
|
}
|
|
|
|
async function deleteComment(event) {
|
|
event.stopPropagation();
|
|
const newFile = await api.deleteComment(comment);
|
|
|
|
// If there is an active track, we are not in PDF mode with a single file.
|
|
// Thus, it's not the opened file that should be updated
|
|
// but the corresponding file in the active track.
|
|
if (activeTracks.value?.length > 0) {
|
|
activeTracks.value[0].files = activeTracks.value[0].files.map((file) => {
|
|
if (file.uuid !== newFile.uuid) return file;
|
|
return newFile;
|
|
});
|
|
} else {
|
|
dialog.updateFile(newFile);
|
|
}
|
|
if (comment.type === "comment-reply") {
|
|
emits("close:comment");
|
|
}
|
|
}
|
|
|
|
function saveEditedComment(event) {
|
|
event.stopImmediatePropagation();
|
|
comment.text = draftText.value;
|
|
comment.date = dayjs().format();
|
|
api.updateComment(comment);
|
|
comment.isEditMode = false;
|
|
}
|
|
|
|
function cancelEditComment(event) {
|
|
event.stopImmediatePropagation();
|
|
comment.isEditMode = false;
|
|
draftText.value = comment.text;
|
|
}
|
|
|
|
function editComment(event) {
|
|
comment.isEditMode = true;
|
|
setTimeout(() => {
|
|
editField.value.focus();
|
|
}, 100);
|
|
}
|
|
|
|
function hightlightCorrespondingMarker() {
|
|
if (comment.type === "comment-reply") return;
|
|
|
|
const correspondingMarker = document.querySelector(
|
|
`.comment-marker[href="#comment-${comment.id}"]`
|
|
);
|
|
if (!correspondingMarker) return;
|
|
|
|
commentNode.value.classList.add("highlight");
|
|
correspondingMarker.classList.add("active");
|
|
correspondingMarker.classList.add("big");
|
|
}
|
|
function unhightlightCorrespondingMarker() {
|
|
if (comment.type === "comment-reply") return;
|
|
|
|
const correspondingMarker = document.querySelector(
|
|
`.comment-marker[href="#comment-${comment.id}"]`
|
|
);
|
|
if (!correspondingMarker) return;
|
|
if (openedComment.value) return;
|
|
|
|
commentNode.value.classList.remove("highlight");
|
|
correspondingMarker.classList.remove("active");
|
|
correspondingMarker.classList.remove("big");
|
|
}
|
|
</script>
|