frontend redesign
This commit is contained in:
21
app/src/lib/api.js
Normal file
21
app/src/lib/api.js
Normal file
@ -0,0 +1,21 @@
|
||||
const version = "v1";
|
||||
const url = new URL(version + "/", import.meta.env.VITE_API_URL).href;
|
||||
|
||||
function join(path) {
|
||||
if (null === path || path === "") return url;
|
||||
|
||||
if (path[0] === "/") path = path.slice(1);
|
||||
|
||||
return new URL(path, url).href;
|
||||
}
|
||||
|
||||
async function services(fetch) {
|
||||
const res = await fetch(join("/services"));
|
||||
const json = await res.json();
|
||||
|
||||
if (!("result" in json)) return [];
|
||||
|
||||
return json.result;
|
||||
}
|
||||
|
||||
export { version, join, services };
|
@ -1,46 +1,24 @@
|
||||
<script>
|
||||
export let title;
|
||||
|
||||
let current = "";
|
||||
let i = 0;
|
||||
|
||||
while (title.length > i) {
|
||||
let c = title[i];
|
||||
setTimeout(
|
||||
() => {
|
||||
current += c;
|
||||
},
|
||||
100 * (i + 1)
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="main">
|
||||
<div class="title">
|
||||
root@ngn.tf:~# {current}
|
||||
</div>
|
||||
<div class="content">
|
||||
<main>
|
||||
<h1 class="title">{title}</h1>
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.main {
|
||||
main {
|
||||
flex: 1;
|
||||
flex-basis: 30%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
background: var(--dark-three);
|
||||
box-shadow: var(--box-shadow);
|
||||
border-radius: var(--radius);
|
||||
border: solid 1px var(--border-color);
|
||||
}
|
||||
|
||||
.title {
|
||||
background: var(--dark-two);
|
||||
padding: 25px;
|
||||
border-radius: 7px 7px 0px 0px;
|
||||
font-size: 20px;
|
||||
main .title {
|
||||
font-family:
|
||||
Consolas,
|
||||
Monaco,
|
||||
@ -50,14 +28,23 @@
|
||||
Bitstream Vera Sans Mono,
|
||||
Courier New,
|
||||
monospace;
|
||||
color: white;
|
||||
color: var(--white-1);
|
||||
}
|
||||
|
||||
.content {
|
||||
background: var(--dark-three);
|
||||
padding: 30px;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
font-size: 25px;
|
||||
main .title::before {
|
||||
content: "#";
|
||||
margin: 0 10px 0 0;
|
||||
color: var(--white-3);
|
||||
}
|
||||
|
||||
main div {
|
||||
border-left: solid 1px var(--black-4);
|
||||
padding: 25px 25px 10px 25px;
|
||||
font-size: var(--size-4);
|
||||
color: var(--white-1);
|
||||
word-wrap: break-word;
|
||||
align-items: center;
|
||||
margin-left: 7px;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { click } from "$lib/util.js";
|
||||
export let title;
|
||||
export let url;
|
||||
let audio;
|
||||
|
||||
let current = "";
|
||||
let i = 0;
|
||||
@ -16,16 +16,9 @@
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
function epicSound() {
|
||||
audio.play();
|
||||
}
|
||||
</script>
|
||||
|
||||
<a on:click={epicSound} data-sveltekit-preload-data href={url}>
|
||||
<audio bind:this={audio} preload="auto">
|
||||
<source src="/click.wav" type="audio/mpeg" />
|
||||
</audio>
|
||||
<a on:click={click} data-sveltekit-preload-data href={url}>
|
||||
<div class="title">
|
||||
{current}
|
||||
</div>
|
||||
|
25
app/src/lib/content.svelte
Normal file
25
app/src/lib/content.svelte
Normal file
@ -0,0 +1,25 @@
|
||||
<script>
|
||||
export let class_name;
|
||||
</script>
|
||||
|
||||
<main class={class_name}>
|
||||
<slot></slot>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
|
||||
padding: 50px;
|
||||
gap: 28px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 900px) {
|
||||
main {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
77
app/src/lib/footer.svelte
Normal file
77
app/src/lib/footer.svelte
Normal file
@ -0,0 +1,77 @@
|
||||
<script>
|
||||
import Link from "$lib/link.svelte";
|
||||
import { color } from "$lib/util.js";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
let visitor_count = 1001;
|
||||
|
||||
function should_congrat() {
|
||||
return visitor_count % 1000 == 0;
|
||||
}
|
||||
</script>
|
||||
|
||||
<footer style="border-top: solid 2px var(--{color()});">
|
||||
<div class="info">
|
||||
<div class="links">
|
||||
<span>
|
||||
<Link href="/" bold={true}>{$_("footer.source")}</Link>
|
||||
</span>
|
||||
<span>/</span>
|
||||
<span>
|
||||
<Link href="/" bold={true}>{$_("footer.license")}</Link>
|
||||
</span>
|
||||
<span>/</span>
|
||||
<span>
|
||||
<Link href="/" bold={true}>{$_("footer.privacy")}</Link>
|
||||
</span>
|
||||
</div>
|
||||
<span>
|
||||
{$_("footer.powered")}
|
||||
</span>
|
||||
</div>
|
||||
<div class="useless">
|
||||
<span>
|
||||
{$_("footer.number", { values: { count: visitor_count } })}
|
||||
{#if should_congrat()}
|
||||
<span style="color: var(--{color()})">({$_("footer.congrats")})</span>
|
||||
{/if}
|
||||
</span>
|
||||
<span>
|
||||
{$_("footer.version", { values: { api_version: "v1", frontend_version: pkg.version } })}
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: var(--black-1);
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
color: var(--white-2);
|
||||
font-size: var(--size-2);
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.useless {
|
||||
margin: 25px 50px 25px 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin: 25px 0 25px 50px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.info .links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
}
|
||||
</style>
|
15
app/src/lib/head.svelte
Normal file
15
app/src/lib/head.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
import { frontend_url, api_url } from "$lib/util.js";
|
||||
export let desc, title;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>[ngn.tf] | {title}</title>
|
||||
|
||||
<meta content="[ngn.tf] | {title}" property="og:title" />
|
||||
<meta content={desc} property="og:description" />
|
||||
<meta content={frontend_url()} property="og:url" />
|
||||
<meta content="#000000" data-react-helmet="true" name="theme-color" />
|
||||
|
||||
<link rel="alternate" type="application/atom+xml" href={api_url("/news/en")} title="Atom Feed" />
|
||||
</svelte:head>
|
@ -1,12 +1,25 @@
|
||||
<script>
|
||||
export let subtitle = "";
|
||||
import { color } from "$lib/util.js";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let picture = "";
|
||||
export let title = "";
|
||||
|
||||
let current = "";
|
||||
|
||||
for (let i = 0; i < title.length; i++) {
|
||||
setTimeout(
|
||||
() => {
|
||||
current += title[i];
|
||||
},
|
||||
100 * (i + 1)
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<h1>
|
||||
<slot></slot>
|
||||
</h1>
|
||||
<h4><c>{subtitle}</c></h4>
|
||||
<h1 style="color: var(--{color()})">{current}</h1>
|
||||
<img src="/profile/{picture}.png" alt="" />
|
||||
</header>
|
||||
|
||||
<style>
|
||||
@ -16,25 +29,54 @@
|
||||
background-size: 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
img {
|
||||
padding: 50px 50px 0 50px;
|
||||
width: 220px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 900;
|
||||
font-size: 500%;
|
||||
padding: 120px;
|
||||
padding-bottom: 0;
|
||||
font-size: var(--size-7);
|
||||
font-family:
|
||||
Consolas,
|
||||
Monaco,
|
||||
Lucida Console,
|
||||
Liberation Mono,
|
||||
DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono,
|
||||
Courier New,
|
||||
monospace;
|
||||
padding: 50px 50px 30px 50px;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
color: white;
|
||||
text-shadow: var(--text-shadow);
|
||||
text-size-adjust: 80%;
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding-bottom: 120px;
|
||||
font-weight: 600;
|
||||
font-size: 200%;
|
||||
text-align: center;
|
||||
color: white;
|
||||
text-size-adjust: 80%;
|
||||
h1::after {
|
||||
content: "_";
|
||||
display: inline-block;
|
||||
animation: blink 1.5s steps(2) infinite;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 900px) {
|
||||
header {
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: 80px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
6
app/src/lib/icon.svelte
Normal file
6
app/src/lib/icon.svelte
Normal file
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
import { color } from "$lib/util.js";
|
||||
export let icon = "";
|
||||
</script>
|
||||
|
||||
<i style="color: var(--{color()});" class="nf {icon}"></i>
|
4
app/src/lib/lang.js
Normal file
4
app/src/lib/lang.js
Normal file
@ -0,0 +1,4 @@
|
||||
export default [
|
||||
{ code: "en", name: "English", icon: "🇬🇧", path: "../locales/en.json" },
|
||||
{ code: "tr", name: "Turkish", icon: "🇹🇷", path: "../locales/tr.json" },
|
||||
];
|
22
app/src/lib/link.svelte
Normal file
22
app/src/lib/link.svelte
Normal file
@ -0,0 +1,22 @@
|
||||
<script>
|
||||
import Icon from "$lib/icon.svelte";
|
||||
import { color } from "$lib/util.js";
|
||||
|
||||
const default_color = "white-1";
|
||||
|
||||
export let active = false;
|
||||
export let link = "";
|
||||
export let icon = "";
|
||||
|
||||
let style = `text-decoration-color: var(--${color()});`;
|
||||
|
||||
if (active) style += `color: var(--${color()});`;
|
||||
else style += `color: var(--${default_color});`;
|
||||
</script>
|
||||
|
||||
{#if icon != ""}
|
||||
<Icon {icon} />
|
||||
{/if}
|
||||
<a {style} href={link}>
|
||||
<slot></slot>
|
||||
</a>
|
@ -1,30 +1,29 @@
|
||||
<script>
|
||||
import { color } from "$lib/util.js";
|
||||
import NavbarLink from "./navbar_link.svelte";
|
||||
import NavbarSwitch from "./navbar_switch.svelte";
|
||||
|
||||
import { color } from "$lib/util.js";
|
||||
import { _ } from "svelte-i18n";
|
||||
</script>
|
||||
|
||||
<nav style="border-bottom: solid 2px var(--{color()});">
|
||||
<h3 style="color: var(--{color()})">[ngn.tf]</h3>
|
||||
|
||||
<div>
|
||||
<NavbarLink link="/">home</NavbarLink>
|
||||
<NavbarLink link="/news">news</NavbarLink>
|
||||
<NavbarLink link="/services">services</NavbarLink>
|
||||
<!-- <NavbarLink link="/donate">donate</NavbarLink> -->
|
||||
<NavbarLink link="/">{$_("navbar.home")}</NavbarLink>
|
||||
<NavbarLink link="/services">{$_("navbar.services")}</NavbarLink>
|
||||
<NavbarLink link="/donate">{$_("navbar.donate")}</NavbarLink>
|
||||
<NavbarSwitch />
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
background: var(--dark-one);
|
||||
background: var(--black-1);
|
||||
padding: 20px 30px 20px 20px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
/*animation-name: borderAnimation;
|
||||
animation-duration: 10s;
|
||||
animation-iteration-count: infinite;*/
|
||||
box-shadow: var(--def-shadow);
|
||||
}
|
||||
|
||||
@ -38,6 +37,6 @@
|
||||
|
||||
h3 {
|
||||
font-weight: 900;
|
||||
font-size: 20px;
|
||||
font-size: var(--size-4);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,10 +1,16 @@
|
||||
<script>
|
||||
import { color, click } from "$lib/util.js";
|
||||
import { page } from "$app/stores";
|
||||
|
||||
export let link;
|
||||
|
||||
function is_active() {
|
||||
return $page.url.pathname == link;
|
||||
}
|
||||
</script>
|
||||
|
||||
<a
|
||||
style="text-decoration-color: var(--{color()})"
|
||||
style="text-decoration-color: var(--{color()}); {is_active() ? `color: var(--${color()})` : ''}"
|
||||
data-sveltekit-preload-data
|
||||
on:click={click}
|
||||
href={link}
|
||||
@ -14,15 +20,8 @@
|
||||
|
||||
<style>
|
||||
a {
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-shadow: 3px 4px 7px rgba(81, 67, 21, 0.8);
|
||||
font-weight: 900;
|
||||
font-size: var(--size-4);
|
||||
color: var(--white-1);
|
||||
}
|
||||
</style>
|
||||
|
38
app/src/lib/navbar_switch.svelte
Normal file
38
app/src/lib/navbar_switch.svelte
Normal file
@ -0,0 +1,38 @@
|
||||
<script>
|
||||
import { locale } from "svelte-i18n";
|
||||
import languages from "$lib/lang.js";
|
||||
let icon = "",
|
||||
indx = 0,
|
||||
len = languages.length;
|
||||
|
||||
function next() {
|
||||
if (indx >= languages.length) indx = 0;
|
||||
|
||||
icon = languages[indx].icon;
|
||||
locale.set(languages[indx++].code);
|
||||
}
|
||||
|
||||
for (indx = 0; indx < len; indx++) {
|
||||
if (languages[indx].code == $locale.slice(0, 2)) {
|
||||
icon = languages[indx++].icon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={next}>{icon}</button>
|
||||
|
||||
<style>
|
||||
button {
|
||||
background: var(--black-2);
|
||||
color: var(--white-1);
|
||||
font-size: var(--size-4);
|
||||
outline: none;
|
||||
border: none;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--black-1);
|
||||
}
|
||||
</style>
|
@ -1,80 +1,102 @@
|
||||
<script>
|
||||
export let desc;
|
||||
export let url;
|
||||
import Icon from "$lib/icon.svelte";
|
||||
import Link from "$lib/link.svelte";
|
||||
|
||||
let icon = "<i class='nf nf-md-clipboard_multiple'></i>";
|
||||
let audio;
|
||||
import { color, time_from_ts } from "$lib/util.js";
|
||||
import { locale } from "svelte-i18n";
|
||||
|
||||
function copy() {
|
||||
audio.play();
|
||||
navigator.clipboard.writeText(url);
|
||||
icon = "<i class='nf nf-md-clipboard_check'></i>";
|
||||
setTimeout(() => {
|
||||
icon = "<i class='nf nf-md-clipboard_multiple'></i>";
|
||||
}, 500);
|
||||
}
|
||||
export let service = {};
|
||||
let style = "";
|
||||
|
||||
if (service.check_res == 0) style = "opacity: 70%";
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<audio bind:this={audio} preload="auto">
|
||||
<source src="/click.wav" type="audio/mpeg" />
|
||||
</audio>
|
||||
<div>
|
||||
<h1><slot></slot></h1>
|
||||
<p>{desc}</p>
|
||||
<main {style}>
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
<h1>{service.name}</h1>
|
||||
<p>{service.desc[$locale.slice(0, 2)]}</p>
|
||||
</div>
|
||||
<div class="links">
|
||||
<Link link={service.clear}><Icon icon="nf-oct-link" /></Link>
|
||||
{#if service.onion != ""}
|
||||
<Link link={service.onion}><Icon icon="nf-linux-tor" /></Link>
|
||||
{/if}
|
||||
{#if service.i2p != ""}
|
||||
<Link link={service.i2p}><span style="color: var(--{color()})">I2P</span></Link>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button on:click={copy}>{@html icon}</button>
|
||||
<a href={url}><i class="nf nf-oct-link_external"></i></a>
|
||||
<div class="check">
|
||||
<h1>Last checked at {time_from_ts(service.check_time)}</h1>
|
||||
{#if service.check_res == 0}
|
||||
<span style="background: var(--white-2)">Down</span>
|
||||
{:else if service.check_res == 1}
|
||||
<span style="background: var(--{color()})">Up</span>
|
||||
{:else if service.check_res == 2}
|
||||
<span style="background: var(--white-2)">Slow</span>
|
||||
{/if}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--black-3);
|
||||
border: solid 1px var(--black-4);
|
||||
text-align: left;
|
||||
|
||||
flex: 1;
|
||||
flex-basis: 40%;
|
||||
}
|
||||
|
||||
main .info {
|
||||
padding: 25px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 30px 30px 30px 30px;
|
||||
background: var(--dark-two);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
border: solid 1px var(--border-color);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: white;
|
||||
gap: 100px;
|
||||
transition: 0.4s;
|
||||
flex-grow: 1;
|
||||
flex: 1 1 0px;
|
||||
justify-content: space-between;
|
||||
color: var(--white-1);
|
||||
}
|
||||
|
||||
div h1 {
|
||||
animation-name: colorAnimation;
|
||||
animation-duration: 10s;
|
||||
animation-iteration-count: infinite;
|
||||
font-size: 30px;
|
||||
main .info .title h1 {
|
||||
font-size: var(--size-5);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
div p {
|
||||
margin-top: 10px;
|
||||
font-size: 20px;
|
||||
main .info .title p {
|
||||
font-size: var(--size-4);
|
||||
color: var(--white-2);
|
||||
}
|
||||
|
||||
a,
|
||||
button {
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
border: none;
|
||||
background: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
main .info .links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
font-size: var(--size-6);
|
||||
}
|
||||
|
||||
a:hover,
|
||||
button:hover {
|
||||
animation-name: colorAnimation;
|
||||
animation-duration: 5s;
|
||||
animation-iteration-count: infinite;
|
||||
main .check {
|
||||
border-top: solid 1px var(--black-4);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: var(--white-1);
|
||||
}
|
||||
|
||||
main .check h1 {
|
||||
padding: 15px 25px 15px 25px;
|
||||
font-size: var(--size-4);
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
main .check span {
|
||||
padding: 15px 25px 15px 25px;
|
||||
font-size: var(--size-5);
|
||||
text-transform: uppercase;
|
||||
color: var(--black-1);
|
||||
font-weight: 1000;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,10 +1,15 @@
|
||||
function click() {
|
||||
let audio = new Audio("/click.wav");
|
||||
audio.play();
|
||||
}
|
||||
import { join } from "$lib/api.js";
|
||||
|
||||
const colors = [
|
||||
"yellow",
|
||||
"cyan",
|
||||
"green",
|
||||
"pinkish",
|
||||
"red",
|
||||
// "blue" (looks kinda ass)
|
||||
];
|
||||
let colors_pos = -1;
|
||||
const colors = ["yellow", "cyan", "green", "pinkish", "red", "blue"];
|
||||
let api_url = join;
|
||||
|
||||
function color() {
|
||||
if (colors_pos < 0) colors_pos = Math.floor(Math.random() * colors.length);
|
||||
@ -13,4 +18,18 @@ function color() {
|
||||
return colors[colors_pos];
|
||||
}
|
||||
|
||||
export { click, color };
|
||||
function click() {
|
||||
let audio = new Audio("/click.wav");
|
||||
audio.play();
|
||||
}
|
||||
|
||||
function frontend_url(path) {
|
||||
if (null !== path && path !== "") return new URL(path, import.meta.env.VITE_FRONTEND_URL).href;
|
||||
else return new URL(import.meta.env.VITE_FRONTEND_URL).href;
|
||||
}
|
||||
|
||||
function time_from_ts(ts) {
|
||||
return new Date(ts * 1000).toLocaleTimeString();
|
||||
}
|
||||
|
||||
export { api_url, frontend_url, click, color, time_from_ts };
|
||||
|
Reference in New Issue
Block a user