Reimplement UI with 0x1eef/tail.css
This commit is contained in:
parent
63b34bd229
commit
664aa74673
18 changed files with 75 additions and 99 deletions
8
.prettierrc
Normal file
8
.prettierrc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"trailingComma": "all",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"printWidth": 100,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "myip.wtf",
|
"name": "myip.wtf",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "myip.wtf",
|
"name": "myip.wtf",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.8.8",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
"ts-loader": "^9.3.1",
|
"ts-loader": "^9.3.1",
|
||||||
"ts-standard": "^12.0.1",
|
"ts-standard": "^12.0.1",
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.8.8",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
"ts-loader": "^9.3.1",
|
"ts-loader": "^9.3.1",
|
||||||
"ts-standard": "^12.0.1",
|
"ts-standard": "^12.0.1",
|
||||||
|
@ -28,6 +28,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm exec webpack",
|
"build": "npm exec webpack",
|
||||||
"test": "npm exec jest -- test"
|
"test": "npm exec jest -- test",
|
||||||
|
"format": "npm exec prettier -- -w src/js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,8 @@
|
||||||
"isp": {
|
"isp": {
|
||||||
"message": "ISP"
|
"message": "ISP"
|
||||||
},
|
},
|
||||||
"city": {
|
"location": {
|
||||||
"message": "City"
|
"message": "Location"
|
||||||
},
|
|
||||||
"country": {
|
|
||||||
"message": "Country"
|
|
||||||
},
|
},
|
||||||
"tor_exit_node": {
|
"tor_exit_node": {
|
||||||
"message": "Tor exit node"
|
"message": "Tor exit node"
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
.error {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
place-content: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error span:first-child {
|
|
||||||
color: var(--primary-red);
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error span {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
body .response {
|
|
||||||
padding: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body .response .row {
|
|
||||||
display: flex;
|
|
||||||
place-content: space-between;
|
|
||||||
font-size: var(--default-font-size);
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
:root {
|
:root {
|
||||||
/* Defaults */
|
/* Defaults */
|
||||||
--default-font-size: 1.15em;
|
--default-font-size: 1.15em;
|
||||||
--default-font-family: "SF Mono","Segoe UI Mono","Roboto Mono",Menlo,Courier,monospace;
|
|
||||||
|
|
||||||
/* Colors */
|
/* Colors */
|
||||||
--primary-red: #d73e48;
|
--primary-red: #d73e48;
|
||||||
|
@ -13,10 +12,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: var(--default-font-family);
|
font-size: small;
|
||||||
width: 368px;
|
width: 400px;
|
||||||
height: 212px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#root, .loader {
|
#root, .loader {
|
||||||
|
@ -37,11 +34,7 @@ a:active, a:focus, a:hover {
|
||||||
footer {
|
footer {
|
||||||
background: var(--secondary-white);
|
background: var(--secondary-white);
|
||||||
color: var(--primary-red);
|
color: var(--primary-red);
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader {
|
.loader {
|
||||||
|
|
1
src/css/tail.min.css
vendored
Normal file
1
src/css/tail.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
0
src/fonts/.gitkeep
Normal file
0
src/fonts/.gitkeep
Normal file
|
@ -1,16 +1,12 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<title></title>
|
<title></title>
|
||||||
|
<link rel="stylesheet" href="/css/tail.min.css"/>
|
||||||
<link rel="stylesheet" href="/css/index.css"/>
|
<link rel="stylesheet" href="/css/index.css"/>
|
||||||
<link rel="stylesheet" href="/css/label.css"/>
|
<link rel="stylesheet" href="/css/components/BooleanLabel.css"/>
|
||||||
<link rel="stylesheet" href="/css/components/ResponseRenderer.css"/>
|
|
||||||
<link rel="stylesheet" href="/css/components/ErrorRenderer.css"/>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root" class="h-full font-sans"></div>
|
||||||
<footer>
|
|
||||||
Powered by <a href="https://clean.myip.wtf">clean.myip.wtf</a>
|
|
||||||
</footer>
|
|
||||||
<script src="/js/index.js"></script>
|
<script src="/js/index.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -7,10 +7,15 @@ export function App() {
|
||||||
const [response, error] = useWebService();
|
const [response, error] = useWebService();
|
||||||
const t = chrome.i18n.getMessage;
|
const t = chrome.i18n.getMessage;
|
||||||
if (response) {
|
if (response) {
|
||||||
return (<ResponseRenderer response={response}/>);
|
return <ResponseRenderer response={response} />;
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
return (<ErrorRenderer error={error}/>);
|
return <ErrorRenderer error={error} />;
|
||||||
} else {
|
} else {
|
||||||
return (<div className="loader">{t("loading")}</div>);
|
return (
|
||||||
|
<div className="w-full h-48" data-testid="loader">
|
||||||
|
<div className="flex flex-col h-5/6 justify-center w-3/4 m-auto"></div>
|
||||||
|
<footer className="flex items-center justify-center h-10">{t("loading")}</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export function ErrorRenderer({ error }: {error: Error}) {
|
export function ErrorRenderer({ error }: { error: Error }) {
|
||||||
const t = chrome.i18n.getMessage;
|
const t = chrome.i18n.getMessage;
|
||||||
return (
|
return (
|
||||||
<div data-testid="error" className="error">
|
<div data-testid="error" className="w-full h-48">
|
||||||
<span>{t("error")}</span>:
|
<div className="flex flex-col h-5/6 justify-center w-3/4 m-auto">
|
||||||
<span> {error.message}</span>
|
</div>
|
||||||
|
<footer className="flex items-center justify-center h-10">
|
||||||
|
<span className="font-bold">{t("error")}</span>: {error.message}
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,39 @@
|
||||||
import React from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { TResponse } from "~/lib/response";
|
import { TResponse } from "~/lib/response";
|
||||||
|
type TFunction = typeof chrome.i18n.getMessage;
|
||||||
|
|
||||||
export function ResponseRenderer({ response }: {response: TResponse}) {
|
export function ResponseRenderer({ response }: { response: TResponse }) {
|
||||||
const t = chrome.i18n.getMessage;
|
const t = chrome.i18n.getMessage;
|
||||||
|
|
||||||
function YesOrNoLabel({ yes }: {yes: boolean}) {
|
|
||||||
if (yes) {
|
|
||||||
return (<label className="label label-rounded label-success">{t("yes")}</label>);
|
|
||||||
} else {
|
|
||||||
return (<label className="label label-rounded label-error">{t("no")}</label>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="response" className="response">
|
<div className="w-full h-48">
|
||||||
<div className="row">
|
<div className="flex flex-col h-5/6 justify-center w-3/4 m-auto" data-testid="response">
|
||||||
<div>{t("ip_address")}</div>
|
<Row name={t("ip_address")} value={response.IPAddress} />
|
||||||
<div>{response.IPAddress}</div>
|
<Row name={t("location")} value={`${response.City}, ${response.Country}`} />
|
||||||
</div>
|
<Row name={t("isp")} value={response.ISP} />
|
||||||
<div className="row">
|
<Row name={t("tor_exit_node")} value={<BooleanLabel on={response.isTorExitNode} t={t} />} />
|
||||||
<div>{t("isp")}</div>
|
|
||||||
<div>{response.ISP}</div>
|
|
||||||
</div>
|
|
||||||
<div className="row">
|
|
||||||
<div>{t("city")}</div>
|
|
||||||
<div>{response.City}</div>
|
|
||||||
</div>
|
|
||||||
<div className="row">
|
|
||||||
<div>{t("country")}</div>
|
|
||||||
<div>{response.Country}</div>
|
|
||||||
</div>
|
|
||||||
<div className="row">
|
|
||||||
<div>{t("tor_exit_node")}</div>
|
|
||||||
<div><YesOrNoLabel yes={response.isTorExitNode}/></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<footer className="flex items-center justify-center h-10">
|
||||||
|
<a target="_blank" href="https://clean.myip.wtf">
|
||||||
|
clean.myip.wtf
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BooleanLabel({ on, t }: { on: boolean; t: TFunction }) {
|
||||||
|
if (on) {
|
||||||
|
return <label className="label label-rounded label-success">{t("yes")}</label>;
|
||||||
|
} else {
|
||||||
|
return <label className="label label-rounded label-error">{t("no")}</label>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Row({ name, value }: { name: string; value: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-8">
|
||||||
|
<div className="flex w-2/4 items-center font-semibold">{name}</div>
|
||||||
|
<div className="flex w-2/4 items-center justify-end">{value}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ export function useWebService(): [Maybe<TResponse>, Maybe<Error>] {
|
||||||
const endpoint = "https://clean.myip.wtf/json";
|
const endpoint = "https://clean.myip.wtf/json";
|
||||||
const [response, setResponse] = useState<Maybe<TResponse>>(null);
|
const [response, setResponse] = useState<Maybe<TResponse>>(null);
|
||||||
const [error, setError] = useState<Maybe<Error>>(null);
|
const [error, setError] = useState<Maybe<Error>>(null);
|
||||||
const options: RequestInit = {cache: "no-store"};
|
const options: RequestInit = { cache: "no-store" };
|
||||||
|
|
||||||
function receive(res: Response) {
|
function receive(res: Response) {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
|
@ -21,8 +21,8 @@ export function useWebService(): [Maybe<TResponse>, Maybe<Error>] {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch(endpoint, options)
|
fetch(endpoint, options)
|
||||||
.then(receive)
|
.then(receive)
|
||||||
.then((json) => setResponse(Response(json)))
|
.then(json => setResponse(Response(json)))
|
||||||
.catch((err) => setError(err));
|
.catch(err => setError(err));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return [response, error];
|
return [response, error];
|
||||||
|
|
|
@ -4,5 +4,5 @@ import { App } from "~/components/App";
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const el: HTMLElement = document.getElementById("root")!;
|
const el: HTMLElement = document.getElementById("root")!;
|
||||||
ReactDOM.createRoot(el).render(<App/>);
|
ReactDOM.createRoot(el).render(<App />);
|
||||||
});
|
});
|
||||||
|
|
|
@ -35,7 +35,8 @@ module.exports = {
|
||||||
{ from: "src/css/", to: "css" },
|
{ from: "src/css/", to: "css" },
|
||||||
{ from: "src/manifest.json", to: "manifest.json" },
|
{ from: "src/manifest.json", to: "manifest.json" },
|
||||||
{ from: "src/images", to: "images/" },
|
{ from: "src/images", to: "images/" },
|
||||||
{ from: "src/_locales", to: "_locales/" }
|
{ from: "src/_locales", to: "_locales/" },
|
||||||
|
{ from: "src/fonts", to: "fonts/" }
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new CleanWebpackPlugin(),
|
new CleanWebpackPlugin(),
|
||||||
|
|
Loading…
Reference in a new issue