Fix progress bar regression throughout the app (#9219)

* Fix useSmoothAnimation enablement not working properly by getting rid of it

Passing duration=0 is more logical and less superfluous

* Refactor UploadBar to handle state more correctly

* Change ProgressBar to new useSmoothAnimation signature and default animated to true for consistency

* Add type guard

* Make types stricter

* Write tests for the ProgressBar component

* Make the new test conform to tsc --strict

* Update UploadBar.tsx

* Update UploadBar.tsx

* Update UploadBar.tsx
This commit is contained in:
Michael Telatynski 2022-08-25 16:39:00 +01:00 committed by GitHub
parent 6407cd4c0d
commit 9b99c967f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 37 deletions

View file

@ -17,17 +17,19 @@ limitations under the License.
import React from 'react';
import { Room } from "matrix-js-sdk/src/models/room";
import filesize from "filesize";
import { IEventRelation } from 'matrix-js-sdk/src/matrix';
import { IAbortablePromise, IEventRelation } from 'matrix-js-sdk/src/matrix';
import { Optional } from "matrix-events-sdk";
import ContentMessages from '../../ContentMessages';
import dis from "../../dispatcher/dispatcher";
import { _t } from '../../languageHandler';
import { ActionPayload } from "../../dispatcher/payloads";
import { Action } from "../../dispatcher/actions";
import ProgressBar from "../views/elements/ProgressBar";
import AccessibleButton from "../views/elements/AccessibleButton";
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
import { IUpload } from "../../models/IUpload";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { ActionPayload } from '../../dispatcher/payloads';
import { UploadPayload } from "../../dispatcher/payloads/UploadPayload";
interface IProps {
room: Room;
@ -35,23 +37,35 @@ interface IProps {
}
interface IState {
currentUpload?: IUpload;
uploadsHere: IUpload[];
currentFile?: string;
currentPromise?: IAbortablePromise<any>;
currentLoaded?: number;
currentTotal?: number;
countFiles: number;
}
export default class UploadBar extends React.Component<IProps, IState> {
function isUploadPayload(payload: ActionPayload): payload is UploadPayload {
return [
Action.UploadStarted,
Action.UploadProgress,
Action.UploadFailed,
Action.UploadFinished,
Action.UploadCanceled,
].includes(payload.action as Action);
}
export default class UploadBar extends React.PureComponent<IProps, IState> {
static contextType = MatrixClientContext;
private dispatcherRef: string;
private mounted: boolean;
private dispatcherRef: Optional<string>;
private mounted = false;
constructor(props) {
super(props);
// Set initial state to any available upload in this room - we might be mounting
// earlier than the first progress event, so should show something relevant.
const uploadsHere = this.getUploadsInRoom();
this.state = { currentUpload: uploadsHere[0], uploadsHere };
this.state = this.calculateState();
}
componentDidMount() {
@ -61,7 +75,7 @@ export default class UploadBar extends React.Component<IProps, IState> {
componentWillUnmount() {
this.mounted = false;
dis.unregister(this.dispatcherRef);
dis.unregister(this.dispatcherRef!);
}
private getUploadsInRoom(): IUpload[] {
@ -69,45 +83,48 @@ export default class UploadBar extends React.Component<IProps, IState> {
return uploads.filter(u => u.roomId === this.props.room.roomId);
}
private calculateState(): IState {
const [currentUpload, ...otherUploads] = this.getUploadsInRoom();
return {
currentFile: currentUpload?.fileName,
currentPromise: currentUpload?.promise,
currentLoaded: currentUpload?.loaded,
currentTotal: currentUpload?.total,
countFiles: otherUploads.length + 1,
};
}
private onAction = (payload: ActionPayload) => {
switch (payload.action) {
case Action.UploadStarted:
case Action.UploadProgress:
case Action.UploadFinished:
case Action.UploadCanceled:
case Action.UploadFailed: {
if (!this.mounted) return;
const uploadsHere = this.getUploadsInRoom();
this.setState({ currentUpload: uploadsHere[0], uploadsHere });
break;
}
if (!this.mounted) return;
if (isUploadPayload(payload)) {
this.setState(this.calculateState());
}
};
private onCancelClick = (ev) => {
private onCancelClick = (ev: ButtonEvent) => {
ev.preventDefault();
ContentMessages.sharedInstance().cancelUpload(this.state.currentUpload.promise, this.context);
ContentMessages.sharedInstance().cancelUpload(this.state.currentPromise!, this.context);
};
render() {
if (!this.state.currentUpload) {
if (!this.state.currentFile) {
return null;
}
// MUST use var name 'count' for pluralization to kick in
const uploadText = _t(
"Uploading %(filename)s and %(count)s others", {
filename: this.state.currentUpload.fileName,
count: this.state.uploadsHere.length - 1,
filename: this.state.currentFile,
count: this.state.countFiles - 1,
},
);
const uploadSize = filesize(this.state.currentUpload.total);
const uploadSize = filesize(this.state.currentTotal!);
return (
<div className="mx_UploadBar">
<div className="mx_UploadBar_filename">{ uploadText } ({ uploadSize })</div>
<AccessibleButton onClick={this.onCancelClick} className='mx_UploadBar_cancel' />
<ProgressBar value={this.state.currentUpload.loaded} max={this.state.currentUpload.total} />
<ProgressBar value={this.state.currentLoaded!} max={this.state.currentTotal!} />
</div>
);
}