* Copyright headers 1 * Licence headers 2 * Copyright Headers 3 * Copyright Headers 4 * Copyright Headers 5 * Copyright Headers 6 * Copyright headers 7 * Add copyright headers for html and config file * Replace license files and update package.json * Update with CLA * lint
312 lines
12 KiB
TypeScript
312 lines
12 KiB
TypeScript
/*
|
|
Copyright 2024 New Vector Ltd.
|
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import React, { ChangeEvent, SyntheticEvent } from "react";
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
|
|
|
import { _t } from "../../../../../languageHandler";
|
|
import SdkConfig from "../../../../../SdkConfig";
|
|
import { Mjolnir } from "../../../../../mjolnir/Mjolnir";
|
|
import { ListRule } from "../../../../../mjolnir/ListRule";
|
|
import { BanList, RULE_SERVER, RULE_USER } from "../../../../../mjolnir/BanList";
|
|
import Modal from "../../../../../Modal";
|
|
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
|
|
import ErrorDialog from "../../../dialogs/ErrorDialog";
|
|
import QuestionDialog from "../../../dialogs/QuestionDialog";
|
|
import AccessibleButton from "../../../elements/AccessibleButton";
|
|
import Field from "../../../elements/Field";
|
|
import SettingsTab from "../SettingsTab";
|
|
import { SettingsSection } from "../../shared/SettingsSection";
|
|
import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection";
|
|
|
|
interface IState {
|
|
busy: boolean;
|
|
newPersonalRule: string;
|
|
newList: string;
|
|
}
|
|
|
|
export default class MjolnirUserSettingsTab extends React.Component<{}, IState> {
|
|
public constructor(props: {}) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
busy: false,
|
|
newPersonalRule: "",
|
|
newList: "",
|
|
};
|
|
}
|
|
|
|
private onPersonalRuleChanged = (e: ChangeEvent<HTMLInputElement>): void => {
|
|
this.setState({ newPersonalRule: e.target.value });
|
|
};
|
|
|
|
private onNewListChanged = (e: ChangeEvent<HTMLInputElement>): void => {
|
|
this.setState({ newList: e.target.value });
|
|
};
|
|
|
|
private onAddPersonalRule = async (e: SyntheticEvent): Promise<void> => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
let kind = RULE_SERVER;
|
|
if (this.state.newPersonalRule.startsWith("@")) {
|
|
kind = RULE_USER;
|
|
}
|
|
|
|
this.setState({ busy: true });
|
|
try {
|
|
const list = await Mjolnir.sharedInstance().getOrCreatePersonalList();
|
|
await list.banEntity(kind, this.state.newPersonalRule, _t("labs_mjolnir|ban_reason"));
|
|
this.setState({ newPersonalRule: "" }); // this will also cause the new rule to be rendered
|
|
} catch (e) {
|
|
logger.error(e);
|
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
title: _t("labs_mjolnir|error_adding_ignore"),
|
|
description: _t("labs_mjolnir|something_went_wrong"),
|
|
});
|
|
} finally {
|
|
this.setState({ busy: false });
|
|
}
|
|
};
|
|
|
|
private onSubscribeList = async (e: SyntheticEvent): Promise<void> => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
this.setState({ busy: true });
|
|
try {
|
|
const room = await MatrixClientPeg.safeGet().joinRoom(this.state.newList);
|
|
await Mjolnir.sharedInstance().subscribeToList(room.roomId);
|
|
this.setState({ newList: "" }); // this will also cause the new rule to be rendered
|
|
} catch (e) {
|
|
logger.error(e);
|
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
title: _t("labs_mjolnir|error_adding_list_title"),
|
|
description: _t("labs_mjolnir|error_adding_list_description"),
|
|
});
|
|
} finally {
|
|
this.setState({ busy: false });
|
|
}
|
|
};
|
|
|
|
private async removePersonalRule(rule: ListRule): Promise<void> {
|
|
this.setState({ busy: true });
|
|
try {
|
|
const list = Mjolnir.sharedInstance().getPersonalList();
|
|
await list!.unbanEntity(rule.kind, rule.entity);
|
|
} catch (e) {
|
|
logger.error(e);
|
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
title: _t("labs_mjolnir|error_removing_ignore"),
|
|
description: _t("labs_mjolnir|something_went_wrong"),
|
|
});
|
|
} finally {
|
|
this.setState({ busy: false });
|
|
}
|
|
}
|
|
|
|
private async unsubscribeFromList(list: BanList): Promise<void> {
|
|
this.setState({ busy: true });
|
|
try {
|
|
await Mjolnir.sharedInstance().unsubscribeFromList(list.roomId);
|
|
await MatrixClientPeg.safeGet().leave(list.roomId);
|
|
} catch (e) {
|
|
logger.error(e);
|
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
title: _t("labs_mjolnir|error_removing_list_title"),
|
|
description: _t("labs_mjolnir|error_removing_list_description"),
|
|
});
|
|
} finally {
|
|
this.setState({ busy: false });
|
|
}
|
|
}
|
|
|
|
private viewListRules(list: BanList): void {
|
|
const room = MatrixClientPeg.safeGet().getRoom(list.roomId);
|
|
const name = room ? room.name : list.roomId;
|
|
|
|
const renderRules = (rules: ListRule[]): JSX.Element => {
|
|
if (rules.length === 0) return <i>{_t("labs_mjolnir|rules_empty")}</i>;
|
|
|
|
const tiles: JSX.Element[] = [];
|
|
for (const rule of rules) {
|
|
tiles.push(
|
|
<li key={rule.kind + rule.entity}>
|
|
<code>{rule.entity}</code>
|
|
</li>,
|
|
);
|
|
}
|
|
return <ul>{tiles}</ul>;
|
|
};
|
|
|
|
Modal.createDialog(QuestionDialog, {
|
|
title: _t("labs_mjolnir|rules_title", { roomName: name }),
|
|
description: (
|
|
<div>
|
|
<h3>{_t("labs_mjolnir|rules_server")}</h3>
|
|
{renderRules(list.serverRules)}
|
|
<h3>{_t("labs_mjolnir|rules_user")}</h3>
|
|
{renderRules(list.userRules)}
|
|
</div>
|
|
),
|
|
button: _t("action|close"),
|
|
hasCancelButton: false,
|
|
});
|
|
}
|
|
|
|
private renderPersonalBanListRules(): JSX.Element {
|
|
const list = Mjolnir.sharedInstance().getPersonalList();
|
|
const rules = list ? [...list.userRules, ...list.serverRules] : [];
|
|
if (!list || rules.length <= 0) return <i>{_t("labs_mjolnir|personal_empty")}</i>;
|
|
|
|
const tiles: JSX.Element[] = [];
|
|
for (const rule of rules) {
|
|
tiles.push(
|
|
<li key={rule.entity} className="mx_MjolnirUserSettingsTab_listItem">
|
|
<AccessibleButton
|
|
kind="danger_sm"
|
|
onClick={() => this.removePersonalRule(rule)}
|
|
disabled={this.state.busy}
|
|
>
|
|
{_t("action|remove")}
|
|
</AccessibleButton>
|
|
|
|
<code>{rule.entity}</code>
|
|
</li>,
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<p>{_t("labs_mjolnir|personal_section")}</p>
|
|
<ul>{tiles}</ul>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
private renderSubscribedBanLists(): JSX.Element {
|
|
const personalList = Mjolnir.sharedInstance().getPersonalList();
|
|
const lists = Mjolnir.sharedInstance().lists.filter((b) => {
|
|
return personalList ? personalList.roomId !== b.roomId : true;
|
|
});
|
|
if (!lists || lists.length <= 0) return <i>{_t("labs_mjolnir|no_lists")}</i>;
|
|
|
|
const tiles: JSX.Element[] = [];
|
|
for (const list of lists) {
|
|
const room = MatrixClientPeg.safeGet().getRoom(list.roomId);
|
|
const name = room ? (
|
|
<span>
|
|
{room.name} (<code>{list.roomId}</code>)
|
|
</span>
|
|
) : (
|
|
<code>list.roomId</code>
|
|
);
|
|
tiles.push(
|
|
<li key={list.roomId} className="mx_MjolnirUserSettingsTab_listItem">
|
|
<AccessibleButton
|
|
kind="danger_sm"
|
|
onClick={() => this.unsubscribeFromList(list)}
|
|
disabled={this.state.busy}
|
|
>
|
|
{_t("action|unsubscribe")}
|
|
</AccessibleButton>
|
|
|
|
<AccessibleButton
|
|
kind="primary_sm"
|
|
onClick={() => this.viewListRules(list)}
|
|
disabled={this.state.busy}
|
|
>
|
|
{_t("labs_mjolnir|view_rules")}
|
|
</AccessibleButton>
|
|
|
|
{name}
|
|
</li>,
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<p>{_t("labs_mjolnir|lists")}</p>
|
|
<ul>{tiles}</ul>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
public render(): React.ReactNode {
|
|
const brand = SdkConfig.get().brand;
|
|
|
|
return (
|
|
<SettingsTab>
|
|
<SettingsSection>
|
|
<SettingsSubsectionText>
|
|
<strong className="warning">{_t("labs_mjolnir|advanced_warning")}</strong>
|
|
<p>{_t("labs_mjolnir|explainer_1", { brand }, { code: (s) => <code>{s}</code> })}</p>
|
|
<p>{_t("labs_mjolnir|explainer_2")}</p>
|
|
</SettingsSubsectionText>
|
|
<SettingsSubsection
|
|
heading={_t("labs_mjolnir|personal_heading")}
|
|
description={_t("labs_mjolnir|personal_description", {
|
|
myBanList: _t("labs_mjolnir|room_name"),
|
|
})}
|
|
>
|
|
{this.renderPersonalBanListRules()}
|
|
<form onSubmit={this.onAddPersonalRule} autoComplete="off">
|
|
<Field
|
|
type="text"
|
|
label={_t("labs_mjolnir|personal_new_label")}
|
|
placeholder={_t("labs_mjolnir|personal_new_placeholder")}
|
|
value={this.state.newPersonalRule}
|
|
onChange={this.onPersonalRuleChanged}
|
|
/>
|
|
<AccessibleButton
|
|
type="submit"
|
|
kind="primary"
|
|
onClick={this.onAddPersonalRule}
|
|
disabled={this.state.busy}
|
|
>
|
|
{_t("action|ignore")}
|
|
</AccessibleButton>
|
|
</form>
|
|
</SettingsSubsection>
|
|
<SettingsSubsection
|
|
heading={_t("labs_mjolnir|lists_heading")}
|
|
description={
|
|
<>
|
|
<strong className="warning">{_t("labs_mjolnir|lists_description_1")}</strong>
|
|
|
|
<span>{_t("labs_mjolnir|lists_description_2")}</span>
|
|
</>
|
|
}
|
|
>
|
|
{this.renderSubscribedBanLists()}
|
|
<form onSubmit={this.onSubscribeList} autoComplete="off">
|
|
<Field
|
|
type="text"
|
|
label={_t("labs_mjolnir|lists_new_label")}
|
|
value={this.state.newList}
|
|
onChange={this.onNewListChanged}
|
|
/>
|
|
<AccessibleButton
|
|
type="submit"
|
|
kind="primary"
|
|
onClick={this.onSubscribeList}
|
|
disabled={this.state.busy}
|
|
>
|
|
{_t("action|subscribe")}
|
|
</AccessibleButton>
|
|
</form>
|
|
</SettingsSubsection>
|
|
</SettingsSection>
|
|
</SettingsTab>
|
|
);
|
|
}
|
|
}
|