From 23a4003f312ca41d3a8860834675fbae994b991b Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Fri, 25 Mar 2022 00:29:01 +0300 Subject: [PATCH] feat: refactor commonDisplay and related code - more typescript - fix https://github.com/QURIresearch/metaforecast-frontend/issues/15 - simplify hasDisplayBeenCaptured code - various cleanups --- src/pages/capture.js | 100 ----- src/pages/capture.tsx | 72 +++ src/pages/index.tsx | 51 +-- src/pages/secretEmbed.tsx | 2 +- ...buttonsForStars.js => buttonsForStars.tsx} | 17 +- src/web/display/commonDisplay.tsx | 416 ++++++++---------- src/web/display/displayForecastsWrappers.tsx | 12 +- ...re.js => displayOneForecastForCapture.tsx} | 53 +-- src/web/display/multiSelectPlatforms.js | 2 +- src/web/{platforms.js => platforms.ts} | 18 +- 10 files changed, 326 insertions(+), 417 deletions(-) delete mode 100644 src/pages/capture.js create mode 100644 src/pages/capture.tsx rename src/web/display/{buttonsForStars.js => buttonsForStars.tsx} (76%) rename src/web/display/{displayOneForecastForCapture.js => displayOneForecastForCapture.tsx} (87%) rename src/web/{platforms.js => platforms.ts} (76%) diff --git a/src/pages/capture.js b/src/pages/capture.js deleted file mode 100644 index 371ddb9..0000000 --- a/src/pages/capture.js +++ /dev/null @@ -1,100 +0,0 @@ -/* Imports */ - -import React from "react"; -import { getFrontpage } from "../backend/frontpage"; -import CommonDisplay from "../web/display/commonDisplay"; -import { displayForecastsWrapperForCapture } from "../web/display/displayForecastsWrappers"; -import { platformsWithLabels } from "../web/platforms.js"; -import searchAccordingToQueryData from "../web/worker/searchAccordingToQueryData.js"; -import Layout from "./layout.js"; - -/* get Props */ - -export async function getServerSideProps(context) { - let urlQuery = context.query; // this is an object, not a string which I have to parse!! - - let initialQueryParameters = { - query: "", - starsThreshold: 2, - numDisplay: 21, // 20 - forecastsThreshold: 0, - forecastingPlatforms: platformsWithLabels, // weird key value format, - ...urlQuery, - }; - - let frontPageForecasts = await getFrontpage(); - frontPageForecasts = frontPageForecasts.map((forecast) => ({ - ...forecast, - item: { - ...forecast.item, - timestamp: forecast.item.timestamp.toJSON(), - }, - })); - - let initialResults; - switch (initialQueryParameters.query != "") { - case true: - initialResults = await searchAccordingToQueryData(initialQueryParameters); - break; - default: - initialResults = frontPageForecasts; - break; - } - - let props = { - initialQueryParameters: initialQueryParameters, - initialResults: initialResults, - defaultResults: frontPageForecasts, - urlQuery: urlQuery, - }; - - return { - props: props, - }; -} - -/* Alternative: getStaticProps -export async function getStaticProps() { - // get frontPageForecasts somehow. - let lastUpdated = calculateLastUpdate(); // metaforecasts.find(forecast => forecast.platform == "Good Judgment Open").timestamp - let initialQueryParameters = { - query: "", - processedUrlYet: false, - starsThreshold: 2, - numDisplay: 21, // 20 - forecastsThreshold: 0, - forecastingPlatforms: platforms, - }; - return { - props: { - frontPageForecasts, - lastUpdated, - }, - }; -} -*/ - -/* Body */ -export default function Home({ - initialResults, - defaultResults, - initialQueryParameters, -}) { - return ( - - false} - displaySeeMoreHint={false} - displayForecastsWrapper={displayForecastsWrapperForCapture} - /> - - ); -} diff --git a/src/pages/capture.tsx b/src/pages/capture.tsx new file mode 100644 index 0000000..1e689c3 --- /dev/null +++ b/src/pages/capture.tsx @@ -0,0 +1,72 @@ +import { GetServerSideProps } from 'next'; +import React from 'react'; + +import { getFrontpage } from '../backend/frontpage'; +import CommonDisplay, { QueryParameters } from '../web/display/commonDisplay'; +import { displayForecastsWrapperForCapture } from '../web/display/displayForecastsWrappers'; +import { platformsWithLabels } from '../web/platforms'; +import searchAccordingToQueryData from '../web/worker/searchAccordingToQueryData'; +import Layout from './layout'; + +/* get Props */ + +export const getServerSideProps: GetServerSideProps = async (context) => { + let urlQuery = context.query; + + let initialQueryParameters: QueryParameters = { + query: "", + numDisplay: 21, + starsThreshold: 2, + forecastsThreshold: 0, + forecastingPlatforms: platformsWithLabels, // weird key value format, + ...urlQuery, + }; + + let frontPageForecasts = await getFrontpage(); + frontPageForecasts = frontPageForecasts.map((forecast) => ({ + ...forecast, + item: { + ...forecast.item, + timestamp: forecast.item.timestamp.toJSON(), + }, + })); + + let initialResults = + initialQueryParameters.query != "" + ? await searchAccordingToQueryData(initialQueryParameters) + : frontPageForecasts; + + let props = { + initialQueryParameters: initialQueryParameters, + initialResults: initialResults, + defaultResults: frontPageForecasts, + urlQuery: urlQuery, + }; + + return { + props: props, + }; +}; + +/* Body */ +export default function Home({ + initialResults, + defaultResults, + initialQueryParameters, +}) { + return ( + + + + ); +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 07af4d7..e0d96a6 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,18 +1,17 @@ -/* Imports */ - +import { GetServerSideProps } from 'next'; import React from 'react'; import { getFrontpage } from '../backend/frontpage'; import CommonDisplay from '../web/display/commonDisplay'; import { displayForecastsWrapperForSearch } from '../web/display/displayForecastsWrappers'; -import { platformsWithLabels } from '../web/platforms.js'; -import searchAccordingToQueryData from '../web/worker/searchAccordingToQueryData.js'; -import Layout from './layout.js'; +import { platformsWithLabels } from '../web/platforms'; +import searchAccordingToQueryData from '../web/worker/searchAccordingToQueryData'; +import Layout from './layout'; /* get Props */ -export async function getServerSideProps(context) { - let urlQuery = context.query; // this is an object, not a string which I have to parse!! +export const getServerSideProps: GetServerSideProps = async (context) => { + let urlQuery = context.query; let initialQueryParameters = { query: "", @@ -32,37 +31,22 @@ export async function getServerSideProps(context) { }, })); - let initialResults; - let props; - switch ( + let initialResults = !!initialQueryParameters && initialQueryParameters.query != "" && initialQueryParameters.query != undefined - ) { - case true: - initialResults = await searchAccordingToQueryData(initialQueryParameters); - props = { - initialQueryParameters: initialQueryParameters, - initialResults: initialResults, - defaultResults: frontPageForecasts, // different from initialResults! - urlQuery: urlQuery, - }; - break; - default: - initialResults = frontPageForecasts; - props = { - initialQueryParameters: initialQueryParameters, - initialResults: initialResults, - defaultResults: frontPageForecasts, // different from initialResults! - urlQuery: urlQuery, - }; - break; - } + ? await searchAccordingToQueryData(initialQueryParameters) + : frontPageForecasts; return { - props: props, + props: { + initialQueryParameters: initialQueryParameters, + initialResults: initialResults, + defaultResults: frontPageForecasts, // different from initialResults! + urlQuery: urlQuery, + }, }; -} +}; /* Body */ export default function Home({ @@ -71,7 +55,7 @@ export default function Home({ initialQueryParameters, }) { return ( - + null} displaySeeMoreHint={true} displayForecastsWrapper={displayForecastsWrapperForSearch} /> diff --git a/src/pages/secretEmbed.tsx b/src/pages/secretEmbed.tsx index de20a24..24b5bb9 100644 --- a/src/pages/secretEmbed.tsx +++ b/src/pages/secretEmbed.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { displayForecast } from '../web/display/displayForecasts.js'; -import { platformsWithLabels } from '../web/platforms.js'; +import { platformsWithLabels } from '../web/platforms'; import searchAccordingToQueryData from '../web/worker/searchAccordingToQueryData'; /* Helper functions */ diff --git a/src/web/display/buttonsForStars.js b/src/web/display/buttonsForStars.tsx similarity index 76% rename from src/web/display/buttonsForStars.js rename to src/web/display/buttonsForStars.tsx index 2be6cc8..a3e1a3d 100644 --- a/src/web/display/buttonsForStars.js +++ b/src/web/display/buttonsForStars.tsx @@ -1,10 +1,15 @@ -import React, { useState } from "react"; +import React from 'react'; -export default function ButtonsForStars({ onChange, value }) { - const onChangeInner = (buttonPressed) => { +interface Props { + onChange: (x: number) => void; + value: number; +} + +const ButtonsForStars: React.FC = ({ onChange, value }) => { + const onChangeInner = (buttonPressed: number) => { onChange(buttonPressed); }; - let setStyle = (buttonNumber) => + let setStyle = (buttonNumber: number) => `flex row-span-1 col-start-${buttonNumber + 1} col-end-${ buttonNumber + 2 } items-center justify-center text-center${ @@ -31,4 +36,6 @@ export default function ButtonsForStars({ onChange, value }) { ); -} +}; + +export default ButtonsForStars; diff --git a/src/web/display/commonDisplay.tsx b/src/web/display/commonDisplay.tsx index e18f993..96de24a 100644 --- a/src/web/display/commonDisplay.tsx +++ b/src/web/display/commonDisplay.tsx @@ -1,29 +1,46 @@ -/* Imports */ - -// React -import { useRouter } from 'next/router'; // https://nextjs.org/docs/api-reference/next/router import React, { Fragment, useState } from 'react'; -import { platformNames } from '../platforms.js'; -// Data +import { platformNames, PlatformWithLabel } from '../platforms'; import searchAccordingToQueryData from '../worker/searchAccordingToQueryData'; -import ButtonsForStars from './buttonsForStars.js'; -// Parts -import Form from './form.js'; -import MultiSelectPlatform from './multiSelectPlatforms.js'; -import { SliderElement } from './slider.js'; +import ButtonsForStars from './buttonsForStars'; +import Form from './form'; +import MultiSelectPlatform from './multiSelectPlatforms'; +import { SliderElement } from './slider'; -/* Definitions */ +export interface QueryParameters { + query: string; + numDisplay: number; + starsThreshold: number; + forecastsThreshold: number; + forecastingPlatforms: PlatformWithLabel[]; +} + +interface Props { + initialResults: any; + defaultResults: any; + initialQueryParameters: QueryParameters; + hasSearchbar: boolean; + hasCapture: boolean; + hasAdvancedOptions: boolean; + placeholder: string; + displaySeeMoreHint: boolean; + displayForecastsWrapper: (opts: { + results: any; + numDisplay: number; + whichResultToDisplayAndCapture: number; + showIdToggle: boolean; + }) => React.ReactNode; +} /* Helper functions */ // URL slugs -let transformObjectIntoUrlSlug = (obj) => { +let transformObjectIntoUrlSlug = (obj: QueryParameters) => { let results = []; for (let key in obj) { - if (typeof obj[key] == "number" || typeof obj[key] == "string") { + if (typeof obj[key] === "number" || typeof obj[key] === "string") { results.push(`${key}=${obj[key]}`); - } else if (key == "forecastingPlatforms") { + } else if (key === "forecastingPlatforms") { let arr = obj[key].map((x) => x.value); let arrstring = arr.join("|"); results.push(`${key}=${arrstring}`); @@ -34,7 +51,7 @@ let transformObjectIntoUrlSlug = (obj) => { }; /* Body */ -export default function CommonDisplay({ +const CommonDisplay: React.FC = ({ initialResults, defaultResults, initialQueryParameters, @@ -42,12 +59,10 @@ export default function CommonDisplay({ hasCapture, hasAdvancedOptions, placeholder, - setHasDisplayBeenCapturedOnChangeSearchInputs, displaySeeMoreHint, displayForecastsWrapper, -}) { +}) => { /* States */ - const router = useRouter(); const [queryParameters, setQueryParameters] = useState( initialQueryParameters @@ -62,18 +77,18 @@ export default function CommonDisplay({ ); const [results, setResults] = useState(initialResults); const [advancedOptions, showAdvancedOptions] = useState(false); - const [hasDisplayBeenCaptured, setHasDisplayBeenCaptured] = useState(false); const [whichResultToDisplayAndCapture, setWhichResultToDisplayAndCapture] = useState(0); const [showIdToggle, setShowIdToggle] = useState(false); /* Functions which I want to have access to the Home namespace */ // I don't want to create an "defaultResults" object for each search. - async function executeSearchOrAnswerWithDefaultResults(queryData) { - console.log("executeSearchOrAnswerWithDefaultResults"); + async function executeSearchOrAnswerWithDefaultResults( + queryData: QueryParameters + ) { // the queryData object has the same contents as queryParameters. // but I wanted to spare myself having to think about namespace conflicts. - let filterManually = (queryData, results) => { + let filterManually = (queryData: QueryParameters, results) => { if ( queryData.forecastingPlatforms && queryData.forecastingPlatforms.length > 0 @@ -85,7 +100,7 @@ export default function CommonDisplay({ forecastingPlatforms.includes(result.item.platform) ); } - if (queryData.starsThreshold == 4) { + if (queryData.starsThreshold === 4) { results = results.filter( (result) => result.item.qualityindicators.stars >= 4 ); @@ -96,76 +111,44 @@ export default function CommonDisplay({ return results; }; - let results; - switch ( - !queryData || - queryData.query == "" || - queryData.query == undefined - ) { - case true: - console.log(1); - results = filterManually(queryData, defaultResults || initialResults); - break; - case false: - console.log(2); - results = await searchAccordingToQueryData(queryData); - break; - default: - console.log(3); - console.log("This should not be happening"); - results = []; - break; - } - console.log("executeSearchOrAnswerWithDefaultResults/queryData", queryData); - console.log("defaultResults", defaultResults); - console.log("initialResults", initialResults); - console.log(results); - setResults(results); // just double check + const queryIsEmpty = + !queryData || queryData.query == "" || queryData.query == undefined; + + let results = queryIsEmpty + ? filterManually(queryData, defaultResults || initialResults) + : await searchAccordingToQueryData(queryData); + + setResults(results); } - // I don't want the function which dispaly forecasts (displayForecasts) to change with a change in queryParameters. But I want it to have access to the queryParameters, and in particular access to queryParameters.numDisplay. Hence why this function lives inside Home. - let getInfoToDisplayForecastsFunction = ( - displayForecastsFunction, - { - results, - hasDisplayBeenCaptured, - setHasDisplayBeenCaptured, - whichResultToDisplayAndCapture, - showIdToggle, - } - ) => { + // I don't want the function which display forecasts (displayForecasts) to change with a change in queryParameters. But I want it to have access to the queryParameters, and in particular access to queryParameters.numDisplay. Hence why this function lives inside Home. + let getInfoToDisplayForecastsFunction = ({ + results, + whichResultToDisplayAndCapture, + showIdToggle, + }) => { let numDisplayRounded = queryParameters.numDisplay % 3 != 0 ? queryParameters.numDisplay + (3 - (Math.round(queryParameters.numDisplay) % 3)) : queryParameters.numDisplay; - console.log("numDisplay", queryParameters.numDisplay); - console.log("numDisplayRounded", numDisplayRounded); - return displayForecastsFunction({ + return displayForecastsWrapper({ results, numDisplay: numDisplayRounded, - hasDisplayBeenCaptured, - setHasDisplayBeenCaptured, whichResultToDisplayAndCapture, showIdToggle, }); }; /* State controllers */ - let onChangeSearchInputs = (newQueryParameters) => { + let onChangeSearchInputs = (newQueryParameters: QueryParameters) => { setQueryParameters(newQueryParameters); // ({ ...newQueryParameters, processedUrlYet: true }); - console.log("onChangeSearchInputs/newQueryParameters", newQueryParameters); setResults([]); - setHasDisplayBeenCaptured(setHasDisplayBeenCapturedOnChangeSearchInputs()); clearTimeout(searchSpeedSettings.timeoutId); let newtimeoutId = setTimeout(async () => { - console.log( - "onChangeSearchInputs/timeout/newQueryParameters", - newQueryParameters - ); let urlSlug = transformObjectIntoUrlSlug(newQueryParameters); let urlWithoutDefaultParameters = urlSlug - .replace("?query=&", "&") + .replace("?query=&", "?") .replace("&starsThreshold=2", "") .replace("&numDisplay=21", "") .replace("&forecastsThreshold=0", "") @@ -180,7 +163,6 @@ export default function CommonDisplay({ ); } } - // router.push(urlWithoutDefaultParameters); } executeSearchOrAnswerWithDefaultResults(newQueryParameters); @@ -191,9 +173,11 @@ export default function CommonDisplay({ }; /* Change the stars threshold */ - let onChangeStars = (value) => { - console.log("onChangeStars/buttons", value); - let newQueryParameters = { ...queryParameters, starsThreshold: value }; + let onChangeStars = (value: number) => { + let newQueryParameters: QueryParameters = { + ...queryParameters, + starsThreshold: value, + }; onChangeSearchInputs(newQueryParameters); }; @@ -204,8 +188,7 @@ export default function CommonDisplay({ : "Show " + Math.round(value) + " result"; }; let onChangeSliderForNumDisplay = (event) => { - console.log("onChangeSliderForNumDisplay", event[0]); - let newQueryParameters = { + let newQueryParameters: QueryParameters = { ...queryParameters, numDisplay: Math.round(event[0]), }; @@ -213,11 +196,10 @@ export default function CommonDisplay({ }; /* Change the forecast threshold */ - let displayFunctionNumForecasts = (value) => { + let displayFunctionNumForecasts = (value: number) => { return "# Forecasts > " + Math.round(value); }; let onChangeSliderForNumForecasts = (event) => { - console.log("onChangeSliderForNumForecasts", event[0]); let newQueryParameters = { ...queryParameters, forecastsThreshold: Math.round(event[0]), @@ -226,15 +208,13 @@ export default function CommonDisplay({ }; /* Change on the search bar */ - let onChangeSearchBar = (value) => { - console.log("onChangeSearchBar/New query:", value); + let onChangeSearchBar = (value: string) => { let newQueryParameters = { ...queryParameters, query: value }; onChangeSearchInputs(newQueryParameters); }; - /*Change selected platforms */ + /* Change selected platforms */ let onChangeSelectedPlatforms = (value) => { - console.log("onChangeSelectedPlatforms/Change in platforms:", value); let newQueryParameters = { ...queryParameters, forecastingPlatforms: value, @@ -249,177 +229,145 @@ export default function CommonDisplay({ // Capture functionality let onClickBack = () => { - let decreaseUntil0 = (num) => (num - 1 > 0 ? num - 1 : 0); + let decreaseUntil0 = (num: number) => (num - 1 > 0 ? num - 1 : 0); setWhichResultToDisplayAndCapture( decreaseUntil0(whichResultToDisplayAndCapture) ); - setHasDisplayBeenCaptured(false); }; - let onClickForward = (whichResultToDisplayAndCapture) => { + let onClickForward = (whichResultToDisplayAndCapture: number) => { setWhichResultToDisplayAndCapture(whichResultToDisplayAndCapture + 1); - setHasDisplayBeenCaptured(false); // setTimeout(()=> {onClickForward(whichResultToDisplayAndCapture+1)}, 5000) }; /* Final return */ return ( - -