useTicketArticlesRows.ts 2.8 KB

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