commit 573dc1ca59cbcbb04dede13c8f3fbd8e7d114372
parent 77003f540b7fa2a52f06ae3b88bc095bb2115c5c
Author: MTRNord <mtrnord1@gmail.com>
Date: Thu, 27 Jan 2022 23:19:20 +0100
Add loading animation and error messages to the submit dialog. Also block multiple submission calls
Diffstat:
7 files changed, 99 insertions(+), 13 deletions(-)
diff --git a/components/submit_page/main.tsx b/components/submit_page/main.tsx
@@ -9,6 +9,7 @@ import { withRouter } from "next/router";
import { WithRouterProps } from "next/dist/client/with-router";
import { BlurhashEncoder } from "../../helpers/BlurhashEncoder";
import { ImageEventContent, MatrixContents, ThumbnailData } from "../../helpers/event_types";
+import { toast } from "react-toastify";
type ThumbnailableElement = HTMLImageElement | HTMLVideoElement;
type ThumbnailTransmissionData = {
@@ -31,6 +32,7 @@ type State = {
currentFileIndex: number;
hasSubmit: boolean;
hasBack: boolean;
+ submit_in_process: boolean;
[key: string]: any;
};
class MainSubmissionForm extends PureComponent<Props, State> {
@@ -42,7 +44,8 @@ class MainSubmissionForm extends PureComponent<Props, State> {
this.state = {
currentFileIndex: 0,
hasSubmit: props.files.length === 1 ? true : false,
- hasBack: false
+ hasBack: false,
+ submit_in_process: false,
};
const range = [...Array(this.props.files.length).keys()]; // eslint-disable-line unicorn/new-for-builtins
for (const index of range) {
@@ -131,19 +134,35 @@ class MainSubmissionForm extends PureComponent<Props, State> {
}
- async handleSubmit(event: { preventDefault: () => void; }) {
+ async handleSubmit() {
+ if (this.state.submit_in_process) {
+ return;
+ }
+ this.setState({ submit_in_process: true });
+
const range = [...Array(this.props.files.length).keys()]; // eslint-disable-line unicorn/new-for-builtins
const posts_for_search: SearchMedia[] = [];
+ // Clear all errors before showing new
+ toast.dismiss();
+
+
if (this.context.client.isGuest) {
+ toast.error(() => <div><h2 className="text-xl text-white">Error</h2><br />You are not logged in!</div>, {
+ autoClose: false
+ });
+ this.setState({ submit_in_process: false });
return;
}
// If any image is invalid do exit submit for now.
- // TODO show an error
for (const index of range) {
- const valid = `${index}_valid`;
+ const valid = this.state[`${index}_valid`];
if (!valid) {
+ toast.error(() => <div><h2 className="text-xl text-white">Error</h2><br />You did not fill the required fields for all images. Please fix this!</div>, {
+ autoClose: false
+ });
+ this.setState({ submit_in_process: false });
return;
}
}
@@ -154,8 +173,6 @@ class MainSubmissionForm extends PureComponent<Props, State> {
// Handle uploads
for (const index of range) {
- console.log(index);
- console.log(this.context.client.profileRoomId);
const title = `${index}_title`;
const description = `${index}_description`;
const tags = `${index}_tags`;
@@ -164,6 +181,10 @@ class MainSubmissionForm extends PureComponent<Props, State> {
const file = this.props.files[index];
if (!this.context.client.profileRoomId) {
+ toast.error(() => <div><h2 className="text-xl text-white">Error</h2><br />Unable to find your profile Room. Please log in again!</div>, {
+ autoClose: false
+ });
+ this.setState({ submit_in_process: false });
return;
}
@@ -207,6 +228,7 @@ class MainSubmissionForm extends PureComponent<Props, State> {
const token = await this.context.client.getOpenidToken();
await fetch("/api/submitSearch", { method: "POST", body: JSON.stringify({ access_token: token, user_id: this.context.client.userId, docs: posts_for_search }) });
await this.props.router.replace("/");
+ this.setState({ submit_in_process: false });
}
// THis is aken from matrix-react-sdk commit efa1667d7e9de9e429a72396a5105d0219006db2
@@ -292,7 +314,6 @@ class MainSubmissionForm extends PureComponent<Props, State> {
file.type
);
if (!thumbnail_data) {
- // TODO this causes issues
continue;
}
@@ -353,6 +374,15 @@ class MainSubmissionForm extends PureComponent<Props, State> {
}
render() {
+ if (this.state.submit_in_process) {
+ return (
+ <main className="max-h-full max-w-full lg:pt-20 pt-56">
+ <div className="mx-auto w-full">
+ <div className="loader">Loading...</div>
+ </div>
+ </main>
+ );
+ }
return (
<main className='min-h-full max-w-full flex flex-col justify-start items-center lg:pt-20 pt-52 z-0 bottom-0 relative mb-8'>
<section className="flex flex-col items-start mb-4">
diff --git a/package-lock.json b/package-lock.json
@@ -18,6 +18,7 @@
"react-blurhash": "0.1.3",
"react-dom": "17.0.2",
"react-dropzone": "11.5.1",
+ "react-toastify": "^8.1.0",
"reflect-metadata": "0.1.13",
"sequelize": "6.14.1",
"sequelize-typescript": "2.1.2",
@@ -2264,6 +2265,14 @@
"node": ">=4"
}
},
+ "node_modules/clsx": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+ "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -6089,6 +6098,18 @@
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true
},
+ "node_modules/react-toastify": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.0.tgz",
+ "integrity": "sha512-M+Q3rTmEw/53Csr7NsV/YnldJe4c7uERcY7Tma9mvLU98QT2VhIkKwjBzzxZkJRk/oBKyUAtkyMjMgO00hx6gQ==",
+ "dependencies": {
+ "clsx": "^1.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -9117,6 +9138,11 @@
"escape-string-regexp": "^1.0.5"
}
},
+ "clsx": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+ "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
+ },
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -11984,6 +12010,14 @@
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true
},
+ "react-toastify": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.0.tgz",
+ "integrity": "sha512-M+Q3rTmEw/53Csr7NsV/YnldJe4c7uERcY7Tma9mvLU98QT2VhIkKwjBzzxZkJRk/oBKyUAtkyMjMgO00hx6gQ==",
+ "requires": {
+ "clsx": "^1.1.1"
+ }
+ },
"read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
diff --git a/package.json b/package.json
@@ -29,6 +29,7 @@
"react-blurhash": "0.1.3",
"react-dom": "17.0.2",
"react-dropzone": "11.5.1",
+ "react-toastify": "^8.1.0",
"reflect-metadata": "0.1.13",
"sequelize": "6.14.1",
"sequelize-typescript": "2.1.2",
@@ -57,4 +58,4 @@
"optionalDependencies": {
"fsevents": "2.3.2"
}
-}
-\ No newline at end of file
+}
diff --git a/pages/_app.tsx b/pages/_app.tsx
@@ -1,9 +1,11 @@
import '../styles/globals.css';
+import 'react-toastify/dist/ReactToastify.min.css';
import type { AppProps } from 'next/app';
import { useState } from 'react';
import { client, ClientContext, guest_client } from '../components/ClientContext';
import React from 'react';
import Head from 'next/head';
+import { ToastContainer } from 'react-toastify';
function MyApp({ Component, pageProps }: AppProps) {
const [value] = useState({
@@ -22,9 +24,21 @@ function MyApp({ Component, pageProps }: AppProps) {
<ClientContext.Provider
value={value}
>
- <Component {...pageProps}/>
+ <Component {...pageProps} />
</ClientContext.Provider>
</React.StrictMode>
+ <ToastContainer
+ position="top-center"
+ autoClose={5000}
+ hideProgressBar={false}
+ newestOnTop={false}
+ closeOnClick
+ rtl={false}
+ pauseOnFocusLoss
+ draggable
+ pauseOnHover
+ theme="colored"
+ />
</>
);
}
diff --git a/pages/login.tsx b/pages/login.tsx
@@ -113,8 +113,8 @@ class Login extends PureComponent<Props, State> {
<h1 className='text-xl text-gray-900 dark:text-gray-200 font-bold'>Log In</h1>
</div>
</div>
- <div className="m-0 w-full">
- <div className="loader fixed top-[50%] left-[50%] transform translate-x-[-50%] translate-y-[-50%]">Loading...</div>
+ <div className="mx-auto w-full">
+ <div className="loader">Loading...</div>
</div>
</main>
<Footer></Footer>
diff --git a/pages/post/[id].tsx b/pages/post/[id].tsx
@@ -136,7 +136,7 @@ class Post extends PureComponent<Props, State> {
<Header></Header>
<main className='w-full lg:pt-20 pt-52 z-0'>
<div className="m-0 w-full">
- <div className="loader fixed top-[50%] left-[50%] transform translate-x-[-50%] translate-y-[-50%]">Loading...</div>
+ <div className="loader">Loading...</div>
</div>
</main>
</div>
diff --git a/pages/submit.tsx b/pages/submit.tsx
@@ -2,6 +2,7 @@ import Head from "next/head";
import { NextRouter, withRouter } from "next/router";
import { PureComponent, ReactNode } from "react";
import { FileRejection } from "react-dropzone";
+import { toast } from "react-toastify";
import { ClientContext } from "../components/ClientContext";
import Footer from "../components/Footer";
import Header from "../components/Header";
@@ -80,6 +81,13 @@ class Submit extends PureComponent<Props, State> implements DropCallbacks {
submitState: "start"
};
}
+ componentDidUpdate(prevProps: Props, prevState: State) {
+ if (this.state.error && this.state.error !== prevState.error) {
+ toast(() => <div><h2 className="text-xl text-white">Error</h2><br />{this.state.error}</div>, {
+ autoClose: false
+ });
+ }
+ }
render(): ReactNode {
return (