feat: refactor commonDisplay and related code

- more typescript
- fix https://github.com/QURIresearch/metaforecast-frontend/issues/15
- simplify hasDisplayBeenCaptured code
- various cleanups
This commit is contained in:
Vyacheslav Matyukhin 2022-03-25 00:29:01 +03:00
parent 9714e4cd0a
commit 23a4003f31
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
10 changed files with 326 additions and 417 deletions

View File

@ -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 (
<Layout key="index" page={"capture"}>
<CommonDisplay
initialResults={initialResults}
defaultResults={defaultResults}
initialQueryParameters={initialQueryParameters}
pageName={"capture"}
hasSearchbar={true}
hasCapture={true}
hasAdvancedOptions={false}
placeholder={"Get best title match..."}
setHasDisplayBeenCapturedOnChangeSearchInputs={() => false}
displaySeeMoreHint={false}
displayForecastsWrapper={displayForecastsWrapperForCapture}
/>
</Layout>
);
}

72
src/pages/capture.tsx Normal file
View File

@ -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 (
<Layout page={"capture"}>
<CommonDisplay
initialResults={initialResults}
defaultResults={defaultResults}
initialQueryParameters={initialQueryParameters}
hasSearchbar={true}
hasCapture={true}
hasAdvancedOptions={false}
placeholder={"Get best title match..."}
displaySeeMoreHint={false}
displayForecastsWrapper={displayForecastsWrapperForCapture}
/>
</Layout>
);
}

View File

@ -1,18 +1,17 @@
/* Imports */ import { GetServerSideProps } from 'next';
import React from 'react'; import React from 'react';
import { getFrontpage } from '../backend/frontpage'; import { getFrontpage } from '../backend/frontpage';
import CommonDisplay from '../web/display/commonDisplay'; import CommonDisplay from '../web/display/commonDisplay';
import { displayForecastsWrapperForSearch } from '../web/display/displayForecastsWrappers'; import { displayForecastsWrapperForSearch } from '../web/display/displayForecastsWrappers';
import { platformsWithLabels } from '../web/platforms.js'; import { platformsWithLabels } from '../web/platforms';
import searchAccordingToQueryData from '../web/worker/searchAccordingToQueryData.js'; import searchAccordingToQueryData from '../web/worker/searchAccordingToQueryData';
import Layout from './layout.js'; import Layout from './layout';
/* get Props */ /* get Props */
export async function getServerSideProps(context) { export const getServerSideProps: GetServerSideProps = async (context) => {
let urlQuery = context.query; // this is an object, not a string which I have to parse!! let urlQuery = context.query;
let initialQueryParameters = { let initialQueryParameters = {
query: "", query: "",
@ -32,37 +31,22 @@ export async function getServerSideProps(context) {
}, },
})); }));
let initialResults; let initialResults =
let props;
switch (
!!initialQueryParameters && !!initialQueryParameters &&
initialQueryParameters.query != "" && initialQueryParameters.query != "" &&
initialQueryParameters.query != undefined initialQueryParameters.query != undefined
) { ? await searchAccordingToQueryData(initialQueryParameters)
case true: : frontPageForecasts;
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;
}
return { return {
props: props, props: {
initialQueryParameters: initialQueryParameters,
initialResults: initialResults,
defaultResults: frontPageForecasts, // different from initialResults!
urlQuery: urlQuery,
},
}; };
} };
/* Body */ /* Body */
export default function Home({ export default function Home({
@ -71,7 +55,7 @@ export default function Home({
initialQueryParameters, initialQueryParameters,
}) { }) {
return ( return (
<Layout key="index" page={"search"}> <Layout page={"search"}>
<CommonDisplay <CommonDisplay
initialResults={initialResults} initialResults={initialResults}
defaultResults={defaultResults} defaultResults={defaultResults}
@ -80,7 +64,6 @@ export default function Home({
hasCapture={false} hasCapture={false}
hasAdvancedOptions={true} hasAdvancedOptions={true}
placeholder={"Find forecasts about..."} placeholder={"Find forecasts about..."}
setHasDisplayBeenCapturedOnChangeSearchInputs={() => null}
displaySeeMoreHint={true} displaySeeMoreHint={true}
displayForecastsWrapper={displayForecastsWrapperForSearch} displayForecastsWrapper={displayForecastsWrapperForSearch}
/> />

View File

@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import { displayForecast } from '../web/display/displayForecasts.js'; import { displayForecast } from '../web/display/displayForecasts.js';
import { platformsWithLabels } from '../web/platforms.js'; import { platformsWithLabels } from '../web/platforms';
import searchAccordingToQueryData from '../web/worker/searchAccordingToQueryData'; import searchAccordingToQueryData from '../web/worker/searchAccordingToQueryData';
/* Helper functions */ /* Helper functions */

View File

@ -1,10 +1,15 @@
import React, { useState } from "react"; import React from 'react';
export default function ButtonsForStars({ onChange, value }) { interface Props {
const onChangeInner = (buttonPressed) => { onChange: (x: number) => void;
value: number;
}
const ButtonsForStars: React.FC<Props> = ({ onChange, value }) => {
const onChangeInner = (buttonPressed: number) => {
onChange(buttonPressed); onChange(buttonPressed);
}; };
let setStyle = (buttonNumber) => let setStyle = (buttonNumber: number) =>
`flex row-span-1 col-start-${buttonNumber + 1} col-end-${ `flex row-span-1 col-start-${buttonNumber + 1} col-end-${
buttonNumber + 2 buttonNumber + 2
} items-center justify-center text-center${ } items-center justify-center text-center${
@ -31,4 +36,6 @@ export default function ButtonsForStars({ onChange, value }) {
</div> </div>
</div> </div>
); );
} };
export default ButtonsForStars;

View File

@ -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 React, { Fragment, useState } from 'react';
import { platformNames } from '../platforms.js'; import { platformNames, PlatformWithLabel } from '../platforms';
// Data
import searchAccordingToQueryData from '../worker/searchAccordingToQueryData'; import searchAccordingToQueryData from '../worker/searchAccordingToQueryData';
import ButtonsForStars from './buttonsForStars.js'; import ButtonsForStars from './buttonsForStars';
// Parts import Form from './form';
import Form from './form.js'; import MultiSelectPlatform from './multiSelectPlatforms';
import MultiSelectPlatform from './multiSelectPlatforms.js'; import { SliderElement } from './slider';
import { SliderElement } from './slider.js';
/* 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 */ /* Helper functions */
// URL slugs // URL slugs
let transformObjectIntoUrlSlug = (obj) => { let transformObjectIntoUrlSlug = (obj: QueryParameters) => {
let results = []; let results = [];
for (let key in obj) { 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]}`); results.push(`${key}=${obj[key]}`);
} else if (key == "forecastingPlatforms") { } else if (key === "forecastingPlatforms") {
let arr = obj[key].map((x) => x.value); let arr = obj[key].map((x) => x.value);
let arrstring = arr.join("|"); let arrstring = arr.join("|");
results.push(`${key}=${arrstring}`); results.push(`${key}=${arrstring}`);
@ -34,7 +51,7 @@ let transformObjectIntoUrlSlug = (obj) => {
}; };
/* Body */ /* Body */
export default function CommonDisplay({ const CommonDisplay: React.FC<Props> = ({
initialResults, initialResults,
defaultResults, defaultResults,
initialQueryParameters, initialQueryParameters,
@ -42,12 +59,10 @@ export default function CommonDisplay({
hasCapture, hasCapture,
hasAdvancedOptions, hasAdvancedOptions,
placeholder, placeholder,
setHasDisplayBeenCapturedOnChangeSearchInputs,
displaySeeMoreHint, displaySeeMoreHint,
displayForecastsWrapper, displayForecastsWrapper,
}) { }) => {
/* States */ /* States */
const router = useRouter();
const [queryParameters, setQueryParameters] = useState( const [queryParameters, setQueryParameters] = useState(
initialQueryParameters initialQueryParameters
@ -62,18 +77,18 @@ export default function CommonDisplay({
); );
const [results, setResults] = useState(initialResults); const [results, setResults] = useState(initialResults);
const [advancedOptions, showAdvancedOptions] = useState(false); const [advancedOptions, showAdvancedOptions] = useState(false);
const [hasDisplayBeenCaptured, setHasDisplayBeenCaptured] = useState(false);
const [whichResultToDisplayAndCapture, setWhichResultToDisplayAndCapture] = const [whichResultToDisplayAndCapture, setWhichResultToDisplayAndCapture] =
useState(0); useState(0);
const [showIdToggle, setShowIdToggle] = useState(false); const [showIdToggle, setShowIdToggle] = useState(false);
/* Functions which I want to have access to the Home namespace */ /* Functions which I want to have access to the Home namespace */
// I don't want to create an "defaultResults" object for each search. // I don't want to create an "defaultResults" object for each search.
async function executeSearchOrAnswerWithDefaultResults(queryData) { async function executeSearchOrAnswerWithDefaultResults(
console.log("executeSearchOrAnswerWithDefaultResults"); queryData: QueryParameters
) {
// the queryData object has the same contents as queryParameters. // the queryData object has the same contents as queryParameters.
// but I wanted to spare myself having to think about namespace conflicts. // but I wanted to spare myself having to think about namespace conflicts.
let filterManually = (queryData, results) => { let filterManually = (queryData: QueryParameters, results) => {
if ( if (
queryData.forecastingPlatforms && queryData.forecastingPlatforms &&
queryData.forecastingPlatforms.length > 0 queryData.forecastingPlatforms.length > 0
@ -85,7 +100,7 @@ export default function CommonDisplay({
forecastingPlatforms.includes(result.item.platform) forecastingPlatforms.includes(result.item.platform)
); );
} }
if (queryData.starsThreshold == 4) { if (queryData.starsThreshold === 4) {
results = results.filter( results = results.filter(
(result) => result.item.qualityindicators.stars >= 4 (result) => result.item.qualityindicators.stars >= 4
); );
@ -96,76 +111,44 @@ export default function CommonDisplay({
return results; return results;
}; };
let results; const queryIsEmpty =
switch ( !queryData || queryData.query == "" || queryData.query == undefined;
!queryData ||
queryData.query == "" || let results = queryIsEmpty
queryData.query == undefined ? filterManually(queryData, defaultResults || initialResults)
) { : await searchAccordingToQueryData(queryData);
case true:
console.log(1); setResults(results);
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
} }
// 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. // 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 = ( let getInfoToDisplayForecastsFunction = ({
displayForecastsFunction,
{
results, results,
hasDisplayBeenCaptured,
setHasDisplayBeenCaptured,
whichResultToDisplayAndCapture, whichResultToDisplayAndCapture,
showIdToggle, showIdToggle,
} }) => {
) => {
let numDisplayRounded = let numDisplayRounded =
queryParameters.numDisplay % 3 != 0 queryParameters.numDisplay % 3 != 0
? queryParameters.numDisplay + ? queryParameters.numDisplay +
(3 - (Math.round(queryParameters.numDisplay) % 3)) (3 - (Math.round(queryParameters.numDisplay) % 3))
: queryParameters.numDisplay; : queryParameters.numDisplay;
console.log("numDisplay", queryParameters.numDisplay); return displayForecastsWrapper({
console.log("numDisplayRounded", numDisplayRounded);
return displayForecastsFunction({
results, results,
numDisplay: numDisplayRounded, numDisplay: numDisplayRounded,
hasDisplayBeenCaptured,
setHasDisplayBeenCaptured,
whichResultToDisplayAndCapture, whichResultToDisplayAndCapture,
showIdToggle, showIdToggle,
}); });
}; };
/* State controllers */ /* State controllers */
let onChangeSearchInputs = (newQueryParameters) => { let onChangeSearchInputs = (newQueryParameters: QueryParameters) => {
setQueryParameters(newQueryParameters); // ({ ...newQueryParameters, processedUrlYet: true }); setQueryParameters(newQueryParameters); // ({ ...newQueryParameters, processedUrlYet: true });
console.log("onChangeSearchInputs/newQueryParameters", newQueryParameters);
setResults([]); setResults([]);
setHasDisplayBeenCaptured(setHasDisplayBeenCapturedOnChangeSearchInputs());
clearTimeout(searchSpeedSettings.timeoutId); clearTimeout(searchSpeedSettings.timeoutId);
let newtimeoutId = setTimeout(async () => { let newtimeoutId = setTimeout(async () => {
console.log(
"onChangeSearchInputs/timeout/newQueryParameters",
newQueryParameters
);
let urlSlug = transformObjectIntoUrlSlug(newQueryParameters); let urlSlug = transformObjectIntoUrlSlug(newQueryParameters);
let urlWithoutDefaultParameters = urlSlug let urlWithoutDefaultParameters = urlSlug
.replace("?query=&", "&") .replace("?query=&", "?")
.replace("&starsThreshold=2", "") .replace("&starsThreshold=2", "")
.replace("&numDisplay=21", "") .replace("&numDisplay=21", "")
.replace("&forecastsThreshold=0", "") .replace("&forecastsThreshold=0", "")
@ -180,7 +163,6 @@ export default function CommonDisplay({
); );
} }
} }
// router.push(urlWithoutDefaultParameters);
} }
executeSearchOrAnswerWithDefaultResults(newQueryParameters); executeSearchOrAnswerWithDefaultResults(newQueryParameters);
@ -191,9 +173,11 @@ export default function CommonDisplay({
}; };
/* Change the stars threshold */ /* Change the stars threshold */
let onChangeStars = (value) => { let onChangeStars = (value: number) => {
console.log("onChangeStars/buttons", value); let newQueryParameters: QueryParameters = {
let newQueryParameters = { ...queryParameters, starsThreshold: value }; ...queryParameters,
starsThreshold: value,
};
onChangeSearchInputs(newQueryParameters); onChangeSearchInputs(newQueryParameters);
}; };
@ -204,8 +188,7 @@ export default function CommonDisplay({
: "Show " + Math.round(value) + " result"; : "Show " + Math.round(value) + " result";
}; };
let onChangeSliderForNumDisplay = (event) => { let onChangeSliderForNumDisplay = (event) => {
console.log("onChangeSliderForNumDisplay", event[0]); let newQueryParameters: QueryParameters = {
let newQueryParameters = {
...queryParameters, ...queryParameters,
numDisplay: Math.round(event[0]), numDisplay: Math.round(event[0]),
}; };
@ -213,11 +196,10 @@ export default function CommonDisplay({
}; };
/* Change the forecast threshold */ /* Change the forecast threshold */
let displayFunctionNumForecasts = (value) => { let displayFunctionNumForecasts = (value: number) => {
return "# Forecasts > " + Math.round(value); return "# Forecasts > " + Math.round(value);
}; };
let onChangeSliderForNumForecasts = (event) => { let onChangeSliderForNumForecasts = (event) => {
console.log("onChangeSliderForNumForecasts", event[0]);
let newQueryParameters = { let newQueryParameters = {
...queryParameters, ...queryParameters,
forecastsThreshold: Math.round(event[0]), forecastsThreshold: Math.round(event[0]),
@ -226,15 +208,13 @@ export default function CommonDisplay({
}; };
/* Change on the search bar */ /* Change on the search bar */
let onChangeSearchBar = (value) => { let onChangeSearchBar = (value: string) => {
console.log("onChangeSearchBar/New query:", value);
let newQueryParameters = { ...queryParameters, query: value }; let newQueryParameters = { ...queryParameters, query: value };
onChangeSearchInputs(newQueryParameters); onChangeSearchInputs(newQueryParameters);
}; };
/*Change selected platforms */ /* Change selected platforms */
let onChangeSelectedPlatforms = (value) => { let onChangeSelectedPlatforms = (value) => {
console.log("onChangeSelectedPlatforms/Change in platforms:", value);
let newQueryParameters = { let newQueryParameters = {
...queryParameters, ...queryParameters,
forecastingPlatforms: value, forecastingPlatforms: value,
@ -249,41 +229,32 @@ export default function CommonDisplay({
// Capture functionality // Capture functionality
let onClickBack = () => { let onClickBack = () => {
let decreaseUntil0 = (num) => (num - 1 > 0 ? num - 1 : 0); let decreaseUntil0 = (num: number) => (num - 1 > 0 ? num - 1 : 0);
setWhichResultToDisplayAndCapture( setWhichResultToDisplayAndCapture(
decreaseUntil0(whichResultToDisplayAndCapture) decreaseUntil0(whichResultToDisplayAndCapture)
); );
setHasDisplayBeenCaptured(false);
}; };
let onClickForward = (whichResultToDisplayAndCapture) => { let onClickForward = (whichResultToDisplayAndCapture: number) => {
setWhichResultToDisplayAndCapture(whichResultToDisplayAndCapture + 1); setWhichResultToDisplayAndCapture(whichResultToDisplayAndCapture + 1);
setHasDisplayBeenCaptured(false);
// setTimeout(()=> {onClickForward(whichResultToDisplayAndCapture+1)}, 5000) // setTimeout(()=> {onClickForward(whichResultToDisplayAndCapture+1)}, 5000)
}; };
/* Final return */ /* Final return */
return ( return (
<Fragment key="index"> <Fragment>
<label <label className="mb-4 mt-4 flex flex-row justify-center items-center">
className="mb-4 mt-4 flex flex-row justify-center items-center" {hasSearchbar ? (
key={"common-1"} <div className="w-10/12 mb-2">
>
<div
className={`w-10/12 mb-2 ${hasSearchbar ? "" : "hidden"}`}
key={"common-1-1"}
>
<Form <Form
value={queryParameters.query} value={queryParameters.query}
onChange={onChangeSearchBar} onChange={onChangeSearchBar}
placeholder={placeholder} placeholder={placeholder}
/> />
</div> </div>
<div ) : null}
className={`w-2/12 flex justify-center ml-4 md:ml-2 lg:ml-0 ${
hasAdvancedOptions ? "" : "hidden" {hasAdvancedOptions ? (
}`} <div className="w-2/12 flex justify-center ml-4 md:ml-2 lg:ml-0">
key={"common-1-2"}
>
<button <button
className="text-gray-500 text-sm mb-2" className="text-gray-500 text-sm mb-2"
onClick={() => showAdvancedOptions(!advancedOptions)} onClick={() => showAdvancedOptions(!advancedOptions)}
@ -291,79 +262,53 @@ export default function CommonDisplay({
Advanced options Advanced options
</button> </button>
</div> </div>
<div ) : null}
className={`w-2/12 flex justify-center ml-4 md:ml-2 gap-1 lg:ml-0 ${
hasCapture ? "" : "hidden" {hasCapture ? (
}`} <div className="w-2/12 flex justify-center ml-4 md:ml-2 gap-1 lg:ml-0">
key={"common-1-3"}
>
<button <button
className="text-blue-500 cursor-pointer text-xl mb-3 pr-3 hover:text-blue-600" className="text-blue-500 cursor-pointer text-xl mb-3 pr-3 hover:text-blue-600"
onClick={() => onClickBack()} onClick={() => onClickBack()}
key={"common-1-3-1"}
> >
</button> </button>
<button <button
className="text-blue-500 cursor-pointer text-xl mb-3 pl-3 hover:text-blue-600" className="text-blue-500 cursor-pointer text-xl mb-3 pl-3 hover:text-blue-600"
onClick={() => onClickForward(whichResultToDisplayAndCapture)} onClick={() => onClickForward(whichResultToDisplayAndCapture)}
key={"common-1-3-2"}
> >
</button> </button>
</div> </div>
) : null}
</label> </label>
<div {hasAdvancedOptions && advancedOptions ? (
className={`flex-1 flex-col mx-auto justify-center items-center w-full ${ <div className="flex-1 flex-col mx-auto justify-center items-center w-full">
hasAdvancedOptions && advancedOptions ? "" : "hidden" <div className="grid sm:grid-rows-4 sm:grid-cols-1 md:grid-rows-2 lg:grid-rows-2 grid-cols-1 md:grid-cols-3 lg:grid-cols-3 items-center content-center bg-gray-50 rounded-md px-8 pt-4 pb-1 shadow mb-4">
}`} <div className="flex row-start-1 row-end-1 col-start-1 col-end-4 md:row-span-1 md:col-start-1 md:col-end-1 md:row-start-1 md:row-end-1 lg:row-span-1 lg:col-start-1 lg:col-end-1 lg:row-start-1 lg:row-end-1 items-center justify-center mb-4">
key={"common-2"}
>
<div
className="grid sm:grid-rows-4 sm:grid-cols-1 md:grid-rows-2 lg:grid-rows-2 grid-cols-1 md:grid-cols-3 lg:grid-cols-3 items-center content-center bg-gray-50 rounded-md px-8 pt-4 pb-1 shadow mb-4"
key={"common-2-1"}
>
<div
className="flex row-start-1 row-end-1 col-start-1 col-end-4 md:row-span-1 md:col-start-1 md:col-end-1 md:row-start-1 md:row-end-1 lg:row-span-1 lg:col-start-1 lg:col-end-1 lg:row-start-1 lg:row-end-1 items-center justify-center mb-4"
key={"common-2-1-1"}
>
<SliderElement <SliderElement
onChange={onChangeSliderForNumForecasts} onChange={onChangeSliderForNumForecasts}
value={queryParameters.forecastsThreshold} value={queryParameters.forecastsThreshold}
displayFunction={displayFunctionNumForecasts} displayFunction={displayFunctionNumForecasts}
key={"common-2-1-1-1"}
/> />
</div> </div>
<div <div className="flex row-start-2 row-end-2 col-start-1 col-end-4 md:row-start-1 md:row-end-1 md:col-start-2 md:col-end-2 lg:row-start-1 lg:row-end-1 lg:col-start-2 items-center justify-center mb-4">
className="flex row-start-2 row-end-2 col-start-1 col-end-4 md:row-start-1 md:row-end-1 md:col-start-2 md:col-end-2 lg:row-start-1 lg:row-end-1 lg:col-start-2 md:col-end-2 items-center justify-center mb-4"
key={"common-2-1-2"}
>
<ButtonsForStars <ButtonsForStars
onChange={onChangeStars} onChange={onChangeStars}
value={queryParameters.starsThreshold} value={queryParameters.starsThreshold}
key={"common-2-1-2-1"}
/> />
</div> </div>
<div <div className="flex row-start-3 row-end-3 col-start-1 col-end-4 md:col-start-3 md:col-end-3 md:row-start-1 md:row-end-1 lg:col-start-3 lg:col-end-3 lg:row-start-1 lg:row-end-1 items-center justify-center mb-4">
className="flex row-start-3 row-end-3 col-start-1 col-end-4 md:col-start-3 md:col-end-3 md:row-start-1 md:row-end-1 lg:col-start-3 lg:col-end-3 lg:row-start-1 lg:row-end-1 items-center justify-center mb-4"
key={"common-2-1-3"}
>
<SliderElement <SliderElement
value={queryParameters.numDisplay} value={queryParameters.numDisplay}
onChange={onChangeSliderForNumDisplay} onChange={onChangeSliderForNumDisplay}
displayFunction={displayFunctionNumDisplaySlider} displayFunction={displayFunctionNumDisplaySlider}
key={"common-2-1-3-1"}
/> />
</div> </div>
<div <div className="flex col-span-3 items-center justify-center">
className="flex col-span-3 items-center justify-center"
key={"common-2-1-4"}
>
<MultiSelectPlatform <MultiSelectPlatform
value={queryParameters.forecastingPlatforms} value={queryParameters.forecastingPlatforms}
onChange={onChangeSelectedPlatforms} onChange={onChangeSelectedPlatforms}
key={"common-2-1-4-1"}
/> />
</div> </div>
<button <button
@ -374,25 +319,26 @@ export default function CommonDisplay({
</button> </button>
</div> </div>
</div> </div>
) : null}
<div key={"common-3"}> <div>
{getInfoToDisplayForecastsFunction(displayForecastsWrapper, { {getInfoToDisplayForecastsFunction({
results, results,
hasDisplayBeenCaptured,
setHasDisplayBeenCaptured,
whichResultToDisplayAndCapture, whichResultToDisplayAndCapture,
showIdToggle, showIdToggle,
})} })}
</div> </div>
<div className={`${displaySeeMoreHint ? "" : "hidden"}`} key={"common-4"}>
{displaySeeMoreHint ? (
<div>
<p <p
className={`mt-4 mb-4 ${ className={`mt-4 mb-4 ${
!results || !results ||
(results.length != 0 && queryParameters.numDisplay < results.length) (results.length != 0 &&
queryParameters.numDisplay < results.length)
? "" ? ""
: "hidden" : "hidden"
}`} }`}
key={"common-4-1"}
> >
{"Can't find what you were looking for?"} {"Can't find what you were looking for?"}
<span <span
@ -405,7 +351,6 @@ export default function CommonDisplay({
numDisplay: queryParameters.numDisplay * 2, numDisplay: queryParameters.numDisplay * 2,
}); });
}} }}
key={"common-4-1-1"}
> >
{" Show more, or"} {" Show more, or"}
</span>{" "} </span>{" "}
@ -413,13 +358,16 @@ export default function CommonDisplay({
href="https://www.metaculus.com/questions/create/" href="https://www.metaculus.com/questions/create/"
className="cursor-pointer text-blue-800 no-underline" className="cursor-pointer text-blue-800 no-underline"
target="_blank" target="_blank"
key={"common-4-1-2"}
> >
suggest a question on Metaculus suggest a question on Metaculus
</a> </a>
</p> </p>
</div> </div>
) : null}
<br></br> <br></br>
</Fragment> </Fragment>
); );
} };
export default CommonDisplay;

View File

@ -15,22 +15,12 @@ export function displayForecastsWrapperForSearch({
export function displayForecastsWrapperForCapture({ export function displayForecastsWrapperForCapture({
results, results,
hasDisplayBeenCaptured,
setHasDisplayBeenCaptured,
whichResultToDisplayAndCapture, whichResultToDisplayAndCapture,
}) { }) {
console.log({
results,
hasDisplayBeenCaptured,
setHasDisplayBeenCaptured,
whichResultToDisplayAndCapture,
});
return ( return (
<div className="grid grid-cols-1 w-full justify-center"> <div className="grid grid-cols-1 w-full justify-center">
{displayOneForecast({ {displayOneForecast({
result: results[whichResultToDisplayAndCapture], result: results[whichResultToDisplayAndCapture],
hasDisplayBeenCaptured,
setHasDisplayBeenCaptured,
})} })}
</div> </div>
); );

View File

@ -1,10 +1,11 @@
import domtoimage from "dom-to-image"; // https://github.com/tsayen/dom-to-image import domtoimage from 'dom-to-image'; // https://github.com/tsayen/dom-to-image
import { useRef, useState } from "react"; import { useEffect, useRef, useState } from 'react';
import { CopyToClipboard } from "react-copy-to-clipboard"; import { CopyToClipboard } from 'react-copy-to-clipboard';
import { uploadToImgur } from "../worker/uploadToImgur";
import { displayForecast } from "./displayForecasts.js";
function displayOneForecastInner(result, containerRef, onLoadCallback) { import { uploadToImgur } from '../worker/uploadToImgur';
import { displayForecast } from './displayForecasts.js';
function displayOneForecastInner(result, containerRef) {
return ( return (
<div ref={containerRef}> <div ref={containerRef}>
{result {result
@ -21,7 +22,6 @@ function displayOneForecastInner(result, containerRef, onLoadCallback) {
let domToImageWrapper = (reactRef) => { let domToImageWrapper = (reactRef) => {
let node = reactRef.current; let node = reactRef.current;
console.log(node);
const scale = 3; // Increase for better quality const scale = 3; // Increase for better quality
const style = { const style = {
transform: "scale(" + scale + ")", transform: "scale(" + scale + ")",
@ -36,7 +36,6 @@ let domToImageWrapper = (reactRef) => {
style, style,
}; };
let image = domtoimage.toPng(node, param); let image = domtoimage.toPng(node, param);
console.log(image);
return image; return image;
}; };
@ -103,18 +102,17 @@ let generateIframeURL = (result) => {
let iframeURL = ""; let iframeURL = "";
if (result) { if (result) {
// if check not strictly necessary today // if check not strictly necessary today
iframeURL = result.item.url let parts = result.item.url
.replace("questions", "questions/embed") .replace("questions", "questions/embed")
.split("/"); .split("/");
iframeURL.pop(); parts.pop();
iframeURL.pop(); parts.pop();
iframeURL = iframeURL.join("/"); iframeURL = parts.join("/");
} }
return iframeURL; return iframeURL;
}; };
let metaculusEmbed = (result) => { let metaculusEmbed = (result) => {
//console.log(item.url)
let platform = ""; let platform = "";
let iframeURL = ""; let iframeURL = "";
if (result) { if (result) {
@ -171,20 +169,24 @@ let generateMetaculusSource = (result, hasDisplayBeenCaptured) => {
} }
}; };
export default function displayOneForecast({ interface Props {
result, result: any;
hasDisplayBeenCaptured, }
setHasDisplayBeenCaptured,
}) { const DisplayOneForecast: React.FC<Props> = ({ result }) => {
const [hasDisplayBeenCaptured, setHasDisplayBeenCaptured] = useState(false);
useEffect(() => {
setHasDisplayBeenCaptured(false);
}, [result]);
const containerRef = useRef(null); const containerRef = useRef(null);
const [imgSrc, setImgSrc] = useState(""); const [imgSrc, setImgSrc] = useState("");
const [mainButtonStatus, setMainButtonStatus] = useState( const [mainButtonStatus, setMainButtonStatus] = useState(
"Capture image and generate code" "Capture image and generate code"
); );
const [clickedAlreadyBool, setClickAlreadyBool] = useState(false);
let exportAsPictureAndCode = () => { let exportAsPictureAndCode = () => {
console.log(containerRef.current);
let handleGettingImgurlImage = (imgurUrl) => { let handleGettingImgurlImage = (imgurUrl) => {
setImgSrc(imgurUrl); setImgSrc(imgurUrl);
setMainButtonStatus("Done!"); setMainButtonStatus("Done!");
@ -215,7 +217,7 @@ export default function displayOneForecast({
return ( return (
<button <button
onClick={() => onCaptureButtonClick()} onClick={() => onCaptureButtonClick()}
className="bg-blue-500 cursor-pointer px-5 py-4 bg-white rounded-md shadow text-white hover:bg-blue-600 active:bg-gray-700" className="bg-blue-500 cursor-pointer px-5 py-4 rounded-md shadow text-white hover:bg-blue-600 active:bg-gray-700"
> >
{mainButtonStatus} {mainButtonStatus}
</button> </button>
@ -232,10 +234,7 @@ export default function displayOneForecast({
{generateCaptureButton(result, onCaptureButtonClick)} {generateCaptureButton(result, onCaptureButtonClick)}
</div> </div>
<div className="flex col-span-1 items-center justify-center"> <div className="flex col-span-1 items-center justify-center">
<img <img src={imgSrc} className={hasDisplayBeenCaptured ? "" : "hidden"} />
src={imgSrc}
className={hasDisplayBeenCaptured ? "" : "hidden"}
></img>
</div> </div>
<div className="flex col-span-1 items-center justify-center"> <div className="flex col-span-1 items-center justify-center">
<div>{generateSource(result, imgSrc, hasDisplayBeenCaptured)}</div> <div>{generateSource(result, imgSrc, hasDisplayBeenCaptured)}</div>
@ -249,7 +248,9 @@ export default function displayOneForecast({
<br></br> <br></br>
</div> </div>
); );
} };
export default DisplayOneForecast;
// https://stackoverflow.com/questions/39501289/in-reactjs-how-to-copy-text-to-clipboard // 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 // Note: https://stackoverflow.com/questions/66016033/can-no-longer-upload-images-to-imgur-from-localhost

View File

@ -1,7 +1,7 @@
import chroma from "chroma-js"; import chroma from "chroma-js";
import React from "react"; import React from "react";
import Select from "react-select"; import Select from "react-select";
import { platformsWithLabels } from "../platforms.js"; import { platformsWithLabels } from "../platforms";
const colourStyles = { const colourStyles = {
control: (styles) => ({ ...styles, backgroundColor: "white" }), control: (styles) => ({ ...styles, backgroundColor: "white" }),

View File

@ -42,11 +42,19 @@ export const platformNames = [
"X-risk estimates", "X-risk estimates",
]; ];
export const platformsWithLabels = platformNames.map((name, i) => ({ export interface PlatformWithLabel {
value: string;
label: string;
color: string;
}
export const platformsWithLabels: PlatformWithLabel[] = platformNames.map(
(name, i) => ({
value: name, value: name,
label: name, label: name,
color: distinctColors[i], color: distinctColors[i],
})); })
);
export const platforms = platformsWithLabels; export const platforms = platformsWithLabels;