diff --git a/src/components/views/rooms/RoomBreadcrumbs.js b/src/components/views/rooms/RoomBreadcrumbs.js index 19420684c4..0a50c8d1f8 100644 --- a/src/components/views/rooms/RoomBreadcrumbs.js +++ b/src/components/views/rooms/RoomBreadcrumbs.js @@ -17,6 +17,7 @@ limitations under the License. import React from "react"; import dis from "../../../dispatcher"; import MatrixClientPeg from "../../../MatrixClientPeg"; +import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import RoomAvatar from '../avatars/RoomAvatar'; import classNames from 'classnames'; @@ -39,22 +40,22 @@ export default class RoomBreadcrumbs extends React.Component { componentWillMount() { this._dispatcherRef = dis.register(this.onAction); - const roomStr = localStorage.getItem("mx_breadcrumb_rooms"); - if (roomStr) { - try { - const roomIds = JSON.parse(roomStr); - this.setState({ - rooms: roomIds.map((r) => { - return { - room: MatrixClientPeg.get().getRoom(r), - animated: false, - }; - }).filter((r) => r.room), - }); - } catch (e) { - console.error("Failed to parse breadcrumbs:", e); + let storedRooms = SettingsStore.getValue("breadcrumb_rooms"); + if (!storedRooms || !storedRooms.length) { + // Fallback to the rooms stored in localstorage for those who would have had this. + // TODO: Remove this after a bit - the feature was only on develop, so a few weeks should be plenty time. + const roomStr = localStorage.getItem("mx_breadcrumb_rooms"); + if (roomStr) { + try { + storedRooms = JSON.parse(roomStr); + } catch (e) { + console.error("Failed to parse breadcrumbs:", e); + } } } + this._loadRoomIds(storedRooms || []); + + this._settingWatchRef = SettingsStore.watchSetting("breadcrumb_rooms", null, this.onBreadcrumbsChanged); MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership); MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); @@ -65,6 +66,8 @@ export default class RoomBreadcrumbs extends React.Component { componentWillUnmount() { dis.unregister(this._dispatcherRef); + SettingsStore.unwatchSetting(this._settingWatchRef); + const client = MatrixClientPeg.get(); if (client) { client.removeListener("Room.myMembership", this.onMyMembership); @@ -85,8 +88,10 @@ export default class RoomBreadcrumbs extends React.Component { } } - const roomStr = JSON.stringify(rooms.map((r) => r.room.roomId)); - localStorage.setItem("mx_breadcrumb_rooms", roomStr); + const roomIds = rooms.map((r) => r.room.roomId); + if (roomIds.length > 0) { + SettingsStore.setValue("breadcrumb_rooms", null, SettingLevel.ACCOUNT, roomIds); + } } onAction(payload) { @@ -126,17 +131,50 @@ export default class RoomBreadcrumbs extends React.Component { } }; - _calculateRoomBadges(room) { - if (!room) return; + onBreadcrumbsChanged = (settingName, roomId, level, valueAtLevel, value) => { + if (!value) return; - const rooms = this.state.rooms.slice(); - const roomModel = rooms.find((r) => r.room.roomId === room.roomId); - if (!roomModel) return; // No applicable room, so don't do math on it + const currentState = this.state.rooms.map((r) => r.room.roomId); + if (currentState.length === value.length) { + let changed = false; + for (let i = 0; i < currentState.length; i++) { + if (currentState[i] !== value[i]) { + changed = true; + break; + } + } + if (!changed) return; + } + + this._loadRoomIds(value); + }; + + _loadRoomIds(roomIds) { + if (!roomIds || roomIds.length <= 0) return; // Skip updates with no rooms + + // If we're here, the list changed. + const rooms = roomIds.map((r) => MatrixClientPeg.get().getRoom(r)).filter((r) => r).map((r) => { + const badges = this._calculateBadgesForRoom(r) || {}; + return { + room: r, + animated: false, + ...badges, + }; + }); + this.setState({ + rooms: rooms, + }); + } + + _calculateBadgesForRoom(room) { + if (!room) return null; // Reset the notification variables for simplicity - roomModel.redBadge = false; - roomModel.formattedCount = "0"; - roomModel.showCount = false; + const roomModel = { + redBadge: false, + formattedCount: "0", + showCount: false, + }; const notifState = RoomNotifs.getRoomNotifsState(room.roomId); if (RoomNotifs.MENTION_BADGE_STATES.includes(notifState)) { @@ -156,6 +194,20 @@ export default class RoomBreadcrumbs extends React.Component { } } + return roomModel; + } + + _calculateRoomBadges(room) { + if (!room) return; + + const rooms = this.state.rooms.slice(); + const roomModel = rooms.find((r) => r.room.roomId === room.roomId); + if (!roomModel) return; // No applicable room, so don't do math on it + + const badges = this._calculateBadgesForRoom(room); + if (!badges) return; // No badges for some reason + + Object.assign(roomModel, badges); this.setState({rooms}); } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 6e17ffbbd7..098ef2c17b 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -258,6 +258,10 @@ export const SETTINGS = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: "en", }, + "breadcrumb_rooms": { + supportedLevels: ['account'], + default: [], + }, "analyticsOptIn": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, displayName: _td('Send analytics data'), diff --git a/src/settings/handlers/AccountSettingsHandler.js b/src/settings/handlers/AccountSettingsHandler.js index 675b2f8ead..71cef52c4e 100644 --- a/src/settings/handlers/AccountSettingsHandler.js +++ b/src/settings/handlers/AccountSettingsHandler.js @@ -19,6 +19,8 @@ import MatrixClientPeg from '../../MatrixClientPeg'; import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler"; import {SettingLevel} from "../SettingsStore"; +const BREADCRUMBS_EVENT_TYPE = "im.vector.riot.breadcrumb_rooms"; + /** * Gets and sets settings at the "account" level for the current user. * This handler does not make use of the roomId parameter. @@ -55,6 +57,9 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa const val = event.getContent()[settingName]; this._watchers.notifyUpdate(settingName, null, SettingLevel.ACCOUNT, val); } + } else if (event.getType() === BREADCRUMBS_EVENT_TYPE) { + const val = event.getContent()['rooms'] || []; + this._watchers.notifyUpdate("breadcrumb_rooms", null, SettingLevel.ACCOUNT, val); } } @@ -68,6 +73,12 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa return !content['disable']; } + // Special case for breadcrumbs + if (settingName === "breadcrumb_rooms") { + const content = this._getSettings(BREADCRUMBS_EVENT_TYPE) || {}; + return content['rooms'] || []; + } + const settings = this._getSettings() || {}; let preferredValue = settings[settingName]; @@ -89,6 +100,13 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa return MatrixClientPeg.get().setAccountData("org.matrix.preview_urls", content); } + // Special case for breadcrumbs + if (settingName === "breadcrumb_rooms") { + const content = this._getSettings(BREADCRUMBS_EVENT_TYPE) || {}; + content['rooms'] = newValue; + return MatrixClientPeg.get().setAccountData(BREADCRUMBS_EVENT_TYPE, content); + } + const content = this._getSettings() || {}; content[settingName] = newValue; return MatrixClientPeg.get().setAccountData("im.vector.web.settings", content);