use-project-notification-preferences.ts 4.0 KB

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