Files
cinny/src/app/hooks/useNotificationMode.ts
T
jaredandClaude Fable 5.1 d4d1b4957f fix(notifications): Advanced Push Rules mode switch keeps highlight/sound tweaks
RuleModeSwitcher rebuilt a rule's actions from scratch, so changing the
mode of e.g. .m.rule.is_user_mention silently dropped its highlight tweak
account-wide. Derive the existing highlight/custom-sound tweaks from the
rule's current actions and pass them through as NotificationModeOptions.
Adds getNotificationModeOptionsFromActions + unit tests.

Fixes #19

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-12 02:05:34 -04:00

92 lines
2.7 KiB
TypeScript

import { PushRuleAction, PushRuleActionName, TweakName } from 'matrix-js-sdk';
import { useCallback, useMemo } from 'react';
export enum NotificationMode {
OFF = 'OFF',
Notify = 'Notify',
NotifyLoud = 'NotifyLoud',
}
export const getNotificationMode = (actions: PushRuleAction[]): NotificationMode => {
const soundTweak = actions.find(
(action) => typeof action === 'object' && action.set_tweak === TweakName.Sound,
);
const notify = actions.find(
(action) => typeof action === 'string' && action === PushRuleActionName.Notify,
);
if (notify && soundTweak) return NotificationMode.NotifyLoud;
if (notify) return NotificationMode.Notify;
return NotificationMode.OFF;
};
export type NotificationModeOptions = {
soundValue?: string;
highlight?: boolean;
};
export const getNotificationModeActions = (
mode: NotificationMode,
options?: NotificationModeOptions,
): PushRuleAction[] => {
if (mode === NotificationMode.OFF) return [];
const actions: PushRuleAction[] = [PushRuleActionName.Notify];
if (mode === NotificationMode.NotifyLoud) {
actions.push({
set_tweak: TweakName.Sound,
value: options?.soundValue ?? 'default',
});
}
if (options?.highlight) {
actions.push({
set_tweak: TweakName.Highlight,
value: true,
});
}
return actions;
};
// Derive the options that would reproduce an existing rule's tweaks, so a mode
// switch rebuilds actions on top of them instead of silently dropping a
// `highlight` tweak (or a custom sound) that isn't part of the mode itself.
export const getNotificationModeOptionsFromActions = (
actions: PushRuleAction[],
): NotificationModeOptions => {
const soundTweak = actions.find(
(action) => typeof action === 'object' && action.set_tweak === TweakName.Sound,
);
const highlightTweak = actions.find(
(action) => typeof action === 'object' && action.set_tweak === TweakName.Highlight,
);
return {
soundValue:
soundTweak && typeof soundTweak === 'object' && typeof soundTweak.value === 'string'
? soundTweak.value
: undefined,
highlight:
!!highlightTweak && typeof highlightTweak === 'object' && highlightTweak.value !== false,
};
};
export type GetNotificationModeCallback = (mode: NotificationMode) => PushRuleAction[];
export const useNotificationModeActions = (
options?: NotificationModeOptions,
): GetNotificationModeCallback => {
const getAction: GetNotificationModeCallback = useCallback(
(mode) => getNotificationModeActions(mode, options),
[options],
);
return getAction;
};
export const useNotificationActionsMode = (actions: PushRuleAction[]): NotificationMode => {
const mode: NotificationMode = useMemo(() => getNotificationMode(actions), [actions]);
return mode;
};