summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHaishan <[email protected]>2020-08-01 20:04:49 +0800
committerHaishan <[email protected]>2020-08-01 20:41:55 +0800
commit4ae2c5c2f319e15ecba245f8b679dbfcd0242a84 (patch)
treed269f9511ba922800c0308b04cf7d0386588270a /src
parent437733f4afe1eae86f946a8aa3336f58d9980d8c (diff)
feat: a simple about page
Diffstat (limited to 'src')
-rw-r--r--src/api/version.ts26
-rw-r--r--src/components/Root.js2
-rw-r--r--src/components/SideBar.js17
-rw-r--r--src/components/SideBar.module.css36
-rw-r--r--src/components/about/About.module.css18
-rw-r--r--src/components/about/About.tsx78
-rw-r--r--src/custom.d.ts4
7 files changed, 163 insertions, 18 deletions
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<VersionData> {
+ 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', <Logs />],
['proxies', '/proxies', <Proxies />],
['rules', '/rules', <Rules />],
+ ['about', '/about', <About />],
__DEV__ ? ['style', '/style', <StyleGuide />] : 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 }) {
/>
))}
</div>
- <button className={s.themeSwitchContainer} onClick={switchThemeHooked}>
- {theme === 'light' ? <MoonA /> : <Sun />}
- </button>
+ <div className={s.footer}>
+ <button
+ className={cx(s.iconWrapper, s.themeSwitchContainer)}
+ onClick={switchThemeHooked}
+ >
+ {theme === 'light' ? <MoonA /> : <Sun />}
+ </button>
+ <Link to="/about" className={s.iconWrapper}>
+ <Info size={20} />
+ </Link>
+ </div>
</div>
);
}
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;
+ 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);
+}
- padding: 5px;
+.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 (
+ <div className={s.root}>
+ <h2>{name}</h2>
+ <p>
+ <span>Version </span>
+ <span className={s.mono}>{version}</span>
+ </p>
+ <p>
+ <a
+ className={s.link}
+ href={link}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ <GitHub size={20} />
+ <span>Source</span>
+ </a>
+ </p>
+ </div>
+ );
+}
+
+function AboutImpl(props: Props) {
+ const { data: version } = useQuery(
+ ['/version', props.apiConfig],
+ fetchVersion,
+ {
+ suspense: true,
+ }
+ );
+ console.log(version);
+
+ return (
+ <>
+ <ContentHeader title="About" />
+ {version ? (
+ <Version
+ name="Clash"
+ version={version.version}
+ link="https://github.com/Dreamacro/clash"
+ />
+ ) : null}
+ <Version
+ name="Yacd"
+ version={__VERSION__}
+ link="https://github.com/haishanh/yacd"
+ />
+ </>
+ );
+}
+
+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;