mirror of
https://github.com/Kxsso/jcutmirror.git
synced 2026-03-27 15:27:02 +08:00
合并浙大上游仓库
This commit is contained in:
@@ -13,13 +13,15 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
project: './tsconfig.json',
|
||||||
|
tsconfigRootDir: './',
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
jsx: true,
|
jsx: true,
|
||||||
},
|
},
|
||||||
ecmaVersion: 'latest',
|
ecmaVersion: 'latest',
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
plugins: ['react', '@typescript-eslint', 'prettier'],
|
plugins: ['react', '@typescript-eslint', 'import', 'prettier'],
|
||||||
rules: {
|
rules: {
|
||||||
// needed by prettier
|
// needed by prettier
|
||||||
'prettier/prettier': 'warn',
|
'prettier/prettier': 'warn',
|
||||||
|
|||||||
2
.github/workflows/testbuild.yml
vendored
2
.github/workflows/testbuild.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [18.x]
|
node-version: [20.x]
|
||||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
image: node:lts-bullseye
|
image:
|
||||||
|
name: node:20
|
||||||
|
pull_policy: if-not-present
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
@@ -8,12 +10,16 @@ gatsby-build:
|
|||||||
stage: build
|
stage: build
|
||||||
variables:
|
variables:
|
||||||
CHOKIDAR_USEPOLLING: 1
|
CHOKIDAR_USEPOLLING: 1
|
||||||
|
GATSBY_TELEMETRY_DISABLED: 1
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- node_modules/
|
- .npm/
|
||||||
script:
|
script:
|
||||||
- bash cached-restore.sh
|
- apt-get update && apt-get install -y libvips42 libvips-dev
|
||||||
- yarn run build
|
- npm config set registry https://registry.npmmirror.com
|
||||||
|
- npm install -g npm@10.5.1
|
||||||
|
- npm ci --cache .npm --prefer-offline
|
||||||
|
- npm run build
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
import './src/styles/global.scss';
|
import './src/styles/global.scss';
|
||||||
|
import React from 'react';
|
||||||
|
import RootLayout from './src/utils/root-layout';
|
||||||
|
|
||||||
|
export const wrapRootElement = ({ element }) => <RootLayout>{element}</RootLayout>
|
||||||
|
|||||||
@@ -16,20 +16,6 @@ module.exports = {
|
|||||||
assetPrefix: config.assetPrefix,
|
assetPrefix: config.assetPrefix,
|
||||||
pathPrefix: config.pathPrefix,
|
pathPrefix: config.pathPrefix,
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
|
||||||
resolve: `gatsby-theme-material-ui`,
|
|
||||||
options: {
|
|
||||||
webFontsConfig: {
|
|
||||||
fonts: {
|
|
||||||
google: [
|
|
||||||
{
|
|
||||||
family: `Metrophobic`
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
resolve: 'gatsby-source-filesystem',
|
resolve: 'gatsby-source-filesystem',
|
||||||
options: {
|
options: {
|
||||||
@@ -57,7 +43,7 @@ module.exports = {
|
|||||||
mdxOptions: {
|
mdxOptions: {
|
||||||
remarkPlugins: [
|
remarkPlugins: [
|
||||||
// Add GitHub Flavored Markdown (GFM) support
|
// Add GitHub Flavored Markdown (GFM) support
|
||||||
// Warning: Most of the remark ecosystem is ESM which means that packages
|
// Warning: Most of the remark ecosystem is ESM which means that packages
|
||||||
// like remark-gfm don’t work out of the box with Gatsby v5. To make remark-gfm
|
// like remark-gfm don’t work out of the box with Gatsby v5. To make remark-gfm
|
||||||
// compatible with the current Gatsby version, we have to use an old version of
|
// compatible with the current Gatsby version, we have to use an old version of
|
||||||
// remark-gfm. Please take care in future updates.
|
// remark-gfm. Please take care in future updates.
|
||||||
@@ -72,11 +58,11 @@ module.exports = {
|
|||||||
rule: {
|
rule: {
|
||||||
include: /icons/,
|
include: /icons/,
|
||||||
omitKeys: [
|
omitKeys: [
|
||||||
'inkscapePageshadow', 'inkscapePageopacity', 'inkscapePagecheckerboard',
|
'inkscapePageshadow', 'inkscapePageopacity', 'inkscapePagecheckerboard',
|
||||||
'inkscapeZoom', 'inkscapeCx', 'inkscapeCy', 'inkscapeWindowWidth', 'inkscapeWindowHeight',
|
'inkscapeZoom', 'inkscapeCx', 'inkscapeCy', 'inkscapeWindowWidth', 'inkscapeWindowHeight',
|
||||||
'inkscapeWindowX', 'inkscapeWindowY', 'inkscapeWindowMaximized', 'inkscapeCurrentLayer',
|
'inkscapeWindowX', 'inkscapeWindowY', 'inkscapeWindowMaximized', 'inkscapeCurrentLayer',
|
||||||
'sodipodiNodetypes', 'sodipodiDocname', 'inkscapeVersion', 'xmlnsInkscape', 'xmlnsSodipodi',
|
'sodipodiNodetypes', 'sodipodiDocname', 'inkscapeVersion', 'xmlnsInkscape', 'xmlnsSodipodi',
|
||||||
'xmlnsSvg',
|
'xmlnsSvg',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +99,6 @@ module.exports = {
|
|||||||
icon: 'resource/icons/favicon.svg',
|
icon: 'resource/icons/favicon.svg',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
`gatsby-plugin-preact`,
|
|
||||||
`gatsby-plugin-sass`,
|
`gatsby-plugin-sass`,
|
||||||
{
|
{
|
||||||
resolve: "gatsby-plugin-sitemap",
|
resolve: "gatsby-plugin-sitemap",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ const { match, compile } = require("path-to-regexp");
|
|||||||
const { createFilePath } = require(`gatsby-source-filesystem`)
|
const { createFilePath } = require(`gatsby-source-filesystem`)
|
||||||
const { createContentDigest } = require(`gatsby-core-utils`)
|
const { createContentDigest } = require(`gatsby-core-utils`)
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
const mdxResolverPassthrough = (fieldName) => async (
|
const mdxResolverPassthrough = (fieldName) => async (
|
||||||
source,
|
source,
|
||||||
@@ -194,3 +195,13 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.onCreateWebpackConfig = ({ actions }) => {
|
||||||
|
actions.setWebpackConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'~': path.resolve(__dirname, 'src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
5
gatsby-ssr.js
Normal file
5
gatsby-ssr.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import './src/styles/global.scss';
|
||||||
|
import React from 'react';
|
||||||
|
import RootLayout from './src/utils/root-layout';
|
||||||
|
|
||||||
|
export const wrapRootElement = ({ element }) => <RootLayout>{element}</RootLayout>
|
||||||
93
package.json
93
package.json
@@ -17,67 +17,66 @@
|
|||||||
"lint-check": "eslint src/**/*"
|
"lint-check": "eslint src/**/*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.9.0",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.8.1",
|
"@emotion/server": "^11.11.0",
|
||||||
|
"@emotion/styled": "^11.11.5",
|
||||||
"@fontsource/poppins": "^4.5.10",
|
"@fontsource/poppins": "^4.5.10",
|
||||||
"@formatjs/intl-displaynames": "^5.4.3",
|
"@formatjs/intl-displaynames": "^5.4.3",
|
||||||
"@iconify/icons-file-icons": "^1.2.3",
|
"@iconify/icons-file-icons": "^1.2.3",
|
||||||
"@iconify/icons-logos": "^1.2.36",
|
"@iconify/icons-logos": "^1.2.36",
|
||||||
"@iconify/icons-mdi": "^1.2.44",
|
"@iconify/icons-mdi": "^1.2.48",
|
||||||
"@iconify/icons-simple-icons": "^1.2.74",
|
"@iconify/icons-simple-icons": "^1.2.74",
|
||||||
"@iconify/react": "^3.2.1",
|
"@iconify/react": "^3.2.2",
|
||||||
|
"@mdx-js/mdx": "^3.0.1",
|
||||||
"@mdx-js/react": "^2.3.0",
|
"@mdx-js/react": "^2.3.0",
|
||||||
"@mui/icons-material": "^5.6.2",
|
"@mui/icons-material": "^5.15.15",
|
||||||
"@mui/material": "^5.6.3",
|
"@mui/material": "^5.15.15",
|
||||||
"gatsby": "^5.9.1",
|
"gatsby": "^5.13.3",
|
||||||
"gatsby-plugin-manifest": "^5.6.0",
|
"gatsby-plugin-manifest": "^5.13.1",
|
||||||
"gatsby-plugin-mdx": "^5.6.0",
|
"gatsby-plugin-mdx": "^5.13.1",
|
||||||
"gatsby-plugin-preact": "^7.6.1",
|
"gatsby-plugin-react-helmet": "^6.13.1",
|
||||||
"gatsby-plugin-react-i18next": "^3.0.1",
|
"gatsby-plugin-react-i18next": "^3.0.1",
|
||||||
"gatsby-plugin-react-svg": "^3.1.0",
|
"gatsby-plugin-react-svg": "^3.3.0",
|
||||||
"gatsby-plugin-sass": "^6.6.0",
|
"gatsby-plugin-sass": "^6.13.1",
|
||||||
"gatsby-plugin-sitemap": "^6.7.0",
|
"gatsby-plugin-sitemap": "^6.13.1",
|
||||||
"gatsby-source-filesystem": "^5.6.0",
|
"gatsby-source-filesystem": "^5.13.1",
|
||||||
"gatsby-theme-material-ui": "^5.2.0",
|
"i18next": "^22.5.1",
|
||||||
"gatsby-theme-material-ui-top-layout": "^5.2.0",
|
"js-search": "^2.0.1",
|
||||||
"i18next": "^21.6.16",
|
|
||||||
"js-search": "^2.0.0",
|
|
||||||
"natsort": "^2.0.3",
|
"natsort": "^2.0.3",
|
||||||
"path-to-regexp": "^6.2.0",
|
"path-to-regexp": "^6.2.2",
|
||||||
"preact": "^10.12.1",
|
"prism-react-renderer": "^1.3.5",
|
||||||
"preact-render-to-string": "^5.2.6",
|
"react": "^18.2.0",
|
||||||
"prism-react-renderer": "^1.3.1",
|
"react-dom": "^18.2.0",
|
||||||
"react": "^18.1.0",
|
|
||||||
"react-dom": "^18.1.0",
|
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-i18next": "^11.16.7",
|
"react-i18next": "^12.3.1",
|
||||||
"remark-gfm": "^1",
|
"remark-gfm": "^1.0.0",
|
||||||
"sass": "^1.53.0"
|
"sass": "^1.74.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.17.10",
|
"@babel/cli": "^7.24.1",
|
||||||
"@babel/plugin-transform-typescript": "^7.16.8",
|
"@babel/plugin-transform-typescript": "^7.24.4",
|
||||||
"@types/js-search": "^1.4.0",
|
"@types/js-search": "^1.4.4",
|
||||||
"@types/mdx": "^2.0.1",
|
"@types/mdx": "^2.0.12",
|
||||||
"@types/mdx-js__react": "^1.5.5",
|
"@types/mdx-js__react": "^1.5.8",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.19.31",
|
||||||
"@types/react": "^18.0.14",
|
"@types/react": "^18.2.75",
|
||||||
"@types/react-dom": "^18.0.5",
|
"@types/react-dom": "^18.2.24",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
"@typescript-eslint/parser": "^5.28.0",
|
"@typescript-eslint/parser": "^5.62.0",
|
||||||
"babel-plugin-i18next-extract": "^0.8.3",
|
"babel-plugin-i18next-extract": "^0.8.3",
|
||||||
"eslint": "^8.2.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-airbnb": "19.0.4",
|
"eslint-config-airbnb": "19.0.4",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.10.0",
|
||||||
"eslint-import-resolver-typescript": "^3.2.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-import": "^2.25.3",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.30.0",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"http-proxy-middleware": "^2.0.6",
|
"http-proxy-middleware": "^2.0.6",
|
||||||
"prettier": "2.7.1",
|
"prettier": "^3.2.5",
|
||||||
"typescript": "^4.7.3"
|
"prettier-eslint": "^16.3.0",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -38,13 +38,18 @@ export default ({ children, codeStyle, language }: CodeBlockProps) => {
|
|||||||
...codeStyle,
|
...codeStyle,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tokens.map((line, i) => (
|
{tokens.map((line, i) => {
|
||||||
<div key={i} {...getLineProps({ line, key: i })}>
|
if (line.length === 1 && line[0].content.indexOf('\u200b') > -1) {
|
||||||
{line.map((token, key) => (
|
return <br />;
|
||||||
<span key={key} {...getTokenProps({ token, key })} />
|
}
|
||||||
))}
|
return (
|
||||||
</div>
|
<div key={i} {...getLineProps({ line, key: i })}>
|
||||||
))}
|
{line.map((token, key) => (
|
||||||
|
<span key={key} {...getTokenProps({ token, key })} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</code>
|
</code>
|
||||||
)}
|
)}
|
||||||
</Highlight>
|
</Highlight>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { PaletteMode, ThemeOptions } from '@mui/material';
|
import { PaletteMode, ThemeOptions, createTheme } from '@mui/material';
|
||||||
import '@fontsource/poppins';
|
import '@fontsource/poppins';
|
||||||
|
|
||||||
export default function configTheme(mode: PaletteMode): ThemeOptions {
|
function configTheme(mode: PaletteMode): ThemeOptions {
|
||||||
return {
|
return {
|
||||||
palette:
|
palette:
|
||||||
mode === 'light'
|
mode === 'light'
|
||||||
@@ -91,6 +91,24 @@ export default function configTheme(mode: PaletteMode): ThemeOptions {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const lightTheme = createTheme(
|
||||||
|
{
|
||||||
|
palette: {
|
||||||
|
mode: 'light',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configTheme('light')
|
||||||
|
);
|
||||||
|
|
||||||
|
export const darkTheme = createTheme(
|
||||||
|
{
|
||||||
|
palette: {
|
||||||
|
mode: 'dark',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configTheme('dark')
|
||||||
|
);
|
||||||
|
|
||||||
/* the code below decares customized color */
|
/* the code below decares customized color */
|
||||||
|
|
||||||
declare module '@mui/material/styles' {
|
declare module '@mui/material/styles' {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Card, Grid, Typography } from '@mui/material';
|
import Card from '@mui/material/Card';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
import AlbumIcon from '@mui/icons-material/Album';
|
import AlbumIcon from '@mui/icons-material/Album';
|
||||||
import { Trans } from 'gatsby-plugin-react-i18next';
|
import { Trans } from 'gatsby-plugin-react-i18next';
|
||||||
import { CardActionArea } from 'gatsby-theme-material-ui';
|
import { LinkCardActionArea as CardActionArea } from './link-mui-components';
|
||||||
|
|
||||||
export interface FileProps {
|
export interface FileProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Box, Card, CardContent, Grid, Typography } from '@mui/material';
|
import Box from '@mui/material/Box';
|
||||||
import { CardActionArea } from 'gatsby-theme-material-ui';
|
import Card from '@mui/material/Card';
|
||||||
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { LinkCardActionArea as CardActionArea } from './link-mui-components';
|
||||||
|
|
||||||
export type mirrorBrief = {
|
export type mirrorBrief = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
39
src/components/link-mui-components.tsx
Normal file
39
src/components/link-mui-components.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import React, { ForwardedRef } from 'react';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
|
import Link from '@mui/material/Link';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import { navigate } from 'gatsby';
|
||||||
|
import { ButtonBaseProps } from '@mui/material';
|
||||||
|
|
||||||
|
interface LinkProps {
|
||||||
|
to?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function linkifyComponent<T, PropType>(
|
||||||
|
ButtonBaseComponent: React.ComponentType<ButtonBaseProps & PropType>
|
||||||
|
) {
|
||||||
|
const f = (
|
||||||
|
props: ButtonBaseProps & PropType & LinkProps,
|
||||||
|
ref: ForwardedRef<T>
|
||||||
|
) => {
|
||||||
|
const propOnClick = props.onClick;
|
||||||
|
const clickHandler: React.MouseEventHandler<HTMLButtonElement> = event => {
|
||||||
|
if (propOnClick) propOnClick(event);
|
||||||
|
if (props.to) navigate(props.to);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<ButtonBaseComponent {...props} onClick={clickHandler} ref={ref}>
|
||||||
|
{props.children}
|
||||||
|
</ButtonBaseComponent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return React.forwardRef(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LinkButton = linkifyComponent(Button);
|
||||||
|
const LinkCardActionArea = linkifyComponent(CardActionArea);
|
||||||
|
const LinkIconButton = linkifyComponent(IconButton);
|
||||||
|
const LinkLink = linkifyComponent(Link);
|
||||||
|
|
||||||
|
export { LinkProps, LinkButton, LinkCardActionArea, LinkIconButton, LinkLink };
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import InfoIcon from '@mui/icons-material/Info';
|
import InfoIcon from '@mui/icons-material/Info';
|
||||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||||
import { Tooltip } from '@mui/material';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import { IconButton } from 'gatsby-theme-material-ui';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { LinkIconButton as IconButton } from './link-mui-components';
|
||||||
import { usePrefs } from './preferences-context';
|
import { usePrefs } from './preferences-context';
|
||||||
|
|
||||||
const nameMode = {
|
const nameMode = {
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { Card, Grid, Typography, Tooltip } from '@mui/material';
|
import Card from '@mui/material/Card';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import { useI18next } from 'gatsby-plugin-react-i18next';
|
import { useI18next } from 'gatsby-plugin-react-i18next';
|
||||||
import { CardActionArea } from 'gatsby-theme-material-ui';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import VerifiedIcon from '@mui/icons-material/Verified';
|
||||||
|
import { LinkCardActionArea as CardActionArea } from './link-mui-components';
|
||||||
import { Locale, Mirror } from '../types/mirror';
|
import { Locale, Mirror } from '../types/mirror';
|
||||||
import StatusIndicator from './status-indicator';
|
import StatusIndicator from './status-indicator';
|
||||||
import { getUrl } from '../utils/url';
|
import { getUrl } from '../utils/url';
|
||||||
import { usePrefs } from './preferences-context';
|
import { usePrefs } from './preferences-context';
|
||||||
import VerifiedIcon from '@mui/icons-material/Verified';
|
|
||||||
import officialCertificatedMirrorList from '../utils/official-certificated-mirror-list';
|
import officialCertificatedMirrorList from '../utils/official-certificated-mirror-list';
|
||||||
|
|
||||||
const SearchItemCard = (props: { queryItem: Mirror }) => {
|
const SearchItemCard = (props: { queryItem: Mirror }) => {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { JSX } from 'preact';
|
|
||||||
import { useStaticQuery, graphql } from 'gatsby';
|
import { useStaticQuery, graphql } from 'gatsby';
|
||||||
import { useTheme } from '@mui/material';
|
import { useTheme } from '@mui/material';
|
||||||
import { useI18next } from 'gatsby-plugin-react-i18next';
|
import { useI18next } from 'gatsby-plugin-react-i18next';
|
||||||
|
|
||||||
type MetaProps = JSX.IntrinsicElements['meta'];
|
type MetaProps = React.JSX.IntrinsicElements['meta'];
|
||||||
|
|
||||||
const Helmet: React.FC = ({children, title, meta}) => {
|
const Helmet: React.FC = ({children, title, meta}) => {
|
||||||
const {languages, language, originalPath, defaultLanguage, siteUrl = ''} = useI18next();
|
const {languages, language, originalPath, defaultLanguage, siteUrl = ''} = useI18next();
|
||||||
@@ -16,11 +15,7 @@ const Helmet: React.FC = ({children, title, meta}) => {
|
|||||||
<>
|
<>
|
||||||
<html lang={language} />
|
<html lang={language} />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
{
|
{meta?.map((m: MetaProps, i: number) => <meta {...m} key={i}/>)}
|
||||||
meta?.map((m: MetaProps) => (
|
|
||||||
<meta {...m} />
|
|
||||||
))
|
|
||||||
}
|
|
||||||
<link rel="canonical" href={createUrlWithLang(language)} />
|
<link rel="canonical" href={createUrlWithLang(language)} />
|
||||||
{languages.map((lng) => (
|
{languages.map((lng) => (
|
||||||
<link rel="alternate" key={lng} href={createUrlWithLang(lng)} hrefLang={lng} />
|
<link rel="alternate" key={lng} href={createUrlWithLang(lng)} hrefLang={lng} />
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import {
|
|||||||
} from '@mui/material/styles';
|
} from '@mui/material/styles';
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import configTheme from './config-theme';
|
import { lightTheme, darkTheme } from './config-theme';
|
||||||
import { readCache, writeCache } from '../utils/cache';
|
import { readCache, writeCache } from '../utils/cache';
|
||||||
|
|
||||||
export type ThemeMode = PaletteMode | 'auto';
|
export type ThemeMode = PaletteMode | 'auto';
|
||||||
|
|
||||||
export declare type ThemeContextInterface = [
|
export declare type ThemeContextInterface = [
|
||||||
ThemeMode,
|
ThemeMode,
|
||||||
(mode: ThemeMode) => void
|
(mode: ThemeMode) => void,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ThemeContext = React.createContext<ThemeContextInterface | null>(
|
export const ThemeContext = React.createContext<ThemeContextInterface | null>(
|
||||||
@@ -27,11 +27,12 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const [mode, setMode] = React.useState<ThemeMode>(savedMode);
|
const [mode, setMode] = React.useState<ThemeMode>(savedMode);
|
||||||
|
|
||||||
let paletteMode: PaletteMode;
|
let paletteMode: PaletteMode;
|
||||||
|
// ensure renders execute equal number of hooks
|
||||||
|
const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
|
||||||
|
const prefersLight = useMediaQuery('(prefers-color-scheme: light)');
|
||||||
if (mode === 'auto') {
|
if (mode === 'auto') {
|
||||||
// wait for media query result stable
|
// wait for media query result stable
|
||||||
// ref: https://github.com/mui/material-ui/issues/15588#issuecomment-567803082
|
// ref: https://github.com/mui/material-ui/issues/15588#issuecomment-567803082
|
||||||
const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');
|
|
||||||
const prefersLight = useMediaQuery('(prefers-color-scheme: light)');
|
|
||||||
if (prefersDark !== !prefersLight) return null;
|
if (prefersDark !== !prefersLight) return null;
|
||||||
const preferredMode = prefersDark ? 'dark' : 'light';
|
const preferredMode = prefersDark ? 'dark' : 'light';
|
||||||
paletteMode = preferredMode;
|
paletteMode = preferredMode;
|
||||||
@@ -39,18 +40,7 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
paletteMode = mode;
|
paletteMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const theme = React.useMemo(
|
const theme = paletteMode === 'light' ? lightTheme : darkTheme;
|
||||||
() =>
|
|
||||||
createTheme(
|
|
||||||
{
|
|
||||||
palette: {
|
|
||||||
mode: paletteMode,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
configTheme(paletteMode)
|
|
||||||
),
|
|
||||||
[paletteMode]
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateMode = (newMode: ThemeMode) => {
|
const updateMode = (newMode: ThemeMode) => {
|
||||||
setMode(newMode);
|
setMode(newMode);
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import Brightness4Icon from '@mui/icons-material/Brightness4';
|
|||||||
import BrightnessAutoIcon from '@mui/icons-material/BrightnessAuto';
|
import BrightnessAutoIcon from '@mui/icons-material/BrightnessAuto';
|
||||||
import BrightnessHighIcon from '@mui/icons-material/BrightnessHigh';
|
import BrightnessHighIcon from '@mui/icons-material/BrightnessHigh';
|
||||||
import ToolTip from '@mui/material/Tooltip';
|
import ToolTip from '@mui/material/Tooltip';
|
||||||
import { IconButton } from 'gatsby-theme-material-ui';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { LinkIconButton as IconButton } from './link-mui-components';
|
||||||
import { ThemeMode, useMode } from './theme-context';
|
import { ThemeMode, useMode } from './theme-context';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
|||||||
236
src/components/ubuntu-config-generator.tsx
Normal file
236
src/components/ubuntu-config-generator.tsx
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
Box,
|
||||||
|
Grid,
|
||||||
|
Typography,
|
||||||
|
FormControlLabel,
|
||||||
|
Switch,
|
||||||
|
FormGroup,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { Trans } from 'gatsby-plugin-react-i18next';
|
||||||
|
import CodeBlock from './code-block';
|
||||||
|
|
||||||
|
const ubuntuVersionMap: Record<number | string, string> = {
|
||||||
|
2404: 'noble',
|
||||||
|
2310: 'mantic',
|
||||||
|
2304: 'lunar',
|
||||||
|
2210: 'kinetic',
|
||||||
|
2204: 'jammy',
|
||||||
|
2004: 'focal',
|
||||||
|
1804: 'bionic',
|
||||||
|
1604: 'xenial',
|
||||||
|
1404: 'trusty',
|
||||||
|
};
|
||||||
|
|
||||||
|
const isLTSVersion = (version: number) => {
|
||||||
|
return Math.floor(version / 100) % 2 === 0 && version % 100 === 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
const configGenOld = (
|
||||||
|
version: number | string,
|
||||||
|
enableHTTPS: boolean,
|
||||||
|
enableSrc: boolean,
|
||||||
|
enableProposed: boolean,
|
||||||
|
enableSecurity: boolean,
|
||||||
|
ubuntuVariant: string
|
||||||
|
) => {
|
||||||
|
const ubuntuName = ubuntuVersionMap[version];
|
||||||
|
const httpProtocol = enableHTTPS ? 'https' : 'http';
|
||||||
|
const debSrcText = enableSrc ? '' : '# ';
|
||||||
|
const proposedText = enableProposed ? '' : '# ';
|
||||||
|
const securityRepo = enableSecurity
|
||||||
|
? 'mirrors.jcut.edu.cn'
|
||||||
|
: 'security.ubuntu.com';
|
||||||
|
return `deb ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/ ${ubuntuName} main restricted universe multiverse
|
||||||
|
${debSrcText}deb-src ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/ ${ubuntuName} main restricted universe multiverse
|
||||||
|
deb ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/ ${ubuntuName}-updates main restricted universe multiverse
|
||||||
|
${debSrcText}deb-src ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/ ${ubuntuName}-updates main restricted universe multiverse
|
||||||
|
deb ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/ ${ubuntuName}-backports main restricted universe multiverse
|
||||||
|
${debSrcText}deb-src ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/ ${ubuntuName}-backports main restricted universe multiverse
|
||||||
|
|
||||||
|
deb ${httpProtocol}://${securityRepo}/${ubuntuVariant}/ ${ubuntuName}-security main restricted universe multiverse
|
||||||
|
${debSrcText}deb-src ${httpProtocol}://${securityRepo}/${ubuntuVariant}/ ${ubuntuName}-security main restricted universe multiverse
|
||||||
|
${proposedText}deb ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/ ${ubuntuName}-proposed main restricted universe multiverse
|
||||||
|
${proposedText}${debSrcText}deb-src ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/ ${ubuntuName}-proposed main restricted universe multiverse`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const configGenNew = (
|
||||||
|
version: number | string,
|
||||||
|
enableHTTPS: boolean,
|
||||||
|
enableSrc: boolean,
|
||||||
|
enableProposed: boolean,
|
||||||
|
enableSecurity: boolean,
|
||||||
|
ubuntuVariant: string
|
||||||
|
) => {
|
||||||
|
const ubuntuName = ubuntuVersionMap[version];
|
||||||
|
const httpProtocol = enableHTTPS ? 'https' : 'http';
|
||||||
|
const debSrcText = enableSrc ? ' deb-src' : '';
|
||||||
|
const securityRepo = enableSecurity
|
||||||
|
? 'mirrors.zju.edu.cn'
|
||||||
|
: 'security.ubuntu.com';
|
||||||
|
let sources = `Types: deb${debSrcText}
|
||||||
|
URIs: ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/
|
||||||
|
Suites: ${ubuntuName} ${ubuntuName}-updates ${ubuntuName}-backports
|
||||||
|
Components: main restricted universe multiverse
|
||||||
|
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
|
||||||
|
\u200b
|
||||||
|
Types: deb${debSrcText}
|
||||||
|
URIs: ${httpProtocol}://${securityRepo}/${ubuntuVariant}/
|
||||||
|
Suites: ${ubuntuName}-security
|
||||||
|
Components: main restricted universe multiverse
|
||||||
|
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
|
||||||
|
`;
|
||||||
|
if (enableProposed) {
|
||||||
|
sources += `\u200b
|
||||||
|
Types: deb${debSrcText}
|
||||||
|
URIs: ${httpProtocol}://mirrors.jcut.edu.cn/${ubuntuVariant}/
|
||||||
|
Suites: ${ubuntuName}-proposed
|
||||||
|
Components: main restricted universe multiverse
|
||||||
|
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return sources;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ({ ubuntuVariant }: { ubuntuVariant: string }) => {
|
||||||
|
const [version, setVersion] = useState(
|
||||||
|
Object.keys(ubuntuVersionMap)
|
||||||
|
.reverse()
|
||||||
|
.filter(v => isLTSVersion(parseInt(v, 10)))[0]
|
||||||
|
);
|
||||||
|
const [enableHTTPS, setEnableHTTPS] = useState(true);
|
||||||
|
const [enableSrc, setEnableSrc] = useState(false);
|
||||||
|
const [enableProposed, setEnableProposed] = useState(false);
|
||||||
|
const [enableSecurity, setEnableSecurity] = useState(true);
|
||||||
|
const shouldUseNewConf = () => parseInt(version, 10) >= 2404;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
direction="row"
|
||||||
|
justifyContent="flex-start"
|
||||||
|
alignItems="flex-end"
|
||||||
|
>
|
||||||
|
<Grid item sx={{ mb: 1 }}>
|
||||||
|
<Typography component="p">请选择您的 Ubuntu 版本:</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<FormControl variant="standard" sx={{ m: 1, minWidth: 120 }}>
|
||||||
|
<InputLabel id="demo-simple-select-helper-label">
|
||||||
|
<Trans>版本</Trans>
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="demo-simple-select-helper-label"
|
||||||
|
id="demo-simple-select-helper"
|
||||||
|
label="Age"
|
||||||
|
onChange={event => {
|
||||||
|
setVersion(event.target.value as string);
|
||||||
|
}}
|
||||||
|
defaultValue={version}
|
||||||
|
>
|
||||||
|
{Object.keys(ubuntuVersionMap)
|
||||||
|
.reverse()
|
||||||
|
.map((v: string) => {
|
||||||
|
const item = parseInt(v, 10);
|
||||||
|
let desc = (item / 100).toFixed(2);
|
||||||
|
if (isLTSVersion(item)) desc += ' LTS';
|
||||||
|
desc += ` (${ubuntuVersionMap[item]})`;
|
||||||
|
return (
|
||||||
|
<MenuItem key={item} value={item}>
|
||||||
|
{desc}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<FormGroup>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={enableHTTPS}
|
||||||
|
onChange={e => setEnableHTTPS(e.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="启用 HTTPS (容器镜像可能不支持)"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={enableSrc}
|
||||||
|
onChange={e => setEnableSrc(e.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="启用源码源(默认禁用以提高速度)"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={enableProposed}
|
||||||
|
onChange={e => setEnableProposed(e.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="启用预发布软件源(不建议启用)"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={enableSecurity}
|
||||||
|
onChange={e => setEnableSecurity(e.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="启用安全更新镜像(仅推荐校网内启用)"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<Grid container direction="row" my={2}>
|
||||||
|
<Typography>该版本 Ubuntu 的默认软件源配置文件是</Typography>
|
||||||
|
<code
|
||||||
|
style={{ margin: '0 0.5rem', fontWeight: 'bold', color: 'brown' }}
|
||||||
|
>
|
||||||
|
{shouldUseNewConf()
|
||||||
|
? '/etc/apt/sources.list.d/ubuntu.sources'
|
||||||
|
: '/etc/apt/sources.list'}
|
||||||
|
</code>
|
||||||
|
<Typography>
|
||||||
|
。将系统自带的该文件做个备份,将该文件替换为下面内容,即可使用我们的软件源镜像。
|
||||||
|
</Typography>
|
||||||
|
{shouldUseNewConf() && (
|
||||||
|
<>
|
||||||
|
<Typography style={{ fontWeight: 'bold' }}>
|
||||||
|
如果您从旧版本 Ubuntu 升级到该版本,可能需要同时备份并清除
|
||||||
|
</Typography>
|
||||||
|
<code style={{ margin: '0 0.5rem', fontWeight: 'bold' }}>
|
||||||
|
/etc/apt/sources.list
|
||||||
|
</code>
|
||||||
|
<Typography style={{ fontWeight: 'bold' }}>的内容。</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
<CodeBlock language="bash">
|
||||||
|
{shouldUseNewConf()
|
||||||
|
? configGenNew(
|
||||||
|
version,
|
||||||
|
enableHTTPS,
|
||||||
|
enableSrc,
|
||||||
|
enableProposed,
|
||||||
|
enableSecurity,
|
||||||
|
ubuntuVariant
|
||||||
|
)
|
||||||
|
: configGenOld(
|
||||||
|
version,
|
||||||
|
enableHTTPS,
|
||||||
|
enableSrc,
|
||||||
|
enableProposed,
|
||||||
|
enableSecurity,
|
||||||
|
ubuntuVariant
|
||||||
|
)}
|
||||||
|
</CodeBlock>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import CssBaseline from '@mui/material/CssBaseline';
|
|
||||||
import Viewport from 'gatsby-theme-material-ui-top-layout/src/components/viewport';
|
|
||||||
import React from 'react';
|
|
||||||
import { ThemeProvider } from '../components/theme-context';
|
|
||||||
import { PrefsProvider } from '../components/preferences-context';
|
|
||||||
|
|
||||||
export default function wrapWithProvider({
|
|
||||||
element,
|
|
||||||
}: {
|
|
||||||
element: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<React.StrictMode>
|
|
||||||
<Viewport />
|
|
||||||
<PrefsProvider>
|
|
||||||
<ThemeProvider>
|
|
||||||
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
|
||||||
<CssBaseline />
|
|
||||||
{element}
|
|
||||||
</ThemeProvider>
|
|
||||||
</PrefsProvider>
|
|
||||||
</React.StrictMode>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
7
src/pages/404.tsx
Normal file
7
src/pages/404.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const NotFoundPage = () => {
|
||||||
|
return <h1>Page Not Found</h1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotFoundPage;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Box, Chip, Grid, Link, Typography } from '@mui/material';
|
import { Box, Chip, ChipProps, Grid, Link, Typography } from '@mui/material';
|
||||||
import { graphql } from 'gatsby';
|
import { graphql } from 'gatsby';
|
||||||
import { Trans, useI18next } from 'gatsby-plugin-react-i18next';
|
import { Trans, useI18next } from 'gatsby-plugin-react-i18next';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
@@ -9,7 +9,7 @@ import SearchTable from '../components/search-table';
|
|||||||
import NavBar from '../components/nav-bar';
|
import NavBar from '../components/nav-bar';
|
||||||
import Seo from '../components/seo';
|
import Seo from '../components/seo';
|
||||||
import ThemeIconButton from '../components/theme-icon-button';
|
import ThemeIconButton from '../components/theme-icon-button';
|
||||||
import { Mirror, MirrorDto } from '../types/mirror';
|
import { Locale, Mirror, MirrorDto } from '../types/mirror';
|
||||||
import frequentlyUsedMirror from '../utils/frequently-used-mirror-list';
|
import frequentlyUsedMirror from '../utils/frequently-used-mirror-list';
|
||||||
import { getUrl } from '../utils/url';
|
import { getUrl } from '../utils/url';
|
||||||
import { readCache, writeCache } from '../utils/cache';
|
import { readCache, writeCache } from '../utils/cache';
|
||||||
@@ -40,7 +40,12 @@ interface Data {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const networkMap = {
|
const networkMap: {
|
||||||
|
[value: number]: {
|
||||||
|
text: string;
|
||||||
|
color: ChipProps['color'];
|
||||||
|
};
|
||||||
|
} = {
|
||||||
0: {
|
0: {
|
||||||
text: '全新版本',
|
text: '全新版本',
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
@@ -124,12 +129,13 @@ const Index = ({ data }: { data: Data }) => {
|
|||||||
.filter(d => d.locale === language)
|
.filter(d => d.locale === language)
|
||||||
.map(
|
.map(
|
||||||
d =>
|
d =>
|
||||||
[d.frontmatter.title, new Date(d.frontmatter.date), d.slug] as (
|
[d.frontmatter.title, new Date(d.frontmatter.date), d.slug] as [
|
||||||
| Date
|
string,
|
||||||
| string
|
Date,
|
||||||
)[]
|
string,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
.sort((a, b) => b[1] - a[1]),
|
.sort((a, b) => b[1].getTime() - a[1].getTime()),
|
||||||
[data, language]
|
[data, language]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -226,17 +232,24 @@ const Index = ({ data }: { data: Data }) => {
|
|||||||
<Typography gutterBottom variant="h5" component="div">
|
<Typography gutterBottom variant="h5" component="div">
|
||||||
<Trans>常用镜像</Trans>
|
<Trans>常用镜像</Trans>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid container spacing={{ xs: 2 }} columns={{ xs: 1, sm: 3, md: 6 }}>
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={{ xs: 2 }}
|
||||||
|
columns={{ xs: 1, sm: 3, md: 6 }}
|
||||||
|
>
|
||||||
{frequentlyUsedMirror.map((e, i) => {
|
{frequentlyUsedMirror.map((e, i) => {
|
||||||
const mirror = mirrors[e.id];
|
const mirror = mirrors[e.id];
|
||||||
return (
|
return (
|
||||||
mirror && (
|
mirror && (
|
||||||
<Grid item xs={1} key={i}>
|
<Grid item xs={1} key={i}>
|
||||||
<FrequentlyUsedMirrorCard
|
<FrequentlyUsedMirrorCard
|
||||||
name={mirror.name[language]}
|
name={mirror.name[language as Locale]}
|
||||||
desc={mirror.desc[language]}
|
desc={mirror.desc[language as Locale]}
|
||||||
icon={e.icon}
|
icon={e.icon}
|
||||||
url={getUrl(mirror.docUrl || mirror.url, !!mirror.docUrl)}
|
url={getUrl(
|
||||||
|
mirror.docUrl || mirror.url,
|
||||||
|
!!mirror.docUrl
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
)
|
)
|
||||||
@@ -249,8 +262,8 @@ const Index = ({ data }: { data: Data }) => {
|
|||||||
<Trans>近期更新</Trans>
|
<Trans>近期更新</Trans>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid>
|
<Grid>
|
||||||
{newsUrls.map(([title, date, url], _) => (
|
{newsUrls.map(([title, date, url], i) => (
|
||||||
<Grid container>
|
<Grid container key={i}>
|
||||||
<Link href={url} underline="hover">
|
<Link href={url} underline="hover">
|
||||||
{title}
|
{title}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import TableCell from '@mui/material/TableCell';
|
|||||||
import TableHead from '@mui/material/TableHead';
|
import TableHead from '@mui/material/TableHead';
|
||||||
import TableRow from '@mui/material/TableRow';
|
import TableRow from '@mui/material/TableRow';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { Link } from 'gatsby-theme-material-ui';
|
|
||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
|
import { MDXComponents } from '@mdx-js/react/lib';
|
||||||
|
import { LinkLink as Link } from '~/components/link-mui-components';
|
||||||
import CodeBlock from '../components/code-block';
|
import CodeBlock from '../components/code-block';
|
||||||
|
|
||||||
const components = {
|
const components: MDXComponents = {
|
||||||
p: (() => {
|
p: (() => {
|
||||||
const Paragraph = props => (
|
const Paragraph = props => (
|
||||||
<Typography {...props} style={{ margin: '8px 0' }} />
|
<Typography {...props} style={{ margin: '8px 0' }} />
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
import { MDXProvider } from '@mdx-js/react';
|
import { MDXProvider } from '@mdx-js/react';
|
||||||
import { ArrowBack, ConnectedTvOutlined } from '@mui/icons-material';
|
|
||||||
import FolderIcon from '@mui/icons-material/Folder';
|
import FolderIcon from '@mui/icons-material/Folder';
|
||||||
import { Box, Grid, Typography } from '@mui/material';
|
import { Box, Grid, Typography } from '@mui/material';
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
import { graphql } from 'gatsby';
|
import { graphql } from 'gatsby';
|
||||||
import { Trans, useI18next } from 'gatsby-plugin-react-i18next';
|
import { Trans, useI18next } from 'gatsby-plugin-react-i18next';
|
||||||
import { Button } from 'gatsby-theme-material-ui';
|
import { LinkButton as Button } from '~/components/link-mui-components';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { PropsWithChildren, useEffect, useState } from 'react';
|
||||||
import Footer from '../components/footer';
|
import Footer from '../components/footer';
|
||||||
import FileList from '../components/file-list';
|
import FileList from '../components/file-list';
|
||||||
import LanguageIconButton from '../components/language-icon-button';
|
|
||||||
import Seo from '../components/seo';
|
import Seo from '../components/seo';
|
||||||
import StatusIndicator from '../components/status-indicator';
|
import StatusIndicator from '../components/status-indicator';
|
||||||
import ThemeIconButton from '../components/theme-icon-button';
|
import ThemeIconButton from '../components/theme-icon-button';
|
||||||
import { MirrorDto } from '../types/mirror';
|
import { Locale, MirrorDto } from '../types/mirror';
|
||||||
import { Link } from '../utils/i18n-link';
|
import { Link } from '../utils/i18n-link';
|
||||||
import components from './components';
|
import components from './components';
|
||||||
import { readCache, writeCache } from '../utils/cache';
|
import { popCache, writeCache } from '../utils/cache';
|
||||||
import { getUrl } from '../utils/url';
|
import { getUrl } from '../utils/url';
|
||||||
import TitleMirrorIcon from '../utils/title-mirror-icon';
|
import TitleMirrorIcon from '../utils/title-mirror-icon';
|
||||||
|
|
||||||
@@ -27,6 +25,10 @@ interface Data {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MirrorDocProps = {
|
||||||
|
data: Data;
|
||||||
|
};
|
||||||
|
|
||||||
async function fetchMirror(id: string): Promise<MirrorDto> {
|
async function fetchMirror(id: string): Promise<MirrorDto> {
|
||||||
const res = await fetch(`/api/mirrors/${id}`);
|
const res = await fetch(`/api/mirrors/${id}`);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@@ -37,7 +39,7 @@ async function fetchMirror(id: string): Promise<MirrorDto> {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MirrorDoc = ({ data, children }: { data: Data }) => {
|
const MirrorDoc = ({ data, children }: PropsWithChildren<MirrorDocProps>) => {
|
||||||
const { language } = useI18next();
|
const { language } = useI18next();
|
||||||
|
|
||||||
const defaultData = {
|
const defaultData = {
|
||||||
@@ -48,11 +50,12 @@ const MirrorDoc = ({ data, children }: { data: Data }) => {
|
|||||||
},
|
},
|
||||||
status: 'unknown',
|
status: 'unknown',
|
||||||
} as MirrorDto;
|
} as MirrorDto;
|
||||||
const mirrorId = data.document.frontmatter.mirrorId;
|
const { mirrorId } = data.document.frontmatter;
|
||||||
|
|
||||||
const [mirror, setMirror] = useState<MirrorDto>(
|
const [mirror, setMirror] = useState<MirrorDto>({
|
||||||
readCache(`mirrors_${mirrorId}`, defaultData)
|
...defaultData,
|
||||||
);
|
...popCache(`mirrors_${mirrorId}`, defaultData),
|
||||||
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMirror(mirrorId)
|
fetchMirror(mirrorId)
|
||||||
.then(d => setMirror(d))
|
.then(d => setMirror(d))
|
||||||
@@ -62,7 +65,7 @@ const MirrorDoc = ({ data, children }: { data: Data }) => {
|
|||||||
const fallbackUrl = `/${mirrorId}`;
|
const fallbackUrl = `/${mirrorId}`;
|
||||||
const mirrorUrl = getUrl(mirror.url ?? fallbackUrl, false);
|
const mirrorUrl = getUrl(mirror.url ?? fallbackUrl, false);
|
||||||
|
|
||||||
const name = mirror.name[language];
|
const name = mirror.name[language as Locale];
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -90,11 +93,9 @@ const MirrorDoc = ({ data, children }: { data: Data }) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
{
|
{/* TODO: add English docs
|
||||||
/* TODO: add English docs
|
|
||||||
<LanguageIconButton />
|
<LanguageIconButton />
|
||||||
*/
|
*/}
|
||||||
}
|
|
||||||
<ThemeIconButton />
|
<ThemeIconButton />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,23 +1,17 @@
|
|||||||
import { MDXProvider } from '@mdx-js/react';
|
import { MDXProvider } from '@mdx-js/react';
|
||||||
import { ArrowBack, DateRange, AccountCircle } from '@mui/icons-material';
|
import { DateRange, AccountCircle } from '@mui/icons-material';
|
||||||
import FolderIcon from '@mui/icons-material/Folder';
|
|
||||||
import { Box, Grid, Typography } from '@mui/material';
|
import { Box, Grid, Typography } from '@mui/material';
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
import { graphql } from 'gatsby';
|
import { graphql } from 'gatsby';
|
||||||
import { Trans, useI18next } from 'gatsby-plugin-react-i18next';
|
import { Trans, useI18next } from 'gatsby-plugin-react-i18next';
|
||||||
import { Button } from 'gatsby-theme-material-ui';
|
import React from 'react';
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import Footer from '../components/footer';
|
import Footer from '../components/footer';
|
||||||
import FileList from '../components/file-list';
|
|
||||||
import LanguageIconButton from '../components/language-icon-button';
|
import LanguageIconButton from '../components/language-icon-button';
|
||||||
import Seo from '../components/seo';
|
import Seo from '../components/seo';
|
||||||
import StatusIndicator from '../components/status-indicator';
|
|
||||||
import ThemeIconButton from '../components/theme-icon-button';
|
import ThemeIconButton from '../components/theme-icon-button';
|
||||||
import { NewsDto } from '../types/news';
|
import { NewsDto } from '../types/news';
|
||||||
import { Link } from '../utils/i18n-link';
|
import { Link } from '../utils/i18n-link';
|
||||||
import components from './components';
|
import components from './components';
|
||||||
import { readCache, writeCache } from '../utils/cache';
|
|
||||||
import { getUrl } from '../utils/url';
|
|
||||||
|
|
||||||
interface Data {
|
interface Data {
|
||||||
document: {
|
document: {
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ export interface MirrorDto {
|
|||||||
name: Record<Locale, string>;
|
name: Record<Locale, string>;
|
||||||
desc: Record<Locale, string>;
|
desc: Record<Locale, string>;
|
||||||
status: MirrorStatus;
|
status: MirrorStatus;
|
||||||
lastUpdated: string;
|
lastUpdated: number;
|
||||||
nextScheduled: string;
|
nextScheduled: number;
|
||||||
lastSuccess: string;
|
lastSuccess: number;
|
||||||
url: string;
|
url: string;
|
||||||
files?: File[];
|
files?: File[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,10 @@ function readCache<T>(key: string, defaultValue: T): T {
|
|||||||
return d ? JSON.parse(d) : defaultValue;
|
return d ? JSON.parse(d) : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { writeCache, readCache };
|
function popCache<T>(key: string, defaultValue: T): T {
|
||||||
|
const d = readCache(key, defaultValue);
|
||||||
|
localStorage.removeItem(`${cachePrefix}${key}`);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { writeCache, readCache, popCache };
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useI18next } from 'gatsby-plugin-react-i18next';
|
import { useI18next } from 'gatsby-plugin-react-i18next';
|
||||||
import { LANGUAGE_KEY } from 'gatsby-plugin-react-i18next/dist/types';
|
import { LANGUAGE_KEY } from 'gatsby-plugin-react-i18next/dist/types';
|
||||||
import { Link as MuiLink } from 'gatsby-theme-material-ui';
|
|
||||||
import { LinkProps } from '@mui/material';
|
import { LinkProps } from '@mui/material';
|
||||||
|
import { LinkLink as MuiLink } from '~/components/link-mui-components';
|
||||||
|
|
||||||
export function linkWithI18n<
|
export function linkWithI18n<
|
||||||
P extends { to: string; onClick?: React.MouseEventHandler }
|
P extends { to: string; onClick?: React.MouseEventHandler }
|
||||||
|
|||||||
19
src/utils/root-layout.tsx
Normal file
19
src/utils/root-layout.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { ThemeProvider } from "../components/theme-context";
|
||||||
|
import { CssBaseline } from "@mui/material";
|
||||||
|
import { PrefsProvider } from "../components/preferences-context";
|
||||||
|
|
||||||
|
export default function RootLayout({ children } : {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<React.StrictMode>
|
||||||
|
<PrefsProvider>
|
||||||
|
<ThemeProvider>
|
||||||
|
<CssBaseline />
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
</PrefsProvider>
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -29,7 +29,10 @@
|
|||||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
"~/*": ["./src/*"]
|
||||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
},
|
||||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
|||||||
Reference in New Issue
Block a user