mirror of
https://github.com/element-hq/element-web
synced 2024-11-27 19:56:47 +03:00
Fix: Avatar preview does not update when same file is selected repeatedly (#8288)
* Fix: Avatar preview does not update when same file is selected repeatedly Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
7600182a35
commit
dbcb56f75e
9 changed files with 48 additions and 3 deletions
|
@ -30,6 +30,7 @@ import Modal from "../../../../Modal";
|
||||||
import InteractiveAuthDialog from "../InteractiveAuthDialog";
|
import InteractiveAuthDialog from "../InteractiveAuthDialog";
|
||||||
import DialogButtons from "../../elements/DialogButtons";
|
import DialogButtons from "../../elements/DialogButtons";
|
||||||
import BaseDialog from "../BaseDialog";
|
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,
|
// 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
|
// 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<IProp
|
||||||
<input type="file"
|
<input type="file"
|
||||||
className="mx_AccessSecretStorageDialog_recoveryKeyEntry_fileInput"
|
className="mx_AccessSecretStorageDialog_recoveryKeyEntry_fileInput"
|
||||||
ref={this.fileUpload}
|
ref={this.fileUpload}
|
||||||
|
onClick={chromeFileInputFix}
|
||||||
onChange={this.onRecoveryKeyFileChange}
|
onChange={this.onRecoveryKeyFileChange}
|
||||||
/>
|
/>
|
||||||
<AccessibleButton kind="primary" onClick={this.onRecoveryKeyFileUploadClick}>
|
<AccessibleButton kind="primary" onClick={this.onRecoveryKeyFileUploadClick}>
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { useTimeout } from "../../../hooks/useTimeout";
|
||||||
import Analytics from "../../../Analytics";
|
import Analytics from "../../../Analytics";
|
||||||
import { TranslatedString } from '../../../languageHandler';
|
import { TranslatedString } from '../../../languageHandler';
|
||||||
import RoomContext from "../../../contexts/RoomContext";
|
import RoomContext from "../../../contexts/RoomContext";
|
||||||
|
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
|
||||||
|
|
||||||
export const AVATAR_SIZE = 52;
|
export const AVATAR_SIZE = 52;
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ const MiniAvatarUploader: React.FC<IProps> = ({ hasAvatar, hasAvatarLabel, noAva
|
||||||
type="file"
|
type="file"
|
||||||
ref={uploadRef}
|
ref={uploadRef}
|
||||||
className="mx_MiniAvatarUploader_input"
|
className="mx_MiniAvatarUploader_input"
|
||||||
|
onClick={chromeFileInputFix}
|
||||||
onChange={async (ev) => {
|
onChange={async (ev) => {
|
||||||
if (!ev.target.files?.length) return;
|
if (!ev.target.files?.length) return;
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import Field from "../elements/Field";
|
||||||
import { mediaFromMxc } from "../../../customisations/Media";
|
import { mediaFromMxc } from "../../../customisations/Media";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import AvatarSetting from "../settings/AvatarSetting";
|
import AvatarSetting from "../settings/AvatarSetting";
|
||||||
|
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
@ -252,6 +253,7 @@ export default class RoomProfileSettings extends React.Component<IProps, IState>
|
||||||
type="file"
|
type="file"
|
||||||
ref={this.avatarUpload}
|
ref={this.avatarUpload}
|
||||||
className="mx_ProfileSettings_avatarUpload"
|
className="mx_ProfileSettings_avatarUpload"
|
||||||
|
onClick={chromeFileInputFix}
|
||||||
onChange={this.onAvatarChanged}
|
onChange={this.onAvatarChanged}
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -37,6 +37,7 @@ import ContentMessages from '../../../ContentMessages';
|
||||||
import MatrixClientContext from '../../../contexts/MatrixClientContext';
|
import MatrixClientContext from '../../../contexts/MatrixClientContext';
|
||||||
import RoomContext from '../../../contexts/RoomContext';
|
import RoomContext from '../../../contexts/RoomContext';
|
||||||
import { useDispatcher } from "../../../hooks/useDispatcher";
|
import { useDispatcher } from "../../../hooks/useDispatcher";
|
||||||
|
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
addEmoji: (emoji: string) => boolean;
|
addEmoji: (emoji: string) => boolean;
|
||||||
|
@ -236,6 +237,7 @@ const UploadButtonContextProvider: React.FC<IUploadButtonProps> = ({ roomId, rel
|
||||||
type="file"
|
type="file"
|
||||||
style={uploadInputStyle}
|
style={uploadInputStyle}
|
||||||
multiple
|
multiple
|
||||||
|
onClick={chromeFileInputFix}
|
||||||
onChange={onUploadFileInputChange}
|
onChange={onUploadFileInputChange}
|
||||||
/>
|
/>
|
||||||
</UploadButtonContext.Provider>;
|
</UploadButtonContext.Provider>;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import Spinner from '../elements/Spinner';
|
||||||
import { mediaFromMxc } from "../../../customisations/Media";
|
import { mediaFromMxc } from "../../../customisations/Media";
|
||||||
import RoomAvatar from '../avatars/RoomAvatar';
|
import RoomAvatar from '../avatars/RoomAvatar';
|
||||||
import BaseAvatar from '../avatars/BaseAvatar';
|
import BaseAvatar from '../avatars/BaseAvatar';
|
||||||
|
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
initialAvatarUrl?: string;
|
initialAvatarUrl?: string;
|
||||||
|
@ -182,7 +183,7 @@ export default class ChangeAvatar extends React.Component<IProps, IState> {
|
||||||
uploadSection = (
|
uploadSection = (
|
||||||
<div className={this.props.className}>
|
<div className={this.props.className}>
|
||||||
{ _t("Upload new:") }
|
{ _t("Upload new:") }
|
||||||
<input type="file" accept="image/*" onChange={this.onFileSelected} />
|
<input type="file" accept="image/*" onClick={chromeFileInputFix} onChange={this.onFileSelected} />
|
||||||
{ this.state.errorText }
|
{ this.state.errorText }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -29,6 +29,7 @@ import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import AvatarSetting from './AvatarSetting';
|
import AvatarSetting from './AvatarSetting';
|
||||||
import ExternalLink from '../elements/ExternalLink';
|
import ExternalLink from '../elements/ExternalLink';
|
||||||
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
|
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
|
||||||
|
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
@ -188,6 +189,7 @@ export default class ProfileSettings extends React.Component<{}, IState> {
|
||||||
type="file"
|
type="file"
|
||||||
ref={this.avatarUpload}
|
ref={this.avatarUpload}
|
||||||
className="mx_ProfileSettings_avatarUpload"
|
className="mx_ProfileSettings_avatarUpload"
|
||||||
|
onClick={chromeFileInputFix}
|
||||||
onChange={this.onAvatarChanged}
|
onChange={this.onAvatarChanged}
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { RoomNotifState } from '../../../../../RoomNotifs';
|
||||||
import defaultDispatcher from "../../../../../dispatcher/dispatcher";
|
import defaultDispatcher from "../../../../../dispatcher/dispatcher";
|
||||||
import { Action } from "../../../../../dispatcher/actions";
|
import { Action } from "../../../../../dispatcher/actions";
|
||||||
import { UserTab } from "../../../dialogs/UserTab";
|
import { UserTab } from "../../../dialogs/UserTab";
|
||||||
|
import { chromeFileInputFix } from "../../../../../utils/BrowserWorkarounds";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
@ -77,7 +78,7 @@ export default class NotificationsSettingsTab extends React.Component<IProps, IS
|
||||||
this.soundUpload.current.click();
|
this.soundUpload.current.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSoundUploadChanged = (e: React.ChangeEvent<HTMLInputElement>): Promise<void> => {
|
private onSoundUploadChanged = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
if (!e.target.files || !e.target.files.length) {
|
if (!e.target.files || !e.target.files.length) {
|
||||||
this.setState({
|
this.setState({
|
||||||
uploadedFile: null,
|
uploadedFile: null,
|
||||||
|
@ -254,7 +255,14 @@ export default class NotificationsSettingsTab extends React.Component<IProps, IS
|
||||||
<h3>{ _t("Set a new custom sound") }</h3>
|
<h3>{ _t("Set a new custom sound") }</h3>
|
||||||
<div className="mx_SettingsFlag">
|
<div className="mx_SettingsFlag">
|
||||||
<form autoComplete="off" noValidate={true}>
|
<form autoComplete="off" noValidate={true}>
|
||||||
<input ref={this.soundUpload} className="mx_NotificationSound_soundUpload" type="file" onChange={this.onSoundUploadChanged} accept="audio/*" />
|
<input
|
||||||
|
ref={this.soundUpload}
|
||||||
|
className="mx_NotificationSound_soundUpload"
|
||||||
|
type="file"
|
||||||
|
onClick={chromeFileInputFix}
|
||||||
|
onChange={this.onSoundUploadChanged}
|
||||||
|
accept="audio/*"
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{ currentUploadedFile }
|
{ currentUploadedFile }
|
||||||
|
|
|
@ -19,6 +19,7 @@ import React, { useRef, useState } from "react";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
|
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
avatarUrl?: string;
|
avatarUrl?: string;
|
||||||
|
@ -89,6 +90,7 @@ export const SpaceAvatar = ({
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
ref={avatarUploadRef}
|
ref={avatarUploadRef}
|
||||||
|
onClick={chromeFileInputFix}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (!e.target.files?.length) return;
|
if (!e.target.files?.length) return;
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
|
|
24
src/utils/BrowserWorkarounds.ts
Normal file
24
src/utils/BrowserWorkarounds.ts
Normal file
|
@ -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<HTMLInputElement>): 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 = '';
|
||||||
|
}
|
Loading…
Reference in a new issue