mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
Merge pull request #32949 from overleaf/kh-default-invitees-to-replies-only
[web] default invitees to replies only GitOrigin-RevId: e3198403917e2679e49e27aaa87ae111675dc974
This commit is contained in:
@@ -8,6 +8,7 @@ import type {
|
|||||||
} from '../../../../../modules/notifications/app/src/types.js'
|
} from '../../../../../modules/notifications/app/src/types.js'
|
||||||
import { sendMB } from '@/infrastructure/event-tracking'
|
import { sendMB } from '@/infrastructure/event-tracking'
|
||||||
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
||||||
|
import { type PermissionsLevel } from '@/features/ide-react/types/permissions'
|
||||||
|
|
||||||
export type SettableNotificationLevel = 'all' | 'replies' | 'off'
|
export type SettableNotificationLevel = 'all' | 'replies' | 'off'
|
||||||
export type NotificationLevel = SettableNotificationLevel | 'global-off'
|
export type NotificationLevel = SettableNotificationLevel | 'global-off'
|
||||||
@@ -56,38 +57,43 @@ function levelToPreferences(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map backend preferences to UI notification level
|
* Map backend preferences to UI notification level, considering the user's
|
||||||
|
* role so that only the relevant key variant is inspected.
|
||||||
*/
|
*/
|
||||||
function preferencesToLevel(
|
function preferencesToLevel(
|
||||||
preferences: GlobalNotificationPreferencesSchema
|
preferences: GlobalNotificationPreferencesSchema,
|
||||||
|
permissionsLevel: PermissionsLevel
|
||||||
): NotificationLevel {
|
): NotificationLevel {
|
||||||
if (preferences.muteAllNotifications) {
|
if (preferences.muteAllNotifications) {
|
||||||
return 'global-off'
|
return 'global-off'
|
||||||
}
|
}
|
||||||
|
|
||||||
// If all notifications are off
|
const isOwner = permissionsLevel === 'owner'
|
||||||
if (
|
|
||||||
!preferences.commentOnOwnProject &&
|
const projectComments = isOwner
|
||||||
!preferences.commentOnInvitedProject &&
|
? preferences.commentOnOwnProject
|
||||||
!preferences.repliesOnOwnProject &&
|
: preferences.commentOnInvitedProject
|
||||||
!preferences.repliesOnInvitedProject &&
|
const projectTrackedChanges = isOwner
|
||||||
!preferences.repliesOnAuthoredThread &&
|
? preferences.trackedChangesOnOwnProject
|
||||||
!preferences.repliesOnParticipatingThread
|
: preferences.trackedChangesOnInvitedProject
|
||||||
) {
|
const projectReplies = isOwner
|
||||||
|
? preferences.repliesOnOwnProject
|
||||||
|
: preferences.repliesOnInvitedProject
|
||||||
|
|
||||||
|
const anyProjectNotifications =
|
||||||
|
projectComments || projectTrackedChanges || projectReplies
|
||||||
|
const anyParticipantNotifications =
|
||||||
|
preferences.repliesOnAuthoredThread ||
|
||||||
|
preferences.repliesOnParticipatingThread
|
||||||
|
|
||||||
|
if (!anyProjectNotifications && !anyParticipantNotifications) {
|
||||||
return 'off'
|
return 'off'
|
||||||
}
|
}
|
||||||
|
|
||||||
// If only reply-related notifications are on
|
if (!anyProjectNotifications && anyParticipantNotifications) {
|
||||||
if (
|
|
||||||
!preferences.commentOnOwnProject &&
|
|
||||||
!preferences.commentOnInvitedProject &&
|
|
||||||
(preferences.repliesOnAuthoredThread ||
|
|
||||||
preferences.repliesOnParticipatingThread)
|
|
||||||
) {
|
|
||||||
return 'replies'
|
return 'replies'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to 'all' for any other combination
|
|
||||||
return 'all'
|
return 'all'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,11 +110,11 @@ export function useProjectNotificationPreferences() {
|
|||||||
`/notifications/preferences/project/${projectId}`
|
`/notifications/preferences/project/${projectId}`
|
||||||
)
|
)
|
||||||
.then(prefs => {
|
.then(prefs => {
|
||||||
setNotificationLevel(preferencesToLevel(prefs))
|
setNotificationLevel(preferencesToLevel(prefs, permissionsLevel))
|
||||||
})
|
})
|
||||||
.catch(debugConsole.error)
|
.catch(debugConsole.error)
|
||||||
.finally(() => setIsLoading(false))
|
.finally(() => setIsLoading(false))
|
||||||
}, [projectId])
|
}, [projectId, permissionsLevel])
|
||||||
|
|
||||||
const setLevel = useCallback(
|
const setLevel = useCallback(
|
||||||
(level: SettableNotificationLevel) => {
|
(level: SettableNotificationLevel) => {
|
||||||
|
|||||||
@@ -53,9 +53,21 @@ const allNotificationsOff = {
|
|||||||
repliesOnParticipatingThread: false,
|
repliesOnParticipatingThread: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderComponent() {
|
const defaultPreferences = {
|
||||||
|
trackedChangesOnOwnProject: true,
|
||||||
|
trackedChangesOnInvitedProject: false,
|
||||||
|
commentOnOwnProject: true,
|
||||||
|
commentOnInvitedProject: false,
|
||||||
|
repliesOnOwnProject: false,
|
||||||
|
repliesOnInvitedProject: false,
|
||||||
|
repliesOnAuthoredThread: true,
|
||||||
|
repliesOnParticipatingThread: true,
|
||||||
|
muteAllNotifications: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderComponent(props: { permissionsLevel?: string } = {}) {
|
||||||
return render(
|
return render(
|
||||||
<EditorProviders>
|
<EditorProviders permissionsLevel={props.permissionsLevel as any}>
|
||||||
<SettingsModalProvider>
|
<SettingsModalProvider>
|
||||||
<ProjectNotificationsSetting />
|
<ProjectNotificationsSetting />
|
||||||
</SettingsModalProvider>
|
</SettingsModalProvider>
|
||||||
@@ -241,6 +253,40 @@ describe('<ProjectNotificationsSetting />', function () {
|
|||||||
).to.be.true
|
).to.be.true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('shows "all" for owner with default preferences', async function () {
|
||||||
|
fetchMock.get(preferencesUrl, defaultPreferences)
|
||||||
|
|
||||||
|
renderComponent({ permissionsLevel: 'owner' })
|
||||||
|
|
||||||
|
await waitFor(
|
||||||
|
() =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
screen.getByLabelText('All project activity', {
|
||||||
|
exact: false,
|
||||||
|
}) as HTMLInputElement
|
||||||
|
).checked
|
||||||
|
).to.be.true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows "replies" for invitee with default preferences', async function () {
|
||||||
|
fetchMock.get(preferencesUrl, defaultPreferences)
|
||||||
|
|
||||||
|
renderComponent({ permissionsLevel: 'readAndWrite' })
|
||||||
|
|
||||||
|
await waitFor(
|
||||||
|
() =>
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
screen.getByLabelText('Replies to your activity only', {
|
||||||
|
exact: false,
|
||||||
|
}) as HTMLInputElement
|
||||||
|
).checked
|
||||||
|
).to.be.true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('POSTs "all" preferences when "All project activity" is selected', async function () {
|
it('POSTs "all" preferences when "All project activity" is selected', async function () {
|
||||||
fetchMock.get(preferencesUrl, allNotificationsOff)
|
fetchMock.get(preferencesUrl, allNotificationsOff)
|
||||||
const saveMock = fetchMock.post(preferencesUrl, { status: 200 })
|
const saveMock = fetchMock.post(preferencesUrl, { status: 200 })
|
||||||
|
|||||||
Reference in New Issue
Block a user