WhatIsNew.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <template>
  2. <div v-if="news !== null">
  3. <modal title="News" class="what-is-news-modal">
  4. <template #body>
  5. <div
  6. class="section news-item"
  7. v-html="sanitize(marked(news.markdown))"
  8. ></div>
  9. </template>
  10. <template #footer>
  11. <span v-if="news.createdBy">
  12. By
  13. <user-id-to-username
  14. :user-id="news.createdBy"
  15. :alt="news.createdBy"
  16. :link="true" /></span
  17. >&nbsp;<span :title="new Date(news.createdAt)">
  18. {{
  19. formatDistance(news.createdAt, new Date(), {
  20. addSuffix: true
  21. })
  22. }}
  23. </span>
  24. </template>
  25. </modal>
  26. </div>
  27. <div v-else></div>
  28. </template>
  29. <script>
  30. import { formatDistance } from "date-fns";
  31. import { marked } from "marked";
  32. import { sanitize } from "dompurify";
  33. import { mapGetters, mapActions } from "vuex";
  34. import ws from "@/ws";
  35. import UserIdToUsername from "@/components/UserIdToUsername.vue";
  36. import Modal from "../Modal.vue";
  37. export default {
  38. components: { Modal, UserIdToUsername },
  39. data() {
  40. return {
  41. isModalActive: false,
  42. news: null
  43. };
  44. },
  45. computed: {
  46. ...mapGetters({
  47. socket: "websockets/getSocket"
  48. })
  49. },
  50. mounted() {
  51. marked.use({
  52. renderer: {
  53. table(header, body) {
  54. return `<table class="table">
  55. <thead>${header}</thead>
  56. <tbody>${body}</tbody>
  57. </table>`;
  58. }
  59. }
  60. });
  61. ws.onConnect(this.init);
  62. },
  63. methods: {
  64. init() {
  65. const newUser = !localStorage.hasOwnProperty("firstVisited");
  66. this.socket.dispatch("news.newest", newUser, res => {
  67. if (res.status !== "success") return;
  68. const { news } = res.data;
  69. this.news = news;
  70. if (this.news) {
  71. if (newUser) {
  72. this.openModal("whatIsNew");
  73. } else if (localStorage.getItem("firstVisited")) {
  74. if (localStorage.getItem("whatIsNew")) {
  75. if (
  76. parseInt(localStorage.getItem("whatIsNew")) <
  77. news.createdAt
  78. ) {
  79. this.openModal("whatIsNew");
  80. localStorage.setItem("whatIsNew", news.createdAt);
  81. }
  82. } else {
  83. if (
  84. parseInt(localStorage.getItem("firstVisited")) <
  85. news.createdAt
  86. )
  87. this.openModal("whatIsNew");
  88. localStorage.setItem("whatIsNew", news.createdAt);
  89. }
  90. }
  91. }
  92. if (!localStorage.getItem("firstVisited"))
  93. localStorage.setItem("firstVisited", Date.now());
  94. });
  95. },
  96. marked,
  97. sanitize,
  98. formatDistance,
  99. ...mapActions("modalVisibility", ["openModal"])
  100. }
  101. };
  102. </script>
  103. <style lang="less">
  104. .what-is-news-modal .modal-card .modal-card-foot {
  105. column-gap: 0;
  106. }
  107. </style>
  108. <style lang="less" scoped>
  109. .night-mode {
  110. .modal-card,
  111. .modal-card-head,
  112. .modal-card-body {
  113. background-color: var(--dark-grey-3);
  114. }
  115. strong,
  116. p {
  117. color: var(--light-grey-2);
  118. }
  119. .section {
  120. background-color: transparent !important;
  121. }
  122. }
  123. .modal-card-head {
  124. border-bottom: none;
  125. background-color: ghostwhite;
  126. padding: 15px;
  127. }
  128. .modal-card-title {
  129. font-size: 14px;
  130. }
  131. .news-item {
  132. box-shadow: unset !important;
  133. }
  134. .delete {
  135. background: transparent;
  136. &:hover {
  137. background: transparent;
  138. }
  139. &:before,
  140. &:after {
  141. background-color: var(--light-grey-3);
  142. }
  143. }
  144. .sect {
  145. div[class^="sect-head"],
  146. div[class*=" sect-head"] {
  147. padding: 12px;
  148. text-transform: uppercase;
  149. font-weight: bold;
  150. color: var(--white);
  151. }
  152. .sect-head-features {
  153. background-color: dodgerblue;
  154. }
  155. .sect-head-improvements {
  156. background-color: seagreen;
  157. }
  158. .sect-head-bugs {
  159. background-color: brown;
  160. }
  161. .sect-head-upcoming {
  162. background-color: mediumpurple;
  163. }
  164. .sect-body {
  165. padding: 15px 25px;
  166. li {
  167. list-style-type: disc;
  168. }
  169. }
  170. }
  171. </style>