Add loading screen for remote fetch and refactor loading ellipses

This commit is contained in:
SleeplessOne1917 2023-07-21 20:22:16 -04:00
parent f580e2d8b7
commit 792c0e63a9
3 changed files with 63 additions and 28 deletions

View file

@ -0,0 +1,34 @@
import { Component } from "inferno";
interface LoadingEllipsesState {
ellipses: string;
}
export class LoadingEllipses extends Component<any, LoadingEllipsesState> {
state: LoadingEllipsesState = {
ellipses: "...",
};
#interval?: NodeJS.Timer;
constructor(props: any, context: any) {
super(props, context);
}
render() {
return this.state.ellipses;
}
componentDidMount() {
this.#interval = setInterval(this.#updateEllipses, 1000);
}
componentWillUnmount() {
clearInterval(this.#interval);
}
#updateEllipses = () => {
this.setState(({ ellipses }) => ({
ellipses: ellipses.length === 3 ? "" : ellipses + ".",
}));
};
}

View file

@ -9,6 +9,7 @@ import {
} from "inferno"; } from "inferno";
import { I18NextService } from "../../services"; import { I18NextService } from "../../services";
import { Icon, Spinner } from "./icon"; import { Icon, Spinner } from "./icon";
import { LoadingEllipses } from "./loading-ellipses";
interface SearchableSelectProps { interface SearchableSelectProps {
id: string; id: string;
@ -22,7 +23,6 @@ interface SearchableSelectProps {
interface SearchableSelectState { interface SearchableSelectState {
selectedIndex: number; selectedIndex: number;
searchText: string; searchText: string;
loadingEllipses: string;
} }
function handleSearch(i: SearchableSelect, e: ChangeEvent<HTMLInputElement>) { function handleSearch(i: SearchableSelect, e: ChangeEvent<HTMLInputElement>) {
@ -70,12 +70,10 @@ export class SearchableSelect extends Component<
> { > {
searchInputRef: RefObject<HTMLInputElement> = createRef(); searchInputRef: RefObject<HTMLInputElement> = createRef();
toggleButtonRef: RefObject<HTMLButtonElement> = createRef(); toggleButtonRef: RefObject<HTMLButtonElement> = createRef();
private loadingEllipsesInterval?: NodeJS.Timer = undefined;
state: SearchableSelectState = { state: SearchableSelectState = {
selectedIndex: 0, selectedIndex: 0,
searchText: "", searchText: "",
loadingEllipses: "...",
}; };
constructor(props: SearchableSelectProps, context: any) { constructor(props: SearchableSelectProps, context: any) {
@ -99,7 +97,7 @@ export class SearchableSelect extends Component<
render() { render() {
const { id, options, onSearch, loading } = this.props; const { id, options, onSearch, loading } = this.props;
const { searchText, selectedIndex, loadingEllipses } = this.state; const { searchText, selectedIndex } = this.state;
return ( return (
<div className="searchable-select dropdown col-12 col-sm-auto flex-grow-1"> <div className="searchable-select dropdown col-12 col-sm-auto flex-grow-1">
@ -116,9 +114,14 @@ export class SearchableSelect extends Component<
onClick={linkEvent(this, focusSearch)} onClick={linkEvent(this, focusSearch)}
ref={this.toggleButtonRef} ref={this.toggleButtonRef}
> >
{loading {loading ? (
? `${I18NextService.i18n.t("loading")}${loadingEllipses}` <>
: options[selectedIndex].label} {I18NextService.i18n.t("loading")}
<LoadingEllipses />
</>
) : (
options[selectedIndex].label
)}
</button> </button>
<div className="modlog-choices-font-size dropdown-menu w-100 p-2"> <div className="modlog-choices-font-size dropdown-menu w-100 p-2">
<div className="input-group"> <div className="input-group">
@ -180,24 +183,4 @@ export class SearchableSelect extends Component<
selectedIndex, selectedIndex,
}; };
} }
componentDidUpdate() {
const { loading } = this.props;
if (loading && !this.loadingEllipsesInterval) {
this.loadingEllipsesInterval = setInterval(() => {
this.setState(({ loadingEllipses }) => ({
loadingEllipses:
loadingEllipses.length === 3 ? "" : loadingEllipses + ".",
}));
}, 750);
} else if (!loading && this.loadingEllipsesInterval) {
clearInterval(this.loadingEllipsesInterval);
}
}
componentWillUnmount() {
if (this.loadingEllipsesInterval) {
clearInterval(this.loadingEllipsesInterval);
}
}
} }

View file

@ -8,6 +8,8 @@ import { InitialFetchRequest } from "../interfaces";
import { FirstLoadService, HttpService, I18NextService } from "../services"; import { FirstLoadService, HttpService, I18NextService } from "../services";
import { RequestState } from "../services/HttpService"; import { RequestState } from "../services/HttpService";
import { HtmlTags } from "./common/html-tags"; import { HtmlTags } from "./common/html-tags";
import { Spinner } from "./common/icon";
import { LoadingEllipses } from "./common/loading-ellipses";
interface RemoteFetchProps { interface RemoteFetchProps {
uri?: string; uri?: string;
@ -90,7 +92,9 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
} }
get content() { get content() {
const status: "success" | "loading" | "empty" = "success"; const status = "loading" as "success" | "loading" | "empty";
const { uri } = getRemoteFetchQueryParams();
switch (status) { switch (status) {
case "success": { case "success": {
@ -103,6 +107,20 @@ export class RemoteFetch extends Component<any, RemoteFetchState> {
</> </>
); );
} }
case "loading": {
return (
<>
<h1>
Fetching {uri ? uriToQuery(uri) : "remote community"}
<LoadingEllipses />
</h1>
<h5>
<Spinner large />
</h5>
</>
);
}
} }
} }