useTicketArticlesRows.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { controlledComputed } from '@vueuse/shared'
  3. import type { TicketArticle } from '#shared/entities/ticket/types.ts'
  4. import { EnumTicketArticleSenderName } from '#shared/graphql/types.ts'
  5. import { i18n } from '#shared/i18n.ts'
  6. import { useSessionStore } from '#shared/stores/session.ts'
  7. import { useTicketInformation } from './useTicketInformation.ts'
  8. import type { Ref } from 'vue'
  9. interface ArticleRow {
  10. type: 'article-bubble'
  11. article: TicketArticle
  12. }
  13. interface ArticleDeliveryRow {
  14. type: 'delivery'
  15. content: string
  16. }
  17. interface MoreRow {
  18. type: 'more'
  19. count: number
  20. }
  21. interface NewRow {
  22. type: 'new'
  23. }
  24. interface DateRow {
  25. type: 'date'
  26. date: string
  27. }
  28. interface SystemRaw {
  29. type: 'system'
  30. subject?: Maybe<string>
  31. to?: Maybe<string>
  32. reaction?: Maybe<string>
  33. }
  34. type TicketArticleRow = (
  35. | ArticleRow
  36. | SystemRaw
  37. | MoreRow
  38. | NewRow
  39. | DateRow
  40. | ArticleDeliveryRow
  41. ) & {
  42. key: string
  43. }
  44. export const useTicketArticleRows = (
  45. articles: Ref<TicketArticle[]>,
  46. totalCount: Ref<number>,
  47. ) => {
  48. const { newArticlesIds } = useTicketInformation()
  49. const session = useSessionStore()
  50. const rows = controlledComputed(articles, () => {
  51. const rows: TicketArticleRow[] = []
  52. const dates = new Set<string>()
  53. const needMoreButton = articles.value.length < totalCount.value
  54. let hasNew = false
  55. // assuming it is sorted by createdAt
  56. articles.value.forEach((article, index) => {
  57. const date = i18n.date(article.createdAt)
  58. if (!dates.has(date)) {
  59. dates.add(date)
  60. rows.push({
  61. type: 'date',
  62. date: article.createdAt,
  63. key: date,
  64. })
  65. }
  66. if (article.preferences?.delivery_message) {
  67. rows.push({
  68. type: 'delivery',
  69. content: article.bodyWithUrls,
  70. key: article.id,
  71. })
  72. } else if (
  73. article.sender?.name === EnumTicketArticleSenderName.System &&
  74. article.type?.name !== 'note'
  75. ) {
  76. rows.push({
  77. type: 'system',
  78. subject: article.subject,
  79. to: article.to?.raw || '',
  80. reaction: article.preferences?.whatsapp?.reaction?.emoji,
  81. key: article.id,
  82. })
  83. } else {
  84. rows.push({
  85. type: 'article-bubble',
  86. article,
  87. key: article.id,
  88. })
  89. }
  90. // after "description" (always first) article is added, add "more" button
  91. if (index === 0 && needMoreButton) {
  92. rows.push({
  93. type: 'more',
  94. key: 'more',
  95. count: totalCount.value - articles.value.length,
  96. })
  97. }
  98. const next = articles.value[index + 1]
  99. if (
  100. !hasNew &&
  101. next &&
  102. session.userId !== next.author.id &&
  103. newArticlesIds.has(next.id)
  104. ) {
  105. hasNew = true
  106. rows.push({
  107. type: 'new',
  108. key: 'new',
  109. })
  110. }
  111. })
  112. return rows
  113. })
  114. return { rows }
  115. }