diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx index a50ac2bb94..ac7eea8e2b 100644 --- a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx +++ b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx @@ -30,6 +30,7 @@ import Modal from "../../../../Modal"; import InteractiveAuthDialog from "../InteractiveAuthDialog"; import DialogButtons from "../../elements/DialogButtons"; import BaseDialog from "../BaseDialog"; +import { chromeFileInputFix } from "../../../../utils/BrowserWorkarounds"; // Maximum acceptable size of a key file. It's 59 characters including the spaces we encode, // so this should be plenty and allow for people putting extra whitespace in the file because @@ -403,6 +404,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent diff --git a/src/components/views/elements/MiniAvatarUploader.tsx b/src/components/views/elements/MiniAvatarUploader.tsx index a590427d23..43e66db09c 100644 --- a/src/components/views/elements/MiniAvatarUploader.tsx +++ b/src/components/views/elements/MiniAvatarUploader.tsx @@ -25,6 +25,7 @@ import { useTimeout } from "../../../hooks/useTimeout"; import Analytics from "../../../Analytics"; import { TranslatedString } from '../../../languageHandler'; import RoomContext from "../../../contexts/RoomContext"; +import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; export const AVATAR_SIZE = 52; @@ -62,6 +63,7 @@ const MiniAvatarUploader: React.FC = ({ hasAvatar, hasAvatarLabel, noAva type="file" ref={uploadRef} className="mx_MiniAvatarUploader_input" + onClick={chromeFileInputFix} onChange={async (ev) => { if (!ev.target.files?.length) return; setBusy(true); diff --git a/src/components/views/room_settings/RoomProfileSettings.tsx b/src/components/views/room_settings/RoomProfileSettings.tsx index aff117681c..85f5ab7600 100644 --- a/src/components/views/room_settings/RoomProfileSettings.tsx +++ b/src/components/views/room_settings/RoomProfileSettings.tsx @@ -22,6 +22,7 @@ import Field from "../elements/Field"; import { mediaFromMxc } from "../../../customisations/Media"; import AccessibleButton from "../elements/AccessibleButton"; import AvatarSetting from "../settings/AvatarSetting"; +import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; interface IProps { roomId: string; @@ -252,6 +253,7 @@ export default class RoomProfileSettings extends React.Component type="file" ref={this.avatarUpload} className="mx_ProfileSettings_avatarUpload" + onClick={chromeFileInputFix} onChange={this.onAvatarChanged} accept="image/*" /> diff --git a/src/components/views/rooms/MessageComposerButtons.tsx b/src/components/views/rooms/MessageComposerButtons.tsx index fc7dd1ed84..17c87b15ca 100644 --- a/src/components/views/rooms/MessageComposerButtons.tsx +++ b/src/components/views/rooms/MessageComposerButtons.tsx @@ -37,6 +37,7 @@ import ContentMessages from '../../../ContentMessages'; import MatrixClientContext from '../../../contexts/MatrixClientContext'; import RoomContext from '../../../contexts/RoomContext'; import { useDispatcher } from "../../../hooks/useDispatcher"; +import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; interface IProps { addEmoji: (emoji: string) => boolean; @@ -236,6 +237,7 @@ const UploadButtonContextProvider: React.FC = ({ roomId, rel type="file" style={uploadInputStyle} multiple + onClick={chromeFileInputFix} onChange={onUploadFileInputChange} /> ; diff --git a/src/components/views/settings/ChangeAvatar.tsx b/src/components/views/settings/ChangeAvatar.tsx index e56de0b204..b0645ac51b 100644 --- a/src/components/views/settings/ChangeAvatar.tsx +++ b/src/components/views/settings/ChangeAvatar.tsx @@ -26,6 +26,7 @@ import Spinner from '../elements/Spinner'; import { mediaFromMxc } from "../../../customisations/Media"; import RoomAvatar from '../avatars/RoomAvatar'; import BaseAvatar from '../avatars/BaseAvatar'; +import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; interface IProps { initialAvatarUrl?: string; @@ -182,7 +183,7 @@ export default class ChangeAvatar extends React.Component { uploadSection = (
{ _t("Upload new:") } - + { this.state.errorText }
); diff --git a/src/components/views/settings/ProfileSettings.tsx b/src/components/views/settings/ProfileSettings.tsx index 9db1d49d83..94d8343999 100644 --- a/src/components/views/settings/ProfileSettings.tsx +++ b/src/components/views/settings/ProfileSettings.tsx @@ -29,6 +29,7 @@ import AccessibleButton from '../elements/AccessibleButton'; import AvatarSetting from './AvatarSetting'; import ExternalLink from '../elements/ExternalLink'; import UserIdentifierCustomisations from '../../../customisations/UserIdentifier'; +import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; interface IState { userId?: string; @@ -188,6 +189,7 @@ export default class ProfileSettings extends React.Component<{}, IState> { type="file" ref={this.avatarUpload} className="mx_ProfileSettings_avatarUpload" + onClick={chromeFileInputFix} onChange={this.onAvatarChanged} accept="image/*" /> diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx b/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx index b13277e744..3467660ff7 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx @@ -31,6 +31,7 @@ import { RoomNotifState } from '../../../../../RoomNotifs'; import defaultDispatcher from "../../../../../dispatcher/dispatcher"; import { Action } from "../../../../../dispatcher/actions"; import { UserTab } from "../../../dialogs/UserTab"; +import { chromeFileInputFix } from "../../../../../utils/BrowserWorkarounds"; interface IProps { roomId: string; @@ -77,7 +78,7 @@ export default class NotificationsSettingsTab extends React.Component): Promise => { + private onSoundUploadChanged = (e: React.ChangeEvent): void => { if (!e.target.files || !e.target.files.length) { this.setState({ uploadedFile: null, @@ -254,7 +255,14 @@ export default class NotificationsSettingsTab extends React.Component{ _t("Set a new custom sound") }
- +
{ currentUploadedFile } diff --git a/src/components/views/spaces/SpaceBasicSettings.tsx b/src/components/views/spaces/SpaceBasicSettings.tsx index 4f305edd8b..14e4d5d563 100644 --- a/src/components/views/spaces/SpaceBasicSettings.tsx +++ b/src/components/views/spaces/SpaceBasicSettings.tsx @@ -19,6 +19,7 @@ import React, { useRef, useState } from "react"; import { _t } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; import Field from "../elements/Field"; +import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; interface IProps { avatarUrl?: string; @@ -89,6 +90,7 @@ export const SpaceAvatar = ({ { if (!e.target.files?.length) return; const file = e.target.files[0]; diff --git a/src/utils/BrowserWorkarounds.ts b/src/utils/BrowserWorkarounds.ts new file mode 100644 index 0000000000..ea8ea2a04f --- /dev/null +++ b/src/utils/BrowserWorkarounds.ts @@ -0,0 +1,24 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { MouseEvent } from "react"; + +export function chromeFileInputFix(event: MouseEvent): void { + // Workaround for Chromium Bug + // Chrome does not fire onChange events if the same file is selected twice + // Only required on Chromium-based browsers (Electron, Chrome, Edge, Opera, Vivaldi, etc) + event.currentTarget.value = ''; +}