123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- <script setup lang="ts">
- import { defineAsyncComponent, ref, onMounted } from "vue";
- import { formatDistance } from "date-fns";
- import { marked } from "marked";
- import DOMPurify from "dompurify";
- import { useWebsocketsStore } from "@/stores/websockets";
- import ws from "@/ws";
- const MainHeader = defineAsyncComponent(
- () => import("@/components/MainHeader.vue")
- );
- const MainFooter = defineAsyncComponent(
- () => import("@/components/MainFooter.vue")
- );
- const UserLink = defineAsyncComponent(
- () => import("@/components/UserLink.vue")
- );
- const { socket } = useWebsocketsStore();
- const news = ref([]);
- const init = () => {
- socket.dispatch("news.getPublished", res => {
- if (res.status === "success") news.value = res.data.news;
- });
- socket.dispatch("apis.joinRoom", "news");
- };
- const { sanitize } = DOMPurify;
- onMounted(() => {
- marked.use({
- renderer: {
- table(header, body) {
- return `<table class="table">
- <thead>${header}</thead>
- <tbody>${body}</tbody>
- </table>`;
- }
- }
- });
- socket.on("event:news.created", res => news.value.unshift(res.data.news));
- socket.on("event:news.updated", res => {
- if (res.data.news.status === "draft") {
- news.value = news.value.filter(
- item => item._id !== res.data.news._id
- );
- return;
- }
- for (let n = 0; n < news.value.length; n += 1) {
- if (news.value[n]._id === res.data.news._id)
- news.value[n] = {
- ...news.value[n],
- ...res.data.news
- };
- }
- });
- socket.on("event:news.deleted", res => {
- news.value = news.value.filter(item => item._id !== res.data.newsId);
- });
- ws.onConnect(init);
- });
- </script>
- <template>
- <div class="app">
- <page-metadata title="News" />
- <main-header />
- <div class="container">
- <div class="content-wrapper">
- <h1 class="has-text-centered page-title">News</h1>
- <div
- v-for="item in news"
- :key="item._id"
- class="section news-item"
- >
- <div v-html="sanitize(marked(item.markdown))"></div>
- <div class="info">
- <hr />
- By
- <user-link
- :user-id="item.createdBy"
- :alt="item.createdBy"
- /> <span
- :title="new Date(item.createdAt).toString()"
- >
- {{
- formatDistance(item.createdAt, new Date(), {
- addSuffix: true
- })
- }}
- </span>
- </div>
- </div>
- <h3 v-if="news.length === 0" class="has-text-centered">
- No news items were found.
- </h3>
- </div>
- </div>
- <main-footer />
- </div>
- </template>
- <style lang="less" scoped>
- .night-mode {
- p {
- color: var(--light-grey-2);
- }
- }
- .container {
- width: calc(100% - 32px);
- }
- .section {
- border: 1px solid var(--light-grey-3);
- max-width: 100%;
- margin-top: 50px;
- &:last-of-type {
- margin-bottom: 50px;
- }
- }
- </style>
|