From 222e82a3eb89b26ea1ec318519e440947fb2ae84 Mon Sep 17 00:00:00 2001 From: chrisrzhou Date: Sat, 8 Aug 2020 19:37:22 -0700 Subject: [PATCH] detypescriptify --- .eslintrc.js | 5 +- package.json | 3 +- src/components/{About.tsx => About.js} | 17 ++--- src/components/{App.tsx => App.js} | 2 +- src/components/Button.js | 11 +++ src/components/Details.js | 70 ++++++++++++++++++ src/components/Details.tsx | 72 ------------------- src/components/Fade.js | 30 ++++++++ .../Globe.js} | 60 ++++++++++++---- src/components/Globe.tsx | 52 -------------- src/components/{Intro.tsx => Intro.js} | 18 ++--- src/components/{ui/Link.tsx => Link.js} | 13 +--- src/components/Overlay.js | 58 +++++++++++++++ src/components/Overlay.tsx | 65 ----------------- src/components/ui/Blur.tsx | 29 -------- src/components/ui/Button.tsx | 16 ----- src/config.js | 8 +-- src/data/crawl.js | 4 -- src/{index.tsx => index.js} | 2 +- src/index.scss | 19 +++++ src/react-app-env.d.ts | 2 - src/state/StateProvider.js | 15 ++++ src/state/StateProvider.tsx | 25 ------- src/state/{index.ts => index.js} | 9 ++- src/state/{selectors.ts => selectors.js} | 10 ++- src/types.ts | 33 --------- tsconfig.json | 25 ------- 27 files changed, 282 insertions(+), 391 deletions(-) rename src/components/{About.tsx => About.js} (86%) rename src/components/{App.tsx => App.js} (88%) create mode 100644 src/components/Button.js create mode 100644 src/components/Details.js delete mode 100644 src/components/Details.tsx create mode 100644 src/components/Fade.js rename src/{markerRenderer.ts => components/Globe.js} (61%) delete mode 100644 src/components/Globe.tsx rename src/components/{Intro.tsx => Intro.js} (50%) rename src/components/{ui/Link.tsx => Link.js} (71%) create mode 100644 src/components/Overlay.js delete mode 100644 src/components/Overlay.tsx delete mode 100644 src/components/ui/Blur.tsx delete mode 100644 src/components/ui/Button.tsx rename src/{index.tsx => index.js} (91%) create mode 100644 src/state/StateProvider.js delete mode 100644 src/state/StateProvider.tsx rename src/state/{index.ts => index.js} (68%) rename src/state/{selectors.ts => selectors.js} (58%) delete mode 100644 src/types.ts delete mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 19b9d59..21176e5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,14 +4,12 @@ module.exports = { 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', 'prettier', - 'prettier/@typescript-eslint', ], - parser: '@typescript-eslint/parser', plugins: [ - '@typescript-eslint', 'prettier', ], rules: { + '@typescript-eslint/explicit-function-return-type': 'off', 'prettier/prettier': [ 'error', { @@ -23,6 +21,7 @@ module.exports = { useTabs: false, }, ], + 'react/prop-types': 'off', }, settings: { react: { diff --git a/package.json b/package.json index 4ddce49..b7297cb 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "crawl": "npm run untrack && babel-node ./src/data/crawl.js", "build": "npm run clean && npm run crawl && react-scripts build", "clean": "rm -rf build", - "lint": "eslint --ext .js,.jsx,.ts,.tsx, --fix src", + "lint": "eslint --fix .", "start": "react-scripts start", "untrack": "git update-index --assume-unchanged ./src/data/data.json" }, @@ -40,7 +40,6 @@ "react-dom": "^16.8.6", "react-globe": "^5.0.1", "react-scripts": "3.4.1", - "react-spring": "^8.0.19", "three": "^0.119.1", "typescript": "^3.4.4" }, diff --git a/src/components/About.tsx b/src/components/About.js similarity index 86% rename from src/components/About.tsx rename to src/components/About.js index 197d804..3539960 100644 --- a/src/components/About.tsx +++ b/src/components/About.js @@ -1,17 +1,12 @@ import React from 'react'; -import Blur from './ui/Blur'; -import Button from './ui/Button'; -import Link from './ui/Link'; +import Fade from './Fade'; +import Button from './Button'; +import Link from './Link'; -interface Props { - onHide: () => void; - shown: boolean; -} - -function About({ onHide, shown }: Props): React.ReactElement { +function About({ onHide, shown }) { return ( - +

About

@@ -48,7 +43,7 @@ function About({ onHide, shown }: Props): React.ReactElement {

-
+ ); } diff --git a/src/components/App.tsx b/src/components/App.js similarity index 88% rename from src/components/App.tsx rename to src/components/App.js index 563a902..87e0232 100644 --- a/src/components/App.tsx +++ b/src/components/App.js @@ -5,7 +5,7 @@ import Globe from './Globe'; import Intro from './Intro'; import Overlay from './Overlay'; -function App(): React.ReactElement { +function App() { return ( <> diff --git a/src/components/Button.js b/src/components/Button.js new file mode 100644 index 0000000..bfa1cfc --- /dev/null +++ b/src/components/Button.js @@ -0,0 +1,11 @@ +import React from 'react'; + +function Button({ label, onClick }) { + return ( + + ); +} + +export default Button; diff --git a/src/components/Details.js b/src/components/Details.js new file mode 100644 index 0000000..6f6f87a --- /dev/null +++ b/src/components/Details.js @@ -0,0 +1,70 @@ +import React from 'react'; + +import { getRandomMarker } from '../state/selectors'; +import { useStateValue } from '../state/StateProvider'; +import Fade from './Fade'; +import Button from './Button'; + +function getSearchURL(city, country, keyword) { + const formattedQuery = `${encodeURIComponent(city)}, ${encodeURIComponent( + country, + )} ${encodeURIComponent(keyword.join('|'))}`.replace(/(%20| )/g, '+'); + return `https://www.google.com/search?q=${formattedQuery}`; +} + +function Details() { + const [state, dispatch] = useStateValue(); + const { keyword, start, focusedMarker } = state; + const randomMarker = getRandomMarker(state); + + let content; + if (focusedMarker) { + const { city, countryCode, countryName, value } = focusedMarker; + const url = getSearchURL(city, countryName, keyword); + const relatedTopics = state.relatedTopics[countryCode] || []; + content = ( + <> +
+
+
+

+ {city}, {countryName} ({value}) +

+
+ RELATED TOPICS + {relatedTopics.map(({ topic, link }) => { + return ( + + {topic} + + ); + })} +
+ + View search results + +
+ + ); + } + + return ( + + {content} + + ); +} + +export default Details; diff --git a/src/components/Details.tsx b/src/components/Details.tsx deleted file mode 100644 index 323946d..0000000 --- a/src/components/Details.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; - -import { getRandomMarker } from '../state/selectors'; -import { useStateValue } from '../state/StateProvider'; -import { ActionType } from '../types'; -import Blur from './ui/Blur'; -import Button from './ui/Button'; - -function getSearchURL( - city: string, - country: string, - keyword: string[], -): string { - const formattedQuery = `${encodeURIComponent(city)}, ${encodeURIComponent( - country, - )} ${encodeURIComponent(keyword.join('|'))}`.replace(/(%20| )/g, '+'); - return `https://www.google.com/search?q=${formattedQuery}`; -} - -function Details(): React.ReactElement { - const [state, dispatch] = useStateValue(); - const { keyword, start, focusedMarker } = state; - const randomMarker = getRandomMarker(state); - if (!focusedMarker) { - return
; - } - const { city, countryCode, countryName, value } = focusedMarker; - const url = getSearchURL(city, countryName, keyword); - const relatedTopics = state.relatedTopics[countryCode] || []; - return ( - -
-
-
-

- {city}, {countryName} ({value}) -

-
- RELATED TOPICS - {relatedTopics.map( - ({ topic, link }): React.ReactNode => { - return ( - - {topic} - - ); - }, - )} -
- -
-
- ); -} - -export default Details; diff --git a/src/components/Fade.js b/src/components/Fade.js new file mode 100644 index 0000000..41c601d --- /dev/null +++ b/src/components/Fade.js @@ -0,0 +1,30 @@ +import React, { useEffect, useState } from 'react'; + +export default function Fade({ children, className, shown }) { + const [shouldRender, setShouldRender] = useState(shown); + + useEffect(() => { + if (shown) { + setShouldRender(true); + } + }, [shown]); + + const onAnimationEnd = () => { + if (!shown) { + setShouldRender(false); + } + }; + + return ( + shouldRender && ( +
+ {children} +
+ ) + ); +} diff --git a/src/markerRenderer.ts b/src/components/Globe.js similarity index 61% rename from src/markerRenderer.ts rename to src/components/Globe.js index 48c030c..bfec039 100644 --- a/src/markerRenderer.ts +++ b/src/components/Globe.js @@ -1,31 +1,36 @@ +import React from 'react'; +import ReactGlobe, { tween } from 'react-globe'; import * as THREE from 'three'; -import { Marker, tween } from 'react-globe'; +import config from '../config'; +import { useStateValue } from '../state/StateProvider'; +import Fade from './Fade'; -function random(scaleFactor: number): number { +import 'tippy.js/dist/tippy.css'; +import 'tippy.js/animations/scale.css'; + +const MARKER_COLOR = '#fcffbe'; +const MARKER_COMPANION_COLOR = '#fff9e6'; + +function random(scaleFactor) { return Math.random() > 0.5 ? scaleFactor * Math.random() : -scaleFactor * Math.random(); } -const MARKER_COLOR = '#fcffbe'; -const MARKER_COMPANION_COLOR = '#fff9e6'; - -export default function markerRenderer(marker: Marker): THREE.Object3D { +function markerRenderer(marker) { const size = Math.max(marker.value / 20, 1); const geometry = new THREE.SphereGeometry(size, 10, 10); const material = new THREE.MeshBasicMaterial({ color: new THREE.Color(MARKER_COLOR), }); - // add light const mesh = new THREE.Mesh(geometry, material); const light = new THREE.PointLight(MARKER_COLOR, 1, 0, 0); mesh.children = []; mesh.add(light); - // add companion markers based on size - const companions: THREE.Mesh[] = []; + const companions = []; for (let i = 0; i < 10; i++) { const companionGeometry = new THREE.SphereGeometry( Math.min((size * Math.random()) / 2, 1), @@ -41,8 +46,8 @@ export default function markerRenderer(marker: Marker): THREE.Object3D { mesh.add(companion); } - companions.forEach((companion, i: number): void => { - function animate(): void { + companions.forEach((companion, i) => { + function animate() { const from = { opacity: 0.1, position: companion.position.clone().toArray(), @@ -60,7 +65,7 @@ export default function markerRenderer(marker: Marker): THREE.Object3D { easingFunction: ['Quadratic', 'InOut'], onUpdate: () => { const [x, y, z] = from.position; - const companionMaterial = companion.material as THREE.MeshBasicMaterial; + const companionMaterial = companion.material; const intensityChange = random(0.05); if ( light.intensity + intensityChange > 0 && @@ -82,3 +87,34 @@ export default function markerRenderer(marker: Marker): THREE.Object3D { }); return mesh; } + +const options = { + ...config.options, + markerTooltipRenderer: (marker) => `${marker.city} (${marker.value})`, + markerRenderer, +}; + +function Globe() { + const [state, dispatch] = useStateValue(); + const { focusedMarker, start } = state; + const markers = start ? state.markers : []; + const focus = + focusedMarker !== undefined ? focusedMarker.coordinates : undefined; + return ( + + dispatch({ type: 'FOCUS', payload: marker })} + /> + + ); +} + +export default Globe; diff --git a/src/components/Globe.tsx b/src/components/Globe.tsx deleted file mode 100644 index 5de2c7a..0000000 --- a/src/components/Globe.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import ReactGlobe, { Coordinates, Marker, Options } from 'react-globe'; - -import config from '../config'; -import markerRenderer from '../markerRenderer'; -import { useStateValue } from '../state/StateProvider'; -import Blur from './ui/Blur'; -import { ActionType } from '../types'; - -import 'tippy.js/dist/tippy.css'; -import 'tippy.js/animations/scale.css'; - -const markerOptions = { - markerTooltipRenderer: (marker: Marker): string => - `${marker.city} (${marker.value})`, - markerRenderer, -}; - -const options = { - ...config.options, - ...markerOptions, -} as Options; - -function Globe(): React.ReactElement { - const [state, dispatch] = useStateValue(); - const { focusedMarker, start } = state; - // @ts-ignore - const markers = start ? state.markers : []; - const focus = - focusedMarker !== undefined ? focusedMarker.coordinates : undefined; - return ( - - - dispatch({ type: ActionType.Focus, payload: marker }) - } - /> - - ); -} - -export default Globe; diff --git a/src/components/Intro.tsx b/src/components/Intro.js similarity index 50% rename from src/components/Intro.tsx rename to src/components/Intro.js index db9a4ff..02ba99c 100644 --- a/src/components/Intro.tsx +++ b/src/components/Intro.js @@ -1,25 +1,21 @@ import React from 'react'; import { useStateValue } from '../state/StateProvider'; -import { ActionType } from '../types'; -import Blur from './ui/Blur'; -import Button from './ui/Button'; -import Link from './ui/Link'; +import Fade from './Fade'; +import Button from './Button'; +import Link from './Link'; -function Intro(): React.ReactElement { +function Intro() { const [{ keyword, start }, dispatch] = useStateValue(); return ( - +

Google Globe Trends

Visualizing {`"${keyword}"`} Google Trends with{' '}

- - ); -} - -export default Button; diff --git a/src/config.js b/src/config.js index 939cb4c..b7d0154 100644 --- a/src/config.js +++ b/src/config.js @@ -1,7 +1,7 @@ -// Update this file to customize trend data and globe UI -module.exports = { +// Update this file to customize trend data and globe properties +export default { data: { - keyword: ['covid', 'coronavirus'], + keyword: ['covid', 'covid19', 'coronavirus'], }, globeBackgroundTexture: 'https://raw.githubusercontent.com/chrisrzhou/react-globe/main/textures/background.png', @@ -13,7 +13,7 @@ module.exports = { ambientLightColor: '#b34444', ambientLightIntensity: 1, cameraAutoRotateSpeed: 0.01, - cameraRotateSpeed: 0.02, + cameraRotateSpeed: 0.2, enableCameraZoom: false, enableDefocus: false, globeCloudsOpacity: 0.1, diff --git a/src/data/crawl.js b/src/data/crawl.js index 7c69bbb..76b36cf 100644 --- a/src/data/crawl.js +++ b/src/data/crawl.js @@ -325,10 +325,6 @@ async function buildData({ keyword }) { const { countryCode } = trend; if (countryCode && !relatedTopics[countryCode]) { await wait(500); // wait/throttle 500ms - const test = await getRelatedTopics({ - keyword, - geo: countryCode, - }); relatedTopics[countryCode] = await getRelatedTopics({ keyword, geo: countryCode, diff --git a/src/index.tsx b/src/index.js similarity index 91% rename from src/index.tsx rename to src/index.js index de9d1ee..dd93e5c 100644 --- a/src/index.tsx +++ b/src/index.js @@ -7,7 +7,7 @@ import StateProvider from './state/StateProvider'; import './index.scss'; -function Root(): React.ReactElement { +function Root() { return ( diff --git a/src/index.scss b/src/index.scss index ed75c53..93c0eae 100644 --- a/src/index.scss +++ b/src/index.scss @@ -16,6 +16,25 @@ $border-color: #fcdcbf; $media-phone-max-width: 320px; +@keyframes fade-in { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +@keyframes fade-out { + 0% { + opacity: 1; + } + 25% { + opacity: 0; + } +} + + // basic dom styles body { * { diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 5b84ad7..6431bc5 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -1,3 +1 @@ /// - -declare module 'es6-tween'; diff --git a/src/state/StateProvider.js b/src/state/StateProvider.js new file mode 100644 index 0000000..738c721 --- /dev/null +++ b/src/state/StateProvider.js @@ -0,0 +1,15 @@ +import React, { createContext, useContext, useReducer } from 'react'; + +const StateContext = createContext(null); + +export default function StateProvider({ children, initialState, reducer }) { + return ( + + {children} + + ); +} + +export function useStateValue() { + return useContext(StateContext); +} diff --git a/src/state/StateProvider.tsx b/src/state/StateProvider.tsx deleted file mode 100644 index e8066b1..0000000 --- a/src/state/StateProvider.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { createContext, useContext, useReducer } from 'react'; - -import { Action, State } from '../types'; - -const StateContext = createContext(null); - -export default function StateProvider({ - children, - initialState, - reducer, -}: { - children: React.ReactElement; - initialState: State; - reducer: (state: State, action: Action) => State; -}): React.ReactElement { - return ( - - {children} - - ); -} - -export function useStateValue(): [State, React.Dispatch] { - return useContext(StateContext); -} diff --git a/src/state/index.ts b/src/state/index.js similarity index 68% rename from src/state/index.ts rename to src/state/index.js index 4f56d21..e6aa959 100644 --- a/src/state/index.ts +++ b/src/state/index.js @@ -1,8 +1,7 @@ import data from '../data/data.json'; import config from '../config'; -import { Action, ActionType, State } from '../types'; -export const initialState: State = { +export const initialState = { keyword: config.data.keyword, lastUpdated: data.lastUpdated, markers: data.trends, @@ -10,15 +9,15 @@ export const initialState: State = { start: false, }; -export function reducer(state: State, action: Action): State { +export function reducer(state, action) { const { type } = action; switch (type) { - case ActionType.Start: + case 'START': return { ...state, start: true, }; - case ActionType.Focus: + case 'FOCUS': return { ...state, focusedMarker: action.payload, diff --git a/src/state/selectors.ts b/src/state/selectors.js similarity index 58% rename from src/state/selectors.ts rename to src/state/selectors.js index 48e0075..447e6a6 100644 --- a/src/state/selectors.ts +++ b/src/state/selectors.js @@ -1,8 +1,6 @@ -import { Marker, State } from '../types'; - -export function getRandomMarker(state: State): Marker { +export function getRandomMarker(state) { const { focusedMarker, markers } = state; - const filteredMarkers = markers.filter((marker: Marker): boolean => { + const filteredMarkers = markers.filter((marker) => { if (!focusedMarker || focusedMarker.city !== marker.city) { return true; } @@ -11,9 +9,9 @@ export function getRandomMarker(state: State): Marker { return filteredMarkers[Math.floor(Math.random() * filteredMarkers.length)]; } -export function getTop5Markers(state: State): Marker[] { +export function getTop5Markers(state) { return state.markers .concat() - .sort((a, b): number => b.value - a.value) + .sort((a, b) => b.value - a.value) .slice(0, 5); } diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 2c10041..0000000 --- a/src/types.ts +++ /dev/null @@ -1,33 +0,0 @@ -export interface Marker { - id: string; - city: string; - coordinates: number[]; - countryCode: string; - countryName: string; - value: number; -} - -export interface RelatedTopic { - link: string; - topic: string; - value: number; -} - -export interface State { - focusedMarker?: Marker; - keyword: string[]; - lastUpdated: number; - markers: Marker[]; - relatedTopics: { [key: string]: RelatedTopic[] }; - start: boolean; -} - -export enum ActionType { - Focus = 'FOCUS', - Start = 'START', -} - -export interface Action { - type: ActionType; - payload?: any; -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 0980b23..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "preserve" - }, - "include": [ - "src" - ] -}