ticketsCachedByOverviewQuery.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. import gql from 'graphql-tag'
  3. import type { TicketsCachedByOverviewQueryVariables } from '#shared/graphql/types.ts'
  4. import registerRelayStylePagination from '#shared/server/apollo/cache/utils/registerRelayStylePagination.ts'
  5. import { useTicketsCachedByOverviewCache } from '#desktop/entities/ticket/composables/useTicketsCachedByOverviewCache.ts'
  6. import type { InMemoryCache } from '@apollo/client/cache'
  7. import type {
  8. FieldMergeFunction,
  9. FieldPolicy,
  10. } from '@apollo/client/cache/inmemory/policies'
  11. import type { InMemoryCacheConfig } from '@apollo/client/cache/inmemory/types'
  12. const modifyOverviewsCache = (
  13. cache: InMemoryCache,
  14. { ticketCount, overviewId }: { ticketCount: number; overviewId: string },
  15. ) => {
  16. const normalizedId = cache.identify({
  17. id: overviewId,
  18. __typename: 'Overview',
  19. })
  20. // Check if the cache already has this field.
  21. const existingData = cache.readFragment<{ cachedTicketCount: number }>({
  22. id: normalizedId,
  23. fragment: gql`
  24. fragment TicketCountFragment on Overview {
  25. cachedTicketCount(cacheTtl: 60)
  26. }
  27. `,
  28. })
  29. if (existingData && existingData.cachedTicketCount === ticketCount) return
  30. cache.writeFragment({
  31. id: normalizedId,
  32. fragment: gql`
  33. fragment TicketCountFragment on Overview {
  34. cachedTicketCount(cacheTtl: 60)
  35. }
  36. `,
  37. data: {
  38. cachedTicketCount: ticketCount,
  39. },
  40. })
  41. }
  42. export default function register(
  43. config: InMemoryCacheConfig,
  44. ): InMemoryCacheConfig {
  45. const currentConfig = registerRelayStylePagination(
  46. config,
  47. 'ticketsCachedByOverview',
  48. ['overviewId', 'orderBy', 'orderDirection'],
  49. )
  50. currentConfig.typePolicies ||= {}
  51. currentConfig.typePolicies.Query ||= {}
  52. currentConfig.typePolicies.Query.fields ||= {}
  53. // Assert that ticketsCachedByOverview is a FieldPolicy
  54. const ticketsCachedByOverviewPolicy = currentConfig.typePolicies.Query.fields
  55. .ticketsCachedByOverview as FieldPolicy
  56. const originalMerge =
  57. ticketsCachedByOverviewPolicy.merge as FieldMergeFunction
  58. // Override merge function to include noChange handling
  59. ticketsCachedByOverviewPolicy.merge = (existing, incoming, options) => {
  60. const { cache, variables } = options
  61. const overviewId = variables?.overviewId as string
  62. if (overviewId && incoming.totalCount !== undefined) {
  63. modifyOverviewsCache(cache, {
  64. ticketCount: incoming.totalCount,
  65. overviewId,
  66. })
  67. }
  68. const cachedData =
  69. useTicketsCachedByOverviewCache().readTicketsByOverviewCache(
  70. variables as TicketsCachedByOverviewQueryVariables,
  71. )
  72. // We receiving null when the query data is still the same.
  73. if ((cachedData || existing) && incoming.edges === null) {
  74. // TODO: Returning cache data destroys currently the references on the tickets...
  75. // TODO: And workaround with "existing" is not working, because also with cache+network it's always empty.
  76. return existing || cachedData!.ticketsCachedByOverview // Return existing data without updating
  77. }
  78. // Otherwise, call the original merge function for normal pagination behavior
  79. return originalMerge(existing, incoming, options)
  80. }
  81. return currentConfig
  82. }