/* 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, { createRef } from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../../../languageHandler"; import AccessibleButton, { ButtonEvent } from "../../../elements/AccessibleButton"; import Notifier from "../../../../../Notifier"; import SettingsStore from "../../../../../settings/SettingsStore"; import { SettingLevel } from "../../../../../settings/SettingLevel"; import { RoomEchoChamber } from "../../../../../stores/local-echo/RoomEchoChamber"; import { EchoChamber } from "../../../../../stores/local-echo/EchoChamber"; import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; import StyledRadioGroup from "../../../elements/StyledRadioGroup"; import { RoomNotifState } from "../../../../../RoomNotifs"; import defaultDispatcher from "../../../../../dispatcher/dispatcher"; import { Action } from "../../../../../dispatcher/actions"; import { UserTab } from "../../../dialogs/UserTab"; import { chromeFileInputFix } from "../../../../../utils/BrowserWorkarounds"; import SettingsTab from "../SettingsTab"; import { SettingsSection } from "../../shared/SettingsSection"; import { SettingsSubsection } from "../../shared/SettingsSubsection"; interface IProps { roomId: string; closeSettingsFn(): void; } interface IState { currentSound: string; uploadedFile: File | null; } export default class NotificationsSettingsTab extends React.Component { private readonly roomProps: RoomEchoChamber; private soundUpload = createRef(); public static contextType = MatrixClientContext; public declare context: React.ContextType; public constructor(props: IProps, context: React.ContextType) { super(props, context); this.roomProps = EchoChamber.forRoom(context.getRoom(this.props.roomId)!); let currentSound = "default"; const soundData = Notifier.getSoundForRoom(this.props.roomId); if (soundData) { currentSound = soundData.name || soundData.url; } this.state = { currentSound, uploadedFile: null, }; } private triggerUploader = async (e: ButtonEvent): Promise => { e.stopPropagation(); e.preventDefault(); this.soundUpload.current?.click(); }; private onSoundUploadChanged = (e: React.ChangeEvent): void => { if (!e.target.files || !e.target.files.length) { this.setState({ uploadedFile: null, }); return; } const file = e.target.files[0]; this.setState({ uploadedFile: file, }); }; private onClickSaveSound = async (e: ButtonEvent): Promise => { e.stopPropagation(); e.preventDefault(); try { await this.saveSound(); } catch (ex) { logger.error(`Unable to save notification sound for ${this.props.roomId}`); logger.error(ex); } }; private async saveSound(): Promise { if (!this.state.uploadedFile) { return; } let type = this.state.uploadedFile.type; if (type === "video/ogg") { // XXX: I've observed browsers allowing users to pick a audio/ogg files, // and then calling it a video/ogg. This is a lame hack, but man browsers // suck at detecting mimetypes. type = "audio/ogg"; } const { content_uri: url } = await this.context.uploadContent(this.state.uploadedFile, { type, }); await SettingsStore.setValue("notificationSound", this.props.roomId, SettingLevel.ROOM_ACCOUNT, { name: this.state.uploadedFile.name, type: type, size: this.state.uploadedFile.size, url, }); this.setState({ uploadedFile: null, currentSound: this.state.uploadedFile.name, }); } private clearSound = (e: ButtonEvent): void => { e.stopPropagation(); e.preventDefault(); SettingsStore.setValue("notificationSound", this.props.roomId, SettingLevel.ROOM_ACCOUNT, null); this.setState({ currentSound: "default", }); }; private onRoomNotificationChange = (value: RoomNotifState): void => { this.roomProps.notificationVolume = value; this.forceUpdate(); }; private onOpenSettingsClick = (event: ButtonEvent): void => { // avoid selecting the radio button event.preventDefault(); this.props.closeSettingsFn(); defaultDispatcher.dispatch({ action: Action.ViewUserSettings, initialTabId: UserTab.Notifications, }); }; public render(): React.ReactNode { let currentUploadedFile: JSX.Element | undefined; if (this.state.uploadedFile) { currentUploadedFile = (
{_t("room_settings|notifications|uploaded_sound")}: {this.state.uploadedFile.name}
); } return (
{_t("notifications|default")}
{_t( "room_settings|notifications|settings_link", {}, { a: (sub) => ( {sub} ), }, )}
), }, { value: RoomNotifState.AllMessagesLoud, className: "mx_NotificationSettingsTab_allMessagesEntry", label: ( <> {_t("notifications|all_messages")}
{_t("notifications|all_messages_description")}
), }, { value: RoomNotifState.MentionsOnly, className: "mx_NotificationSettingsTab_mentionsKeywordsEntry", label: ( <> {_t("notifications|mentions_and_keywords")}
{_t( "notifications|mentions_and_keywords_description", {}, { a: (sub) => ( {sub} ), }, )}
), }, { value: RoomNotifState.Mute, className: "mx_NotificationSettingsTab_noneEntry", label: ( <> {_t("common|off")}
{_t("notifications|mute_description")}
), }, ]} onChange={this.onRoomNotificationChange} value={this.roomProps.notificationVolume} />
{_t("room_settings|notifications|notification_sound")}:{" "} {this.state.currentSound}
{_t("action|reset")}

{_t("room_settings|notifications|custom_sound_prompt")}

{currentUploadedFile}
{_t("room_settings|notifications|browse_button")} {_t("action|save")}
); } }