From a80784966565ed406a9062493fb7574b7ff4b634 Mon Sep 17 00:00:00 2001 From: Mark Chigrin Date: Mon, 7 Feb 2022 21:14:33 +0000 Subject: [PATCH] Add prettier Add favorites page --- .prettierrc | 7 ++++ package.json | 3 ++ src/app/hooks.ts | 5 +++ src/app/index.tsx | 10 +++--- src/app/store.ts | 16 +++++---- src/components/Button.tsx | 39 +++++++++++----------- src/components/Column.tsx | 9 +++++ src/components/Container.tsx | 10 ++++++ src/components/Joke.tsx | 39 ++++++++++++++++++++++ src/components/Like.tsx | 8 +++++ src/index.css | 2 +- src/index.tsx | 16 ++++----- src/pages/Favorites.tsx | 25 ++++++++++++++ src/pages/Main.tsx | 62 +++++++++++------------------------ src/pages/index.tsx | 18 ++++++---- src/services/api.ts | 19 +++++------ src/services/favoriteJokes.ts | 24 ++++++++++++++ src/types/index.ts | 7 ++-- yarn.lock | 10 +++--- 19 files changed, 222 insertions(+), 107 deletions(-) create mode 100644 .prettierrc create mode 100644 src/app/hooks.ts create mode 100644 src/components/Column.tsx create mode 100644 src/components/Container.tsx create mode 100644 src/components/Joke.tsx create mode 100644 src/components/Like.tsx create mode 100644 src/pages/Favorites.tsx create mode 100644 src/services/favoriteJokes.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..23b8710 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "printWidth": 120 +} diff --git a/package.json b/package.json index ef8d458..9faa5bf 100644 --- a/package.json +++ b/package.json @@ -40,5 +40,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "prettier": "^2.5.1" } } diff --git a/src/app/hooks.ts b/src/app/hooks.ts new file mode 100644 index 0000000..e165076 --- /dev/null +++ b/src/app/hooks.ts @@ -0,0 +1,5 @@ +import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' +import type { RootState, AppDispatch } from './store' + +export const useAppDispatch = () => useDispatch() +export const useAppSelector: TypedUseSelectorHook = useSelector diff --git a/src/app/index.tsx b/src/app/index.tsx index 119630d..5a21d19 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,9 +1,9 @@ -import { Provider } from "react-redux"; -import { BrowserRouter } from "react-router-dom"; +import { Provider } from 'react-redux' +import { BrowserRouter } from 'react-router-dom' -import { Pages } from "pages"; +import { Pages } from 'pages' -import { store } from "./store"; +import { store } from './store' export const App: React.VFC = () => ( @@ -11,4 +11,4 @@ export const App: React.VFC = () => ( -); +) diff --git a/src/app/store.ts b/src/app/store.ts index 322d203..7891fea 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,13 +1,15 @@ -import { configureStore } from "@reduxjs/toolkit"; +import { configureStore } from '@reduxjs/toolkit' -import { api } from "services/api"; +import { api } from 'services/api' +import { favoriteJokes } from 'services/favoriteJokes' export const store = configureStore({ reducer: { [api.reducerPath]: api.reducer, + [favoriteJokes.name]: favoriteJokes.reducer, }, - middleware: (getDefaultMiddleware) => [ - ...getDefaultMiddleware(), - api.middleware, - ], -}); + middleware: (getDefaultMiddleware) => [...getDefaultMiddleware(), api.middleware], +}) + +export type RootState = ReturnType +export type AppDispatch = typeof store.dispatch diff --git a/src/components/Button.tsx b/src/components/Button.tsx index e7f99dc..bf65c50 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,22 +1,23 @@ -import clsx from "clsx"; +import clsx from 'clsx' +import React from 'react' +import { Link, LinkProps } from 'react-router-dom' const variants = { - primary: "is-primary", - success: "is-success", - warning: "is-warning", - error: "is-error", -} as const; + primary: 'is-primary', + success: 'is-success', + warning: 'is-warning', + error: 'is-error', +} as const -export const Button: React.FC< - React.ComponentPropsWithRef<"button"> & { variant?: keyof typeof variants } -> = ({ variant, ...props }) => ( - + Back + + + ) +} diff --git a/src/pages/Main.tsx b/src/pages/Main.tsx index e17029e..693faed 100644 --- a/src/pages/Main.tsx +++ b/src/pages/Main.tsx @@ -1,47 +1,25 @@ -import styled from "@emotion/styled"; -import { Button } from "components/Button"; -import { useState } from "react"; -import { useGetRandomJokeQuery } from "services/api"; +import { useState } from 'react' -const Container = styled.div` - display: grid; - place-items: center; - height: 100%; - margin: 0 auto; - max-width: 30rem; -`; - -const Column = styled.div<{ gap?: number | string }>` - display: flex; - flex-direction: column; - justify-content: center; - gap: ${({ gap = "2rem" }) => gap}; - text-align: center; - margin: 0 1rem; -`; +import { Button, ButtonLink } from 'components/Button' +import { Column } from 'components/Column' +import { Joke } from 'components/Joke' +import { api } from 'services/api' export const Main: React.VFC = () => { - const [auto, setAuto] = useState(false); - const { - data: joke, - isLoading, - isFetching, - refetch, - } = useGetRandomJokeQuery(undefined, { pollingInterval: auto ? 3000 : 0 }); + const [auto, setAuto] = useState(false) + const [refetch, { isFetching }] = api.endpoints.getJoke.useLazyQuery({ + pollingInterval: auto ? 3000 : 0, + }) return ( - - - {isLoading && "Loading..."} - {joke?.value} - - - - + + + + + + Favorites - - ); -}; + + ) +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 4e55c8b..4987e7a 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,9 +1,15 @@ -import { Route, Routes } from "react-router"; +import { Route, Routes } from 'react-router' -import { Main } from "./Main"; +import { Container } from 'components/Container' + +import { Main } from './Main' +import { Favorites } from './Favorites' export const Pages = () => ( - - } /> - -); + + + } /> + } /> + + +) diff --git a/src/services/api.ts b/src/services/api.ts index d2faadf..ede4393 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,20 +1,17 @@ -import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' -import { Joke } from "types"; +import { Joke } from 'types' export const api = createApi({ - reducerPath: "chuckNorrisApi", + reducerPath: 'chuckNorrisApi', baseQuery: fetchBaseQuery({ - baseUrl: "https://api.chucknorris.io/jokes/", + baseUrl: 'https://api.chucknorris.io/jokes/', }), endpoints: (build) => ({ - getRandomJoke: build.query({ - query: () => "random", - }), - getJokeById: build.query({ - query: (id) => id, + getJoke: build.query({ + query: (id) => id ?? 'random', }), }), -}); +}) -export const { useGetRandomJokeQuery, useGetJokeByIdQuery } = api; +export const { useGetJokeQuery } = api diff --git a/src/services/favoriteJokes.ts b/src/services/favoriteJokes.ts new file mode 100644 index 0000000..a70a961 --- /dev/null +++ b/src/services/favoriteJokes.ts @@ -0,0 +1,24 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' + +import { Id } from 'types' + +type JokeAction = PayloadAction + +export const favoriteJokes = createSlice({ + name: 'favoriteJokes', + initialState: [] as Id[], + reducers: { + updateJoke(state, { payload }: JokeAction) { + if (state.includes(payload)) { + return state.filter((id) => id !== payload) + } + if (state.length === 10) { + state.splice(0, 1) + } + state.push(payload) + }, + purgeList() { + return [] + }, + }, +}) diff --git a/src/types/index.ts b/src/types/index.ts index acc37cd..7bd8944 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,7 +1,8 @@ +export type Id = string export interface Joke { - id: string; - url: string; - value: string; + id: Id + url: string + value: string // created_at: string; // updated_at: string; diff --git a/yarn.lock b/yarn.lock index 81e7134..5d6a5e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5939,11 +5939,6 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -nes.css@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/nes.css/-/nes.css-2.3.0.tgz#112c834dd8a18cda570ab76d66b26b078a94c919" - integrity sha512-lCFZs9vj3f5RVdbvTL/kSxiYsOARwSeAdJaMNo+bCgmWOO9x8ay7QpT4yQVKHy3r5Dttzd0uqVdpt3fvvx6EpQ== - no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -6888,6 +6883,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"