From 439a9045da6993be2abffe3701ec79e59f82a145 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Mon, 9 May 2022 21:37:28 +0400 Subject: [PATCH] refactor: some typescript improvements --- .../flow/rebuildNetliftySiteWithNewData.ts | 4 +- src/backend/platforms/betfair.ts | 7 ++- src/backend/platforms/givewellopenphil.ts | 4 +- src/backend/utils/algolia.ts | 5 +- src/web/display/DashboardCreator.tsx | 4 +- src/web/display/SliderElement.tsx | 36 ++++++------ .../components/QuestionCard/index.tsx | 2 +- src/web/questions/pages/QuestionPage.tsx | 2 +- src/web/search/components/QueryForm.tsx | 4 +- src/web/search/components/SearchScreen.tsx | 26 +++++---- src/web/urql.ts | 3 + src/web/worker/searchWithAlgolia.ts | 56 +++++++++---------- 12 files changed, 79 insertions(+), 74 deletions(-) diff --git a/src/backend/flow/rebuildNetliftySiteWithNewData.ts b/src/backend/flow/rebuildNetliftySiteWithNewData.ts index 85edd16..72ab28c 100644 --- a/src/backend/flow/rebuildNetliftySiteWithNewData.ts +++ b/src/backend/flow/rebuildNetliftySiteWithNewData.ts @@ -2,7 +2,7 @@ import axios from "axios"; import { applyIfSecretExists } from "../utils/getSecrets"; -async function rebuildNetlifySiteWithNewData_inner(cookie) { +async function rebuildNetlifySiteWithNewData_inner(cookie: string) { let payload = {}; let response = await axios.post(cookie, payload); let data = response.data; @@ -10,6 +10,6 @@ async function rebuildNetlifySiteWithNewData_inner(cookie) { } export async function rebuildNetlifySiteWithNewData() { - let cookie = process.env.REBUIDNETLIFYHOOKURL; + const cookie = process.env.REBUIDNETLIFYHOOKURL || ""; await applyIfSecretExists(cookie, rebuildNetlifySiteWithNewData_inner); } diff --git a/src/backend/platforms/betfair.ts b/src/backend/platforms/betfair.ts index 4af99fc..6e71ebe 100644 --- a/src/backend/platforms/betfair.ts +++ b/src/backend/platforms/betfair.ts @@ -8,10 +8,10 @@ import { FetchedQuestion, Platform } from "./"; const platformName = "betfair"; /* Definitions */ -let endpoint = process.env.SECRET_BETFAIR_ENDPOINT; +const endpoint = process.env.SECRET_BETFAIR_ENDPOINT; /* Utilities */ -let arraysEqual = (a, b) => { +const arraysEqual = (a: string[], b: string[]) => { if (a === b) return true; if (a == null || b == null) return false; if (a.length !== b.length) return false; @@ -26,7 +26,8 @@ let arraysEqual = (a, b) => { } return true; }; -let mergeRunners = (runnerCatalog, runnerBook) => { + +const mergeRunners = (runnerCatalog, runnerBook) => { let keys = Object.keys(runnerCatalog); let result = []; for (let key of keys) { diff --git a/src/backend/platforms/givewellopenphil.ts b/src/backend/platforms/givewellopenphil.ts index 22c37b8..e8d9754 100644 --- a/src/backend/platforms/givewellopenphil.ts +++ b/src/backend/platforms/givewellopenphil.ts @@ -8,8 +8,8 @@ import { Platform } from "./"; const platformName = "givewellopenphil"; /* Support functions */ -async function fetchPage(url: string) { - let response = await axios({ +async function fetchPage(url: string): Promise { + const response = await axios({ url: url, method: "GET", headers: { diff --git a/src/backend/utils/algolia.ts b/src/backend/utils/algolia.ts index 5728fec..e50c2f6 100644 --- a/src/backend/utils/algolia.ts +++ b/src/backend/utils/algolia.ts @@ -5,13 +5,14 @@ import { Question } from "@prisma/client"; import { prisma } from "../database/prisma"; import { platforms } from "../platforms"; -let cookie = process.env.ALGOLIA_MASTER_API_KEY; -const algoliaAppId = process.env.NEXT_PUBLIC_ALGOLIA_APP_ID; +let cookie = process.env.ALGOLIA_MASTER_API_KEY || ""; +const algoliaAppId = process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || ""; const client = algoliasearch(algoliaAppId, cookie); const index = client.initIndex("metaforecast"); export type AlgoliaQuestion = Omit & { timestamp: string; + optionsstringforsearch?: string; }; const getoptionsstringforsearch = (record: Question): string => { diff --git a/src/web/display/DashboardCreator.tsx b/src/web/display/DashboardCreator.tsx index 4e40e15..48ddde5 100644 --- a/src/web/display/DashboardCreator.tsx +++ b/src/web/display/DashboardCreator.tsx @@ -1,4 +1,4 @@ -import React, { EventHandler, SyntheticEvent, useState } from "react"; +import React, { ChangeEvent, EventHandler, SyntheticEvent, useState } from "react"; import { Button } from "../common/Button"; import { InfoBox } from "../common/InfoBox"; @@ -18,7 +18,7 @@ export const DashboardCreator: React.FC = ({ handleSubmit }) => { const [value, setValue] = useState(exampleInput); const [acting, setActing] = useState(false); - const handleChange = (event) => { + const handleChange = (event: ChangeEvent) => { setValue(event.target.value); }; diff --git a/src/web/display/SliderElement.tsx b/src/web/display/SliderElement.tsx index c14da06..9112d0f 100644 --- a/src/web/display/SliderElement.tsx +++ b/src/web/display/SliderElement.tsx @@ -1,6 +1,7 @@ -/* Imports */ import React from "react"; -import { Handles, Rail, Slider, Tracks } from "react-compound-slider"; +import { + GetHandleProps, GetTrackProps, Handles, Rail, Slider, SliderItem, Tracks +} from "react-compound-slider"; // https://sghall.github.io/react-compound-slider/#/getting-started/tutorial @@ -24,12 +25,11 @@ const railStyle = { }; /* Support functions */ -function Handle({ - handle: { id, value, percent }, - getHandleProps, - displayFunction, - handleWidth, -}) { +const Handle: React.FC<{ + handle: SliderItem; + getHandleProps: GetHandleProps; + displayFunction: (value: number) => string; +}> = ({ handle: { id, value, percent }, getHandleProps, displayFunction }) => { return ( <>
@@ -53,9 +53,13 @@ function Handle({ >
); -} +}; -function Track({ source, target, getTrackProps }) { +const Track: React.FC<{ + source: SliderItem; + target: SliderItem; + getTrackProps: GetTrackProps; +}> = ({ source, target, getTrackProps }) => { return (
); -} +}; interface Props { value: number; - onChange: (event: any) => void; + onChange: (value: number) => void; displayFunction: (value: number) => string; } /* Body */ -// Two functions, essentially identical. export const SliderElement: React.FC = ({ onChange, value, @@ -96,21 +99,20 @@ export const SliderElement: React.FC = ({ } domain={[0, 200]} values={[value]} - onChange={onChange} + onChange={(values) => onChange(values[0])} > {({ getRailProps }) =>
} {({ handles, getHandleProps }) => ( -
+
{handles.map((handle) => ( ))}
@@ -118,7 +120,7 @@ export const SliderElement: React.FC = ({ {({ tracks, getTrackProps }) => ( -
+
{tracks.map(({ id, source, target }) => ( = ({
)} - {question.platform.id === "guesstimate" && ( + {question.platform.id === "guesstimate" && question.visualization && (
- {question.platform.id === "guesstimate" ? ( + {question.platform.id === "guesstimate" && question.visualization ? ( void; @@ -9,7 +11,7 @@ export const QueryForm: React.FC = ({ onChange, placeholder, }) => { - const handleInputChange = (event) => { + const handleInputChange = (event: ChangeEvent) => { event.preventDefault(); onChange(event.target.value); // In this case, the query, e.g. "COVID.19" }; diff --git a/src/web/search/components/SearchScreen.tsx b/src/web/search/components/SearchScreen.tsx index 63c54cd..31d09c7 100644 --- a/src/web/search/components/SearchScreen.tsx +++ b/src/web/search/components/SearchScreen.tsx @@ -1,5 +1,5 @@ import { useRouter } from "next/router"; -import React, { Fragment, useMemo, useState } from "react"; +import React, { useMemo, useState } from "react"; import { useQuery } from "urql"; import { PlatformConfig } from "../../../backend/platforms"; @@ -126,7 +126,8 @@ export const SearchScreen: React.FC = ({ }; const updateRoute = () => { - const stringify = (key: string, value: any) => { + const stringify = (key: string, obj: { [k: string]: any }) => { + const value = obj[key]; if (key === "forecastingPlatforms") { return value.join("|"); } else { @@ -134,15 +135,16 @@ export const SearchScreen: React.FC = ({ } }; - const query = {}; + const query: { [k: string]: string } = {}; for (const key of Object.keys(defaultQueryParameters)) { - const value = stringify(key, queryParameters[key]); - const defaultValue = stringify(key, defaultQueryParameters[key]); + const value = stringify(key, queryParameters); + const defaultValue = stringify(key, defaultQueryParameters); if (value === defaultValue) continue; query[key] = value; } - if (numDisplay !== defaultNumDisplay) query["numDisplay"] = numDisplay; + if (numDisplay !== defaultNumDisplay) + query["numDisplay"] = String(numDisplay); router.replace( { @@ -191,8 +193,8 @@ export const SearchScreen: React.FC = ({ (Math.round(value) === 1 ? "" : "s") ); }; - const onChangeSliderForNumDisplay = (event) => { - setNumDisplay(Math.round(event[0])); + const onChangeSliderForNumDisplay = (value: number) => { + setNumDisplay(Math.round(value)); setForceSearch(forceSearch + 1); // FIXME - force new search iff numDisplay is greater than last search limit }; @@ -200,10 +202,10 @@ export const SearchScreen: React.FC = ({ const displayFunctionNumForecasts = (value: number) => { return "# Forecasts > " + Math.round(value); }; - const onChangeSliderForNumForecasts = (event) => { + const onChangeSliderForNumForecasts = (value: number) => { setQueryParameters({ ...queryParameters, - forecastsThreshold: Math.round(event[0]), + forecastsThreshold: Math.round(value), }); }; @@ -230,7 +232,7 @@ export const SearchScreen: React.FC = ({ /* Final return */ return ( - + <> + ); }; diff --git a/src/web/urql.ts b/src/web/urql.ts index c083933..a691bdd 100644 --- a/src/web/urql.ts +++ b/src/web/urql.ts @@ -36,5 +36,8 @@ export const getUrqlClientOptions = (ssr: SSRExchange) => ({ export const ssrUrql = () => { const ssrCache = ssrExchange({ isClient: false }); const client = initUrqlClient(getUrqlClientOptions(ssrCache), false); + if (!client) { + throw new Error("Expected non-null client instance from initUrqlClient"); + } return [ssrCache, client] as const; }; diff --git a/src/web/worker/searchWithAlgolia.ts b/src/web/worker/searchWithAlgolia.ts index 9eba5b4..7b53b14 100644 --- a/src/web/worker/searchWithAlgolia.ts +++ b/src/web/worker/searchWithAlgolia.ts @@ -1,18 +1,31 @@ import algoliasearch from "algoliasearch"; +import { Hit } from "@algolia/client-search"; + import { AlgoliaQuestion } from "../../backend/utils/algolia"; const client = algoliasearch( - process.env.NEXT_PUBLIC_ALGOLIA_APP_ID, - process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY + process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || "", + process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY || "" ); const index = client.initIndex("metaforecast"); -let buildFilter = ({ +interface SearchOpts { + queryString: string; + hitsPerPage?: number; + starsThreshold: number; + filterByPlatforms: string[]; + forecastsThreshold: number; +} + +const buildFilter = ({ starsThreshold, filterByPlatforms, forecastsThreshold, -}) => { +}: Pick< + SearchOpts, + "starsThreshold" | "filterByPlatforms" | "forecastsThreshold" +>) => { const starsFilter = starsThreshold ? `qualityindicators.stars >= ${starsThreshold}` : null; @@ -35,26 +48,15 @@ let buildFilter = ({ return finalFilter; }; -let buildFacetFilter = ({ filterByPlatforms }) => { - let platformsFilter = []; - if (filterByPlatforms.length > 0) { - platformsFilter = [ - [filterByPlatforms.map((platform) => `platform:${platform}`)], - ]; - } - console.log(platformsFilter); - console.log( - "searchWithAlgolia.js/searchWithAlgolia/buildFacetFilter", - platformsFilter - ); - return platformsFilter; -}; - -let noExactMatch = (queryString, result) => { +const noExactMatch = (queryString: string, result: Hit) => { queryString = queryString.toLowerCase(); - let title = result.title.toLowerCase(); - let description = result.description.toLowerCase(); - let optionsstringforsearch = result.optionsstringforsearch.toLowerCase(); + + const title = result.title.toLowerCase(); + const description = result.description.toLowerCase(); + const optionsstringforsearch = ( + result.optionsstringforsearch || "" + ).toLowerCase(); + return !( title.includes(queryString) || description.includes(queryString) || @@ -62,14 +64,6 @@ let noExactMatch = (queryString, result) => { ); }; -interface SearchOpts { - queryString: string; - hitsPerPage?: number; - starsThreshold: number; - filterByPlatforms: string[]; - forecastsThreshold: number; -} - // only query string export default async function searchWithAlgolia({ queryString,