From 4ae2c5c2f319e15ecba245f8b679dbfcd0242a84 Mon Sep 17 00:00:00 2001 From: Haishan Date: Sat, 1 Aug 2020 20:04:49 +0800 Subject: [PATCH] feat: a simple about page --- src/api/version.ts | 26 +++++++++ src/components/Root.js | 2 + src/components/SideBar.js | 17 ++++-- src/components/SideBar.module.css | 38 ++++++++----- src/components/about/About.module.css | 18 +++++++ src/components/about/About.tsx | 78 +++++++++++++++++++++++++++ src/custom.d.ts | 4 ++ 7 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 src/api/version.ts create mode 100644 src/components/about/About.module.css create mode 100644 src/components/about/About.tsx diff --git a/src/api/version.ts b/src/api/version.ts new file mode 100644 index 0000000..eb8a86c --- /dev/null +++ b/src/api/version.ts @@ -0,0 +1,26 @@ +import { getURLAndInit } from 'src/misc/request-helper'; +import { ClashAPIConfig } from 'src/types'; + +type VersionData = { + version: string; + premium?: boolean; +}; + +export async function fetchVersion( + endpoint: string, + apiConfig: ClashAPIConfig +): Promise { + let json = { rules: [] }; + try { + const { url, init } = getURLAndInit(apiConfig); + const res = await fetch(url + endpoint, init); + if (res.ok) { + json = await res.json(); + } + } catch (err) { + // log and ignore + // eslint-disable-next-line no-console + console.log(`failed to fetch ${endpoint}`, err); + } + return json; +} diff --git a/src/components/Root.js b/src/components/Root.js index 7b0ae04..f0a67ec 100644 --- a/src/components/Root.js +++ b/src/components/Root.js @@ -3,6 +3,7 @@ import './Root.css'; import React, { Suspense } from 'react'; import { HashRouter as Router, Route, Routes } from 'react-router-dom'; import { RecoilRoot } from 'recoil'; +import { About } from 'src/components/about/About'; import { actions, initialState } from '../store'; import APIDiscovery from './APIDiscovery'; @@ -41,6 +42,7 @@ const routes = [ ['logs', '/logs', ], ['proxies', '/proxies', ], ['rules', '/rules', ], + ['about', '/about', ], __DEV__ ? ['style', '/style', ] : false, ].filter(Boolean); diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 05f6eba..29724ae 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -2,7 +2,7 @@ import cx from 'clsx'; import { motion } from 'framer-motion'; import PropTypes from 'prop-types'; import React from 'react'; -// import { Command, Activity, Globe, Link2, Settings, File } from 'react-feather'; +import { Info } from 'react-feather'; import { FcAreaChart, FcDocument, @@ -16,7 +16,6 @@ import { Link, useLocation } from 'react-router-dom'; import { getTheme, switchTheme } from '../store/app'; import s from './SideBar.module.css'; import { connect } from './StateProvider'; -import SvgYacd from './SvgYacd'; const { useCallback } = React; @@ -115,9 +114,17 @@ function SideBar({ dispatch, theme }) { /> ))} - +
+ + + + +
); } diff --git a/src/components/SideBar.module.css b/src/components/SideBar.module.css index af91f75..275a007 100644 --- a/src/components/SideBar.module.css +++ b/src/components/SideBar.module.css @@ -68,34 +68,44 @@ } } -.themeSwitchContainer { - --sz: 40px; - - @media (max-width: 768px) { - display: none; - } - +.footer { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); +} + +@media (max-width: 768px) { + .footer { + display: none; + } +} + +.iconWrapper { + --sz: 40px; + width: var(--sz); height: var(--sz); display: flex; justify-content: center; align-items: center; - color: var(--color-text); - padding: 5px; + color: var(--color-text); + border-radius: 100%; + border: 1px solid transparent; +} +.iconWrapper:hover { + opacity: 0.6; +} +.iconWrapper:focus { + border-color: var(--color-focus-blue); +} + +.themeSwitchContainer { appearance: none; outline: none; user-select: none; background: none; cursor: pointer; - border: 1px solid transparent; - border-radius: 100%; - &:focus { - border-color: var(--color-focus-blue); - } } diff --git a/src/components/about/About.module.css b/src/components/about/About.module.css new file mode 100644 index 0000000..632b3ee --- /dev/null +++ b/src/components/about/About.module.css @@ -0,0 +1,18 @@ +.root { + padding: 6px 15px; + @media (--breakpoint-not-small) { + padding: 10px 40px; + } +} + +.mono { + font-family: var(--font-mono); +} + +.link { + color: var(--color-text-secondary); + display: inline-flex; +} +.link:hover { + color: var(--color-text-highlight); +} diff --git a/src/components/about/About.tsx b/src/components/about/About.tsx new file mode 100644 index 0000000..7648934 --- /dev/null +++ b/src/components/about/About.tsx @@ -0,0 +1,78 @@ +import * as React from 'react'; +import { GitHub } from 'react-feather'; +import { useQuery } from 'react-query'; +import { fetchVersion } from 'src/api/version'; +import ContentHeader from 'src/components/ContentHeader'; +import { connect } from 'src/components/StateProvider'; +import { getClashAPIConfig } from 'src/store/app'; +import { ClashAPIConfig } from 'src/types'; + +import s from './About.module.css'; + +type Props = { apiConfig: ClashAPIConfig }; + +function Version({ + name, + link, + version, +}: { + name: string; + link: string; + version: string; +}) { + return ( +
+

{name}

+

+ Version + {version} +

+

+ + + Source + +

+
+ ); +} + +function AboutImpl(props: Props) { + const { data: version } = useQuery( + ['/version', props.apiConfig], + fetchVersion, + { + suspense: true, + } + ); + console.log(version); + + return ( + <> + + {version ? ( + + ) : null} + + + ); +} + +const mapState = (s) => ({ + apiConfig: getClashAPIConfig(s), +}); + +export const About = connect(mapState)(AboutImpl); diff --git a/src/custom.d.ts b/src/custom.d.ts index 9041f77..e60e225 100644 --- a/src/custom.d.ts +++ b/src/custom.d.ts @@ -3,3 +3,7 @@ declare module '*.module.css' { const classes: { [key: string]: string }; export default classes; } + +// webpack definePlugin replacing variables +declare const __VERSION__: string; +declare const __DEV__: string;