feat: new dashboards logic, closes #17
This commit is contained in:
parent
d92f18db3f
commit
986f7ab888
17
src/pages/_middleware.ts
Normal file
17
src/pages/_middleware.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function middleware(req: NextRequest) {
|
||||
const { pathname, searchParams } = req.nextUrl;
|
||||
|
||||
console.log(pathname);
|
||||
if (pathname === "/dashboards") {
|
||||
const dashboardId = searchParams.get("dashboardId");
|
||||
if (dashboardId) {
|
||||
return NextResponse.redirect(
|
||||
new URL(`/dashboards/view/${dashboardId}`, req.url)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
|
@ -1,211 +0,0 @@
|
|||
/* Imports */
|
||||
import axios from "axios";
|
||||
import { GetServerSideProps, NextPage } from "next";
|
||||
import { useRouter } from "next/router"; // https://nextjs.org/docs/api-reference/next/router
|
||||
import { useState } from "react";
|
||||
|
||||
import { DashboardItem } from "../backend/dashboards";
|
||||
import { getPlatformsConfig, PlatformConfig } from "../backend/platforms";
|
||||
import { DashboardCreator } from "../web/display/DashboardCreator";
|
||||
import { DisplayForecasts } from "../web/display/DisplayForecasts";
|
||||
import { Layout } from "../web/display/Layout";
|
||||
import { addLabelsToForecasts, FrontendForecast } from "../web/platforms";
|
||||
import { getDashboardForecastsByDashboardId } from "../web/worker/getDashboardForecasts";
|
||||
|
||||
interface Props {
|
||||
initialDashboardForecasts: FrontendForecast[];
|
||||
initialDashboardId: string | null;
|
||||
initialDashboardItem: DashboardItem | null;
|
||||
platformsConfig: PlatformConfig[];
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async (
|
||||
context
|
||||
) => {
|
||||
const dashboardIdQ = context.query.dashboardId;
|
||||
const dashboardId: string | undefined =
|
||||
typeof dashboardIdQ === "object" ? dashboardIdQ[0] : dashboardIdQ;
|
||||
|
||||
const platformsConfig = getPlatformsConfig({ withGuesstimate: false });
|
||||
|
||||
if (!dashboardId) {
|
||||
return {
|
||||
props: {
|
||||
platformsConfig,
|
||||
initialDashboardForecasts: [],
|
||||
initialDashboardId: null,
|
||||
initialDashboardItem: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const { dashboardForecasts, dashboardItem } =
|
||||
await getDashboardForecastsByDashboardId({
|
||||
dashboardId,
|
||||
});
|
||||
const frontendDashboardForecasts = addLabelsToForecasts(
|
||||
dashboardForecasts,
|
||||
platformsConfig
|
||||
);
|
||||
|
||||
return {
|
||||
props: {
|
||||
initialDashboardForecasts: frontendDashboardForecasts,
|
||||
initialDashboardId: dashboardId,
|
||||
initialDashboardItem: dashboardItem,
|
||||
platformsConfig,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/* Body */
|
||||
const DashboardsPage: NextPage<Props> = ({
|
||||
initialDashboardForecasts,
|
||||
initialDashboardItem,
|
||||
platformsConfig,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const [dashboardForecasts, setDashboardForecasts] = useState(
|
||||
initialDashboardForecasts
|
||||
);
|
||||
const [dashboardItem, setDashboardItem] = useState(initialDashboardItem);
|
||||
|
||||
const handleSubmit = async (data) => {
|
||||
console.log(data);
|
||||
// Send to server to create
|
||||
// Get back the id
|
||||
let response = await axios({
|
||||
url: "/api/create-dashboard-from-ids",
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
data: JSON.stringify(data),
|
||||
}).then((res) => res.data);
|
||||
let dashboardId = response.dashboardId;
|
||||
if (!!dashboardId) {
|
||||
console.log("response: ", response);
|
||||
if (typeof window !== "undefined") {
|
||||
let urlWithoutDefaultParameters = `/dashboards?dashboardId=${dashboardId}`;
|
||||
if (!window.location.href.includes(urlWithoutDefaultParameters)) {
|
||||
window.history.replaceState(
|
||||
null,
|
||||
"Metaforecast",
|
||||
urlWithoutDefaultParameters
|
||||
);
|
||||
}
|
||||
}
|
||||
// router.push(`?dashboardId=${dashboardId}`)
|
||||
// display it
|
||||
|
||||
let { dashboardForecasts, dashboardItem } =
|
||||
await getDashboardForecastsByDashboardId({
|
||||
dashboardId,
|
||||
});
|
||||
setDashboardForecasts(
|
||||
addLabelsToForecasts(dashboardForecasts, platformsConfig)
|
||||
);
|
||||
setDashboardItem(dashboardItem);
|
||||
}
|
||||
};
|
||||
|
||||
let isGraubardEasterEgg = (name: string) =>
|
||||
name == "Clay Graubard" ? true : false;
|
||||
|
||||
return (
|
||||
<Layout page="dashboard">
|
||||
{/* Display forecasts */}
|
||||
<div className="mt-7 mb-7">
|
||||
<h1
|
||||
className={
|
||||
!!dashboardItem && !!dashboardItem.title
|
||||
? "text-4xl text-center text-gray-600 mt-2 mb-2"
|
||||
: "hidden"
|
||||
}
|
||||
>
|
||||
{!!dashboardItem ? dashboardItem.title : ""}
|
||||
</h1>
|
||||
<p
|
||||
className={
|
||||
!!dashboardItem &&
|
||||
!!dashboardItem.creator &&
|
||||
!isGraubardEasterEgg(dashboardItem.creator)
|
||||
? "text-lg text-center text-gray-600 mt-2 mb-2"
|
||||
: "hidden"
|
||||
}
|
||||
>
|
||||
{!!dashboardItem ? `Created by: ${dashboardItem.creator}` : ""}
|
||||
</p>
|
||||
<p
|
||||
className={
|
||||
!!dashboardItem &&
|
||||
!!dashboardItem.creator &&
|
||||
isGraubardEasterEgg(dashboardItem.creator)
|
||||
? "text-lg text-center text-gray-600 mt-2 mb-2"
|
||||
: "hidden"
|
||||
}
|
||||
>
|
||||
{!!dashboardItem ? `Created by: @` : ""}
|
||||
<a
|
||||
href={"https://twitter.com/ClayGraubard"}
|
||||
className="text-blue-600"
|
||||
>
|
||||
Clay Graubard
|
||||
</a>
|
||||
</p>
|
||||
<p
|
||||
className={
|
||||
!!dashboardItem && !!dashboardItem.description
|
||||
? "text-lg text-center text-gray-600 mt-2 mb-2"
|
||||
: "hidden"
|
||||
}
|
||||
>
|
||||
{!!dashboardItem ? `${dashboardItem.description}` : ""}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
|
||||
<DisplayForecasts
|
||||
results={dashboardForecasts}
|
||||
numDisplay={dashboardForecasts.length}
|
||||
showIdToggle={false}
|
||||
/>
|
||||
</div>
|
||||
{/* */}
|
||||
<h3 className="flex items-center col-start-2 col-end-2 w-full justify-center mt-8 mb-4">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="flex-grow bg-gray-300 rounded h-0.5"
|
||||
></span>
|
||||
<span
|
||||
className={
|
||||
!!dashboardForecasts && dashboardForecasts.length > 0
|
||||
? `mx-3 text-md font-medium text-center`
|
||||
: "hidden"
|
||||
}
|
||||
>
|
||||
Or create your own
|
||||
</span>
|
||||
<span
|
||||
className={
|
||||
!dashboardForecasts || dashboardForecasts.length == 0
|
||||
? `mx-3 text-md font-medium text-center`
|
||||
: "hidden"
|
||||
}
|
||||
>
|
||||
Create a dashboard!
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="flex-grow bg-gray-300 rounded h-0.5"
|
||||
></span>
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-3 justify-center">
|
||||
<div className="flex col-start-2 col-end-2 items-center justify-center">
|
||||
<DashboardCreator handleSubmit={handleSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardsPage;
|
88
src/pages/dashboards/index.tsx
Normal file
88
src/pages/dashboards/index.tsx
Normal file
|
@ -0,0 +1,88 @@
|
|||
import axios from "axios";
|
||||
import { GetServerSideProps, NextPage } from "next";
|
||||
import { useRouter } from "next/router"; // https://nextjs.org/docs/api-reference/next/router
|
||||
|
||||
import { DashboardItem } from "../../backend/dashboards";
|
||||
import { getPlatformsConfig, PlatformConfig } from "../../backend/platforms";
|
||||
import { DashboardCreator } from "../../web/display/DashboardCreator";
|
||||
import { Layout } from "../../web/display/Layout";
|
||||
import { LineHeader } from "../../web/display/LineHeader";
|
||||
import { addLabelsToForecasts, FrontendForecast } from "../../web/platforms";
|
||||
import { getDashboardForecastsByDashboardId } from "../../web/worker/getDashboardForecasts";
|
||||
|
||||
interface Props {
|
||||
initialDashboardForecasts: FrontendForecast[];
|
||||
initialDashboardId: string | null;
|
||||
initialDashboardItem: DashboardItem | null;
|
||||
platformsConfig: PlatformConfig[];
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async (
|
||||
context
|
||||
) => {
|
||||
const dashboardIdQ = context.query.dashboardId;
|
||||
const dashboardId: string | undefined =
|
||||
typeof dashboardIdQ === "object" ? dashboardIdQ[0] : dashboardIdQ;
|
||||
|
||||
const platformsConfig = getPlatformsConfig({ withGuesstimate: false });
|
||||
|
||||
if (!dashboardId) {
|
||||
return {
|
||||
props: {
|
||||
platformsConfig,
|
||||
initialDashboardForecasts: [],
|
||||
initialDashboardId: null,
|
||||
initialDashboardItem: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const { dashboardForecasts, dashboardItem } =
|
||||
await getDashboardForecastsByDashboardId({
|
||||
dashboardId,
|
||||
});
|
||||
const frontendDashboardForecasts = addLabelsToForecasts(
|
||||
dashboardForecasts,
|
||||
platformsConfig
|
||||
);
|
||||
|
||||
return {
|
||||
props: {
|
||||
initialDashboardForecasts: frontendDashboardForecasts,
|
||||
initialDashboardId: dashboardId,
|
||||
initialDashboardItem: dashboardItem,
|
||||
platformsConfig,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/* Body */
|
||||
const DashboardsPage: NextPage<Props> = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const handleSubmit = async (data) => {
|
||||
// Send to server to create
|
||||
// Get back the id
|
||||
let response = await axios({
|
||||
url: "/api/create-dashboard-from-ids",
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
data: JSON.stringify(data),
|
||||
}).then((res) => res.data);
|
||||
await router.push(`/dashboards/view/${response.dashboardId}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout page="dashboard">
|
||||
<div className="flex flex-col my-8 space-y-8">
|
||||
<LineHeader>Create a dashboard!</LineHeader>
|
||||
|
||||
<div className="self-center">
|
||||
<DashboardCreator handleSubmit={handleSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardsPage;
|
118
src/pages/dashboards/view/[id].tsx
Normal file
118
src/pages/dashboards/view/[id].tsx
Normal file
|
@ -0,0 +1,118 @@
|
|||
import { GetServerSideProps, NextPage } from "next";
|
||||
import Link from "next/link";
|
||||
|
||||
import { DashboardItem } from "../../../backend/dashboards";
|
||||
import { getPlatformsConfig } from "../../../backend/platforms";
|
||||
import { DisplayForecasts } from "../../../web/display/DisplayForecasts";
|
||||
import { InfoBox } from "../../../web/display/InfoBox";
|
||||
import { Layout } from "../../../web/display/Layout";
|
||||
import { LineHeader } from "../../../web/display/LineHeader";
|
||||
import { addLabelsToForecasts, FrontendForecast } from "../../../web/platforms";
|
||||
import { getDashboardForecastsByDashboardId } from "../../../web/worker/getDashboardForecasts";
|
||||
|
||||
interface Props {
|
||||
dashboardForecasts: FrontendForecast[];
|
||||
dashboardId: string;
|
||||
dashboardItem: DashboardItem;
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async (
|
||||
context
|
||||
) => {
|
||||
const dashboardId = context.query.id as string;
|
||||
|
||||
const platformsConfig = getPlatformsConfig({ withGuesstimate: false });
|
||||
|
||||
const { dashboardForecasts, dashboardItem } =
|
||||
await getDashboardForecastsByDashboardId({
|
||||
dashboardId,
|
||||
});
|
||||
const frontendDashboardForecasts = addLabelsToForecasts(
|
||||
dashboardForecasts,
|
||||
platformsConfig
|
||||
);
|
||||
|
||||
return {
|
||||
props: {
|
||||
dashboardForecasts: frontendDashboardForecasts,
|
||||
dashboardId,
|
||||
dashboardItem,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const DashboardMetadata: React.FC<{ dashboardItem: DashboardItem }> = ({
|
||||
dashboardItem,
|
||||
}) => (
|
||||
<div>
|
||||
{dashboardItem?.title ? (
|
||||
<h1 className="text-4xl text-center text-gray-600 mt-2 mb-2">
|
||||
{dashboardItem.title}
|
||||
</h1>
|
||||
) : null}
|
||||
|
||||
{dashboardItem && dashboardItem.creator ? (
|
||||
<p className="text-lg text-center text-gray-600 mt-2 mb-2">
|
||||
Created by:{" "}
|
||||
{dashboardItem.creator === "Clay Graubard" ? (
|
||||
<>
|
||||
@
|
||||
<a
|
||||
href="https://twitter.com/ClayGraubard"
|
||||
className="text-blue-600"
|
||||
>
|
||||
Clay Graubard
|
||||
</a>
|
||||
</>
|
||||
) : (
|
||||
dashboardItem.creator
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
{dashboardItem?.description ? (
|
||||
<p className="text-lg text-center text-gray-600 mt-2 mb-2">
|
||||
{dashboardItem.description}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
/* Body */
|
||||
const ViewDashboardPage: NextPage<Props> = ({
|
||||
dashboardForecasts,
|
||||
dashboardItem,
|
||||
dashboardId,
|
||||
}) => {
|
||||
return (
|
||||
<Layout page="view-dashboard">
|
||||
<div className="flex flex-col my-8 space-y-8">
|
||||
{dashboardItem ? (
|
||||
<DashboardMetadata dashboardItem={dashboardItem} />
|
||||
) : null}
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<DisplayForecasts
|
||||
results={dashboardForecasts}
|
||||
numDisplay={dashboardForecasts.length}
|
||||
showIdToggle={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl self-center">
|
||||
<InfoBox>
|
||||
Dashboards cannot be changed after they are created.
|
||||
</InfoBox>
|
||||
</div>
|
||||
|
||||
<LineHeader>
|
||||
<Link href="/dashboards" passHref>
|
||||
<a>Create your own dashboard</a>
|
||||
</Link>
|
||||
</LineHeader>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewDashboardPage;
|
10
src/web/display/Button.tsx
Normal file
10
src/web/display/Button.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
|
||||
|
||||
export const Button: React.FC<Props> = ({ children, ...rest }) => (
|
||||
<button
|
||||
{...rest}
|
||||
className="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded text-center"
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
5
src/web/display/InfoBox.tsx
Normal file
5
src/web/display/InfoBox.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
export const InfoBox: React.FC = ({ children }) => (
|
||||
<p className="bg-gray-200 text-gray-700 py-2 px-4 border border-transparent text-center">
|
||||
{children}
|
||||
</p>
|
||||
);
|
7
src/web/display/LineHeader.tsx
Normal file
7
src/web/display/LineHeader.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
export const LineHeader: React.FC = ({ children }) => (
|
||||
<h3 className="flex items-center justify-center w-full">
|
||||
<span aria-hidden="true" className="flex-grow bg-gray-300 rounded h-0.5" />
|
||||
<span className="mx-3 text-md font-medium text-center">{children}</span>
|
||||
<span aria-hidden="true" className="flex-grow bg-gray-300 rounded h-0.5" />
|
||||
</h3>
|
||||
);
|
|
@ -1,4 +1,7 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { EventHandler, SyntheticEvent, useState } from "react";
|
||||
|
||||
import { Button } from "./Button";
|
||||
import { InfoBox } from "./InfoBox";
|
||||
|
||||
const exampleInput = `{
|
||||
"title": "Random example",
|
||||
|
@ -13,32 +16,27 @@ interface Props {
|
|||
|
||||
export const DashboardCreator: React.FC<Props> = ({ handleSubmit }) => {
|
||||
const [value, setValue] = useState(exampleInput);
|
||||
const [displayingDoneMessage, setDisplayingDoneMessage] = useState(false);
|
||||
const [displayingDoneMessageTimer, setDisplayingDoneMessageTimer] =
|
||||
useState(null);
|
||||
const [acting, setActing] = useState(false);
|
||||
|
||||
const handleChange = (event) => {
|
||||
setValue(event.target.value);
|
||||
};
|
||||
|
||||
const handleSubmitInner = (event) => {
|
||||
clearTimeout(displayingDoneMessageTimer);
|
||||
const handleSubmitInner: EventHandler<SyntheticEvent> = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
console.log(value);
|
||||
try {
|
||||
let newData = JSON.parse(value);
|
||||
const newData = JSON.parse(value);
|
||||
|
||||
if (!newData || !newData.ids || newData.ids.length == 0) {
|
||||
throw Error("Not enough objects");
|
||||
} else {
|
||||
handleSubmit(newData);
|
||||
setDisplayingDoneMessage(true);
|
||||
const timer = setTimeout(() => setDisplayingDoneMessage(false), 3000);
|
||||
setDisplayingDoneMessageTimer(timer);
|
||||
setActing(true);
|
||||
await handleSubmit(newData);
|
||||
setActing(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setDisplayingDoneMessage(false);
|
||||
setActing(false);
|
||||
const substituteText = `Error: ${error.message}
|
||||
|
||||
Try something like:
|
||||
|
@ -50,36 +48,21 @@ Your old input was: ${value}`;
|
|||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmitInner} className="block place-centers">
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
rows={8}
|
||||
cols={50}
|
||||
className=""
|
||||
/>
|
||||
<br />
|
||||
<div className="grid grid-cols-3 text-center">
|
||||
<button
|
||||
className="block col-start-2 col-end-2 w-full bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5 p-10 text-center"
|
||||
onClick={handleSubmitInner}
|
||||
<form onSubmit={handleSubmitInner}>
|
||||
<div className="flex flex-col items-center space-y-5 max-w-2xl">
|
||||
<textarea value={value} onChange={handleChange} rows={8} cols={50} />
|
||||
<Button
|
||||
disabled={acting}
|
||||
onClick={acting ? undefined : handleSubmitInner}
|
||||
>
|
||||
Create dashboard
|
||||
</button>
|
||||
<button
|
||||
className={
|
||||
displayingDoneMessage
|
||||
? "block col-start-2 col-end-2 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-2 border border-blue-500 hover:border-transparent rounded mt-2 p-2 text-center "
|
||||
: "hidden "
|
||||
}
|
||||
>
|
||||
Done!
|
||||
</button>
|
||||
<p className="block col-start-1 col-end-4 bg-gray-200 text-gray-700 py-2 px-4 border border-transparent mt-5 p-10 text-center mb-6">
|
||||
{acting ? "Creating..." : "Create dashboard"}
|
||||
</Button>
|
||||
|
||||
<InfoBox>
|
||||
You can find the necessary ids by toggling the advanced options in the
|
||||
search, or by visiting{" "}
|
||||
<a href="/api/all-forecasts">/api/all-forecasts</a>
|
||||
</p>
|
||||
</InfoBox>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { DisplayForecasts } from "./DisplayForecasts";
|
||||
import displayOneForecast from "./displayOneForecastForCapture";
|
||||
import { DisplayOneForecastForCapture } from "./DisplayOneForecastForCapture";
|
||||
|
||||
export function displayForecastsWrapperForSearch({
|
||||
results,
|
||||
|
@ -23,9 +23,9 @@ export function displayForecastsWrapperForCapture({
|
|||
}) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 w-full justify-center">
|
||||
{displayOneForecast({
|
||||
result: results[whichResultToDisplayAndCapture],
|
||||
})}
|
||||
<DisplayOneForecastForCapture
|
||||
result={results[whichResultToDisplayAndCapture]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ interface Props {
|
|||
result: FrontendForecast;
|
||||
}
|
||||
|
||||
const DisplayOneForecast: React.FC<Props> = ({ result }) => {
|
||||
export const DisplayOneForecastForCapture: React.FC<Props> = ({ result }) => {
|
||||
const [hasDisplayBeenCaptured, setHasDisplayBeenCaptured] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -248,8 +248,6 @@ const DisplayOneForecast: React.FC<Props> = ({ result }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default DisplayOneForecast;
|
||||
|
||||
// https://stackoverflow.com/questions/39501289/in-reactjs-how-to-copy-text-to-clipboard
|
||||
// Note: https://stackoverflow.com/questions/66016033/can-no-longer-upload-images-to-imgur-from-localhost
|
||||
// Use: http://imgurtester:3000/embed for testing.
|
||||
|
|
15
src/web/hooks.ts
Normal file
15
src/web/hooks.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import React, { DependencyList, EffectCallback, useEffect } from "react";
|
||||
|
||||
export const useNoInitialEffect = (
|
||||
effect: EffectCallback,
|
||||
deps: DependencyList
|
||||
) => {
|
||||
const initial = React.useRef(true);
|
||||
useEffect(() => {
|
||||
if (initial.current) {
|
||||
initial.current = false;
|
||||
return;
|
||||
}
|
||||
return effect();
|
||||
}, deps);
|
||||
};
|
|
@ -1,25 +1,15 @@
|
|||
import { useRouter } from "next/router";
|
||||
import React, { DependencyList, EffectCallback, Fragment, useEffect, useState } from "react";
|
||||
import React, { Fragment, useState } from "react";
|
||||
|
||||
import { ButtonsForStars } from "../display/ButtonsForStars";
|
||||
import { MultiSelectPlatform } from "../display/MultiSelectPlatform";
|
||||
import { QueryForm } from "../display/QueryForm";
|
||||
import { SliderElement } from "../display/SliderElement";
|
||||
import { useNoInitialEffect } from "../hooks";
|
||||
import { FrontendForecast } from "../platforms";
|
||||
import searchAccordingToQueryData from "../worker/searchAccordingToQueryData";
|
||||
import { Props as AnySearchPageProps, QueryParameters } from "./anySearchPage";
|
||||
|
||||
const useNoInitialEffect = (effect: EffectCallback, deps: DependencyList) => {
|
||||
const initial = React.useRef(true);
|
||||
useEffect(() => {
|
||||
if (initial.current) {
|
||||
initial.current = false;
|
||||
return;
|
||||
}
|
||||
return effect();
|
||||
}, deps);
|
||||
};
|
||||
|
||||
interface Props extends AnySearchPageProps {
|
||||
hasSearchbar: boolean;
|
||||
hasCapture: boolean;
|
||||
|
|
Loading…
Reference in New Issue
Block a user