diff --git a/example/.env.development b/example/.env.development index fd0a22f8..1d440799 100644 --- a/example/.env.development +++ b/example/.env.development @@ -1,3 +1,6 @@ GATSBY_IS_DEV=true GATSBY_GITHUB_CLIENT_ID=23f389d8ae84d868b545 GATSBY_GITHUB_CLIENT_SECRET=38d366c69a5c7caca8b1f066ebed85ba62fc4141 + +GATSBY_COLLECTION_CLIENT_ID=f9d100ebbb7404db8b52 +GATSBY_COLLECTION_CLIENT_SECRET=486f19c5abf97a27e2cefe0d5bfef2e15ed332e6 \ No newline at end of file diff --git a/gatsby-theme-oi-wiki/package-lock.json b/gatsby-theme-oi-wiki/package-lock.json new file mode 100644 index 00000000..f5ad4488 --- /dev/null +++ b/gatsby-theme-oi-wiki/package-lock.json @@ -0,0 +1,328 @@ +{ + "name": "gatsby-theme-oi-wiki", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz", + "integrity": "sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, + "@types/react": { + "version": "17.0.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.14.tgz", + "integrity": "sha512-0WwKHUbWuQWOce61UexYuWTGuGY/8JvtUe/dtQ6lR4sZ3UiylHotJeWpf3ArP9+DSGUoLY3wbU59VyMrJps5VQ==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, + "@types/use-persisted-state": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@types/use-persisted-state/-/use-persisted-state-0.3.0.tgz", + "integrity": "sha512-ZT98QuckR95qM7W97lGVqc7fFS9TT6f3txp7R40fl0zxa5BLm3GG7j0i51G12h8DkoJxFAf2oQyYKU99h0pxFA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz", + "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/types": "3.10.1", + "@typescript-eslint/typescript-estree": "3.10.1", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.1.tgz", + "integrity": "sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "3.10.1", + "@typescript-eslint/types": "3.10.1", + "@typescript-eslint/typescript-estree": "3.10.1", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/types": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz", + "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz", + "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "3.10.1", + "@typescript-eslint/visitor-keys": "3.10.1", + "debug": "^4.1.1", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz", + "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "csstype": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", + "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } +} diff --git a/gatsby-theme-oi-wiki/package.json b/gatsby-theme-oi-wiki/package.json index 416aed7c..fe20ba56 100644 --- a/gatsby-theme-oi-wiki/package.json +++ b/gatsby-theme-oi-wiki/package.json @@ -19,6 +19,7 @@ "@mgtd/vssue-api-github-v3": "^2.4.8", "@mgtd/vssue-api-github-v4": "^2.4.5", "ace-builds": "^1.4.12", + "@octokit/graphql": "^4.6.4", "autosuggest-highlight": "^3.1.1", "axios": "^0.21.1", "classnames": "^2.2.6", @@ -41,7 +42,9 @@ "gatsby-transformer-remark-rehype": "^4.0.0", "gatsby-transformer-sharp": "^3.9", "hast-util-to-text": "^3.0.0", + "js-sha256": "^0.9.0", "lodash": "^4.17.21", + "luxon": "^2.0.2", "mark.js": "^8.11.1", "mdast-util-toc": "^6.0.0", "path-browserify": "^1.0.1", @@ -64,6 +67,7 @@ "use-persisted-state": "^0.3.0" }, "devDependencies": { + "@types/luxon": "^2.0.4", "@types/autosuggest-highlight": "^3.1.1", "@types/mark.js": "^8.11.6", "@types/react-helmet": "^6.1.1", diff --git a/gatsby-theme-oi-wiki/src/components/Collection/AddCommentDialog.tsx b/gatsby-theme-oi-wiki/src/components/Collection/AddCommentDialog.tsx new file mode 100644 index 00000000..8348eb3c --- /dev/null +++ b/gatsby-theme-oi-wiki/src/components/Collection/AddCommentDialog.tsx @@ -0,0 +1,98 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Button, CircularProgress, createStyles, Dialog, DialogActions, DialogContent, DialogTitle, Grid, TextField } from '@material-ui/core' +import { makeStyles } from '@material-ui/styles' +import React, { useState } from 'react' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { CollectionClient } from './CollectionClient' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { CollectionUser } from './types' + +const useStyles = makeStyles(() => createStyles({ + textBox: { + // width: theme.spacing(), + }, + dialog: { + // width: theme.spacing(30), + }, +})) + +interface AddCommentDialogProps { + issueId: number; + user: CollectionUser; + client: CollectionClient; + finishCallback: () => void; + onClose: () => void; + open: boolean; +} + +const AddCommentDialog: React.FC = (props) => { + const styles = useStyles() + const { + client, + issueId, + finishCallback, + onClose, + open, + } = props + const [errorText, setErrorText] = useState('') + const [text, setText] = useState('') + const [loading, setLoading] = useState(false) + const sendComment = async (): Promise => { + if (text === '') { + setErrorText('请输入评论内容') + return + } + setLoading(true) + await client.sendComment(issueId, text) + setLoading(false) + onClose() + finishCallback() + } + return <> + { + if (reason !== 'backdropClick') onClose() + }} + > + 发表评论 + +
+ { + setText(e.target.value) + setErrorText('') + }} + > +
+ {loading && + + + + } +
+ + + + +
+ +} + +export default AddCommentDialog diff --git a/gatsby-theme-oi-wiki/src/components/Collection/Collection.tsx b/gatsby-theme-oi-wiki/src/components/Collection/Collection.tsx new file mode 100644 index 00000000..ac982f2d --- /dev/null +++ b/gatsby-theme-oi-wiki/src/components/Collection/Collection.tsx @@ -0,0 +1,239 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Avatar, Button, CircularProgress, createStyles, Divider, FormControl, Grid, GridSize, Hidden, InputLabel, makeStyles, MenuItem, Select, Theme, Tooltip, Typography } from '@material-ui/core' +import { Pagination } from '@material-ui/lab' +import GithubV4 from '@mgtd/vssue-api-github-v4' +import React, { useState, useEffect } from 'react' +import createPersistedState from 'use-persisted-state' +import { CollectionClient } from './CollectionClient' +import DetailInputDialog from './DetailInputDialog' +import ProposalCard from './ProposalCard' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { CollectionItem, CollectionUser, SortMethod } from './types' + +const useToken = createPersistedState('github-access-token') +const useItemsPerPage = createPersistedState('collection-items-per-page') +const usePreferredSortMethod = createPersistedState('collection-preferred-sort-method') +const REPO_OWNER = process.env.GATSBY_GITHUB_COLLECTION_REPO_OWNER || 'officeyutong' +const REPO_NAME = process.env.GATSBY_GITHUB_COLLECTION_REPO_NAME || 'collection-test' +const PROYX_URL = process.env.GATSBY_GITHUB_COLLECTION_PROXYURL || 'https://sparkling-silence-bf63.officeyutong.workers.dev/?' +const apiClient = new GithubV4({ + baseURL: 'https://github.com', + owner: REPO_OWNER, + repo: REPO_NAME, + // 使用和评论区相同的OAuth App + clientId: process.env.GATSBY_GITHUB_CLIENT_ID, + clientSecret: process.env.GATSBY_GITHUB_CLIENT_SECRET, + labels: [], + proxy: (url: string) => `${PROYX_URL}${url}`, +}) + +const useStyles = makeStyles((theme: Theme) => createStyles({ + divider: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + formControl: { + width: '100%', + }, + pagination: { + paddingTop: theme.spacing(2), + }, +})) + +// eslint-disable-next-line @typescript-eslint/ban-types +const Collection: React.FC<{ id: string }> = ({ id }) => { + const styles = useStyles() + const [token, setToken] = useToken(null) + const [data, setData] = useState([]) + const [loaded, setLoaded] = useState(false) + const [user, setUser] = useState({ login: false }) + const [pageCount, setPageCount] = useState(0) + const [page, setPage] = useState(1) + const [, setCount] = useState(0) + const [itemsPerPage, setItemsPerPage] = useItemsPerPage(10) + const [sortMethod0, setSortMethod] = usePreferredSortMethod('support') + const [contentLoading, setContentLoading] = useState(false) + const [showDetailInput, setShowDetailInput] = useState(false) + const [shouldLoad, setShouldLoad] = useState(false) + const login = user.login + const client = new CollectionClient(token, REPO_OWNER, REPO_NAME) + const sortMethod: SortMethod = React.useMemo(() => { + if (sortMethod0 === 'comment' || sortMethod0 === 'support') return sortMethod0 + return 'support' + }, [sortMethod0]) + const revokeToken = (): void => { + setToken(null) + setUser({ login: false }) + } + const loadPage = async (page: number): Promise => { + setContentLoading(true) + const resp = await client.getCollection(id, page, itemsPerPage, sortMethod) + setContentLoading(false) + setPage(page) + setPageCount(resp.totalPage) + setCount(resp.proposalCount) + setData(resp.data) + setLoaded(true) + } + useEffect(() => { + (async () => { + if (token) { + try { + const { username, avatar, homepage } = await apiClient.getUser({ accessToken: token }) + const resp = await client.getUserDetails(username) + setUser({ + login: true, + avatar: avatar as string, + homepage: homepage as string, + id: resp.id, + node_id: resp.node_id, + username: username, + }) + // console.log(resp) + } catch (e) { + revokeToken() + // return + } + } else { + const token = await apiClient.handleAuth() + console.log('token=', token) + setToken(token) + } + // await loadPage(1) + // setLoaded(true) + setShouldLoad(true) + })() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loaded, token]) + useEffect(() => { + if (shouldLoad) loadPage(1) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [itemsPerPage, sortMethod, shouldLoad]) + return
+ {!loaded + ?
+ + + + + +
+ :
+ + {user.login && + + + + + + } + + {user.login + ? + + {user.username} + + + : } + + +
+ +
+
+
+ + {contentLoading + ?
+ + + + + +
+ :
+ {data.map((x, i) => { + const newval = [...data] + for (const val of newval) { if (val.id === x.id) { val.supportCount = v } } + setData(newval) + }} + removeCallback={() => { + setData(data.filter(y => y.id !== x.id)) + }} + >)} +
} + + + + + 排序方式 + + + + + + 每页条数 + + + + + {pageCount >= 1 && + + loadPage(p)} + className={styles.pagination} + > + + } + + + {login && } + + +
} + {showDetailInput && setShowDetailInput(false)} finishCallback={() => loadPage(page)} client={client} pageId={id}>} +
+} + +export default Collection diff --git a/gatsby-theme-oi-wiki/src/components/Collection/CollectionClient.ts b/gatsby-theme-oi-wiki/src/components/Collection/CollectionClient.ts new file mode 100644 index 00000000..f8d2dee4 --- /dev/null +++ b/gatsby-theme-oi-wiki/src/components/Collection/CollectionClient.ts @@ -0,0 +1,228 @@ +/* eslint-disable*/ +import axios, { AxiosInstance } from 'axios' +import { sha256 } from 'js-sha256' +import { CollectionItem, GeneralGithubUser, ProposalComment, ProposalMeta, SortMethod } from './types'; +import { graphql } from '@octokit/graphql' +import { graphql as TGraphQL } from '@octokit/graphql/dist-types/types'; +const LABEL_PROPOSAL = 'collection-proposal' +class CollectionClient { + private owner: string; + private repo: string; + private client: AxiosInstance; + private gqlclient: TGraphQL; + private token: string | null; + // eslint-disable-next-line space-before-function-paren + constructor(token: string | null, owner: string, repo: string) { + this.repo = repo + this.owner = owner + this.token = token + this.client = axios.create({ + headers: { + Authorization: token !== null ? `token ${this.token}` : undefined, + Accept: 'application/vnd.github.v3+json;application/vnd.github.squirrel-girl-preview' + }, + baseURL: 'https://api.github.com' + }) + this.client.interceptors.response.use(r => r, err => { + console.log(err) + err.data = null + return err + }) + this.gqlclient = graphql.defaults({ + headers: { + Authorization: token !== null ? `token ${this.token}` : undefined, + } + }) + } + makeRepoPrefix(): string { + return `/repos/${this.owner}/${this.repo}` + } + async checkLabel() { + if (this.token === null) return + for (const name of [LABEL_PROPOSAL]) { + const resp = await this.client.get(`${this.makeRepoPrefix()}/labels/${name}`); + if (resp.status !== 200) { + console.log("Creating label", name) + await this.client.post(`${this.makeRepoPrefix()}/labels`, { name: name }) + } + } + } + async checkData() { + await this.checkLabel() + } + makeSearchPrefix(pageId: string): string { + return `collection-${sha256(pageId).substr(0, 8)}` + } + makeProposalTitle(pageId: string, name: string): string { + return `collection-${sha256(pageId).substr(0, 8)}-${sha256(name)}` + } + async sendComment(issueId: number, text: string): Promise { + await this.client.post(`/repos/${this.owner}/${this.repo}/issues/${issueId}/comments`, { body: text }) + } + async getIssueCommentCount(issudId: number): Promise { + const resp = (await this.client.get(`/repos/${this.owner}/${this.repo}/issues/${issudId}`)).data + return resp.comments as number + } + async deleteProposal(nodeId: string): Promise<{ ok: boolean; message?: string; }> { + // const title = this.makeProposalTitle(id, name) + await this.gqlclient(` + mutation ($input: DeleteIssueInput!) { + deleteIssue(input: $input) { + clientMutationId + } + } + `, { + input: { + issueId: nodeId + } + }) + return { ok: true } + } + async sendProposal(id: string, name: string, url: string, description: string): Promise<{ ok: boolean; message: string }> { + const proposalTitle = this.makeProposalTitle(id, name) + const searchtext = `${proposalTitle} repo:${this.owner}/${this.repo} in:title` + { + const resp = (await this.client.get('/search/issues', { params: { q: searchtext, page: 1 } })).data as { total_count: number } + if (resp.total_count > 0) return { ok: false, message: '此标题的提案已经存在!' } + } + { + const resp = (await this.client.post(`/repos/${this.owner}/${this.repo}/issues`, { + title: proposalTitle, + body: JSON.stringify({ name: name, url: url, description: description }), + labels: [LABEL_PROPOSAL], + })) + if (resp.status !== 201) { + return { ok: false, message: `${resp.status}: ${resp.statusText}` } + } + } + + return { ok: true, message: '' } + } + async getUserDetails(username: string): Promise { + return (await this.client.get(`/users/${username}`)).data + } + async getComments(issueId: number, page: number = 1, itemsPerPage: number = 20): Promise { + const resp = (await this.client.get(`/repos/${this.owner}/${this.repo}/issues/${issueId}/comments`, { + params: { + per_page: itemsPerPage, + page: page + } + })).data as ProposalComment[]; + return resp; + } + async setSupportState(nodeId: string, state: boolean) { + if (state) { + await this.gqlclient(` + mutation ($data: AddReactionInput!) { + addReaction(input: $data) { + reaction { + content + } + subject { + id + } + } + } + `, { + data: { subjectId: nodeId, content: 'THUMBS_UP' } + }) + } else { + await this.gqlclient(` + mutation ($data: RemoveReactionInput!) { + removeReaction(input: $data) { + reaction { + content + } + subject { + id + } + } + } + `, { data: { subjectId: nodeId, content: 'THUMBS_UP' } }) + } + } + async getSelfSupported(id: number): Promise { + const { repository } = await this.gqlclient(` + query repository( + $owner: String!, + $name: String!, + $number: Int!) { + repository(owner: $owner, name: $name) { + issue(number: $number) { + reactionGroups { + content + # reactors { + # totalCount + # } + viewerHasReacted + } + } + } + } + `, { owner: this.owner, name: this.repo, number: id }) as { + repository: { + issue: { + reactionGroups: { + content: string; + // reactors: { totalCount: number; }; + viewerHasReacted: boolean; + }[] + } + } + } + for (const reaction of repository.issue.reactionGroups) { + if (reaction.content === 'THUMBS_UP') { + return reaction.viewerHasReacted + } + } + return false + } + async getCollection(id: string, page: number = 1, itemsPerPage: number = 10, orderBy: SortMethod = 'support'): Promise<{ totalPage: number; proposalCount: number; data: CollectionItem[] }> { + await this.checkData() + const query = `${this.makeSearchPrefix(id)} label:${LABEL_PROPOSAL} repo:${this.owner}/${this.repo} in:title` + const resp = (await this.client.get('/search/issues', { + params: { + q: query, + sort: orderBy === 'support' ? 'reactions-+1' : 'comments', + order: 'desc', + page: page, + per_page: itemsPerPage + } + })).data as { + total_count: number; + items: { + comments: number; + number: number; + title: string; + body: string + reactions: { + "+1": number; + } + user: GeneralGithubUser; + node_id: string; + }[] + } + const totalPage = Math.ceil(resp.total_count / itemsPerPage) + return { + totalPage: totalPage, + proposalCount: resp.total_count, + data: resp.items.map(x => { + const meta = JSON.parse(x.body) as ProposalMeta + return { + commentCount: x.comments, + description: meta.description, + id: x.number, + name: meta.name, + url: meta.url, + supportCount: x.reactions['+1'], + user: x.user, + nodeId: x.node_id, + } + }) + } + } +} + +export { + CollectionClient, +} diff --git a/gatsby-theme-oi-wiki/src/components/Collection/DetailInputDialog.tsx b/gatsby-theme-oi-wiki/src/components/Collection/DetailInputDialog.tsx new file mode 100644 index 00000000..4f549249 --- /dev/null +++ b/gatsby-theme-oi-wiki/src/components/Collection/DetailInputDialog.tsx @@ -0,0 +1,99 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Button, CircularProgress, createStyles, Dialog, DialogActions, DialogContent, DialogTitle, Grid, makeStyles, TextField, Theme } from '@material-ui/core' +import React, { useState } from 'react' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { CollectionClient } from './CollectionClient' + +const useStyles = makeStyles((theme: Theme) => createStyles({ + input: { + marginBottom: theme.spacing(1), + width: '100%', + }, + dialogContent: { + // width: '400px', + }, +})) + +const DetailInputDialog: React.FC<{ open: boolean; onClose: () => void; finishCallback: () => void; client: CollectionClient; pageId: string }> = ({ + finishCallback, + onClose, + open, + client, + pageId, +}) => { + const styles = useStyles() + const [name, setName] = useState('') + const [url, setUrl] = useState('') + const [description, setDescription] = useState('') + const [loading, setLoading] = useState(false) + const [nameErrorText, setNameErrorText] = useState('') + const confirm = async (): Promise => { + setLoading(true) + const resp = await client.sendProposal(pageId, name, url, description) + if (!resp.ok) { + setNameErrorText(resp.message) + setLoading(false) + return + } + onClose() + finishCallback() + } + return
+ { + if (reason !== 'backdropClick') onClose() + }} + fullWidth + maxWidth='sm' + > + 添加提案 + + { + setName(e.target.value) + setNameErrorText('') + }} + helperText={nameErrorText !== '' ? nameErrorText : '题目名用以同其他提案相区分,请保证唯一性'} + > + setUrl(e.target.value)} + > + setDescription(e.target.value)} + > + + {loading && + + + + } + + + + + + +
+} + +export default DetailInputDialog diff --git a/gatsby-theme-oi-wiki/src/components/Collection/ProposalCard.tsx b/gatsby-theme-oi-wiki/src/components/Collection/ProposalCard.tsx new file mode 100644 index 00000000..67538003 --- /dev/null +++ b/gatsby-theme-oi-wiki/src/components/Collection/ProposalCard.tsx @@ -0,0 +1,176 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Accordion, AccordionDetails, AccordionSummary, Badge, Button, Container, createStyles, Grid, makeStyles, Typography, Theme } from '@material-ui/core' +import React, { useEffect, useState } from 'react' +import { ExpandMore as ExpandMoreIcon, ThumbUp as ThumbUpIcon, Comment as CommentIcon, Delete as DeleteIcon } from '@material-ui/icons' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { ProposalMeta, SortMethod, CollectionUser, GeneralGithubUser } from './types' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { CollectionClient } from './CollectionClient' +import ProposalComments from './ProposalComments' + +interface ProposalCardProps extends ProposalMeta { + client: CollectionClient; + sortMethod: SortMethod; + user: CollectionUser; + id: number; + commentCount: number; + supportCount: number; + nodeId: string; + proposalUser: GeneralGithubUser; + updateSupportCount: (val: number) => void; + removeCallback: () => void; +} +const useStyles = makeStyles((theme: Theme) => createStyles({ + card: { + marginBottom: theme.spacing(1), + }, + utilsContainer: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + utilsButton: { + marginRight: theme.spacing(1), + }, +})) +const ProposalCard: React.FC = (props) => { + const { + client, + name, + commentCount, + url, + id, + description, + user, + nodeId, + supportCount, + proposalUser, + updateSupportCount, + removeCallback, + } = props + const styles = useStyles() + const [supportStateLoaded, setSupportStateLoaded] = useState(false) + const [selfSupported, setSelfSupported] = useState(false) + const [expanding, setExpanding] = useState(false) + const [toggling, setToggling] = useState(false) + const [removing, setRemoving] = useState(false) + const alreadyLogin = user.login + useEffect(() => { + if (!supportStateLoaded && user.login) { + client.getSelfSupported(id).then(ok => { + setSelfSupported(ok) + setSupportStateLoaded(true) + }) + } + }, [client, id, supportStateLoaded, user.login]) + const toggleSelfSupportState = async (): Promise => { + setToggling(true) + if (selfSupported) { + await client.setSupportState(nodeId, false) + updateSupportCount(supportCount - 1) + } else { + await client.setSupportState(nodeId, true) + updateSupportCount(supportCount + 1) + } + setToggling(false) + setSelfSupported(b => !b) + } + const removeThisProposal = async (): Promise => { + setRemoving(true) + await client.deleteProposal(nodeId) + setRemoving(false) + removeCallback() + } + return
+ setExpanding(!expanding)}> + } + > + + + + {name} + + + + + + + + + + + + + + + + + + + + + {url !== '' && + 题目链接: + {url} + + } + + + {description} + + + + + + {alreadyLogin && user.id === proposalUser.id && } + } + > + + + + +
+} + +export default ProposalCard diff --git a/gatsby-theme-oi-wiki/src/components/Collection/ProposalComments.tsx b/gatsby-theme-oi-wiki/src/components/Collection/ProposalComments.tsx new file mode 100644 index 00000000..f437bff9 --- /dev/null +++ b/gatsby-theme-oi-wiki/src/components/Collection/ProposalComments.tsx @@ -0,0 +1,163 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Avatar, Button, CircularProgress, Container, createStyles, Divider, Grid, makeStyles, Theme, Typography } from '@material-ui/core' +import React, { useEffect, useState } from 'react' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { CollectionClient } from './CollectionClient' +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { CollectionUser, ProposalComment } from './types' +import { Pagination } from '@material-ui/lab' +import AddCommentDialog from './AddCommentDialog' +import { Add as AddIcon } from '@material-ui/icons' +import { DateTime } from 'luxon' +interface ProposalCommentsProps { + issueId: number; + client: CollectionClient; + user: CollectionUser; + extraButtonSlot: React.ReactNode; + alreadyLogin: boolean; +} +const COMMENTS_PER_PAGE = 5 +const useStyles = makeStyles((theme: Theme) => createStyles({ + pagination: { + marginTop: theme.spacing(1), + }, + commentItem: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + }, + utilsContainer: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), + paddingLeft: theme.spacing(0), + }, + headerTop: { + marginTop: theme.spacing(2), + }, + utilsButton: { + marginRight: theme.spacing(1), + }, +})) +const ProposalComments: React.FC = (props) => { + const styles = useStyles() + const { + issueId, + client, + user, + extraButtonSlot, + alreadyLogin, + } = props + const [loaded, setLoaded] = useState(false) + const [loading, setLoading] = useState(false) + const [data, setData] = useState([]) + const [pageCount, setPageCount] = useState(0) + const [page, setPage] = useState(-1) + const [showAddDialog, setShowAddDialog] = useState(false) + const loadPage = async (page: number): Promise => { + // -1表示加载最后一页 + setLoading(true) + const commentCount = await client.getIssueCommentCount(issueId) + const pages = Math.ceil(commentCount / COMMENTS_PER_PAGE) + if (page === -1) page = pages + const resp = await client.getComments(issueId, page, COMMENTS_PER_PAGE) + console.debug('Loaded comments for issueId ', issueId, resp) + setData(resp) + setPageCount(pages) + setPage(page) + setLoading(false) + setLoaded(true) + } + useEffect(() => { + if (!loaded) loadPage(1) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loaded]) + useEffect(() => { + setLoaded(false) + }, [issueId]) + return <> + {alreadyLogin && <> + + <> + {extraButtonSlot} + + + + } + + 评论 + + + + {loading && + + + + } + {loaded && <> + {pageCount === 0 + ? <> + + + 暂无评论... + + + + : <> + {data.map((x, i) =>
+ + + + + + + + + {x.user.login} + + + + + {DateTime.fromISO(x.created_at).toJSDate().toLocaleString()} + + + + + {x.body} + + + + +
)} + + + loadPage(p)} + className={styles.pagination} + > + + + } + } + {showAddDialog && loadPage(-1)} + onClose={() => setShowAddDialog(false)} + issueId={issueId} + open={showAddDialog} + user={user} + >} + +} + +export default ProposalComments diff --git a/gatsby-theme-oi-wiki/src/components/Collection/index.tsx b/gatsby-theme-oi-wiki/src/components/Collection/index.tsx new file mode 100644 index 00000000..9a570ecd --- /dev/null +++ b/gatsby-theme-oi-wiki/src/components/Collection/index.tsx @@ -0,0 +1,29 @@ +import Collection from './Collection' +import { Accordion, AccordionDetails, AccordionSummary, Container, makeStyles, Typography } from '@material-ui/core' +import React from 'react' +import { ExpandMore as ExpandMoreIcon, List as ListIcon } from '@material-ui/icons' +const useStyles = makeStyles({ + metaicon: { + verticalAlign: 'sub', + }, +}) +const CollectionComponent: React.FC<{ id: string }> = ({ id }) => { + const classes = useStyles() + return + } + aria-controls="problem collection" + > + + 题单 + + + + + + + + +} + +export default CollectionComponent diff --git a/gatsby-theme-oi-wiki/src/components/Collection/types.ts b/gatsby-theme-oi-wiki/src/components/Collection/types.ts new file mode 100644 index 00000000..80f4c483 --- /dev/null +++ b/gatsby-theme-oi-wiki/src/components/Collection/types.ts @@ -0,0 +1,79 @@ +/* eslint-disable no-undef */ +/* eslint-disable camelcase */ +/* eslint-disable spaced-comment */ + +type CollectionUser = { login: false; } | { + login: true; + username: string; + avatar: string; + homepage: string; + id: number; + node_id: string; +} + +interface GeneralGithubUser { + login: string;//用户名 + id: number; + node_id: string; + avatar_id: string; + gravatar_id: string; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: 'User'; + site_admin: boolean; + avatar_url: string; +} + +interface Response_getIssue { + id: string; + title: string; + content: string; + link: string; +} + +type ReturnValue_getIssue = Response_getIssue | null; + +interface ProposalMeta { + name: string; + url: string; + description: string; +} +interface CollectionItem extends ProposalMeta { + id: number; + commentCount: number; + supportCount: number; + user: GeneralGithubUser; + nodeId: string; +} +type SortMethod = 'support' | 'comment' +type ButtonClickEvent = React.MouseEventHandler; + +interface ProposalComment { + id: number; + node_id: string; + body: string; + user: GeneralGithubUser; + created_at: string; + updated_at: string; +} + +export type { + CollectionUser, + ReturnValue_getIssue, + Response_getIssue, + GeneralGithubUser, + ProposalMeta, + CollectionItem, + SortMethod, + ButtonClickEvent, + ProposalComment, +} diff --git a/gatsby-theme-oi-wiki/src/components/Mdx.tsx b/gatsby-theme-oi-wiki/src/components/Mdx.tsx index 84d8deaf..fbd6a617 100644 --- a/gatsby-theme-oi-wiki/src/components/Mdx.tsx +++ b/gatsby-theme-oi-wiki/src/components/Mdx.tsx @@ -23,6 +23,7 @@ const Mdx: React.FC = ({ data: { mdx }, location }) => { const tags = mdx.frontmatter.tags || [] const noMeta = mdx.frontmatter.noMeta || false const noComment = mdx.frontmatter.noComment || false + const noCollection = mdx.frontmatter.noCollection || false const noEdit = false const headings = mdx.headings || null const relativePath = mdx.parent.relativePath || '' @@ -91,6 +92,7 @@ const Mdx: React.FC = ({ data: { mdx }, location }) => { modifiedTime={modifiedTime} noMeta={noMeta} noComment={noComment} + noCollection={noCollection} noEdit={noEdit} isWIP={isWIP} > diff --git a/gatsby-theme-oi-wiki/src/components/StyledLayout.tsx b/gatsby-theme-oi-wiki/src/components/StyledLayout.tsx index 521bacc7..426afc94 100644 --- a/gatsby-theme-oi-wiki/src/components/StyledLayout.tsx +++ b/gatsby-theme-oi-wiki/src/components/StyledLayout.tsx @@ -23,6 +23,7 @@ import Meta, { MetaProps } from './Meta' import Title from './Title' import NavAndDrawer from './NavAndDrawer' import { RequiredNonNull } from '../types/common' +import Collection from './Collection' const useStyles = makeStyles((theme) => ({ toolbar: { @@ -74,6 +75,7 @@ interface MyLayoutProps extends Partial { headings?: TocObj; noMeta?: boolean; noComment?: boolean; + noCollection?: boolean; noEdit?: boolean; noToc?: boolean; overflow?: boolean; @@ -108,6 +110,7 @@ const MyLayout: React.FC = (props) => { headings = null, noMeta = false, noComment = false, + noCollection = false, noEdit = true, noToc = !props.headings, overflow = false, @@ -122,7 +125,7 @@ const MyLayout: React.FC = (props) => { const desc = description || siteDesc const WIPAlert = ( - } className={classes.wip}> + } className={classes.wip}> 本文内容尚不完善,我们正在努力施工中。您可以保存此页链接稍后再看,或者帮助我们修订此页面! ) @@ -130,10 +133,10 @@ const MyLayout: React.FC = (props) => { <> {`${(!title || title === siteTitle) ? '' : `${title} - `}${siteTitle}`} - - + + - +
= (props) => { lg={gridWidthMdUp} xl={gridWidthMdUp} > -
+
- <Divider className={classes.divider}/> + <Divider className={classes.divider} /> {isWIP && WIPAlert} <Typography variant="body1" component="div"> {children} </Typography> {!noMeta && <Meta {...titleMetaProps} {...metaProps} />} {!noComment && <div style={{ width: '100%', marginTop: theme.spacing(2) }}> - <Comment title={title}/> + <Comment title={title} /> + </div>} + {!noCollection && <div style={{ width: '100%', marginTop: theme.spacing(2) }}> + <Collection id={title}></Collection> </div>} </div> </main> </div> </Grid> </Grid> - <Divider/> + <Divider /> <div className={classes.footer}> - <Footer/> + <Footer /> </div> </div> {!noToc && headings && <Grid item xs> - <Toc toc={headings} pathname={location.pathname}/> + <Toc toc={headings} pathname={location.pathname} /> </Grid>} - <BackTop/> + <BackTop /> </> ) } const StyledLayout: React.FC<MyLayoutProps> = (props) => <ThemeProvider theme={adaptiveTheme}> - <CssBaseline/> - <CustomCssBaseline/> - <LightCssBaseline/> - <DarkCssBaseline/> - <SecondaryColorCssBaseline/> - <AutoCssBaseline/> + <CssBaseline /> + <CustomCssBaseline /> + <LightCssBaseline /> + <DarkCssBaseline /> + <SecondaryColorCssBaseline /> + <AutoCssBaseline /> <MyLayout {...props} /> </ThemeProvider> diff --git a/yarn.lock b/yarn.lock index 1b9c87b9..1bc807fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2189,6 +2189,57 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@octokit/endpoint@^6.0.1": + version "6.0.12" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" + integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== + dependencies: + "@octokit/types" "^6.0.3" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^4.6.4": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.4.tgz#0c3f5bed440822182e972317122acb65d311a5ed" + integrity sha512-SWTdXsVheRmlotWNjKzPOb6Js6tjSqA2a8z9+glDJng0Aqjzti8MEWOtuT8ZSu6wHnci7LZNuarE87+WJBG4vg== + dependencies: + "@octokit/request" "^5.6.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + +"@octokit/openapi-types@^9.1.1": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-9.1.1.tgz#fb87f2e2f44b95a5720d61dee409a9f1fbc59217" + integrity sha512-xmyPP9tVb4T4A6Lk6SL6ScnIqAHpPV4jfMZI8VtY286212ri9J/6IFGuLsZ26daADUmriuLejake4k+azEfnaw== + +"@octokit/request-error@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" + integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== + dependencies: + "@octokit/types" "^6.0.3" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.0.tgz#6084861b6e4fa21dc40c8e2a739ec5eff597e672" + integrity sha512-4cPp/N+NqmaGQwbh3vUsYqokQIzt7VjsgTYVXiwpUP2pxd5YiZB2XuTedbb0SPtv9XS7nzAKjAuQxmY8/aZkiA== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.1.0" + "@octokit/types" "^6.16.1" + is-plain-object "^5.0.0" + node-fetch "^2.6.1" + universal-user-agent "^6.0.0" + +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1": + version "6.21.1" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.21.1.tgz#d0f2b7598c88e13d0bd87e330d975e3fb2a90180" + integrity sha512-PP+m3T5EWZKawru4zi/FvX8KL2vkO5f1fLthx78/7743p7RtJUevt3z7698k+7oAYRA7YuVqfXthSEHqkDvZ8g== + dependencies: + "@octokit/openapi-types" "^9.1.1" + "@pmmmwh/react-refresh-webpack-plugin@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz#1eec460596d200c0236bf195b078a5d1df89b766" @@ -2502,6 +2553,11 @@ dependencies: "@types/jquery" "*" +"@types/luxon@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.0.4.tgz#f7b5a86ccd843c0ccaddfaedd9ee1081bc1cde3b" + integrity sha512-l3xuhmyF2kBldy15SeY6d6HbK2BacEcSK1qTF1ISPtPHr29JH0C1fndz9ExXLKpGl0J6pZi+dGp1i5xesMt60Q== + "@types/mathjax@^0.0.36": version "0.0.36" resolved "https://registry.yarnpkg.com/@types/mathjax/-/mathjax-0.0.36.tgz#18cf766f88ac0cd4e7ee8282b1286049bb6aa682" @@ -2640,9 +2696,23 @@ integrity sha1-EHPEvIJHVK49EM+riKsCN7qWTk0= "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" - integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.5.tgz#fdd299f23205c3455af88ce618dd65c14cb73e22" + integrity sha512-wnra4Vw9dopnuybR6HBywJ/URYpYrKLoepBTEtgfJup8Ahoi2zJECPP2cwiXp7btTvOT2CULv87aQRA4eZSP6g== + +"@types/use-persisted-state@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@types/use-persisted-state/-/use-persisted-state-0.3.0.tgz#23370e89bd6c8468afdb05dde2d1bab898128229" + integrity sha512-ZT98QuckR95qM7W97lGVqc7fFS9TT6f3txp7R40fl0zxa5BLm3GG7j0i51G12h8DkoJxFAf2oQyYKU99h0pxFA== + dependencies: + "@types/react" "*" + +"@types/vfile-message@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/vfile-message/-/vfile-message-2.0.0.tgz#690e46af0fdfc1f9faae00cd049cc888957927d5" + integrity sha512-GpTIuDpb9u4zIO165fUy9+fXcULdD8HFRNli04GehoMVbeNq7D6OBnqSmg3lxZnC+UvgUhEWKxdKiwYUkGltIw== + dependencies: + vfile-message "*" "@types/use-persisted-state@^0.3.0": version "0.3.0" @@ -5166,6 +5236,11 @@ dependency-graph@^0.11.0: resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== +deprecation@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -8794,6 +8869,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-png@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-png/-/is-png-2.0.0.tgz#ee8cbc9e9b050425cedeeb4a6fb74a649b0a4a8d" @@ -9060,6 +9140,11 @@ jpeg-js@^0.4.0: resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b" integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q== +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -9737,6 +9822,11 @@ lru-queue@^0.1.0: dependencies: es5-ext "~0.10.2" +luxon@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.0.2.tgz#11f2cd4a11655fdf92e076b5782d7ede5bcdd133" + integrity sha512-ZRioYLCgRHrtTORaZX1mx+jtxKtKuI5ZDvHNAmqpUzGqSrR+tL4FVLn/CUGMA3h0+AKD1MAxGI5GnCqR5txNqg== + make-dir@^1.0.0, make-dir@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"