use-project-notification-preferences.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import { useCallback, useEffect, useState } from 'react'
  2. import { useProjectContext } from '@/shared/context/project-context'
  3. import { getJSON, postJSON } from '@/infrastructure/fetch-json'
  4. import { debugConsole } from '@/utils/debugging'
  5. import type { NotificationPreferencesSchema } from '../../../../../modules/notifications/app/src/types.js'
  6. export type NotificationLevel = 'all' | 'replies' | 'off'
  7. /**
  8. * Map UI notification level to backend preferences
  9. */
  10. function levelToPreferences(
  11. level: NotificationLevel
  12. ): NotificationPreferencesSchema {
  13. switch (level) {
  14. case 'all':
  15. return {
  16. trackedChangesOnOwnProject: true,
  17. trackedChangesOnInvitedProject: true,
  18. commentOnOwnProject: true,
  19. commentOnInvitedProject: true,
  20. repliesOnOwnProject: true,
  21. repliesOnInvitedProject: true,
  22. repliesOnAuthoredThread: true,
  23. repliesOnParticipatingThread: true,
  24. }
  25. case 'replies':
  26. return {
  27. trackedChangesOnOwnProject: false,
  28. trackedChangesOnInvitedProject: false,
  29. commentOnOwnProject: false,
  30. commentOnInvitedProject: false,
  31. repliesOnOwnProject: false,
  32. repliesOnInvitedProject: false,
  33. repliesOnAuthoredThread: true,
  34. repliesOnParticipatingThread: true,
  35. }
  36. case 'off':
  37. return {
  38. trackedChangesOnOwnProject: false,
  39. trackedChangesOnInvitedProject: false,
  40. commentOnOwnProject: false,
  41. commentOnInvitedProject: false,
  42. repliesOnOwnProject: false,
  43. repliesOnInvitedProject: false,
  44. repliesOnAuthoredThread: false,
  45. repliesOnParticipatingThread: false,
  46. }
  47. }
  48. }
  49. /**
  50. * Map backend preferences to UI notification level
  51. */
  52. function preferencesToLevel(
  53. preferences: NotificationPreferencesSchema
  54. ): NotificationLevel {
  55. // If all notifications are off
  56. if (
  57. !preferences.commentOnOwnProject &&
  58. !preferences.commentOnInvitedProject &&
  59. !preferences.repliesOnOwnProject &&
  60. !preferences.repliesOnInvitedProject &&
  61. !preferences.repliesOnAuthoredThread &&
  62. !preferences.repliesOnParticipatingThread
  63. ) {
  64. return 'off'
  65. }
  66. // If only reply-related notifications are on
  67. if (
  68. !preferences.commentOnOwnProject &&
  69. !preferences.commentOnInvitedProject &&
  70. (preferences.repliesOnAuthoredThread ||
  71. preferences.repliesOnParticipatingThread)
  72. ) {
  73. return 'replies'
  74. }
  75. // Default to 'all' for any other combination
  76. return 'all'
  77. }
  78. export function useProjectNotificationPreferences() {
  79. const { projectId } = useProjectContext()
  80. const [notificationLevel, setNotificationLevel] =
  81. useState<NotificationLevel>('all')
  82. const [isLoading, setIsLoading] = useState(true)
  83. // Load preferences on mount
  84. useEffect(() => {
  85. getJSON<NotificationPreferencesSchema>(
  86. `/notifications/preferences/project/${projectId}`
  87. )
  88. .then(prefs => {
  89. setNotificationLevel(preferencesToLevel(prefs))
  90. })
  91. .catch(debugConsole.error)
  92. .finally(() => setIsLoading(false))
  93. }, [projectId])
  94. const setLevel = useCallback(
  95. (level: NotificationLevel) => {
  96. setNotificationLevel(level)
  97. const preferences = levelToPreferences(level)
  98. postJSON(`/notifications/preferences/project/${projectId}`, {
  99. body: preferences,
  100. }).catch(debugConsole.error)
  101. },
  102. [projectId]
  103. )
  104. return {
  105. notificationLevel,
  106. setNotificationLevel: setLevel,
  107. isLoading,
  108. }
  109. }