/* Copyright 2024 New Vector Ltd. Copyright 2020, 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, { lazy } from "react"; import { _t } from "../../../languageHandler"; import SdkConfig from "../../../SdkConfig"; import Modal from "../../../Modal"; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; import { formatBytes, formatCountLong } from "../../../utils/FormattingUtils"; import EventIndexPeg from "../../../indexing/EventIndexPeg"; import { SettingLevel } from "../../../settings/SettingLevel"; import SeshatResetDialog from "../dialogs/SeshatResetDialog"; import InlineSpinner from "../elements/InlineSpinner"; import ExternalLink from "../elements/ExternalLink"; import { SettingsSubsectionText } from "./shared/SettingsSubsection"; interface IState { enabling: boolean; eventIndexSize: number; roomCount: number; eventIndexingEnabled: boolean; } export default class EventIndexPanel extends React.Component<{}, IState> { public constructor(props: {}) { super(props); this.state = { enabling: false, eventIndexSize: 0, roomCount: 0, eventIndexingEnabled: SettingsStore.getValueAt(SettingLevel.DEVICE, "enableEventIndexing"), }; } public updateCurrentRoom = async (): Promise => { const eventIndex = EventIndexPeg.get(); const stats = await eventIndex?.getStats().catch(() => {}); // This call may fail if sporadically, not a huge issue as we will try later again and probably succeed. if (!stats) return; this.setState({ eventIndexSize: stats.size, roomCount: stats.roomCount, }); }; public componentWillUnmount(): void { const eventIndex = EventIndexPeg.get(); if (eventIndex !== null) { eventIndex.removeListener("changedCheckpoint", this.updateCurrentRoom); } } public componentDidMount(): void { this.updateState(); } public async updateState(): Promise { const eventIndex = EventIndexPeg.get(); const eventIndexingEnabled = SettingsStore.getValueAt(SettingLevel.DEVICE, "enableEventIndexing"); const enabling = false; let eventIndexSize = 0; let roomCount = 0; if (eventIndex !== null) { eventIndex.on("changedCheckpoint", this.updateCurrentRoom); const stats = await eventIndex.getStats().catch(() => {}); // This call may fail if sporadically, not a huge issue as we // will try later again in the updateCurrentRoom call and // probably succeed. if (stats) { eventIndexSize = stats.size; roomCount = stats.roomCount; } } this.setState({ enabling, eventIndexSize, roomCount, eventIndexingEnabled, }); } private onManage = async (): Promise => { Modal.createDialog( lazy(() => import("../../../async-components/views/dialogs/eventindex/ManageEventIndexDialog")), { onFinished: () => {}, }, undefined, /* priority = */ false, /* static = */ true, ); }; private onEnable = async (): Promise => { this.setState({ enabling: true, }); await EventIndexPeg.initEventIndex(); await EventIndexPeg.get()?.addInitialCheckpoints(); EventIndexPeg.get()?.startCrawler(); await SettingsStore.setValue("enableEventIndexing", null, SettingLevel.DEVICE, true); await this.updateState(); }; private confirmEventStoreReset = (): void => { const { close } = Modal.createDialog(SeshatResetDialog, { onFinished: async (success): Promise => { if (success) { await SettingsStore.setValue("enableEventIndexing", null, SettingLevel.DEVICE, false); await EventIndexPeg.deleteEventIndex(); await this.onEnable(); close(); } }, }); }; public render(): React.ReactNode { let eventIndexingSettings: JSX.Element | undefined; const brand = SdkConfig.get().brand; if (EventIndexPeg.get() !== null) { eventIndexingSettings = ( <> {_t("settings|security|message_search_enabled", { size: formatBytes(this.state.eventIndexSize, 0), // This drives the singular / plural string // selection for "room" / "rooms" only. count: this.state.roomCount, rooms: formatCountLong(this.state.roomCount), })} {_t("action|manage")} ); } else if (!this.state.eventIndexingEnabled && EventIndexPeg.supportIsInstalled()) { eventIndexingSettings = ( <> {_t("settings|security|message_search_disabled")}
{_t("action|enable")} {this.state.enabling ? :
}
); } else if (EventIndexPeg.platformHasSupport() && !EventIndexPeg.supportIsInstalled()) { const nativeLink = "https://github.com/vector-im/element-desktop/blob/develop/" + "docs/native-node-modules.md#" + "adding-seshat-for-search-in-e2e-encrypted-rooms"; eventIndexingSettings = ( {_t( "settings|security|message_search_unsupported", { brand, }, { nativeLink: (sub) => ( {sub} ), }, )} ); } else if (!EventIndexPeg.platformHasSupport()) { eventIndexingSettings = ( {_t( "settings|security|message_search_unsupported_web", { brand, }, { desktopLink: (sub) => ( {sub} ), }, )} ); } else { eventIndexingSettings = ( <> {this.state.enabling ? : _t("settings|security|message_search_failed")} {EventIndexPeg.error && (
{_t("common|advanced")} {EventIndexPeg.error instanceof Error ? EventIndexPeg.error.message : _t("error|unknown")}

{_t("action|reset")}

)} ); } return eventIndexingSettings; } }