275 lines
8.2 KiB
JavaScript
275 lines
8.2 KiB
JavaScript
import React from "react";
|
|
import "./KeywordNotification.scss";
|
|
|
|
import initMatrix from "../../../client/initMatrix";
|
|
import { openReusableContextMenu } from "../../../client/action/navigation";
|
|
import { getEventCords } from "../../../util/common";
|
|
|
|
import Text from "../../atoms/text/Text";
|
|
import Chip from "../../atoms/chip/Chip";
|
|
import Input from "../../atoms/input/Input";
|
|
import Button from "../../atoms/button/Button";
|
|
import { MenuHeader } from "../../atoms/context-menu/ContextMenu";
|
|
import SettingTile from "../setting-tile/SettingTile";
|
|
|
|
import NotificationSelector from "./NotificationSelector";
|
|
|
|
import ChevronBottomIC from "../../../../public/res/ic/outlined/chevron-bottom.svg";
|
|
import CrossIC from "../../../../public/res/ic/outlined/cross.svg";
|
|
|
|
import { useAccountData } from "../../hooks/useAccountData";
|
|
import {
|
|
notifType,
|
|
typeToLabel,
|
|
getActionType,
|
|
getTypeActions,
|
|
} from "./GlobalNotification";
|
|
|
|
const DISPLAY_NAME = ".m.rule.contains_display_name";
|
|
const ROOM_PING = ".m.rule.roomnotif";
|
|
const USERNAME = ".m.rule.contains_user_name";
|
|
const KEYWORD = "keyword";
|
|
|
|
function useKeywordNotif() {
|
|
const mx = initMatrix.matrixClient;
|
|
const pushRules = useAccountData("m.push_rules")?.getContent();
|
|
const override = pushRules?.global?.override ?? [];
|
|
const content = pushRules?.global?.content ?? [];
|
|
|
|
const rulesToType = {
|
|
[DISPLAY_NAME]: notifType.NOISY,
|
|
[ROOM_PING]: notifType.NOISY,
|
|
[USERNAME]: notifType.NOISY,
|
|
};
|
|
|
|
const setRule = (rule, type) => {
|
|
const evtContent = pushRules ?? {};
|
|
if (!evtContent.global) evtContent.global = {};
|
|
if (!evtContent.global.override) evtContent.global.override = [];
|
|
if (!evtContent.global.content) evtContent.global.content = [];
|
|
const or = evtContent.global.override;
|
|
const ct = evtContent.global.content;
|
|
|
|
if (rule === DISPLAY_NAME || rule === ROOM_PING) {
|
|
let orRule = or.find((r) => r?.rule_id === rule);
|
|
if (!orRule) {
|
|
orRule = {
|
|
conditions: [],
|
|
actions: [],
|
|
rule_id: rule,
|
|
default: true,
|
|
enabled: true,
|
|
};
|
|
or.push(orRule);
|
|
}
|
|
if (rule === DISPLAY_NAME) {
|
|
orRule.conditions = [{ kind: "contains_display_name" }];
|
|
orRule.actions = getTypeActions(type, true);
|
|
} else {
|
|
orRule.conditions = [
|
|
{ kind: "event_match", key: "content.body", pattern: "@room" },
|
|
{ kind: "sender_notification_permission", key: "room" },
|
|
];
|
|
orRule.actions = getTypeActions(type, true);
|
|
}
|
|
} else if (rule === USERNAME) {
|
|
let usernameRule = ct.find((r) => r?.rule_id === rule);
|
|
if (!usernameRule) {
|
|
const userId = mx.getUserId();
|
|
const username = userId.match(/^@?(\S+):(\S+)$/)?.[1] ?? userId;
|
|
usernameRule = {
|
|
actions: [],
|
|
default: true,
|
|
enabled: true,
|
|
pattern: username,
|
|
rule_id: rule,
|
|
};
|
|
ct.push(usernameRule);
|
|
}
|
|
usernameRule.actions = getTypeActions(type, true);
|
|
} else {
|
|
const keyRules = ct.filter((r) => r.rule_id !== USERNAME);
|
|
keyRules.forEach((r) => {
|
|
// eslint-disable-next-line no-param-reassign
|
|
r.actions = getTypeActions(type, true);
|
|
});
|
|
}
|
|
|
|
mx.setAccountData("m.push_rules", evtContent);
|
|
};
|
|
|
|
const addKeyword = (keyword) => {
|
|
if (content.find((r) => r.rule_id === keyword)) return;
|
|
content.push({
|
|
rule_id: keyword,
|
|
pattern: keyword,
|
|
enabled: true,
|
|
default: false,
|
|
actions: getTypeActions(rulesToType[KEYWORD] ?? notifType.NOISY, true),
|
|
});
|
|
mx.setAccountData("m.push_rules", pushRules);
|
|
};
|
|
const removeKeyword = (rule) => {
|
|
pushRules.global.content = content.filter(
|
|
(r) => r.rule_id !== rule.rule_id
|
|
);
|
|
mx.setAccountData("m.push_rules", pushRules);
|
|
};
|
|
|
|
const dsRule = override.find((rule) => rule.rule_id === DISPLAY_NAME);
|
|
const roomRule = override.find((rule) => rule.rule_id === ROOM_PING);
|
|
const usernameRule = content.find((rule) => rule.rule_id === USERNAME);
|
|
const keywordRule = content.find((rule) => rule.rule_id !== USERNAME);
|
|
|
|
if (dsRule) rulesToType[DISPLAY_NAME] = getActionType(dsRule);
|
|
if (roomRule) rulesToType[ROOM_PING] = getActionType(roomRule);
|
|
if (usernameRule) rulesToType[USERNAME] = getActionType(usernameRule);
|
|
if (keywordRule) rulesToType[KEYWORD] = getActionType(keywordRule);
|
|
|
|
return {
|
|
rulesToType,
|
|
pushRules,
|
|
setRule,
|
|
addKeyword,
|
|
removeKeyword,
|
|
};
|
|
}
|
|
|
|
function GlobalNotification() {
|
|
const { rulesToType, pushRules, setRule, addKeyword, removeKeyword } =
|
|
useKeywordNotif();
|
|
|
|
const keywordRules =
|
|
pushRules?.global?.content.filter((r) => r.rule_id !== USERNAME) ?? [];
|
|
|
|
const onSelect = (evt, rule) => {
|
|
openReusableContextMenu(
|
|
"bottom",
|
|
getEventCords(evt, ".btn-surface"),
|
|
(requestClose) => (
|
|
<NotificationSelector
|
|
value={rulesToType[rule]}
|
|
onSelect={(value) => {
|
|
if (rulesToType[rule] !== value) setRule(rule, value);
|
|
requestClose();
|
|
}}
|
|
/>
|
|
)
|
|
);
|
|
};
|
|
|
|
const handleSubmit = (evt) => {
|
|
evt.preventDefault();
|
|
const { keywordInput } = evt.target.elements;
|
|
const value = keywordInput.value.trim();
|
|
if (value === "") return;
|
|
addKeyword(value);
|
|
keywordInput.value = "";
|
|
};
|
|
|
|
return (
|
|
<div className="keyword-notification">
|
|
<MenuHeader>Mentions & keywords</MenuHeader>
|
|
<SettingTile
|
|
title="Message containing my display name"
|
|
options={
|
|
<Button
|
|
onClick={(evt) => onSelect(evt, DISPLAY_NAME)}
|
|
iconSrc={ChevronBottomIC}
|
|
>
|
|
{typeToLabel[rulesToType[DISPLAY_NAME]]}
|
|
</Button>
|
|
}
|
|
content={
|
|
<Text variant="b3">
|
|
Default notification settings for all message containing your
|
|
display name.
|
|
</Text>
|
|
}
|
|
/>
|
|
<SettingTile
|
|
title="Message containing my username"
|
|
options={
|
|
<Button
|
|
onClick={(evt) => onSelect(evt, USERNAME)}
|
|
iconSrc={ChevronBottomIC}
|
|
>
|
|
{typeToLabel[rulesToType[USERNAME]]}
|
|
</Button>
|
|
}
|
|
content={
|
|
<Text variant="b3">
|
|
Default notification settings for all message containing your
|
|
username.
|
|
</Text>
|
|
}
|
|
/>
|
|
<SettingTile
|
|
title="Message containing @room"
|
|
options={
|
|
<Button
|
|
onClick={(evt) => onSelect(evt, ROOM_PING)}
|
|
iconSrc={ChevronBottomIC}
|
|
>
|
|
{typeToLabel[rulesToType[ROOM_PING]]}
|
|
</Button>
|
|
}
|
|
content={
|
|
<Text variant="b3">
|
|
Default notification settings for all messages containing @room.
|
|
</Text>
|
|
}
|
|
/>
|
|
{rulesToType[KEYWORD] && (
|
|
<SettingTile
|
|
title="Message containing keywords"
|
|
options={
|
|
<Button
|
|
onClick={(evt) => onSelect(evt, KEYWORD)}
|
|
iconSrc={ChevronBottomIC}
|
|
>
|
|
{typeToLabel[rulesToType[KEYWORD]]}
|
|
</Button>
|
|
}
|
|
content={
|
|
<Text variant="b3">
|
|
Default notification settings for all message containing keywords.
|
|
</Text>
|
|
}
|
|
/>
|
|
)}
|
|
<SettingTile
|
|
title="Keywords"
|
|
content={
|
|
<div className="keyword-notification__keyword">
|
|
<Text variant="b3">
|
|
Get notification when a message contains keyword.
|
|
</Text>
|
|
<form onSubmit={handleSubmit}>
|
|
<Input name="keywordInput" required />
|
|
<Button variant="primary" type="submit">
|
|
Add
|
|
</Button>
|
|
</form>
|
|
{keywordRules.length > 0 && (
|
|
<div>
|
|
{keywordRules.map((rule) => (
|
|
<Chip
|
|
iconSrc={CrossIC}
|
|
key={rule.rule_id}
|
|
text={rule.pattern}
|
|
iconColor={CrossIC}
|
|
onClick={() => removeKeyword(rule)}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default GlobalNotification;
|