合并浙大上游仓库

This commit is contained in:
zyh001
2024-05-15 18:46:34 +08:00
parent 2e623948dd
commit 95706fa4f2
30 changed files with 508 additions and 184 deletions

View File

@@ -13,13 +13,15 @@ module.exports = {
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: './',
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['react', '@typescript-eslint', 'prettier'],
plugins: ['react', '@typescript-eslint', 'import', 'prettier'],
rules: {
// needed by prettier
'prettier/prettier': 'warn',

View File

@@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:

View File

@@ -1,4 +1,6 @@
image: node:lts-bullseye
image:
name: node:20
pull_policy: if-not-present
stages:
- build
@@ -8,12 +10,16 @@ gatsby-build:
stage: build
variables:
CHOKIDAR_USEPOLLING: 1
GATSBY_TELEMETRY_DISABLED: 1
cache:
paths:
- node_modules/
- .npm/
script:
- bash cached-restore.sh
- yarn run build
- apt-get update && apt-get install -y libvips42 libvips-dev
- 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:
paths:
- public

View File

@@ -1 +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>

View File

@@ -16,20 +16,6 @@ module.exports = {
assetPrefix: config.assetPrefix,
pathPrefix: config.pathPrefix,
plugins: [
{
resolve: `gatsby-theme-material-ui`,
options: {
webFontsConfig: {
fonts: {
google: [
{
family: `Metrophobic`
},
],
},
},
}
},
{
resolve: 'gatsby-source-filesystem',
options: {
@@ -57,7 +43,7 @@ module.exports = {
mdxOptions: {
remarkPlugins: [
// 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 dont 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
// remark-gfm. Please take care in future updates.
@@ -72,11 +58,11 @@ module.exports = {
rule: {
include: /icons/,
omitKeys: [
'inkscapePageshadow', 'inkscapePageopacity', 'inkscapePagecheckerboard',
'inkscapeZoom', 'inkscapeCx', 'inkscapeCy', 'inkscapeWindowWidth', 'inkscapeWindowHeight',
'inkscapeWindowX', 'inkscapeWindowY', 'inkscapeWindowMaximized', 'inkscapeCurrentLayer',
'inkscapePageshadow', 'inkscapePageopacity', 'inkscapePagecheckerboard',
'inkscapeZoom', 'inkscapeCx', 'inkscapeCy', 'inkscapeWindowWidth', 'inkscapeWindowHeight',
'inkscapeWindowX', 'inkscapeWindowY', 'inkscapeWindowMaximized', 'inkscapeCurrentLayer',
'sodipodiNodetypes', 'sodipodiDocname', 'inkscapeVersion', 'xmlnsInkscape', 'xmlnsSodipodi',
'xmlnsSvg',
'xmlnsSvg',
]
}
}
@@ -113,7 +99,6 @@ module.exports = {
icon: 'resource/icons/favicon.svg',
},
},
`gatsby-plugin-preact`,
`gatsby-plugin-sass`,
{
resolve: "gatsby-plugin-sitemap",

View File

@@ -2,6 +2,7 @@ const { match, compile } = require("path-to-regexp");
const { createFilePath } = require(`gatsby-source-filesystem`)
const { createContentDigest } = require(`gatsby-core-utils`)
const config = require('./config');
const path = require("path");
const mdxResolverPassthrough = (fieldName) => async (
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
View 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>

View File

@@ -17,67 +17,66 @@
"lint-check": "eslint src/**/*"
},
"dependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@emotion/react": "^11.11.4",
"@emotion/server": "^11.11.0",
"@emotion/styled": "^11.11.5",
"@fontsource/poppins": "^4.5.10",
"@formatjs/intl-displaynames": "^5.4.3",
"@iconify/icons-file-icons": "^1.2.3",
"@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/react": "^3.2.1",
"@iconify/react": "^3.2.2",
"@mdx-js/mdx": "^3.0.1",
"@mdx-js/react": "^2.3.0",
"@mui/icons-material": "^5.6.2",
"@mui/material": "^5.6.3",
"gatsby": "^5.9.1",
"gatsby-plugin-manifest": "^5.6.0",
"gatsby-plugin-mdx": "^5.6.0",
"gatsby-plugin-preact": "^7.6.1",
"@mui/icons-material": "^5.15.15",
"@mui/material": "^5.15.15",
"gatsby": "^5.13.3",
"gatsby-plugin-manifest": "^5.13.1",
"gatsby-plugin-mdx": "^5.13.1",
"gatsby-plugin-react-helmet": "^6.13.1",
"gatsby-plugin-react-i18next": "^3.0.1",
"gatsby-plugin-react-svg": "^3.1.0",
"gatsby-plugin-sass": "^6.6.0",
"gatsby-plugin-sitemap": "^6.7.0",
"gatsby-source-filesystem": "^5.6.0",
"gatsby-theme-material-ui": "^5.2.0",
"gatsby-theme-material-ui-top-layout": "^5.2.0",
"i18next": "^21.6.16",
"js-search": "^2.0.0",
"gatsby-plugin-react-svg": "^3.3.0",
"gatsby-plugin-sass": "^6.13.1",
"gatsby-plugin-sitemap": "^6.13.1",
"gatsby-source-filesystem": "^5.13.1",
"i18next": "^22.5.1",
"js-search": "^2.0.1",
"natsort": "^2.0.3",
"path-to-regexp": "^6.2.0",
"preact": "^10.12.1",
"preact-render-to-string": "^5.2.6",
"prism-react-renderer": "^1.3.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"path-to-regexp": "^6.2.2",
"prism-react-renderer": "^1.3.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-i18next": "^11.16.7",
"remark-gfm": "^1",
"sass": "^1.53.0"
"react-i18next": "^12.3.1",
"remark-gfm": "^1.0.0",
"sass": "^1.74.1"
},
"devDependencies": {
"@babel/cli": "^7.17.10",
"@babel/plugin-transform-typescript": "^7.16.8",
"@types/js-search": "^1.4.0",
"@types/mdx": "^2.0.1",
"@types/mdx-js__react": "^1.5.5",
"@types/node": "^18.0.0",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
"@typescript-eslint/eslint-plugin": "^5.28.0",
"@typescript-eslint/parser": "^5.28.0",
"@babel/cli": "^7.24.1",
"@babel/plugin-transform-typescript": "^7.24.4",
"@types/js-search": "^1.4.4",
"@types/mdx": "^2.0.12",
"@types/mdx-js__react": "^1.5.8",
"@types/node": "^18.19.31",
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"babel-plugin-i18next-extract": "^0.8.3",
"eslint": "^8.2.0",
"eslint": "^8.57.0",
"eslint-config-airbnb": "19.0.4",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.2.1",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.30.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-config-prettier": "^8.10.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"http-proxy-middleware": "^2.0.6",
"prettier": "2.7.1",
"typescript": "^4.7.3"
"prettier": "^3.2.5",
"prettier-eslint": "^16.3.0",
"typescript": "^5.4.5"
},
"repository": {
"type": "git",

View File

@@ -38,13 +38,18 @@ export default ({ children, codeStyle, language }: CodeBlockProps) => {
...codeStyle,
}}
>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
))}
{tokens.map((line, i) => {
if (line.length === 1 && line[0].content.indexOf('\u200b') > -1) {
return <br />;
}
return (
<div key={i} {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
);
})}
</code>
)}
</Highlight>

View File

@@ -1,7 +1,7 @@
import { PaletteMode, ThemeOptions } from '@mui/material';
import { PaletteMode, ThemeOptions, createTheme } from '@mui/material';
import '@fontsource/poppins';
export default function configTheme(mode: PaletteMode): ThemeOptions {
function configTheme(mode: PaletteMode): ThemeOptions {
return {
palette:
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 */
declare module '@mui/material/styles' {

View File

@@ -1,8 +1,10 @@
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 { Trans } from 'gatsby-plugin-react-i18next';
import { CardActionArea } from 'gatsby-theme-material-ui';
import { LinkCardActionArea as CardActionArea } from './link-mui-components';
export interface FileProps {
name: string;

View File

@@ -1,6 +1,10 @@
import { Box, Card, CardContent, Grid, Typography } from '@mui/material';
import { CardActionArea } from 'gatsby-theme-material-ui';
import Box from '@mui/material/Box';
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 { LinkCardActionArea as CardActionArea } from './link-mui-components';
export type mirrorBrief = {
name: string;

View 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 };

View File

@@ -1,9 +1,9 @@
import InfoIcon from '@mui/icons-material/Info';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import { Tooltip } from '@mui/material';
import { IconButton } from 'gatsby-theme-material-ui';
import Tooltip from '@mui/material/Tooltip';
import { useTranslation } from 'react-i18next';
import * as React from 'react';
import { LinkIconButton as IconButton } from './link-mui-components';
import { usePrefs } from './preferences-context';
const nameMode = {

View File

@@ -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 { CardActionArea } from 'gatsby-theme-material-ui';
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 StatusIndicator from './status-indicator';
import { getUrl } from '../utils/url';
import { usePrefs } from './preferences-context';
import VerifiedIcon from '@mui/icons-material/Verified';
import officialCertificatedMirrorList from '../utils/official-certificated-mirror-list';
const SearchItemCard = (props: { queryItem: Mirror }) => {

View File

@@ -1,10 +1,9 @@
import React from 'react';
import { JSX } from 'preact';
import { useStaticQuery, graphql } from 'gatsby';
import { useTheme } from '@mui/material';
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 {languages, language, originalPath, defaultLanguage, siteUrl = ''} = useI18next();
@@ -16,11 +15,7 @@ const Helmet: React.FC = ({children, title, meta}) => {
<>
<html lang={language} />
<title>{title}</title>
{
meta?.map((m: MetaProps) => (
<meta {...m} />
))
}
{meta?.map((m: MetaProps, i: number) => <meta {...m} key={i}/>)}
<link rel="canonical" href={createUrlWithLang(language)} />
{languages.map((lng) => (
<link rel="alternate" key={lng} href={createUrlWithLang(lng)} hrefLang={lng} />

View File

@@ -5,14 +5,14 @@ import {
} from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import * as React from 'react';
import configTheme from './config-theme';
import { lightTheme, darkTheme } from './config-theme';
import { readCache, writeCache } from '../utils/cache';
export type ThemeMode = PaletteMode | 'auto';
export declare type ThemeContextInterface = [
ThemeMode,
(mode: ThemeMode) => void
(mode: ThemeMode) => void,
];
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);
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') {
// wait for media query result stable
// 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;
const preferredMode = prefersDark ? 'dark' : 'light';
paletteMode = preferredMode;
@@ -39,18 +40,7 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
paletteMode = mode;
}
const theme = React.useMemo(
() =>
createTheme(
{
palette: {
mode: paletteMode,
},
},
configTheme(paletteMode)
),
[paletteMode]
);
const theme = paletteMode === 'light' ? lightTheme : darkTheme;
const updateMode = (newMode: ThemeMode) => {
setMode(newMode);

View File

@@ -2,9 +2,9 @@ import Brightness4Icon from '@mui/icons-material/Brightness4';
import BrightnessAutoIcon from '@mui/icons-material/BrightnessAuto';
import BrightnessHighIcon from '@mui/icons-material/BrightnessHigh';
import ToolTip from '@mui/material/Tooltip';
import { IconButton } from 'gatsby-theme-material-ui';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { LinkIconButton as IconButton } from './link-mui-components';
import { ThemeMode, useMode } from './theme-context';
export default () => {

View 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>
);
};

View File

@@ -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
View File

@@ -0,0 +1,7 @@
import React from 'react';
const NotFoundPage = () => {
return <h1>Page Not Found</h1>;
};
export default NotFoundPage;

View File

@@ -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 { Trans, useI18next } from 'gatsby-plugin-react-i18next';
import React, { useEffect, useState } from 'react';
@@ -9,7 +9,7 @@ import SearchTable from '../components/search-table';
import NavBar from '../components/nav-bar';
import Seo from '../components/seo';
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 { getUrl } from '../utils/url';
import { readCache, writeCache } from '../utils/cache';
@@ -40,7 +40,12 @@ interface Data {
};
}
const networkMap = {
const networkMap: {
[value: number]: {
text: string;
color: ChipProps['color'];
};
} = {
0: {
text: '全新版本',
color: 'primary',
@@ -124,12 +129,13 @@ const Index = ({ data }: { data: Data }) => {
.filter(d => d.locale === language)
.map(
d =>
[d.frontmatter.title, new Date(d.frontmatter.date), d.slug] as (
| Date
| string
)[]
[d.frontmatter.title, new Date(d.frontmatter.date), d.slug] as [
string,
Date,
string,
]
)
.sort((a, b) => b[1] - a[1]),
.sort((a, b) => b[1].getTime() - a[1].getTime()),
[data, language]
);
@@ -226,17 +232,24 @@ const Index = ({ data }: { data: Data }) => {
<Typography gutterBottom variant="h5" component="div">
<Trans></Trans>
</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) => {
const mirror = mirrors[e.id];
return (
mirror && (
<Grid item xs={1} key={i}>
<FrequentlyUsedMirrorCard
name={mirror.name[language]}
desc={mirror.desc[language]}
name={mirror.name[language as Locale]}
desc={mirror.desc[language as Locale]}
icon={e.icon}
url={getUrl(mirror.docUrl || mirror.url, !!mirror.docUrl)}
url={getUrl(
mirror.docUrl || mirror.url,
!!mirror.docUrl
)}
/>
</Grid>
)
@@ -249,8 +262,8 @@ const Index = ({ data }: { data: Data }) => {
<Trans></Trans>
</Typography>
<Grid>
{newsUrls.map(([title, date, url], _) => (
<Grid container>
{newsUrls.map(([title, date, url], i) => (
<Grid container key={i}>
<Link href={url} underline="hover">
{title}
</Link>

View File

@@ -8,11 +8,12 @@ import TableCell from '@mui/material/TableCell';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Typography from '@mui/material/Typography';
import { Link } from 'gatsby-theme-material-ui';
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';
const components = {
const components: MDXComponents = {
p: (() => {
const Paragraph = props => (
<Typography {...props} style={{ margin: '8px 0' }} />

View File

@@ -1,22 +1,20 @@
import { MDXProvider } from '@mdx-js/react';
import { ArrowBack, ConnectedTvOutlined } from '@mui/icons-material';
import FolderIcon from '@mui/icons-material/Folder';
import { Box, Grid, Typography } from '@mui/material';
import Paper from '@mui/material/Paper';
import { graphql } from 'gatsby';
import { Trans, useI18next } from 'gatsby-plugin-react-i18next';
import { Button } from 'gatsby-theme-material-ui';
import React, { useEffect, useState } from 'react';
import { LinkButton as Button } from '~/components/link-mui-components';
import React, { PropsWithChildren, useEffect, useState } from 'react';
import Footer from '../components/footer';
import FileList from '../components/file-list';
import LanguageIconButton from '../components/language-icon-button';
import Seo from '../components/seo';
import StatusIndicator from '../components/status-indicator';
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 components from './components';
import { readCache, writeCache } from '../utils/cache';
import { popCache, writeCache } from '../utils/cache';
import { getUrl } from '../utils/url';
import TitleMirrorIcon from '../utils/title-mirror-icon';
@@ -27,6 +25,10 @@ interface Data {
};
}
type MirrorDocProps = {
data: Data;
};
async function fetchMirror(id: string): Promise<MirrorDto> {
const res = await fetch(`/api/mirrors/${id}`);
if (!res.ok) {
@@ -37,7 +39,7 @@ async function fetchMirror(id: string): Promise<MirrorDto> {
return json;
}
const MirrorDoc = ({ data, children }: { data: Data }) => {
const MirrorDoc = ({ data, children }: PropsWithChildren<MirrorDocProps>) => {
const { language } = useI18next();
const defaultData = {
@@ -48,11 +50,12 @@ const MirrorDoc = ({ data, children }: { data: Data }) => {
},
status: 'unknown',
} as MirrorDto;
const mirrorId = data.document.frontmatter.mirrorId;
const { mirrorId } = data.document.frontmatter;
const [mirror, setMirror] = useState<MirrorDto>(
readCache(`mirrors_${mirrorId}`, defaultData)
);
const [mirror, setMirror] = useState<MirrorDto>({
...defaultData,
...popCache(`mirrors_${mirrorId}`, defaultData),
});
useEffect(() => {
fetchMirror(mirrorId)
.then(d => setMirror(d))
@@ -62,7 +65,7 @@ const MirrorDoc = ({ data, children }: { data: Data }) => {
const fallbackUrl = `/${mirrorId}`;
const mirrorUrl = getUrl(mirror.url ?? fallbackUrl, false);
const name = mirror.name[language];
const name = mirror.name[language as Locale];
return (
<Box
sx={{
@@ -90,11 +93,9 @@ const MirrorDoc = ({ data, children }: { data: Data }) => {
</Typography>
</Link>
<Grid item>
{
/* TODO: add English docs
{/* TODO: add English docs
<LanguageIconButton />
*/
}
*/}
<ThemeIconButton />
</Grid>
</Grid>

View File

@@ -1,23 +1,17 @@
import { MDXProvider } from '@mdx-js/react';
import { ArrowBack, DateRange, AccountCircle } from '@mui/icons-material';
import FolderIcon from '@mui/icons-material/Folder';
import { DateRange, AccountCircle } from '@mui/icons-material';
import { Box, Grid, Typography } from '@mui/material';
import Paper from '@mui/material/Paper';
import { graphql } from 'gatsby';
import { Trans, useI18next } from 'gatsby-plugin-react-i18next';
import { Button } from 'gatsby-theme-material-ui';
import React, { useEffect, useState } from 'react';
import React from 'react';
import Footer from '../components/footer';
import FileList from '../components/file-list';
import LanguageIconButton from '../components/language-icon-button';
import Seo from '../components/seo';
import StatusIndicator from '../components/status-indicator';
import ThemeIconButton from '../components/theme-icon-button';
import { NewsDto } from '../types/news';
import { Link } from '../utils/i18n-link';
import components from './components';
import { readCache, writeCache } from '../utils/cache';
import { getUrl } from '../utils/url';
interface Data {
document: {

View File

@@ -20,9 +20,9 @@ export interface MirrorDto {
name: Record<Locale, string>;
desc: Record<Locale, string>;
status: MirrorStatus;
lastUpdated: string;
nextScheduled: string;
lastSuccess: string;
lastUpdated: number;
nextScheduled: number;
lastSuccess: number;
url: string;
files?: File[];
}

View File

@@ -11,4 +11,10 @@ function readCache<T>(key: string, defaultValue: T): T {
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 };

View File

@@ -1,8 +1,8 @@
import * as React from 'react';
import { useI18next } from 'gatsby-plugin-react-i18next';
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 { LinkLink as MuiLink } from '~/components/link-mui-components';
export function linkWithI18n<
P extends { to: string; onClick?: React.MouseEventHandler }

19
src/utils/root-layout.tsx Normal file
View 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>
);
}

View File

@@ -29,7 +29,10 @@
// "rootDir": "./", /* Specify the root folder within your source files. */
"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. */
"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. */
},
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "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. */