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
92 lines
2.7 KiB
TypeScript
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;
|
|
};
|