Reimplement UI with 0x1eef/tail.css

This commit is contained in:
0x1eef 2024-04-05 08:56:51 -03:00
parent 63b34bd229
commit 664aa74673
18 changed files with 75 additions and 99 deletions

8
.prettierrc Normal file
View file

@ -0,0 +1,8 @@
{
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": false,
"printWidth": 100,
"arrowParens": "avoid"
}

6
package-lock.json generated
View file

@ -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",

View file

@ -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"
} }
} }

View file

@ -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"

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

0
src/fonts/.gitkeep Normal file
View file

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

View file

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

View file

@ -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>&nbsp;{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>
); );
} }

View file

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

View file

@ -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];

View file

@ -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 />);
}); });

View file

@ -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(),