useUserCurrentTicketOverviews.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. import { isEqual, keyBy, mapValues } from 'lodash-es'
  3. import { storeToRefs } from 'pinia'
  4. import { computed, ref } from 'vue'
  5. import type {
  6. Exact,
  7. UserCurrentOverviewOrderingUpdatesSubscription,
  8. UserCurrentOverviewOrderingUpdatesSubscriptionVariables,
  9. UserCurrentTicketOverviewsQuery,
  10. UserCurrentTicketOverviewUpdatesSubscription,
  11. UserCurrentTicketOverviewUpdatesSubscriptionVariables,
  12. } from '#shared/graphql/types.ts'
  13. import { QueryHandler } from '#shared/server/apollo/handler/index.ts'
  14. import { useSessionStore } from '#shared/stores/session.ts'
  15. import { useUserCurrentTicketOverviewsQuery } from '#desktop/entities/ticket/graphql/queries/userCurrentTicketOverviews.api.ts'
  16. import { UserCurrentOverviewOrderingFullAttributesUpdatesDocument } from '#desktop/entities/ticket/graphql/subscriptions/useCurrentOverviewOrderingFullAttributesUpdates.api.ts'
  17. import { UserCurrentTicketOverviewFullAttributesUpdatesDocument } from '#desktop/entities/ticket/graphql/subscriptions/userCurrentTicketOverviewFullAttributesUpdates.api.ts'
  18. const initializeOverviewsSubscriptions = (
  19. query: QueryHandler<
  20. UserCurrentTicketOverviewsQuery,
  21. Exact<{ ignoreUserConditions: boolean; withTicketCount: boolean }>
  22. >,
  23. ) => {
  24. query.subscribeToMore<
  25. UserCurrentTicketOverviewUpdatesSubscriptionVariables,
  26. UserCurrentTicketOverviewUpdatesSubscription
  27. >({
  28. document: UserCurrentTicketOverviewFullAttributesUpdatesDocument,
  29. variables: { ignoreUserConditions: false },
  30. updateQuery(_, { subscriptionData }) {
  31. const ticketOverviews =
  32. subscriptionData.data.userCurrentTicketOverviewUpdates?.ticketOverviews
  33. // if we return empty array here, the actual query will be aborted, because we have fetchPolicy "cache-and-network"
  34. // if we return existing value, it will throw an error, because "overviews" doesn't exist yet on the query result
  35. if (!ticketOverviews)
  36. return null as unknown as UserCurrentTicketOverviewsQuery
  37. return {
  38. userCurrentTicketOverviews: ticketOverviews,
  39. } as unknown as UserCurrentTicketOverviewsQuery
  40. },
  41. })
  42. // Subscription for overview ordering updates
  43. query.subscribeToMore<
  44. UserCurrentOverviewOrderingUpdatesSubscriptionVariables,
  45. UserCurrentOverviewOrderingUpdatesSubscription
  46. >({
  47. document: UserCurrentOverviewOrderingFullAttributesUpdatesDocument,
  48. variables: { ignoreUserConditions: false },
  49. updateQuery(_, { subscriptionData }) {
  50. const overviews =
  51. subscriptionData.data.userCurrentOverviewOrderingUpdates?.overviews
  52. if (!overviews) return null as unknown as UserCurrentTicketOverviewsQuery
  53. return {
  54. userCurrentTicketOverviews: overviews,
  55. } as UserCurrentTicketOverviewsQuery
  56. },
  57. })
  58. }
  59. export const useUserCurrentTicketOverviews = () => {
  60. const { user } = storeToRefs(useSessionStore())
  61. const overviewHandler = new QueryHandler(
  62. useUserCurrentTicketOverviewsQuery({
  63. withTicketCount: false,
  64. ignoreUserConditions: false,
  65. }),
  66. )
  67. initializeOverviewsSubscriptions(overviewHandler)
  68. const overviewsRaw = overviewHandler.result()
  69. const overviewsLoading = overviewHandler.loading()
  70. const overviews = computed(
  71. () => overviewsRaw.value?.userCurrentTicketOverviews || [],
  72. )
  73. const overviewsById = computed(() => keyBy(overviews.value, 'id'))
  74. const overviewsByInternalId = computed<Record<ID, string>>(
  75. (currentLookup) => {
  76. const newLookup = mapValues(keyBy(overviews.value, 'internalId'), 'id')
  77. if (currentLookup && isEqual(currentLookup, newLookup))
  78. return currentLookup
  79. return newLookup
  80. },
  81. )
  82. const overviewsByLink = computed(() => keyBy(overviews.value, 'link'))
  83. const hasOverviews = computed(() => overviews.value.length > 0)
  84. const overviewIds = computed(() => Object.keys(overviewsById.value))
  85. const lastUsedOverviews = computed<Record<ID, string>>(
  86. (currentLastUsedOverviews) => {
  87. const lastUsedOverviews =
  88. user.value?.preferences?.overviews_last_used || {}
  89. const newLastUsedOverviews = Object.keys(lastUsedOverviews).reduce(
  90. (result: Record<ID, string>, internalId) => {
  91. const id = overviewsByInternalId.value[internalId]
  92. if (id) {
  93. result[id] = lastUsedOverviews[internalId]
  94. }
  95. return result
  96. },
  97. {},
  98. )
  99. if (
  100. currentLastUsedOverviews &&
  101. isEqual(currentLastUsedOverviews, newLastUsedOverviews)
  102. ) {
  103. return currentLastUsedOverviews
  104. }
  105. return newLastUsedOverviews
  106. },
  107. )
  108. const overviewsSortedByLastUsedIds = computed<ID[]>(
  109. (currentSortedLastUsedIds) => {
  110. const newSortedLastUsedIds = Object.keys(lastUsedOverviews.value).sort(
  111. (a, b) =>
  112. lastUsedOverviews.value[b].localeCompare(lastUsedOverviews.value[a]),
  113. )
  114. if (
  115. currentSortedLastUsedIds &&
  116. isEqual(currentSortedLastUsedIds, newSortedLastUsedIds)
  117. )
  118. return currentSortedLastUsedIds
  119. return newSortedLastUsedIds
  120. },
  121. )
  122. // Active Overview (foreground)
  123. const currentTicketOverviewLink = ref('')
  124. const lastTicketOverviewLink = ref('')
  125. const setCurrentTicketOverviewLink = (link: string) => {
  126. // Remember the previous link, so we can use it for some conditions in the background polling.
  127. lastTicketOverviewLink.value = currentTicketOverviewLink.value
  128. currentTicketOverviewLink.value = link
  129. }
  130. return {
  131. overviews,
  132. lastUsedOverviews,
  133. overviewsSortedByLastUsedIds,
  134. overviewsById,
  135. overviewIds,
  136. overviewsLoading,
  137. overviewsByLink,
  138. hasOverviews,
  139. lastTicketOverviewLink,
  140. currentTicketOverviewLink,
  141. setCurrentTicketOverviewLink,
  142. }
  143. }