shack/components/comments/comment.vue

199 lines
3.3 KiB
Vue

<template>
<div class="container">
<div class="frame">
<div class="crumbs">
<div
v-for="index in depth"
:key="`${comment.id}-${index}`"
:style="{ color: `hsl(${index * 60}, 50%, 75%)`, 'border-color': `hsl(${index * 60}, 50%, 90%)` }"
class="crumb"
>{{ String.fromCharCode(96 + index) }}</div>
</div>
<div
class="comment"
:class="{ nested: comment.parentId }"
:style="{ 'border-left': `solid 2px hsl(${(depth + 1) * 60}, 50%, 90%)` }"
>
<div class="header">
<img
src="/assets/icons/blocked.svg"
class="avatar"
>
<a
:href="`/user/${comment.user.username}`"
class="username link"
>u/{{ comment.user.username }}</a>
<ul class="labels nolist">
<li
v-if="comment.user.id === post.user.id"
class="label op"
>op</li>
</ul>
<span
:title="format(comment.createdAt, 'MMM d, yyyy hh:mm:ss')"
class="timestamp"
>{{ formatDistance(comment.createdAt, now, { includeSeconds: true }) }} ago</span>
</div>
<p class="body">{{ comment.body }}</p>
<div class="actions">
<button
type="button"
class="action link nobutton"
@click="isReplying = !isReplying"
>reply</button>
</div>
<Writer
v-if="isReplying"
:comment="comment"
:post="post"
@cancel="isReplying = false"
/>
</div>
</div>
<ul class="replies nolist">
<li
v-for="reply in comment.comments"
:key="reply.id"
>
<Comment
:comment="reply"
:post="post"
:depth="depth + 1"
/>
</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { format, formatDistance } from 'date-fns';
import Writer from './writer.vue';
import { usePageContext } from '../../renderer/usePageContext';
const { now } = usePageContext();
defineProps({
comment: {
type: Object,
default: null,
},
post: {
type: Object,
default: null,
},
depth: {
type: Number,
default: 0,
},
});
const isReplying = ref(false);
</script>
<style scoped>
.comment {
display: block;
flex-grow: 1;
background: var(--background);
box-sizing: border-box;
border-radius: .25rem .25rem .25rem 0;
margin: .25rem 0;
&.nested {
position: relative;
}
}
.header {
display: flex;
font-size: .9rem;
padding: .5rem .5rem .25rem .5rem;
}
.username {
color: inherit;
font-weight: bold;
margin-right: .5rem;
}
.timestamp {
color: var(--shadow);
}
.body {
padding: .25rem .5rem .5rem .5rem;
margin: 0;
}
.actions {
border-top: solid 1px var(--shadow-weak-40);
}
.action {
padding: .5rem;
color: var(--grey-dark-20);
cursor: pointer;
}
.labels {
display: inline-block;
margin-right: .5rem;
}
.label {
background: var(--grey);
border-radius: .25rem;
color: var(--text-light);
font-weight: bold;
&.op {
color: var(--op);
background: none;
}
}
.avatar {
width: 1rem;
height: 1rem;
margin-right: .5rem;
opacity: .25;
}
.replies {
display: flex;
flex-direction: column;
}
.frame {
display: flex;
align-items: stretch;
}
.crumbs {
display: flex;
align-items: stretch;
margin-top: -.5rem;
}
.crumb {
display: flex;
align-items: center;
box-sizing: border-box;
padding: .5rem;
border-left: solid 2px var(--shadow-weak-40);
color: var(--shadow-weak-20);
font-size: .8rem;
}
</style>