diff --git a/res/css/views/settings/tabs/user/_MjolnirUserSettingsTab.scss b/res/css/views/settings/tabs/user/_MjolnirUserSettingsTab.scss index 930dbeb440..c60cbc5dea 100644 --- a/res/css/views/settings/tabs/user/_MjolnirUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_MjolnirUserSettingsTab.scss @@ -18,6 +18,6 @@ limitations under the License. @mixin mx_Settings_fullWidthField; } -.mx_MjolnirUserSettingsTab_personalRule { +.mx_MjolnirUserSettingsTab_listItem { margin-bottom: 2px; } diff --git a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js index 97f92bb0b2..4e05b57567 100644 --- a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js @@ -18,8 +18,9 @@ import React from 'react'; import {_t} from "../../../../../languageHandler"; import {Mjolnir} from "../../../../../mjolnir/Mjolnir"; import {ListRule} from "../../../../../mjolnir/ListRule"; -import {RULE_SERVER, RULE_USER} from "../../../../../mjolnir/BanList"; +import {BanList, RULE_SERVER, RULE_USER} from "../../../../../mjolnir/BanList"; import Modal from "../../../../../Modal"; +import MatrixClientPeg from "../../../../../MatrixClientPeg"; const sdk = require("../../../../.."); @@ -30,6 +31,7 @@ export default class MjolnirUserSettingsTab extends React.Component { this.state = { busy: false, newPersonalRule: "", + newList: "", }; } @@ -37,6 +39,10 @@ export default class MjolnirUserSettingsTab extends React.Component { this.setState({newPersonalRule: e.target.value}); }; + _onNewListChanged = (e) => { + this.setState({newList: e.target.value}); + }; + _onAddPersonalRule = async (e) => { e.preventDefault(); e.stopPropagation(); @@ -55,8 +61,8 @@ export default class MjolnirUserSettingsTab extends React.Component { console.error(e); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to remove Mjolnir rule', '', ErrorDialog, { - title: _t('Error removing ignored user/server'), + Modal.createTrackedDialog('Failed to add Mjolnir rule', '', ErrorDialog, { + title: _t('Error adding ignored user/server'), description: _t('Something went wrong. Please try again or view your console for hints.'), }); } finally { @@ -64,6 +70,28 @@ export default class MjolnirUserSettingsTab extends React.Component { } }; + _onSubscribeList = async (e) => { + e.preventDefault(); + e.stopPropagation(); + + this.setState({busy: true}); + try { + const room = await MatrixClientPeg.get().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) { + console.error(e); + + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Failed to subscribe to Mjolnir list', '', ErrorDialog, { + title: _t('Error subscribing to list'), + description: _t('Please verify the room ID or alias and try again.'), + }); + } finally { + this.setState({busy: false}); + } + }; + async _removePersonalRule(rule: ListRule) { this.setState({busy: true}); try { @@ -82,6 +110,28 @@ export default class MjolnirUserSettingsTab extends React.Component { } } + async _unsubscribeFromList(list: BanList) { + this.setState({busy: true}); + try { + await Mjolnir.sharedInstance().unsubscribeFromList(list.roomId); + await MatrixClientPeg.get().leave(list.roomId); + } catch (e) { + console.error(e); + + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Failed to unsubscribe from Mjolnir list', '', ErrorDialog, { + title: _t('Error unsubscribing from list'), + description: _t('Please try again or view your console for hints.'), + }); + } finally { + this.setState({busy: false}); + } + } + + _viewListRules(list: BanList) { + // TODO + } + _renderPersonalBanListRules() { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); @@ -92,9 +142,12 @@ export default class MjolnirUserSettingsTab extends React.Component { const tiles = []; for (const rule of rules) { tiles.push( -
  • - this._removePersonalRule(rule)} - disabled={this.state.busy}> +
  • + this._removePersonalRule(rule)} + disabled={this.state.busy} + > {_t("Remove")}   {rule.entity} @@ -102,9 +155,52 @@ export default class MjolnirUserSettingsTab extends React.Component { ); } - return

    {_t("You are currently ignoring:")}

    - -
    ; + return ( +
    +

    {_t("You are currently ignoring:")}

    + +
    + ); + } + + _renderSubscribedBanLists() { + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + + const personalList = Mjolnir.sharedInstance().getPersonalList(); + const lists = Mjolnir.sharedInstance().lists.filter(b => personalList ? personalList.roomId !== b.roomId : true); + if (!lists || lists.length <= 0) return {_t("You are not subscribed to any lists")}; + + const tiles = []; + for (const list of lists) { + const room = MatrixClientPeg.get().getRoom(list.roomId); + const name = room ? {room.name} ({list.roomId}) : list.roomId; + tiles.push( +
  • + this._unsubscribeFromList(list)} + disabled={this.state.busy} + > + {_t("Unsubscribe")} +   + this._viewListRules(list)} + disabled={this.state.busy} + > + {_t("View rules")} +   + {name} +
  • , + ); + } + + return ( +
    +

    {_t("You are currently subscribed to:")}

    + +
    + ); } render() { @@ -155,8 +251,12 @@ export default class MjolnirUserSettingsTab extends React.Component { value={this.state.newPersonalRule} onChange={this._onPersonalRuleChanged} /> - + {_t("Ignore")} @@ -171,7 +271,28 @@ export default class MjolnirUserSettingsTab extends React.Component { "If this isn't what you want, please use a different tool to ignore users.", )} -

    TODO

    +
    + {this._renderSubscribedBanLists()} +
    +
    +
    + + + {_t("Subscribe")} + + +
    ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index fa15433a1a..561dbc4da9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -641,11 +641,20 @@ "click to reveal": "click to reveal", "Labs": "Labs", "Ignored/Blocked": "Ignored/Blocked", - "Error removing ignored user/server": "Error removing ignored user/server", + "Error adding ignored user/server": "Error adding ignored user/server", "Something went wrong. Please try again or view your console for hints.": "Something went wrong. Please try again or view your console for hints.", + "Error subscribing to list": "Error subscribing to list", + "Please verify the room ID or alias and try again.": "Please verify the room ID or alias and try again.", + "Error removing ignored user/server": "Error removing ignored user/server", + "Error unsubscribing from list": "Error unsubscribing from list", + "Please try again or view your console for hints.": "Please try again or view your console for hints.", "You have not ignored anyone.": "You have not ignored anyone.", "Remove": "Remove", "You are currently ignoring:": "You are currently ignoring:", + "You are not subscribed to any lists": "You are not subscribed to any lists", + "Unsubscribe": "Unsubscribe", + "View rules": "View rules", + "You are currently subscribed to:": "You are currently subscribed to:", "Ignored users": "Ignored users", "⚠ These settings are meant for advanced users.": "⚠ These settings are meant for advanced users.", "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.": "Add users and servers you want to ignore here. Use asterisks to have Riot match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.", @@ -658,6 +667,8 @@ "Subscribed lists": "Subscribed lists", "Subscribing to a ban list will cause you to join it!": "Subscribing to a ban list will cause you to join it!", "If this isn't what you want, please use a different tool to ignore users.": "If this isn't what you want, please use a different tool to ignore users.", + "Room ID or alias of ban list": "Room ID or alias of ban list", + "Subscribe": "Subscribe", "Notifications": "Notifications", "Start automatically after system login": "Start automatically after system login", "Always show the window menu bar": "Always show the window menu bar", diff --git a/src/mjolnir/Mjolnir.js b/src/mjolnir/Mjolnir.js index d90ea9cd04..5edfe3750e 100644 --- a/src/mjolnir/Mjolnir.js +++ b/src/mjolnir/Mjolnir.js @@ -33,6 +33,14 @@ export class Mjolnir { constructor() { } + get roomIds(): string[] { + return this._roomIds; + } + + get lists(): BanList[] { + return this._lists; + } + start() { this._mjolnirWatchRef = SettingsStore.watchSetting("mjolnirRooms", null, this._onListsChanged.bind(this)); @@ -101,6 +109,18 @@ export class Mjolnir { return list; } + async subscribeToList(roomId: string) { + const roomIds = [...this._roomIds, roomId]; + await SettingsStore.setValue("mjolnirRooms", null, SettingLevel.ACCOUNT, roomIds); + this._lists.push(new BanList(roomId)); + } + + async unsubscribeFromList(roomId: string) { + const roomIds = this._roomIds.filter(r => r !== roomId); + await SettingsStore.setValue("mjolnirRooms", null, SettingLevel.ACCOUNT, roomIds); + this._lists = this._lists.filter(b => b.roomId !== roomId); + } + _onEvent(event) { if (!MatrixClientPeg.get()) return; if (!this._roomIds.includes(event.getRoomId())) return;