feat: a whole lot of stuff

This commit is contained in:
Spectralitree 2021-12-11 12:01:36 +01:00
parent a2f22843ee
commit eba1a4543f
10 changed files with 428 additions and 36 deletions

View File

@ -1,6 +1,8 @@
<script lang="ts">
import { Router, Link, Route } from "svelte-navigator";
import Navigation from "./lib/Navigation.svelte";
import Dash from "./pages/Dash.svelte";
import Home from "./pages/Home.svelte";
// theme cdns (I might make some myself too)
@ -40,7 +42,10 @@
<Navigation bind:style={style}/>
<div>
<Route path="/">
<h2>Ooga booga</h2>
<Home />
</Route>
<Route path="/dash">
<Dash />
</Route>
</div>
</Router>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

23
src/functions.ts Normal file
View File

@ -0,0 +1,23 @@
import { currentUser, loggedIn } from "./stores";
import PKAPI from "./api";
import type Sys from './api/system';
function blockQuote(text: string) {
let match = text.match(/(?<=\n|^)(> [^\n]*(?:\n>[^\n]*)*)/gim);
let parse: string[] = [];
for (let i = 0; i < match.length; i++) {
parse[i] = match[i].replace(/(?<=\n|^)> ?/gim, "");
text = text.replace(match[i], `<div class="bq">${parse[i]}</div>`);
}
return text;
}
export function parseMarkdown(text: string) {
text = blockQuote(text);
text = text.replace(/\*{3}(.*?)\*{3}/gim, '<b><i>$1</i></b>');
text = text.replace(/\*{2}(.*?)\*{2}/gim, '<b>$1</b>');
text = text.replace(/\*{1}(.*?)\*{1}/gim, '<i>$1</i>');
text = text.replace(/\n/gim, '<br />')
return text;
}

View File

@ -0,0 +1,32 @@
<script lang="ts">
import { Modal, CardHeader, CardTitle, Image } from 'sveltestrap';
import FaUserCircle from 'svelte-icons/fa/FaUserCircle.svelte'
import default_avatar from '../assets/default_avatar.png';
import type Sys from '../api/system';
export let item: Sys;
let avatarOpen = false;
const toggleAvatarModal = () => (avatarOpen = !avatarOpen);
</script>
<CardHeader>
<CardTitle style="margin-top: 0px; margin-bottom: 0px; outline: none; align-items: center;" class="d-flex justify-content-between align-middle">
<div>
<div class="icon d-inline-block">
<FaUserCircle />
</div>
<span style="vertical-align: middle;">{item.name} ({item.id})</span>
</div>
{#if item && item.avatar_url}
<img on:click={toggleAvatarModal} class="rounded-circle avatar" src={item.avatar_url} alt="Your system avatar" />
{:else}
<img class="rounded-circle avatar" src={default_avatar} alt="your system avatar (default)" />
{/if}
<Modal isOpen={avatarOpen} toggle={toggleAvatarModal}>
<div slot="external" on:click={toggleAvatarModal} style="height: 100%; width: max-content; max-width: 100%; margin-left: auto; margin-right: auto; display: flex;">
<Image style="display: block; margin: auto;" src={item.avatar_url} thumbnail alt="Your system avatar" />
</div>
</Modal>
</CardTitle>
</CardHeader>

View File

@ -1,42 +1,51 @@
<script lang="ts">
import {Navbar, NavbarBrand, Nav, NavItem, NavLink, Collapse, NavbarToggler, Dropdown, DropdownItem, DropdownMenu, DropdownToggle} from 'sveltestrap';
import { createEventDispatcher } from 'svelte';
import { loggedIn } from '../stores';
import { Link } from 'svelte-navigator';
const dispatch = createEventDispatcher();
function changeStyle(style: string) {
dispatch('styleChange', style.toLowerCase());
}
export let style: string;
let isOpen = false;
const toggle = () => (isOpen = !isOpen);
</script>
<Navbar color="transparent" light expand="lg">
<NavbarBrand>pk-webs</NavbarBrand>
<NavbarToggler on:click={toggle}></NavbarToggler>
<Collapse {isOpen} navbar expand="lg">
<Nav class="ms-auto" navbar>
<Dropdown nav inNavbar>
<DropdownToggle>Styles</DropdownToggle>
<DropdownMenu end>
<DropdownItem on:click={() => changeStyle("light")}>Light</DropdownItem>
<DropdownItem on:click={() => changeStyle("dark")}>Dark</DropdownItem>
</DropdownMenu>
</Dropdown>
<NavItem>
<NavLink href="/dash">Dash</NavLink>
</NavItem>
<NavItem>
<NavLink href="/settings">Settings</NavLink>
</NavItem>
<NavItem>
<NavLink href="/templates">templates</NavLink>
</NavItem>
<NavItem>
<NavLink href="/public">Public</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>
let loggedIn_value: boolean;
loggedIn.subscribe(value => {
loggedIn_value = value;
});
</script>
<div style="background-color: #292929">
<Navbar color="light" light expand="lg" class="mb-4">
<NavbarBrand>pk-webs</NavbarBrand>
<NavbarToggler on:click={toggle}></NavbarToggler>
<Collapse {isOpen} navbar expand="lg">
<Nav class="ms-auto" navbar>
<Dropdown nav inNavbar>
<DropdownToggle color="transparent">Styles</DropdownToggle>
<DropdownMenu end>
<DropdownItem on:click={() => style = "light"}>Light</DropdownItem>
<DropdownItem on:click={() => style = "dark"}>Dark</DropdownItem>
</DropdownMenu>
</Dropdown>
{#if loggedIn_value || localStorage.getItem("pk-token")}
<Dropdown nav inNavbar>
<DropdownToggle color="transparent">Dash</DropdownToggle>
<DropdownMenu end>
<Link to="/dash" state={{tab: "system"}}><DropdownItem>System</DropdownItem></Link>
<Link to="/dash" state={{tab: "members"}}><DropdownItem>Members</DropdownItem></Link>
</DropdownMenu>
</Dropdown>
{/if}
<NavItem>
<NavLink href="/settings">Settings</NavLink>
</NavItem>
<NavItem>
<NavLink href="/templates">Templates</NavLink>
</NavItem>
<NavItem>
<NavLink href="/public">Public</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>
</div>

View File

@ -0,0 +1,53 @@
<script lang="ts">
import { currentUser } from '../../stores';
import { Modal, Card, CardHeader, CardBody, CardTitle, Image, ModalHeader, Col, Row, Button } from 'sveltestrap';
import CardsHeader from '../CardsHeader.svelte';
import { parseMarkdown } from '../../functions';
export let user;
$: htmlDescription = parseMarkdown(user.description);
let bannerOpen = false;
const toggleBannerModal = () => (bannerOpen = !bannerOpen);
</script>
<Card>
<CardsHeader bind:item={user}/>
<CardBody style="border-left: 4px solid #{user.color}">
<Row>
<Col xs={12} lg={4} class="mb-2">
<b>ID:</b> {user.id}
</Col>
<Col xs={12} lg={4} class="mb-2">
<b>Name:</b> {user.name}
</Col>
{#if user.tag}
<Col xs={12} lg={4} class="mb-2">
<b>Tag:</b> {user.tag}
</Col>
{/if}
<Col xs={12} lg={4} class="mb-2">
<b>Timezone:</b> {user.timezone}
</Col>
{#if user.color}
<Col xs={12} lg={4} class="mb-2">
<b>Color:</b> {user.color}
</Col>
{/if}
{#if user.banner}
<Col xs={12} lg={3} class="mb-2">
<b>Banner:</b> <Button size="sm" color="light" on:click={toggleBannerModal}>View</Button>
<Modal isOpen={bannerOpen} toggle={toggleBannerModal}>
<div slot="external" on:click={toggleBannerModal} style="height: 100%; width: max-content; max-width: 100%; margin-left: auto; margin-right: auto; display: flex;">
<Image style="display: block; margin: auto;" src={user.banner} thumbnail alt="Your system banner" />
</div>
</Modal>
</Col>
{/if}
<div class="mt-2">
<b>Description:</b><br />
{@html htmlDescription}
</div>
</Row>
</CardBody>
</Card>

80
src/pages/Dash.svelte Normal file
View File

@ -0,0 +1,80 @@
<script lang="ts">
import { Container, Col, Row, TabContent, TabPane, Card } from 'sveltestrap';
import { navigate, useLocation } from "svelte-navigator";
import { currentUser, loggedIn } from '../stores';
import PrivateSystem from '../lib/cards/PrivateSystem.svelte';
import PKAPI from '../api';
import type Sys from '../api/system';
let location = useLocation();
let tabPane = $location.state && $location.state.tab;
if (tabPane === undefined) {
tabPane = "system";
}
let current;
currentUser.subscribe(value => {
current = value;
});
if (!current) {
login(localStorage.getItem("pk-token"));
}
let user = current !== null ? current : JSON.parse(localStorage.getItem("pk-user"));
if (!localStorage.getItem("pk-token") && !user) {
navigate("/");
}
async function login(token: string) {
const api = new PKAPI();
try {
if (!token) {
throw new Error("Token cannot be empty.")
}
const res: Sys = await api.getSystem({token: token});
localStorage.setItem("pk-token", token);
localStorage.setItem("pk-user", JSON.stringify(res));
loggedIn.update(() => true);
currentUser.update(() => res);
user = res;
} catch (error) {
console.log(error);
localStorage.removeItem("pk-token");
localStorage.removeItem("pk-user");
currentUser.update(() => null);
navigate("/");
}
}
</script>
{#if user && user.banner}
<div class="banner" style="background-image: url({user.banner})" />
{/if}
<Container>
<Row>
<Col class="mx-auto" xs={12} lg={9}>
<TabContent class="mt-3">
<TabPane tabId="system" tab="System" active={tabPane === "system"}>
<Card style="border-radius: 0; border: none;">
<PrivateSystem bind:user={user}/>
</Card>
</TabPane>
<TabPane tabId="members" tab="Members" active={tabPane === "members"}>
<Card style="border-radius: 0; border: none;">
alo
</Card>
</TabPane>
</TabContent>
</Col>
</Row>
</Container>
<svelte:head>
<title>pk-webs | dash</title>
</svelte:head>

112
src/pages/Home.svelte Normal file
View File

@ -0,0 +1,112 @@
<script lang="ts">
import { onMount } from 'svelte';
import { Container, Card, CardHeader, CardBody, CardTitle, Col, Row, Spinner, Input, Button, Label, Alert } from 'sveltestrap';
import FaLockOpen from 'svelte-icons/fa/FaLockOpen.svelte';
import { loggedIn, currentUser } from '../stores';
import { Link } from 'svelte-navigator';
import PKAPI from '../api/index';
import type Sys from '../api/system';
let loading = false;
let err: string;
let token: string;
let isLoggedIn: boolean;
let user;
loggedIn.subscribe(value => {
isLoggedIn = value;
});
currentUser.subscribe(value => {
user = value;
})
onMount(() => {
if (localStorage.getItem("pk-token")) {
login(localStorage.getItem("pk-token"));
}
});
async function login(token: string) {
loading = true;
const api = new PKAPI();
try {
if (!token) {
throw new Error("Token cannot be empty.")
}
const res: Sys = await api.getSystem({token: token});
localStorage.setItem("pk-token", token);
localStorage.setItem("pk-user", JSON.stringify(res));
err = null;
loggedIn.update(() => true);
currentUser.update(() => res);
} catch (error) {
console.log(error);
localStorage.removeItem("pk-token");
localStorage.removeItem("pk-user");
currentUser.update(() => null);
err = error.message;
}
loading = false;
}
function logout() {
token = null;
localStorage.removeItem("pk-token");
localStorage.removeItem("pk-user");
loggedIn.update(() => false);
currentUser.update(() => null);
}
</script>
<Container>
<Row>
<Col class="mx-auto" xs={12} lg={9}>
{#if err}
<Alert color="danger" >{err}</Alert>
{/if}
<Card class="mb-4">
<CardHeader>
<CardTitle style="margin-top: 8px; outline: none;">
<div class="icon d-inline-block">
<FaLockOpen />
</div>Log in {#if loading} <div style="float: right"><Spinner color="primary" /></div> {/if}
</CardTitle>
</CardHeader>
<CardBody>
{#if loading}
verifying login...
{:else if isLoggedIn}
{#if user && user.name}
<p>Welcome back, <b>{user.name}</b>!</p>
{:else}
<p>Welcome back!</p>
{/if}
<Link to="/dash"><Button style="float: left;" color='primary'>Go to dash</Button></Link><Button style="float: right;" color='danger' on:click={logout}>Log out</Button>
{:else}
<Row>
<Label>Enter your token here. You can get this by using <b>pk;token</b></Label>
<Col xs={12} md={10}>
<Input class="mb-2" type="text" bind:value={token}/>
</Col>
<Col xs={12} md={2}>
<Button style="width: 100%" color="primary" on:click={() => login(token)}>Submit</Button>
</Col>
</Row>
{/if}
</CardBody>
</Card>
{#if isLoggedIn}
<Card>
<CardBody>
Some cool stuff will go here.
</CardBody>
</Card>
{/if}
</Col>
</Row>
</Container>

25
src/stores.ts Normal file
View File

@ -0,0 +1,25 @@
import { writable } from 'svelte/store';
export const loggedIn = writable(false);
/* export const user = writable({
id: null,
uuid: null,
name: null,
description: null,
tag: null,
avatar_url: null,
banner: null,
timezone: null,
created: null,
color: null,
privacy: {
description_privacy: null,
member_list_privacy: null,
front_privacy: null,
front_history_privacy: null,
group_list_privacy: null
}
}); */
export const currentUser = writable(null);

53
style.css Normal file
View File

@ -0,0 +1,53 @@
.icon {
height: 1.5em;
width: 1.5em;
margin-right: 0.5em;
}
.avatar {
height: 2.5em;
width: 2.5em;
}
.modal-content {
border: none !important;
}
.modal {
display: flex;
}
.banner {
z-index: -200;
width: 100vw;
height: 40vh;
position: absolute;
top: 0;
left: 0;
background-size: cover;
}
.bq {
padding-left: 0.5em;
margin: 0.25em 0 0.25em 0;
border-left: 4px solid rgba(128, 128, 128, 0.3);
}
.nav-link {
background-color: var(--bs-body-bg) !important;
border-color: rgba(128, 128, 128, 0.3) !important;
border-bottom-color: transparent !important;
}
.nav-tabs {
gap: 0.25em;
}
.nav-tabs {
border-bottom: none !important;
}
/* .nav-tabs {
justify-content: center !important;
} */