From f19e8055a87e8548a0ce28720db819a2b19919c3 Mon Sep 17 00:00:00 2001 From: Wei He Date: Fri, 9 Oct 2020 02:22:07 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Upgrade=20to=20next.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .babelrc | 11 + .env.example | 2 +- .eslintrc.json => .eslintrc | 12 +- .gitignore | 1 + .prettierignore | 1 + config-overrides.js | 3 - config/jest/cssTransform.js | 14 + config/jest/fileTransform.js | 40 + {src => config/jest}/setupTests.ts | 0 functions/graphql.js | 26 - functions/image.js | 50 - netlify.toml | 44 - next-env.d.ts | 2 + next.config.js | 19 + package.json | 66 +- pages/[_owner]/[_name].tsx | 8 + pages/_app.tsx | 43 + pages/api/graphql.ts | 31 + pages/api/image.ts | 40 + pages/index.tsx | 8 + public/index.html | 48 - server.js | 3 + src/App.css | 10 - src/App.test.tsx | 8 - src/App.tsx | 37 - .../{config.css => config.module.css} | 2 +- src/components/configuration/config.tsx | 143 +- .../configuration/textAreaWrapper.tsx | 2 - .../footer/__snapshots__/footer.test.tsx.snap | 2 +- .../footer/{footer.css => footer.module.css} | 2 +- src/components/footer/footer.test.tsx | 2 +- src/components/footer/footer.tsx | 4 +- .../header/__snapshots__/header.test.tsx.snap | 6 +- .../header/{header.css => header.module.css} | 10 +- src/components/header/header.tsx | 18 +- src/components/mainRenderer.tsx | 14 +- ...mainWrapper.css => mainWrapper.module.css} | 6 +- src/components/mainWrapper.tsx | 26 +- .../preview/__snapshots__/card.test.tsx.snap | 4 +- .../__snapshots__/preview.test.tsx.snap | 4 +- .../preview/{badge.css => badge.module.css} | 0 src/components/preview/badge.tsx | 10 +- .../preview/{card.css => card.module.css} | 2 +- src/components/preview/card.tsx | 24 +- .../{preview.css => preview.module.css} | 8 +- src/components/preview/preview.tsx | 40 +- .../repo/__snapshots__/repo.test.tsx.snap | 8 +- .../repo/{repo.css => repo.module.css} | 4 +- src/components/repo/repo.test.tsx | 4 +- src/components/repo/repo.tsx | 14 +- src/index.css | 11 + src/index.tsx | 12 - src/react-app-env.d.ts | 1 - src/serviceWorker.ts | 146 - tsconfig.json | 5 +- yarn.lock | 10441 ++++------------ 56 files changed, 2578 insertions(+), 8924 deletions(-) create mode 100644 .babelrc rename .eslintrc.json => .eslintrc (62%) delete mode 100644 config-overrides.js create mode 100644 config/jest/cssTransform.js create mode 100644 config/jest/fileTransform.js rename {src => config/jest}/setupTests.ts (100%) delete mode 100644 functions/graphql.js delete mode 100644 functions/image.js delete mode 100644 netlify.toml create mode 100644 next-env.d.ts create mode 100644 next.config.js create mode 100644 pages/[_owner]/[_name].tsx create mode 100644 pages/_app.tsx create mode 100644 pages/api/graphql.ts create mode 100644 pages/api/image.ts create mode 100644 pages/index.tsx delete mode 100644 public/index.html create mode 100644 server.js delete mode 100644 src/App.css delete mode 100644 src/App.test.tsx delete mode 100644 src/App.tsx rename src/components/configuration/{config.css => config.module.css} (73%) rename src/components/footer/{footer.css => footer.module.css} (82%) rename src/components/header/{header.css => header.module.css} (79%) rename src/components/{mainWrapper.css => mainWrapper.module.css} (78%) rename src/components/preview/{badge.css => badge.module.css} (100%) rename src/components/preview/{card.css => card.module.css} (97%) rename src/components/preview/{preview.css => preview.module.css} (88%) rename src/components/repo/{repo.css => repo.module.css} (82%) delete mode 100644 src/index.tsx delete mode 100644 src/react-app-env.d.ts delete mode 100644 src/serviceWorker.ts diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..06d4a2c8 --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": ["next/babel"], + "plugins": [ + [ + "relay", + { + "artifactDirectory": "./src/components/__generated__" + } + ] + ] +} diff --git a/.env.example b/.env.example index 3bdee9fc..36700ca2 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,3 @@ GITHUB_TOKEN= -HOST=github-socialify.netlify.app +HOST=github-socialify.git.ci SCREENSHOT_ENDPOINT=https://screenshotter.git.ci/screenshot diff --git a/.eslintrc.json b/.eslintrc similarity index 62% rename from .eslintrc.json rename to .eslintrc index 8189fd1c..e7f3ed4e 100644 --- a/.eslintrc.json +++ b/.eslintrc @@ -5,22 +5,14 @@ "node": true }, "extends": [ + "react-app", "plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:jest/recommended", "prettier-standard" ], - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parser": "@typescript-eslint/parser", "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": 2018, - "sourceType": "module" + "project": "./tsconfig.json" }, "plugins": ["react", "@typescript-eslint", "react-hooks", "prettier"], "rules": { diff --git a/.gitignore b/.gitignore index b59ec02d..97ff67a4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* .env +/.next diff --git a/.prettierignore b/.prettierignore index 5500e38d..e834ca47 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,3 +2,4 @@ __generated__/ package.json build/ +.next/ diff --git a/config-overrides.js b/config-overrides.js deleted file mode 100644 index fb62c14f..00000000 --- a/config-overrides.js +++ /dev/null @@ -1,3 +0,0 @@ -const { override, addBabelPlugins } = require('customize-cra') - -module.exports = override(addBabelPlugins('babel-plugin-relay')) diff --git a/config/jest/cssTransform.js b/config/jest/cssTransform.js new file mode 100644 index 00000000..2588212a --- /dev/null +++ b/config/jest/cssTransform.js @@ -0,0 +1,14 @@ +'use strict' + +// This is a custom Jest transformer turning style imports into empty objects. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process() { + return 'module.exports = {};' + }, + getCacheKey() { + // The output is always the same. + return 'cssTransform' + } +} diff --git a/config/jest/fileTransform.js b/config/jest/fileTransform.js new file mode 100644 index 00000000..6c893ef2 --- /dev/null +++ b/config/jest/fileTransform.js @@ -0,0 +1,40 @@ +'use strict' + +const path = require('path') +const camelcase = require('camelcase') + +// This is a custom Jest transformer turning file imports into filenames. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process(src, filename) { + const assetFilename = JSON.stringify(path.basename(filename)) + + if (filename.match(/\.svg$/)) { + // Based on how SVGR generates a component name: + // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 + const pascalCaseFilename = camelcase(path.parse(filename).name, { + pascalCase: true + }) + const componentName = `Svg${pascalCaseFilename}` + return `const React = require('react'); + module.exports = { + __esModule: true, + default: ${assetFilename}, + ReactComponent: React.forwardRef(function ${componentName}(props, ref) { + return { + $$typeof: Symbol.for('react.element'), + type: 'svg', + ref: ref, + key: null, + props: Object.assign({}, props, { + children: ${assetFilename} + }) + }; + }), + };` + } + + return `module.exports = ${assetFilename};` + } +} diff --git a/src/setupTests.ts b/config/jest/setupTests.ts similarity index 100% rename from src/setupTests.ts rename to config/jest/setupTests.ts diff --git a/functions/graphql.js b/functions/graphql.js deleted file mode 100644 index 8fc4f5c0..00000000 --- a/functions/graphql.js +++ /dev/null @@ -1,26 +0,0 @@ -const fetch = require('node-fetch') - -const API_ENDPOINT = 'https://api.github.com/graphql' - -exports.handler = async (event, context) => { - if (event.httpMethod !== 'POST') { - return { - statusCode: 405, - body: 'Method Not Allowed' - } - } - - return fetch(API_ENDPOINT, { - method: 'POST', - headers: { - Accept: 'application/json', - Authorization: `bearer ${process.env.GITHUB_TOKEN}`, - 'Content-Type': 'application/json' - }, - body: event.body - }) - .then(res => - res.text().then(data => ({ statusCode: res.status, body: data })) - ) - .catch(error => ({ statusCode: 422, body: String(error) })) -} diff --git a/functions/image.js b/functions/image.js deleted file mode 100644 index 4c7d0e9b..00000000 --- a/functions/image.js +++ /dev/null @@ -1,50 +0,0 @@ -const fetch = require('node-fetch') -const queryString = require('query-string') - -const SCREENSHOT_ENDPOINT = - process.env.SCREENSHOT_ENDPOINT || 'https://screenshotter.git.ci/screenshot' -const HOST = process.env.HOST || 'github-socialify.netlify.app' - -exports.handler = async (event, context) => { - let [, owner, repo, filetype] = event.path.split('/') - if (filetype === 'jpg') { - filetype = 'jpeg' - } - - const url = `https://${HOST}/${owner}/${repo}?${queryString.stringify( - event.queryStringParameters - )}` - const screenshotConfig = { - url, - filetype, - viewport: '2048,1024', - dpr: 2, - selector: '.card-wrapper', - css: '.card-wrapper{border-radius:0 !important}' - } - const screenshotterUrl = `${SCREENSHOT_ENDPOINT}?${queryString.stringify( - screenshotConfig - )}` - - return fetch(screenshotterUrl) - .then(res => { - if (!res.ok) throw new Error('Request failed') - return res.buffer() - }) - .then(buffer => { - return buffer.toString('base64') - }) - .then(base64 => { - return { - statusCode: 200, - headers: { - 'content-type': `image/${filetype}` - // Disabled below because netlify cache disregards query strings - // 'cache-control': 'public, max-age=14400' - }, - body: base64, - isBase64Encoded: true - } - }) - .catch(error => ({ statusCode: 422, body: String(error) })) -} diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index def1521d..00000000 --- a/netlify.toml +++ /dev/null @@ -1,44 +0,0 @@ -[dev] - command = "yarn start" - publish = "build" - functions = "functions" - -[build] - command = "yarn build" - publish = "build" - functions = "functions" - -[[headers]] - for = "*.js*" - [headers.values] - cache-control = "public, max-age=14400" - -[[headers]] - for = "*.css" - [headers.values] - cache-control = "public, max-age=14400" - -[[headers]] - for = "assets/*" - [headers.values] - cache-control = "public, max-age=14400" - -[[redirects]] - from = "/graphql" - to = "/.netlify/functions/graphql" - status = 200 - -[[redirects]] - from = "/:owner/:repo/jpg*" - to = "/.netlify/functions/image/:splat" - status = 200 - -[[redirects]] - from = "/:owner/:repo/png*" - to = "/.netlify/functions/image/:splat" - status = 200 - -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 00000000..7b7aa2c7 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/next.config.js b/next.config.js new file mode 100644 index 00000000..4b3913ac --- /dev/null +++ b/next.config.js @@ -0,0 +1,19 @@ +module.exports = { + target: process.env.NETLIFY ? 'experimental-serverless-trace' : 'server', + async rewrites() { + return [ + { + source: '/:owner/:name/png', + destination: '/api/image' + }, + { + source: '/:owner/:name/jpg', + destination: '/api/image' + }, + { + source: '/graphql', + destination: '/api/graphql' + } + ] + } +} diff --git a/package.json b/package.json index 5ef0ea65..90e85d09 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,17 @@ { - "name": "Socialify", + "name": "socialify", "version": "1.0.0", "description": "Socialify your project. Share with the world!", "author": "@CryogenicPlanet, @wei", "license": "MIT", "repository": "https://github.com/wei/socialify.git", "scripts": { - "dev": "netlify dev", - "start": "react-app-rewired start", - "build": "react-app-rewired build", - "test": "react-app-rewired test", - "eject": "react-app-rewired eject", + "dev": "next dev -p ${PORT-3000}", + "debug": "NODE_OPTIONS='--inspect' next", + "build": "next build", + "test": "jest", + "test:watch": "jest --watch", + "start": "node server.js", "lint": "eslint . --ext .ts,.tsx", "lint:fix": "yarn lint --fix", "prettier": "prettier --check .", @@ -18,21 +19,15 @@ "relay": "relay-compiler --language typescript --extensions ts tsx" }, "dependencies": { - "@types/jest": "^24.0.0", - "@types/node": "^12.0.0", - "@types/react": "^16.9.0", - "@types/react-dom": "^16.9.0", - "@types/react-relay": "^7.0.11", "antd": "^4.6.5", "copee": "^1.0.6", "hero-patterns": "^2.1.0", + "next": "^9.5.4", "node-fetch": "^2.6.1", - "query-string": "^6.13.2", "react": "^16.13.1", "react-dom": "^16.13.1", "react-relay": "^10.0.1", "react-router-dom": "^5.2.0", - "react-scripts": "3.4.3", "relay-runtime": "^10.0.1", "typescript": "~3.7.2", "use-debounce": "^5.0.1" @@ -41,21 +36,31 @@ "@types/enzyme": "^3.10.7", "@types/enzyme-adapter-react-16": "^1.0.6", "@types/enzyme-to-json": "^1.5.4", + "@types/jest": "^24.0.0", "@types/lodash": "^4.14.161", + "@types/node": "^12.0.0", + "@types/node-fetch": "^2.5.7", + "@types/react": "^16.9.0", + "@types/react-relay": "^7.0.11", "@types/react-router-dom": "^5.1.5", "@types/relay-runtime": "^10.0.4", "@typescript-eslint/eslint-plugin": "^4.2.0", "@typescript-eslint/parser": "^4.2.0", + "babel-eslint": "^10.1.0", + "babel-jest": "^26.5.2", "babel-plugin-relay": "^10.0.1", - "customize-cra": "^1.0.0", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", "enzyme-to-json": "^3.6.1", + "eslint": "^7.10.0", "eslint-config-prettier": "^6.11.0", "eslint-config-prettier-standard": "^3.0.1", + "eslint-config-react-app": "^5.2.1", "eslint-config-standard": "^14.1.1", + "eslint-plugin-flowtype": "^5.2.0", "eslint-plugin-import": "^2.22.0", "eslint-plugin-jest": "^24.0.2", + "eslint-plugin-jsx-a11y": "^6.3.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-promise": "^4.2.1", @@ -65,10 +70,10 @@ "graphql": "^15.3.0", "graphql-compiler": "^1.7.0", "husky": "^4.3.0", - "netlify-cli": "^2.64.1", + "identity-obj-proxy": "^3.0.0", + "jest": "^26.5.2", "prettier": "^2.1.2", "prettier-config-standard": "^1.0.1", - "react-app-rewired": "^2.1.6", "relay-compiler": "^10.0.1", "relay-compiler-language-typescript": "^13.0.1", "relay-config": "^10.0.1" @@ -86,8 +91,31 @@ ] }, "jest": { + "roots": [ + "/src" + ], + "collectCoverageFrom": [ + "src/**/*.{js,jsx,ts,tsx}", + "!src/**/*.d.ts" + ], "setupFilesAfterEnv": [ - "src/setupTests.ts" + "/config/jest/setupTests.ts" + ], + "testMatch": [ + "/src/**/__tests__/**/*.{js,jsx,ts,tsx}", + "/src/**/*.{spec,test}.{js,jsx,ts,tsx}" + ], + "moduleNameMapper": { + "\\.(css|less)$": "identity-obj-proxy" + }, + "transform": { + "^.+\\.(js|jsx|ts|tsx)$": "/node_modules/babel-jest", + "^.+\\.css$": "/config/jest/cssTransform.js", + "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "/config/jest/fileTransform.js" + }, + "transformIgnorePatterns": [ + "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$", + "^.+\\.module\\.(css|sass|scss)$" ], "snapshotSerializers": [ "enzyme-to-json/serializer" @@ -95,7 +123,7 @@ }, "husky": { "hooks": { - "pre-commit": "yarn prettier && yarn lint && CI=true yarn test" + "pre-commit": "yarn prettier && yarn lint && yarn test" } } -} \ No newline at end of file +} diff --git a/pages/[_owner]/[_name].tsx b/pages/[_owner]/[_name].tsx new file mode 100644 index 00000000..dd6854a0 --- /dev/null +++ b/pages/[_owner]/[_name].tsx @@ -0,0 +1,8 @@ +import React from 'react' +import MainRenderer from '../../src/components/mainRenderer' + +const RepoPage = () => { + return +} + +export default RepoPage diff --git a/pages/_app.tsx b/pages/_app.tsx new file mode 100644 index 00000000..1bc9f2ab --- /dev/null +++ b/pages/_app.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import App from 'next/app' +import Head from 'next/head' +import { Layout } from 'antd' + +import '../src/index.css' + +import HeaderElement from '../src/components/header/header' +import FooterElement from '../src/components/footer/footer' +const { Footer, Content } = Layout + +export default class MyApp extends App { + public render() { + const { Component, pageProps } = this.props + + return ( + <> + + + + + + + + GitHub Socialify + + + + + +
+ +
+ + ) + } +} diff --git a/pages/api/graphql.ts b/pages/api/graphql.ts new file mode 100644 index 00000000..b1d0c351 --- /dev/null +++ b/pages/api/graphql.ts @@ -0,0 +1,31 @@ +import { NextApiRequest, NextApiResponse } from 'next' +import fetch from 'node-fetch' + +const API_ENDPOINT = 'https://api.github.com/graphql' + +export default async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method !== 'POST') { + res.status(405).send('Method Not Allowed') + return + } + try { + const response = await fetch(API_ENDPOINT, { + method: 'POST', + headers: { + Accept: 'application/json', + Authorization: `bearer ${process.env.GITHUB_TOKEN}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(req.body) + }) + if (!response.ok) { + res.status(response.status).send(await response.text()) + return + } + const text = await response.text() + res.status(200).send(text) + } catch (error) { + console.error(error) + res.status(422).send(error.message) + } +} diff --git a/pages/api/image.ts b/pages/api/image.ts new file mode 100644 index 00000000..b2f01d82 --- /dev/null +++ b/pages/api/image.ts @@ -0,0 +1,40 @@ +import { NextApiRequest, NextApiResponse } from 'next' +import fetch from 'node-fetch' + +const SCREENSHOT_ENDPOINT = + process.env.SCREENSHOT_ENDPOINT || 'https://screenshotter.git.ci/screenshot' +const HOST = process.env.HOST || 'socialify.git.ci' + +export default async (req: NextApiRequest, res: NextApiResponse) => { + const pathname = (req.url || '').split('?')[0] + const query = (req.url || '').split('?')[1] || '' + let [, owner, repo, filetype] = pathname.split('/') + if (filetype === 'jpg') { + filetype = 'jpeg' + } + + const url = `https://${HOST}/${owner}/${repo}?${query}` + const screenshotConfig: { [key: string]: string } = { + url, + filetype, + viewport: '2048,1024', + dpr: '2', + selector: '.card-wrapper', + css: '.card-wrapper{border-radius:0 !important}' + } + const screenshotterUrl = `${SCREENSHOT_ENDPOINT}?${Object.entries( + screenshotConfig + ) + .map(([k, v]) => `${k}=${encodeURIComponent(v)}`) + .join('&')}` + + try { + const response = await fetch(screenshotterUrl) + if (!response.ok) throw new Error('Request failed') + const buffer = await response.buffer() + res.setHeader('content-type', `image/${filetype}`) + res.status(200).send(buffer) + } catch (error) { + res.status(422).send(error.message) + } +} diff --git a/pages/index.tsx b/pages/index.tsx new file mode 100644 index 00000000..a7c9417b --- /dev/null +++ b/pages/index.tsx @@ -0,0 +1,8 @@ +import React from 'react' +import Repo from '../src/components/repo/repo' + +const HomePage = () => { + return +} + +export default HomePage diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 12a3534a..00000000 --- a/public/index.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - GitHub Socialify - - - - -
- - - diff --git a/server.js b/server.js new file mode 100644 index 00000000..0fa0e21e --- /dev/null +++ b/server.js @@ -0,0 +1,3 @@ +const cli = require('next/dist/cli/next-start') + +cli.nextStart(['-p', process.env.PORT || 3000]) diff --git a/src/App.css b/src/App.css deleted file mode 100644 index a351602d..00000000 --- a/src/App.css +++ /dev/null @@ -1,10 +0,0 @@ -@import '~antd/dist/antd.css'; - -.footer { - position: fixed; - z-index: 1; - width: 100%; - bottom: 0; - padding: 5px 15px; - background-color: rgba(240, 242, 245, 0.9); -} diff --git a/src/App.test.tsx b/src/App.test.tsx deleted file mode 100644 index 8b701007..00000000 --- a/src/App.test.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react' -import { shallow } from 'enzyme' -// import App from './App' - -test('placeholder', () => { - const placeholder = shallow(placeholder) - expect(placeholder.text()).toEqual('placeholder') -}) diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 47f429d6..00000000 --- a/src/App.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' - -import { Route, BrowserRouter as Router, Switch } from 'react-router-dom' - -import { Layout } from 'antd' - -import './App.css' - -import Repo from './components/repo/repo' -import MainRenderer from './components/mainRenderer' -import HeaderElement from './components/header/header' -import FooterElement from './components/footer/footer' - -const { Footer, Content } = Layout - -const App = () => { - return ( - - - - - - - - - - - - -
- -
-
- ) -} - -export default App diff --git a/src/components/configuration/config.css b/src/components/configuration/config.module.css similarity index 73% rename from src/components/configuration/config.css rename to src/components/configuration/config.module.css index 7772d63a..e858fdc8 100644 --- a/src/components/configuration/config.css +++ b/src/components/configuration/config.module.css @@ -1,4 +1,4 @@ -.text-area-wrapper { +.textAreaWrapper { display: flex; align-items: center; width: 100%; diff --git a/src/components/configuration/config.tsx b/src/components/configuration/config.tsx index 3fb8fcd5..fe8da1b3 100644 --- a/src/components/configuration/config.tsx +++ b/src/components/configuration/config.tsx @@ -1,8 +1,6 @@ import React, { useContext, useEffect } from 'react' +import { useRouter } from 'next/router' import { Col, Form, Row, Space } from 'antd' - -import { useHistory, useLocation } from 'react-router-dom' - import ConfigContext from '../../contexts/ConfigContext' import ConfigType, { @@ -16,12 +14,12 @@ import ConfigType, { import { mainRendererQueryResponse } from '../__generated__/mainRendererQuery.graphql' -import './config.css' +import styles from './config.module.css' + import SelectWrapper from './selectWrapper' import CheckBoxWrapper from './checkBoxWrapper' -import TextAreaWrapper from './textAreaWrapper' - import InputWrapper from './inputWrapper' +import TextAreaWrapper from './textAreaWrapper' type ConfigProp = { repository: mainRendererQueryResponse['repository'] @@ -29,13 +27,16 @@ type ConfigProp = { } const Config = ({ repository, owner }: ConfigProp) => { - const history = useHistory() - const location = useLocation() + const router = useRouter() + const { config, setConfig } = useContext(ConfigContext) const handleChanges = (changes: { value: any; key: keyof ConfigType }[]) => { let newConfig: ConfigType = { ...config } - const urlParams = new URLSearchParams(location.search) + const urlParams = router.query + // Remove extraneous params from route + delete urlParams._owner + delete urlParams._name changes.forEach(({ value, key }) => { const currentValue = newConfig[key] ? newConfig[key] : {} if (value.required === true) { @@ -45,20 +46,25 @@ const Config = ({ repository, owner }: ConfigProp) => { } if (value && value.state === true && value.editable) { - urlParams.set(key, '1') - urlParams.set(`${key}Editable`, value.value) + urlParams[key] = '1' + urlParams[`${key}Editable`] = value.value } else if (value && value.state === true) { - urlParams.set(key, '1') + urlParams[key] = '1' } else if (value && value.required === true) { - urlParams.set(key, value.val) + urlParams[key] = value.val } else { - urlParams.set(key, '0') + urlParams[key] = '0' } }) - urlParams.sort() - - history.push(`?${urlParams.toString()}`) + router.push( + `${window.location.pathname}?${Object.entries(urlParams) + .sort() + .map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`) + .join('&')}`, + undefined, + { shallow: true } + ) } const handleChange = (value: any, key: keyof ConfigType) => { @@ -66,60 +72,69 @@ const Config = ({ repository, owner }: ConfigProp) => { } useEffect(() => { - if (repository) { - const languages = repository.languages?.nodes || [] - const language = - languages.length > 0 ? languages[0]?.name || 'unknown' : 'unknown' - - const newConfig: OptionalConfigs = { - owner: { state: true, value: owner }, - description: { - state: false, - editable: true, - value: repository.description || '' - }, - language: { state: true, value: language }, - stargazers: { state: true, value: repository.stargazerCount }, - forks: { state: false, value: repository.forkCount }, - pulls: { state: false, value: repository.pullRequests.totalCount }, - issues: { state: false, value: repository.issues.totalCount } - } + const handleRouteChange = (asPath: string) => { + if (repository) { + const languages = repository.languages?.nodes || [] + const language = + languages.length > 0 ? languages[0]?.name || 'unknown' : 'unknown' - const params = new URLSearchParams(location.search) + const newConfig: OptionalConfigs = { + owner: { state: true, value: owner }, + description: { + state: false, + editable: true, + value: repository.description || '' + }, + language: { state: true, value: language }, + stargazers: { state: true, value: repository.stargazerCount }, + forks: { state: false, value: repository.forkCount }, + pulls: { state: false, value: repository.pullRequests.totalCount }, + issues: { state: false, value: repository.issues.totalCount } + } - Array.from(params.keys()).forEach(stringKey => { - const key = stringKey as keyof ConfigType - if (key in newConfig) { - const query = params.get(key) - const currentConfig = newConfig[key as keyof typeof newConfig] - const newChange = { - state: query === '1' - } - if (currentConfig?.editable) { - const editableValue = params.get(`${key}Editable`) - if (editableValue != null) { - Object.assign(newChange, { - value: editableValue - }) - } - } + const params = new URLSearchParams(asPath.split('?')[1]) - Object.assign(newConfig[key as keyof typeof newConfig], newChange) - } else if (key in RequiredConfigsKeys) { - const query = params.get(key) - if (query != null) { + Array.from(params.keys()).forEach(stringKey => { + const key = stringKey as keyof ConfigType + if (key in newConfig) { + const query = params.get(key) + const currentConfig = newConfig[key as keyof typeof newConfig] const newChange = { - [key]: query + state: query === '1' + } + if (currentConfig?.editable) { + const editableValue = params.get(`${key}Editable`) + if (editableValue != null) { + Object.assign(newChange, { + value: editableValue + }) + } } - Object.assign(newConfig, newChange) + Object.assign(newConfig[key as keyof typeof newConfig], newChange) + } else if (key in RequiredConfigsKeys) { + const query = params.get(key) + if (query != null) { + const newChange = { + [key]: query + } + + Object.assign(newConfig, newChange) + } } - } - }) - setConfig({ ...config, ...newConfig, name: repository.name }) + }) + setConfig({ ...config, ...newConfig, name: repository.name }) + } + } + + router.events.on('routeChangeComplete', handleRouteChange) + handleRouteChange(router.asPath) + + return () => { + router.events.off('routeChangeComplete', handleRouteChange) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location]) + }, []) if (!repository) { return null @@ -129,7 +144,7 @@ const Config = ({ repository, owner }: ConfigProp) => { const language = languages.length > 0 ? languages[0] : null return ( -
+
@@ -227,7 +242,7 @@ const Config = ({ repository, owner }: ConfigProp) => { /> -
+
{ - console.log('handleChange', value) handleChange({ value: value, editable: true, state: true }, keyName) }, 500) const processChange = (e: React.ChangeEvent) => { setInternalValue(e.target.value) - console.log('processChange', internalValue) debounce.callback(e.target.value) } diff --git a/src/components/footer/__snapshots__/footer.test.tsx.snap b/src/components/footer/__snapshots__/footer.test.tsx.snap index 4522d21a..364e0069 100644 --- a/src/components/footer/__snapshots__/footer.test.tsx.snap +++ b/src/components/footer/__snapshots__/footer.test.tsx.snap @@ -2,7 +2,7 @@ exports[`Footer renders 1`] = `
{ const footer = shallow(