use-project-notification-preferences.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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.ts'
  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. commentOnOwnProject: true,
  17. commentOnInvitedProject: true,
  18. repliesOnOwnProject: true,
  19. repliesOnInvitedProject: true,
  20. repliesOnAuthoredThread: true,
  21. repliesOnParticipatingThread: true,
  22. }
  23. case 'replies':
  24. return {
  25. commentOnOwnProject: false,
  26. commentOnInvitedProject: false,
  27. repliesOnOwnProject: false,
  28. repliesOnInvitedProject: false,
  29. repliesOnAuthoredThread: true,
  30. repliesOnParticipatingThread: true,
  31. }
  32. case 'off':
  33. return {
  34. commentOnOwnProject: false,
  35. commentOnInvitedProject: false,
  36. repliesOnOwnProject: false,
  37. repliesOnInvitedProject: false,
  38. repliesOnAuthoredThread: false,
  39. repliesOnParticipatingThread: false,
  40. }
  41. }
  42. }
  43. /**
  44. * Map backend preferences to UI notification level
  45. */
  46. function preferencesToLevel(
  47. preferences: NotificationPreferencesSchema
  48. ): NotificationLevel {
  49. // If all notifications are off
  50. if (
  51. !preferences.commentOnOwnProject &&
  52. !preferences.commentOnInvitedProject &&
  53. !preferences.repliesOnOwnProject &&
  54. !preferences.repliesOnInvitedProject &&
  55. !preferences.repliesOnAuthoredThread &&
  56. !preferences.repliesOnParticipatingThread
  57. ) {
  58. return 'off'
  59. }
  60. // If only reply-related notifications are on
  61. if (
  62. !preferences.commentOnOwnProject &&
  63. !preferences.commentOnInvitedProject &&
  64. (preferences.repliesOnAuthoredThread ||
  65. preferences.repliesOnParticipatingThread)
  66. ) {
  67. return 'replies'
  68. }
  69. // Default to 'all' for any other combination
  70. return 'all'
  71. }
  72. export function useProjectNotificationPreferences() {
  73. const { projectId } = useProjectContext()
  74. const [notificationLevel, setNotificationLevel] =
  75. useState<NotificationLevel>('all')
  76. const [isLoading, setIsLoading] = useState(true)
  77. // Load preferences on mount
  78. useEffect(() => {
  79. getJSON<NotificationPreferencesSchema>(
  80. `/notifications/preferences/project/${projectId}`
  81. )
  82. .then(prefs => {
  83. setNotificationLevel(preferencesToLevel(prefs))
  84. })
  85. .catch(debugConsole.error)
  86. .finally(() => setIsLoading(false))
  87. }, [projectId])
  88. const setLevel = useCallback(
  89. (level: NotificationLevel) => {
  90. setNotificationLevel(level)
  91. const preferences = levelToPreferences(level)
  92. postJSON(`/notifications/preferences/project/${projectId}`, {
  93. body: preferences,
  94. }).catch(debugConsole.error)
  95. },
  96. [projectId]
  97. )
  98. return {
  99. notificationLevel,
  100. setNotificationLevel: setLevel,
  101. isLoading,
  102. }
  103. }