Abort abort abort

This commit is contained in:
May 2024-04-29 17:02:42 -07:00
parent 2112c75a7d
commit ab673ea46e
32 changed files with 190 additions and 3083 deletions

193
package-lock.json generated
View file

@ -37,7 +37,7 @@
"@types/range-parser": "^1.2.6",
"discord-api-types": "^0.37.61",
"sass": "^1.57.1",
"svelte": "^3.55.1",
"svelte": "^4.2.15",
"svelte-preprocess": "^5.1.3",
"tslib": "^2.6.2",
"vite": "^4.5.0"
@ -46,6 +46,19 @@
"node": ">=v21"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
@ -70,12 +83,54 @@
"node": ">=18.14.1"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.6.tgz",
@ -199,6 +254,12 @@
"@types/express": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
"node_modules/@types/express": {
"version": "4.17.14",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
@ -292,6 +353,18 @@
"node": ">= 0.6"
}
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@ -310,6 +383,15 @@
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
},
"node_modules/aria-query": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
"dependencies": {
"dequal": "^2.0.3"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@ -334,6 +416,15 @@
"form-data": "^4.0.0"
}
},
"node_modules/axobject-query": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
"integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
"dev": true,
"dependencies": {
"dequal": "^2.0.3"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -466,6 +557,19 @@
"fsevents": "~2.3.2"
}
},
"node_modules/code-red": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
"integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15",
"@types/estree": "^1.0.1",
"acorn": "^8.10.0",
"estree-walker": "^3.0.3",
"periscopic": "^3.1.0"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -562,6 +666,19 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/css-tree": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
"dev": true,
"dependencies": {
"mdn-data": "2.0.30",
"source-map-js": "^1.0.1"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
}
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
@ -603,6 +720,15 @@
"node": ">= 0.8"
}
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@ -705,6 +831,15 @@
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"dependencies": {
"@types/estree": "^1.0.0"
}
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@ -1077,6 +1212,15 @@
"node": ">=0.12.0"
}
},
"node_modules/is-reference": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
"dev": true,
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -1091,6 +1235,12 @@
"node": ">=6"
}
},
"node_modules/locate-character": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
"dev": true
},
"node_modules/magic-string": {
"version": "0.30.5",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
@ -1103,6 +1253,12 @@
"node": ">=12"
}
},
"node_modules/mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
"dev": true
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -1348,6 +1504,17 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/periscopic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
"integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
"dev": true,
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^3.0.0",
"is-reference": "^3.0.0"
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@ -1685,12 +1852,28 @@
}
},
"node_modules/svelte": {
"version": "3.55.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
"version": "4.2.15",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.15.tgz",
"integrity": "sha512-j9KJSccHgLeRERPlhMKrCXpk2TqL2m5Z+k+OBTQhZOhIdCCd3WfqV+ylPWeipEwq17P/ekiSFWwrVQv93i3bsg==",
"dev": true,
"dependencies": {
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
"@jridgewell/trace-mapping": "^0.3.18",
"@types/estree": "^1.0.1",
"acorn": "^8.9.0",
"aria-query": "^5.3.0",
"axobject-query": "^4.0.0",
"code-red": "^1.0.3",
"css-tree": "^2.3.1",
"estree-walker": "^3.0.3",
"is-reference": "^3.0.1",
"locate-character": "^3.0.0",
"magic-string": "^0.30.4",
"periscopic": "^3.1.0"
},
"engines": {
"node": ">= 8"
"node": ">=16"
}
},
"node_modules/svelte-hmr": {

View file

@ -46,7 +46,7 @@
"@types/range-parser": "^1.2.6",
"discord-api-types": "^0.37.61",
"sass": "^1.57.1",
"svelte": "^3.55.1",
"svelte": "^4.2.15",
"svelte-preprocess": "^5.1.3",
"tslib": "^2.6.2",
"vite": "^4.5.0"

View file

@ -1,54 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>$FileId</title>
<!--metaTags-->
<meta name="og:site_name" content="$Uploader">
<meta name="title" content="$FileName">
<meta name="description" content="$FileSize file on monofile $Version, the Discord-based file sharing service">
<link
rel="stylesheet"
href="./style/downloads.scss"
>
<link
rel="stylesheet"
href="/api/v1/account/me/css"
>
<link
rel="icon"
type="image/svg"
href="/static/assets/icons/file_icon.svg"
>
</head>
<body>
<div id="appContent">
<div id="uploadWindow">
<h1>
$FileName
</h1>
<p style="color:#999999">
<span class="number">$FileSize</span>&nbsp;&nbsp;&nbsp;&nbsp;uploaded by <span class="number">$Uploader</span>
</p>
<!--preview-->
<button style="position:relative;width:100%;top:10px;">
<a id="dlbtn" href="/file/$FileId" download="$FileName" style="position:absolute;left:0px;top:0px;height:100%;width:100%;"></a>
download
</button>
<div style="min-height:15px" />
</div>
</div>
</body>
</html>

View file

@ -1,85 +0,0 @@
/*
could probably replace this with fonts served directly
from the server but it's fine for now
*/
@import url("/static/assets/fonts/inconsolata.css");
@import url("/static/assets/fonts/source_sans.css");
@import url("/static/assets/fonts/fira_code.css");
$FallbackFonts:
-apple-system,
system-ui,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
sans-serif;
%normal {
font-family: "Source Sans Pro", $FallbackFonts
}
/*
everything that's not a span
and/or has the normal class
(it's just in case)
*/
*:not(span), .normal { @extend %normal; }
/*
for code blocks / terminal
*/
.monospace {
font-family: "Fira Code", monospace
}
/*
colors
*/
$Background: #252525;
/* hsl(210,12.9,24.3) */
$darkish: rgb(54, 62, 70);
/*
then other stuff
*/
body {
background-color: rgb(30, 33, 36); // this is here so that
// pulling down to refresh
// on mobile looks good
}
#appContent {
background-color: $Background
}
/*
scrollbars
*/
* {
/* nice scrollbars aren't needed on mobile so */
@media screen and (min-width:500px) {
&::-webkit-scrollbar {
width:5px;
}
&::-webkit-scrollbar-track {
background-color:#222222;
}
&::-webkit-scrollbar-thumb {
background-color:#333;
&:hover {
background-color:#373737;
}
}
}
}

View file

@ -1,41 +0,0 @@
@use "base";
@use "app/topbar";
@use "app/pulldown";
@use "app/uploads";
.menuBtn {
text-decoration:none;
font-size:16px;
transition-duration: 100ms;
color:#555555;
background-color: #00000000;
border:none;
margin:0 0 0 0;
cursor:pointer;
position:relative;
top:-1px;
&:hover {
color:slategray;
transition-duration: 100ms;
}
}
#appContent {
position:absolute;
left:0px;
top:40px;
width:100%;
height: calc( 100% - 40px );
background-image: linear-gradient(#333,base.$Background);
@media screen and (max-width:500px) {
background-image: linear-gradient(#303030,base.$Background);
}
}
.number {
font-family: "Inconsolata", monospace;
}

View file

@ -1,49 +0,0 @@
@use "../base";
@use "pulldown/help";
@use "pulldown/accounts";
@use "pulldown/files";
@use "pulldown/modals";
#overlay, .modalContainer {
position:absolute;
left:0px;
height: 100%;
width:100%;
top:0px;
border:none;
outline:none;
background-color:rgba(170, 170, 170, 0.25);
z-index: 1000;
}
.pulldown {
position: absolute;
width: 300px;
height: 400px;
background-color: #222222;
color: #dddddd;
top:0px;
left:50%;
transform:translateX(-50%);
@media screen and (max-width: 500px) {
width: 100%;
height: 100%;
}
p, h1, h2 {
margin:0px;
}
z-index: 1001;
}
.pulldown_display {
position:absolute;
left:0px;
top:0px;
width:100%;
height:100%;
}

View file

@ -1,225 +0,0 @@
.pulldown_display[data-name=accounts] {
.notLoggedIn {
.container_div {
position:absolute;
top:50%;
transform:translateY(-50%);
width:100%;
text-align:center;
h1 {
font-weight:600;
font-size:24px;
@media screen and (max-width:500px) {
font-size:30px;
}
}
.flavor {
font-size:14px;
/* good enoough */
@media screen and (max-width:500px) {
font-size:16px;
}
color:#999999;
margin: 0 0 10px 0;
}
button {
cursor:pointer;
background-color:#393939;
color:#DDDDDD;
border:none;
outline:none;
padding:5px;
transition-duration: 250ms;
/*overflow:clip;*/
@media screen and (max-width: 500px) {
font-size:16px;
padding:10px;
}
&:hover {
transition-duration: 250ms;
background-color:#434343;
color: #ffffff;
}
flex-basis:50%;
flex-grow:1;
}
button.flavor {
padding: 0;
background: none;
}
input[type=text],input[type=password] {
border:none;
border-radius:0;
width:100%;
padding:5px;
background-color:#333333;
color:#dddddd;
outline:none;
@media screen and (max-width: 500px) {
font-size:16px;
padding:10px;
}
}
.pwError {
div {
border:none;
border-radius:0;
width:100%;
padding:5px;
background-color:#663333;
color:#dddddd;
outline:none;
font-size:14px;
text-align:left;
@media screen and (max-width: 500px) {
font-size:16px;
padding:10px;
}
}
}
.lgBtnContainer {
display:flex;
position:relative;
left:20px;
width:calc( 100% - 40px );
gap:10px;
overflow:clip;
}
.fields {
display:flex;
flex-direction:column;
position:relative;
left:20px;
width:calc( 100% - 40px );
gap:5px;
overflow:clip;
}
/*
a {
text-decoration: none;
color:#999999;
font-size:14px;
@media screen and (max-width:500px) {
font-size:16px;
}
&::after {
content:"";
font-size:0px;
opacity: 0;
transition-duration:250ms;
}
&:hover {
&::after {
font-size:13px;
opacity: 1;
transition-duration:250ms;
}
}
}
*/
}
}
.loggedIn {
position:absolute;
/*
left:10px;
top:10px;
*/
left:0px;
top:0px;
width:calc( 100% - 20px );
height:calc( 100% - 20px );
padding:10px;
overflow-y:auto;
h1 {
font-weight:600;
font-size:20px;
color: #AAAAAA;
@media screen and (max-width:500px) {
font-size:24px;
}
.monospace {
font-size:18px;
@media screen and (max-width:500px) {
font-size:22px;
}
}
}
.category {
p {
text-align:left;
}
}
}
}
@keyframes bounce {
0% {
top: 0.25em;
}/*
25% {
top: 0.25em;
}
75% {
top: -0.25em;
}*/
100% {
top: -0.25em;
}
}
.loader {
i {
font-style: normal;
position: relative;
animation-name: bounce;
animation-duration: 500ms;
animation-iteration-count: infinite;
animation-direction: alternate;
top:0.25em;
&:nth-of-type(1) {
animation-delay: 0ms;
}
&:nth-of-type(2) {
animation-delay: 125ms;
}
&:nth-of-type(3) {
animation-delay: 250ms;
}
}
}

View file

@ -1,160 +0,0 @@
.pulldown_display[data-name=files] {
.notLoggedIn {
position:absolute;
top:50%;
left:0px;
transform:translateY(-50%);
width:100%;
text-align:center;
.flavor {
font-size:16px;
color:#999999;
margin: 0 0 10px 0;
}
button {
--col: #999999;
background-color: #232323;
color:var(--col);
font-size:14px;
border:1px solid var(--col);
padding:2px 20px 2px 20px;
cursor:pointer;
transition-duration:250ms;
&:hover {
background-color:#333333;
transition-duration:250ms;
--col:#BBBBBB;
}
}
}
.loggedIn {
display: flex;
flex-direction: column;
max-height:100%;
overflow:hidden;
.searchBar {
transition-duration:150ms;
background-color:#212121;
width:100%;
padding:8px;
color:#dddddd;
border:none;
border-bottom: 1px solid #aaaaaa;
outline: none;
border-radius:0px;
font-size:14px;
&:focus {
transition-duration:150ms;
border-bottom: 1px solid #dddddd;
}
@media screen and (max-width: 500px) {
padding:12px;
font-size:16px;
}
}
.fileList {
overflow-y:auto;
overflow-x:hidden;
padding:5px 0;
.flFile {
padding: 3px 8px;
position:relative;
@media screen and (max-width: 500px) {
padding:7px 12px;
}
.detail {
color:#777777;
font-size:14px;
position:relative;
@media screen and (max-width: 500px) {
font-size:16px;
}
img {
width: 14px;
height: 14px;
/* this is shit but it's the best way i can think of to do this */
/* other than flexbox but i don't feel like doing that rn */
position:relative;
top:2px;
}
}
h2 {
font-size:18px;
text-overflow:ellipsis;
overflow:hidden;
font-weight:600;
@media screen and (max-width: 500px) {
font-size:20px;
}
}
p, h2 {
margin:0 0 0 0;
white-space: nowrap;
}
button {
background-color:#00000000;
border:none;
outline:none;
cursor:pointer;
&.hitbox {
position:absolute;
left:0px;
top:0px;
height:100%;
width:100%;
z-index:10;
}
&.more {
min-height:100%;
width:auto;
aspect-ratio: 1 / 1;
z-index:11;
position:relative;
img {
margin:auto;
}
}
}
.flexCont {
display: flex;
.fileInfo {
width:100%;
min-width:0;
}
}
@media screen and (min-width:500px) {
&:hover {
background-color: #252525;
}
}
}
}
}
}

View file

@ -1,22 +0,0 @@
.pulldown_display[data-name=help] {
overflow-y:auto;
.faqGroup {
padding:6px 10px 4px 10px;
h2 {
font-weight: 400;
color:#DDDDDD;
font-size:16px;
margin:0 0 0 0;
}
p {
color:#999999;
font-size:16px;
margin:0 0 0 0;
}
}
}

View file

@ -1,115 +0,0 @@
.optPicker {
button, .inp {
position:relative;
width:100%;
height:50px;
background-color: #222222;
border:none;
border-bottom:1px solid #AAAAAA;
transition-duration:150ms;
img {
position:absolute;
left:13px;
top:50%;
transform:translateY(-50%);
}
p,input {
text-align:left;
position:absolute;
top:50%;
left:50px;
color:#DDDDDD;
transform:translateY(-50%);
font-size:14px;
background-color:#00000000;
border:none;
span {
color:#777777;
font-size:12px;
}
}
input {
height:100%;
width:calc(100% - 50px);
outline:none; /* bad idea but i don't even care anymore */
margin:0px;
padding:0px;
}
@media screen and (max-width:500px) {
height:70px;
p,input {
font-size:16px;
left:70px;
span {
font-size:14px;
}
}
input {
width:calc( 100% - 70px );
}
img {
width:30px;
height:30px;
left:20px;
}
}
}
button {
cursor:pointer;
&:hover {
transition-duration:150ms;
background-color: #252525;
}
}
.category {
border-bottom: 1px solid #AAAAAA;
p {
color: #AAAAAA;
font-size: 14px;
margin: 10px 0px 3px 0px;
text-align:center;
@media screen and (max-width:500px) {
font-size:16px;
}
}
}
}
.mdHitbox {
position:absolute;
width:100%;
height:100%;
top:0%;
left:0%;
cursor:pointer;
z-index: 0;
border:none;
background-color: #00000000;
outline:none;
}
.modal {
position:absolute;
background-color:#222222;
width:100%;
transform:translateY(-100%);
top:100%;
left:0%;
z-index: 1;
}

View file

@ -1,21 +0,0 @@
@use "../base";
#topbar {
position:absolute;
left:0px;
top:0px;
width:100%;
height:40px;
/* hsl(210,9.1,12.9) */
background-color: rgb(30, 33, 36);
display:flex;
flex-direction: row;
justify-content: center;
align-items: center;
column-gap:5px;
}

View file

@ -1,115 +0,0 @@
#uploadWindow {
#add_new_files {
background-color:#191919;
border: 1px solid gray;
padding: 0px 0px 10px 0px;
p {
font-family: "Fira Code", monospace;
text-align: left;
margin: 0px 0px 0px 10px;
font-size: 30px;
span {
position:relative;
&.add_files_txt {
font-size:16px;
top:-4px;
left:10px;
@media screen and (max-width:500px) {
font-size:20px;
top:-6px;
left:10px;
}
}
}
@media screen and (max-width:500px) {
font-size: 40px;
span.add_files_txt {
font-size:20px;
top:-6px;
left:10px;
}
}
}
#file_add_btns {
width:calc( 100% - 20px );
margin:auto;
position:relative;
display:flex;
flex-direction:row;
column-gap:10px;
button, input[type=text], input[type=submit] {
background-color:#333333;
color:#DDDDDD;
border:none;
border-radius: 0px;
outline:none;
padding:5px;
flex-basis:50%;
flex-grow:1;
transition-duration:250ms;
@media screen and (max-width: 500px) {
font-size:16px;
padding:10px;
}
}
button, input[type=submit] {
cursor:pointer;
&:hover {
@media screen and (min-width: 500px) {
transition-duration:250ms;
flex-basis: 60%;
}
background-color:#393939;
color: #ffffff;
}
}
.fileUpload {
width:100%;
height:100px;
position:relative;
background-color:#262626;
transition-duration:250ms;
input[type=file] {
opacity: 0;
position:absolute;
left:0px;
top:0px;
width:100%;
height:100%;
cursor:pointer;
}
p {
position:absolute;
top:50%;
transform:translateY(-50%);
font-size:12px;
width:100%;
text-align:center;
padding:0px;
margin: 0px;
}
&:hover {
transition-duration:250ms;
background-color:#292929;
}
}
}
}
}

View file

@ -1,59 +0,0 @@
// should probably start using mixins for thingss like this
#uploadWindow {
.file {
background-color:#191919;
border: 1px solid gray;
padding: 10px;
overflow:clip;
position:relative;
h2 {
font-size: 16px;
margin: 0px;
font-weight:600;
width:calc( 100% - 20px );
}
input[type=text] {
background-color:#333333;
color:#DDDDDD;
border:none;
outline:none;
padding:5px;
position:relative;
width:100%;
transition-duration:250ms;
@media screen and (max-width: 500px) {
font-size:16px;
padding:10px;
}
}
.buttonContainer {
display:flex;
column-gap:10px;
button {
flex-basis: 50%;
flex-grow: 1;
padding:5px;
}
}
.uploadingContainer {
color: #AAAAAA;
}
.hitbox {
opacity:0;
position:absolute;
left:0px;
top:0px;
height:100%;
width:100%;
}
}
}

View file

@ -1,86 +0,0 @@
@use "uploader/add_new_files";
@use "uploader/file";
#uploadWindow {
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
padding:10px 15px 10px 15px;
display:flex;
flex-direction: column;
width:350px;
@media screen and (min-width:500px) {
max-height: calc( 100% - 80px );
}
background-color:#222222;
color:#ddd;
h1, p, a {
margin: 0px;
font-size: 14px;
}
h1 > button {
background-color: #0000 !important;
padding: 0;
margin-top: 5px;
img {
color: #666666;
}
&:hover {
img { color: #DDD; }
}
}
a {
color:#999;
}
h1 {
font-weight:600;
font-size: 25px;
}
.uploadContainer {
overflow:auto;
}
button, input[type=submit] {
cursor:pointer;
background-color:#393939;
color:#DDDDDD;
border:none;
outline:none;
padding:5px;
transition-duration: 250ms;
/*overflow:clip;*/
@media screen and (max-width: 500px) {
font-size:16px;
padding:10px;
}
&:hover {
transition-duration: 250ms;
background-color:#434343;
color: #ffffff;
}
}
@media screen and (max-width: 500px) {
width: calc( 100% - 20px );
height: calc( 100% - 20px );
border-radius:0px;
background-color:#00000000;
transform:none;
left:10px;
top:10px;
padding:0px;
}
}

View file

@ -1,26 +0,0 @@
// probably dont need to import the entire
// uploads css file
// so i might just make a separate file with mixins
// and import them
@use "app/uploads";
@use "base";
#appContent {
position:absolute;
left:0px;
top:0px;
width:100%;
height:100%;
background-image: linear-gradient(#333,base.$Background);
@media screen and (max-width:500px) {
background-image: linear-gradient(#303030,base.$Background);
}
}
#uploadWindow {
img, video, audio {
width:100%;
}
}

View file

@ -1,168 +0,0 @@
#uploadWindow {
color: #FFFFFF
}
body {
background-color:#DDDDDD;
}
#appContent {
background: darkgray;
@media screen and (max-width:500px) {
background:white;
}
}
#uploadWindow {
background: white;
color:black;
h1, p, a {
margin: 0px;
font-size: 14px;
}
a {
color:rgb(153, 153, 153);
}
h1 {
font-weight:600;
font-size: 25px;
text-align:center;
@media screen and (max-width:500px) {
font-size: 30px;
}
}
& > p:nth-of-type(1) {
text-align:center;
font-style: italic;
font-weight:600;
font-size: 16px;
color:black !important;
@media screen and (max-width:500px) {
font-size: 21px;
}
}
button {
cursor:pointer;
color:black;
border:none;
outline:none;
padding:5px;
background: #AAAAAA;
@media screen and (max-width: 500px) {
font-size:16px;
padding:10px;
}
&:hover {
outline: 1px solid #333333;
color: black;
background-color:#AAAAAA;
}
}
& > button:nth-last-of-type(1) {
background-color:#66AAFF;
&:hover {
background-color:#66AAFF;
}
}
#add_new_files {
background-color: #AAAAAA66;
border:1px solid #AAAAAA;
#file_add_btns {
button, input[type=text] {
transition-duration:0s;
@media screen and (max-width: 500px) {
font-size:16px;
padding:10px;
}
}
input[type=text] {
font-family: "Fira Code", monospace;
background-color:#AAAAAA;
color:black;
}
button {
cursor:pointer;
background-color:#AAAAAA;
color: black;
&:hover {
flex-basis: 50%;
transition-duration:0s;
background-color:#AAAAAA;
color: black;
outline: 1px solid #333333;
}
}
.fileUpload {
background-color:#AAAAAA;
transition-duration:250ms;
&:hover {
transition-duration:0s;
background-color:#AAAAAA;
}
}
}
}
.file {
background-color: #AAAAAA66;
border: 1px solid #AAAAAA;
input[type=text] {
font-family: "Fira Code", monospace;
background-color:#AAAAAA;
color:black;
}
}
}
* {
/* nice scrollbars aren't needed on mobile so */
@media screen and (min-width:500px) {
&::-webkit-scrollbar {
width:5px;
}
&::-webkit-scrollbar-track {
background-color:#AAAAAA;
}
&::-webkit-scrollbar-thumb {
background-color:#DDDDDD;
&:hover {
background-color:#FFFFFF;
}
}
}
}
#topbar {
background-color: #DDDDDD;
}
.error {
.code {
color: black;
}
}

View file

@ -1,22 +1,4 @@
<script lang="ts">
import { onMount } from "svelte";
import Topbar from "./elem/Topbar.svelte";
import PulldownManager from "./elem/PulldownManager.svelte";
import UploadWindow from "./elem/UploadWindow.svelte";
import { pulldownManager } from "./elem/stores.js";
let topbar: Topbar;
let pulldown: PulldownManager;
onMount(() => {
pulldownManager.set(pulldown)
})
</script>
<Topbar bind:this={topbar} pulldown={pulldown} />
<div id="appContent">
<PulldownManager bind:this={pulldown} />
<UploadWindow/>
</div>
<div id="appContent"></div>

View file

@ -1,49 +0,0 @@
<script context="module" lang="ts">
import { writable } from "svelte/store";
// can't find a better way to do this
import Files from "./pulldowns/Files.svelte";
import Accounts from "./pulldowns/Accounts.svelte";
import Help from "./pulldowns/Help.svelte";
export let allPulldowns = new Map()
allPulldowns
.set("account",Accounts)
.set("help",Help)
.set("files",Files)
export const pulldownOpen = writable<string|false>(false);
</script>
<script lang="ts">
import { onMount } from "svelte";
import { fade, scale } from "svelte/transition";
export function isOpen() {
return $pulldownOpen
}
export function openPulldown(name: string) {
pulldownOpen.set(name)
}
export function closePulldown() {
pulldownOpen.set(false)
}
onMount(() => {
})
</script>
{#if $pulldownOpen}
<div class="pulldown" transition:fade={{duration:200}}>
<svelte:component this={allPulldowns.get($pulldownOpen)} />
</div>
<button
id="overlay"
on:click={closePulldown}
transition:fade={{duration:200}}
/>
{/if}

View file

@ -1,28 +0,0 @@
<script lang="ts">
import { circOut } from "svelte/easing";
import { scale } from "svelte/transition";
import PulldownManager, {pulldownOpen} from "./PulldownManager.svelte";
import { account } from "./stores.js";
import { _void } from "./transition/_void.js";
export let pulldown: PulldownManager;
</script>
<div id="topbar">
{#if $pulldownOpen}
<button
class="menuBtn"
on:click={pulldown.closePulldown}
transition:_void={{duration:200,prop:"width",easingFunc:circOut}}
>close</button>
{/if}
<!-- too lazy to make this better -->
<button class="menuBtn" on:click={() => pulldown.openPulldown("files")}>files</button>
<button class="menuBtn" on:click={() => pulldown.openPulldown("account")}>{$account?.username ? `@${$account.username}` : "account"}</button>
<button class="menuBtn" on:click={() => pulldown.openPulldown("help")}>help</button>
<div /> <!-- not sure what's offcenter but something is
so this div is here to ""fix"" that -->
</div>

View file

@ -1,378 +0,0 @@
<script lang="ts">
import { _void } from "./transition/_void.js"
import { padding_scaleY } from "./transition/padding_scaleY.js"
import { fade } from "svelte/transition"
import { circIn, circOut } from "svelte/easing"
import { serverStats, refresh_stats, account } from "./stores.js"
import bytes from "bytes"
import AttachmentZone from "./uploader/AttachmentZone.svelte"
// stats
refresh_stats()
// uploads
interface Upload {
file: string | File
params: {
uploadId?: string
}
uploadStatus: {
fileId?: string,
error?: string,
}
maximized?: boolean,
viewingUrl?: boolean
}
let attachmentZone
let uploads: Record<string, Upload> = {}
let uploadInProgress = false
let notificationPermission =
globalThis?.Notification?.permission ?? "denied"
let handle_file_upload = (file: Event & { detail: File|string }) => {
uploads[Math.random().toString().slice(2)] = {
file: file.detail,
params: {
uploadId: "",
},
uploadStatus: {}
}
uploads = uploads
}
let handle_fetch_promise = (x: string, prom: Promise<Response>) => {
return prom
.then(async (res) => {
let txt = await res.text()
if (!res.ok) uploads[x].uploadStatus.error = txt
else {
uploads[x].uploadStatus.fileId = txt
try {
new Notification("Upload complete", {
body: `View at ${location.origin}/${uploads[x].uploadStatus.fileId}`,
actions: [
{
action: "open",
title: "Open",
},
{
action: "copy",
title: "Copy",
},
],
}).addEventListener(
"notificationclick",
(event) => {
if ("action" in event && event.action === "open") {
open(
"/download/" +
uploads[x].uploadStatus.fileId
)
} else {
navigator.clipboard.writeText(
`${location.origin}/${uploads[x].uploadStatus.fileId}`
)
}
}
)
} catch (_) {}
refresh_stats()
}
})
.catch((err) => {
uploads[x].uploadStatus.error = err.toString()
})
}
let upload_files = async () => {
uploadInProgress = true
let sequential = localStorage.getItem("sequentialMode") == "true"
// go through all files
for (let [x, v] of Object.entries(uploads)) {
// quick patch-in to allow for a switch to have everything upload sequentially
// switch will have a proper menu option later, for now i'm lazy so it's just gonna be a Secret
let hdl = () => {
let fd = new FormData()
if (v.params.uploadId) fd.append("uploadId", v.params.uploadId)
fd.append("file", v.file)
return handle_fetch_promise(x,fetch("/api/v1/file",{
method: "PUT",
body: fd
}))
}
if (sequential) await hdl()
else hdl()
}
}
// animation
function fileTransition(node: HTMLElement) {
return {
duration: 300,
css: (t: number) => {
let eased = circOut(t)
return `
height: ${eased * (node.offsetHeight - 22)}px;
padding: ${eased * 10}px 10px;
`
},
}
}
</script>
<div id="uploadWindow">
<h1>
monofile
{#if notificationPermission === "default"}
<button
on:click={() => {
Notification.requestPermission().then(
(permission) => (notificationPermission = permission)
)
}}
style="float:right"
title="Notify me when the upload finishes"
>
<svg
width="20"
height="20"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-label="Notify me when the upload finishes"
><path
d="M9.042 19.003h5.916a3 3 0 0 1-5.916 0Zm2.958-17a7.5 7.5 0 0 1 7.5 7.5v4l1.418 3.16A.95.95 0 0 1 20.052 18h-16.1a.95.95 0 0 1-.867-1.338l1.415-3.16V9.49l.005-.25A7.5 7.5 0 0 1 12 2.004Z"
fill="currentColor"
/></svg
>
</button>
{/if}
</h1>
<p style:color="#999999">
<span class="number"
>{$serverStats?.version ? `v${$serverStats?.version}` : "•••"}</span
>&nbsp;&nbsp;&nbsp;&nbsp;Discord based file sharing
</p>
<div style:min-height="10px" />
<!-- consider splitting the file thing into a separate element maybe -->
<div class="uploadContainer">
{#each Object.entries(uploads) as upload (upload[0])}
<!-- container to allow for animate directive -->
<div>
<div
class="file"
transition:fileTransition
style:border={upload[1].uploadStatus.error
? "1px solid #BB7070"
: ""}
>
<h2>
{typeof upload[1].file == "string" ? upload[1].file : upload[1].file.name}
<span style:color="#999999" style:font-weight="400"
>{@html typeof upload[1].file == "string" ? "clone" : `upload&nbsp;(${bytes(upload[1].file.size)})`}</span>
</h2>
{#if upload[1].maximized && !uploadInProgress}
<div transition:padding_scaleY|local>
<div style:height="10px" />
<input
placeholder="custom id"
type="text"
bind:value={uploads[upload[0]].params.uploadId}
/>
<div style:height="10px" />
<div class="buttonContainer">
<button
on:click={() => {
delete uploads[upload[0]]
uploads = uploads
}}
>
delete
</button>
<button
on:click={() =>
(uploads[upload[0]].maximized = false)}
>
minimize
</button>
</div>
</div>
{:else if !uploadInProgress}
<button
on:click={() =>
(uploads[upload[0]].maximized = true)}
class="hitbox"
/>
{:else}
<div
transition:padding_scaleY|local
class="uploadingContainer"
>
{#if !upload[1].uploadStatus.fileId}
<p
in:fade={{
duration: 300,
delay: 400,
easingFunc: circOut,
}}
out:padding_scaleY={{
easingFunc: circIn,
op: true,
}}
>
{upload[1].uploadStatus.error ??
"Uploading..."}
</p>
{/if}
{#if upload[1].uploadStatus.fileId}
<div
style:height="10px"
transition:padding_scaleY
/>
{#if !upload[1].viewingUrl}
<div
class="buttonContainer"
out:_void
in:_void={{ easingFunc: circOut }}
>
<button
on:click={() =>
(uploads[
upload[0]
].viewingUrl = true)}
>
view url
</button>
<button
on:click={() =>
navigator.clipboard.writeText(
`https://${window.location.host}/download/${upload[1].uploadStatus.fileId}`
)}
>
copy url
</button>
</div>
{:else}
<div
class="buttonContainer"
out:_void
in:_void={{ easingFunc: circOut }}
>
<input
type="text"
readonly
value={`https://${window.location.host}/download/${upload[1].uploadStatus.fileId}`}
style:flex-basis="80%"
/>
<button
on:click={() =>
(uploads[
upload[0]
].viewingUrl = false)}
style:flex-basis="20%"
>
ok
</button>
</div>
{/if}
{/if}
</div>
{/if}
</div>
<div style:height="10px" transition:padding_scaleY />
</div>
{/each}
</div>
{#if uploadInProgress == false}
<!-- if required for upload, check if logged in -->
{#if $serverStats?.accounts?.requiredForUpload ? !!$account?.username : true}
<AttachmentZone
bind:this={attachmentZone}
on:addFiles={handle_file_upload}
/>
<div
style:min-height="10px"
transition:_void={{ rTarg: "height", prop: "min-height" }}
/>
{#if Object.keys(uploads).length > 0}
<button
in:padding_scaleY={{ easingFunc: circOut }}
out:_void
on:click={upload_files}>upload</button
>
<div
transition:_void={{ rTarg: "height", prop: "min-height" }}
style:min-height="10px"
/>
{/if}
{:else}
<p transition:_void style:color="#999999" style:text-align="center">
Please log in to upload files.
</p>
<div
transition:_void={{ rTarg: "height", prop: "min-height" }}
style:min-height="10px"
/>
{/if}
{/if}
<p style:color="#999999" style:text-align="center">
Hosting <span class="number" style:font-weight="600"
>{$serverStats?.files ?? "•••"}</span
>
files — Maximum filesize is
<span class="number" style:font-weight="600">
{
$serverStats?.maxDiscordFiles
? bytes($serverStats.maxDiscordFileSize * $serverStats.maxDiscordFiles)
: "•••"
}</span>
<br />
</p>
<p style:color="#999999" style:text-align="center" style:font-size="12px">
Made with {Math.floor(Math.random() * 10) == 0 ? "🐟" : "❤"} by
<a href="https://cetera.uk" style:font-size="12px"
><svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 173.8 275.72"
height="16"
style="vertical-align:middle"
fill="currentColor"
>
<circle cx="37.13" cy="26.43" r="21.6" />
<circle cx="34.62" cy="117.87" r="34.62" class="middle" />
<circle cx="119.78" cy="130.68" r="21.6" class="middle" />
<circle cx="127.16" cy="46.64" r="46.65" />
<circle cx="102.68" cy="219.58" r="56.14" class="bottom" />
</svg> Etcetera</a
>
<a href="https://github.com/mollersuite/monofile" style:font-size="12px"
>source</a
>
</p>
<div style:height="10px" />
</div>

View file

@ -1,80 +0,0 @@
<script lang="ts">
import { fade, slide } from "svelte/transition";
interface BaseModalOption {
name:string,
icon:string,
id: string | number | symbol | boolean
}
type ModalOption = BaseModalOption & {inputSettings: {password?: boolean}, id: any} | BaseModalOption & { description: string }
type ModalOptions = ModalOption[]
type OptionPickerReturns = {selected: any} & Record<any,any> | null
let activeModal: {resolve: (val: OptionPickerReturns) => void, title: string, modal: ModalOptions } | undefined;
let modalResults: Record<string | number | symbol, string> = {};
export function picker(title: string,mdl: ModalOptions): Promise<OptionPickerReturns> {
if (activeModal) forceCancel()
return new Promise<OptionPickerReturns>((resolve,reject) => {
activeModal = {
resolve,
title,
modal:mdl
}
modalResults = {}
})
}
export function forceCancel() {
if (activeModal && activeModal.resolve) {
activeModal.resolve(null)
}
activeModal = undefined
}
</script>
{#if activeModal}
<div class="modalContainer" transition:fade={{duration:200}}>
<button class="mdHitbox" on:click|self={forceCancel}></button>
<div class="modal" transition:slide={{duration:200}}>
<div class="optPicker">
<div class="category">
<p style:margin-bottom="10px">{activeModal.title}</p>
</div>
{#each activeModal.modal as option (option.id)}
{#if "inputSettings" in option}
<div class="inp">
<img src={option.icon} alt={option.id.toString()}>
<!-- i have to do this stupidness because of svelte but -->
<!-- its reason for blocking this is pretty good sooooo -->
{#if option.inputSettings.password}
<input placeholder={option.name} type="password" bind:value={modalResults[option.id]}>
{:else}
<input placeholder={option.name} bind:value={modalResults[option.id]}>
{/if}
</div>
{:else}
<button on:click={() => {activeModal?.resolve({...modalResults,selected:option.id});activeModal=undefined;modalResults={};}}>
<img src={option.icon} alt={option.id.toString()}>
<p>{option.name}<span><br />{option.description}</span></p>
</button>
{/if}
{/each}
<button on:click={forceCancel}>
<img src="/static/assets/icons/delete.svg" alt="cancel">
<p>Cancel</p>
</button>
</div>
</div>
</div>
{/if}

View file

@ -1,331 +0,0 @@
import { fetchAccountData, account, refreshNeeded } from "../stores"
import { get } from "svelte/store";
import type OptionPicker from "./OptionPicker.svelte";
export function deleteAccount(optPicker: OptionPicker) {
optPicker.picker("What should we do with your files?",[
{
name: "Delete my files",
icon: "/static/assets/icons/admin/delete_file.svg",
description: "Your files will be permanently deleted",
id: true
},
{
name: "Do nothing",
icon: "/static/assets/icons/file.svg",
description: "Your files will not be affected",
id: false
}
]).then((exp) => {
if (exp) {
let deleteFiles = exp.selected
optPicker.picker(`Enter your username to continue.`,[
{
name: "Enter your username",
icon: "/static/assets/icons/person.svg",
inputSettings: {},
id:"username"
},
{
name: `Delete account ${deleteFiles ? "& files" : ""}`,
icon: "/static/assets/icons/delete_account.svg",
description: `This cannot be undone.`,
id: true
}
]).then((fin) => {
if (fin && fin.selected) {
if (fin.username != (get(account)||{}).username) {
optPicker.picker("Incorrect username. Please try again.",[])
return
}
fetch(`/auth/delete_account`,{method:"POST", body:JSON.stringify({
deleteFiles
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchAccountData()
})
}
})
}
})
}
export function userChange(optPicker: OptionPicker) {
optPicker.picker("Change username",[
{
name: "New username",
icon: "/static/assets/icons/person.svg",
id: "username",
inputSettings: {}
},
{
name: "Update username",
icon: "/static/assets/icons/update.svg",
description: "",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/auth/change_username`,{method:"POST", body:JSON.stringify({
username:exp.username
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchAccountData()
})
}
})
}
export function forgotPassword(optPicker: OptionPicker) {
optPicker.picker("Forgot your password?",[
{
name: "Username",
icon: "/static/assets/icons/person.svg",
id: "user",
inputSettings: {}
},
{
name: "OK",
icon: "/static/assets/icons/update.svg",
description: "",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/auth/request_emergency_login`,{method:"POST", body:JSON.stringify({
account:exp.user
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
} else {
optPicker.picker(`Please follow the instructions sent to your inbox.`,[])
}
})
}
})
}
export function emailPotentialRemove(optPicker: OptionPicker) {
optPicker.picker("What would you like to do?",[
{
name: "Set a new email",
icon: "/static/assets/icons/change_email.svg",
description: "",
id: "set"
},
{
name: "Disconnect email",
icon: "/static/assets/icons/disconnect_email.svg",
description: "",
id: "disconnect"
}
]).then((exp) => {
if (exp && exp.selected) {
switch (exp.selected) {
case "set":
emailChange(optPicker);
break
case "disconnect":
fetch("/auth/remove_email", {method: "POST"}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchAccountData()
})
}
}
})
}
export function emailChange(optPicker: OptionPicker) {
optPicker.picker("Change email",[
{
name: "New email",
icon: "/static/assets/icons/mail.svg",
id: "email",
inputSettings: {}
},
{
name: "Request email change",
icon: "/static/assets/icons/update.svg",
description: "",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/auth/request_email_change`,{method:"POST", body:JSON.stringify({
email:exp.email
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
} else {
optPicker.picker(`Please continue to your inbox at ${exp.email.split("@")[1]} and click on the attached link.`,[])
}
})
}
})
}
export function pwdChng(optPicker: OptionPicker) {
optPicker.picker("Change password",[
{
name: "New password",
icon: "/static/assets/icons/change_password.svg",
id: "password",
inputSettings: {
password: true
}
},
{
name: "Update password",
icon: "/static/assets/icons/update.svg",
description: "This will log you out of all sessions",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/auth/change_password`,{method:"POST", body:JSON.stringify({
password:exp.password
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchAccountData()
})
}
})
}
export function customcss(optPicker: OptionPicker) {
optPicker.picker("Set custom CSS",[
{
name: "Enter a file ID",
icon: "/static/assets/icons/file.svg",
id: "fileid",
inputSettings: {}
},
{
name: "OK",
icon: "/static/assets/icons/update.svg",
description: "Refresh to apply changes",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/api/v1/account/customization/css`, {
method: "PUT",
body: JSON.stringify({
fileId: exp.fileid,
}),
}).then((response) => {
if (response.status != 200) {
optPicker.picker(
`${response.status} ${
response.headers.get("x-backup-status-message") ||
response.statusText ||
""
}`,
[]
)
}
fetchAccountData()
refreshNeeded.set(true)
})
}
})
}
export function embedColor(optPicker: OptionPicker) {
optPicker.picker("Set embed color",[
{
name: "FFFFFF",
icon: "/static/assets/icons/pound.svg",
id: "color",
inputSettings: {}
},
{
name: "OK",
icon: "/static/assets/icons/update.svg",
description: "",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/api/v1/account/customization/embed/color`, {
method: "POST",
body: JSON.stringify({
color: exp.color,
}),
}).then((response) => {
if (response.status != 200) {
optPicker.picker(
`${response.status} ${
response.headers.get("x-backup-status-message") ||
response.statusText ||
""
}`,
[]
)
}
fetchAccountData()
})
}
})
}
export function embedSize(optPicker: OptionPicker) {
optPicker.picker("Set embed image size",[
{
name: "Large",
icon: "/static/assets/icons/image.svg",
description: "",
id: true
},
{
name: "Small",
icon: "/static/assets/icons/small_image.svg",
description: "",
id: false
}
]).then((exp) => {
if (exp && exp.selected !== null) {
fetch(`/api/v1/account/customization/embed/size`, {
method: "POST",
body: JSON.stringify({
largeImage: exp.selected,
}),
}).then((response) => {
if (response.status != 200) {
optPicker.picker(
`${response.status} ${
response.headers.get("x-backup-status-message") ||
response.statusText ||
""
}`,
[]
)
}
fetchAccountData()
})
}
})
}

View file

@ -1,231 +0,0 @@
import { fetchAccountData, fetchFilePointers, account } from "../stores"
import { get } from "svelte/store";
import type OptionPicker from "./OptionPicker.svelte";
export function pwdReset(optPicker: OptionPicker) {
optPicker.picker("Reset password",[
{
name: "Target user",
icon: "/static/assets/icons/person.svg",
id: "target",
inputSettings: {}
},
{
name: "New password",
icon: "/static/assets/icons/change_password.svg",
id: "password",
inputSettings: {
password: true
}
},
{
name: "Update password",
icon: "/static/assets/icons/update.svg",
description: "This will log the target user out of all sessions",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/admin/reset`,{method:"POST", body:JSON.stringify({
target: exp.target,
password:exp.password
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
})
}
})
}
export function chgOwner(optPicker: OptionPicker) {
optPicker.picker("Transfer file ownership",[
{
name: "File ID",
icon: "/static/assets/icons/file.svg",
id: "file",
inputSettings: {}
},
{
name: "New owner",
icon: "/static/assets/icons/person.svg",
id: "owner",
inputSettings: {}
},
{
name: "Transfer file ownership",
icon: "/static/assets/icons/update.svg",
description: "This will transfer the file to this user",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/admin/transfer`,{method:"POST", body:JSON.stringify({
owner: exp.owner,
target: exp.file
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
})
}
})
}
export function chgId(optPicker: OptionPicker) {
optPicker.picker("Change file ID",[
{
name: "Target file",
icon: "/static/assets/icons/file.svg",
id: "file",
inputSettings: {}
},
{
name: "New ID",
icon: "/static/assets/icons/admin/change_file_id.svg",
id: "new",
inputSettings: {}
},
{
name: "Update",
icon: "/static/assets/icons/update.svg",
description: "File will not be available at its old ID",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/admin/idchange`,{method:"POST", body:JSON.stringify({
target: exp.file,
new: exp.new
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
})
}
})
}
export function delFile(optPicker: OptionPicker) {
optPicker.picker("Delete file",[
{
name: "File ID",
icon: "/static/assets/icons/file.svg",
id: "file",
inputSettings: {}
},
{
name: "Delete",
icon: "/static/assets/icons/admin/delete_file.svg",
description: "This can't be undone",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/admin/delete`,{method:"POST", body:JSON.stringify({
target: exp.file
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
})
}
})
}
export function elevateUser(optPicker: OptionPicker) {
optPicker.picker("Elevate user",[
{
name: "Username",
icon: "/static/assets/icons/person.svg",
id: "user",
inputSettings: {}
},
{
name: "Elevate to admin",
icon: "/static/assets/icons/update.svg",
description: "",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/admin/elevate`,{method:"POST", body:JSON.stringify({
target: exp.user
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
})
}
})
}
// im really lazy so i just stole this from account.js
export function deleteAccount(optPicker: OptionPicker) {
optPicker.picker("What should we do with the target account's files?",[
{
name: "Delete files",
icon: "/static/assets/icons/admin/delete_file.svg",
description: "Files will be permanently deleted",
id: true
},
{
name: "Do nothing",
icon: "/static/assets/icons/file.svg",
description: "Files will not be affected",
id: false
}
]).then((exp) => {
if (exp) {
let deleteFiles = exp.selected
optPicker.picker(`Enter the target account's username to continue.`,[
{
name: "Enter account username",
icon: "/static/assets/icons/person.svg",
inputSettings: {},
id:"username"
},
{
name: "Optional reason",
icon: "/static/assets/icons/more.svg",
inputSettings: {},
id:"reason"
},
{
name: `Delete account ${deleteFiles ? "& its files" : ""}`,
icon: "/static/assets/icons/delete_account.svg",
description: `This cannot be undone.`,
id: true
}
]).then((fin) => {
if (fin && fin.selected) {
fetch(`/admin/delete_account`,{method:"POST", body:JSON.stringify({
target: fin.username,
reason: fin.reason,
deleteFiles
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchAccountData()
})
}
})
}
})
}

View file

@ -1,220 +0,0 @@
import { fetchAccountData, fetchFilePointers, account } from "../stores"
import { get } from "svelte/store";
import type OptionPicker from "./OptionPicker.svelte"
import type { FilePointer } from "../../../server/lib/files";
export let options = {
FV: [
{
name: "Public",
icon: "/static/assets/icons/public.svg",
description: "Everyone can view your uploads",
id: "public"
},
{
name: "Anonymous",
icon: "/static/assets/icons/anonymous.svg",
description: "Your username will be hidden",
id: "anonymous"
},
{
name: "Private",
icon: "/static/assets/icons/private.svg",
description: "Nobody but you can view your uploads",
id: "private"
}
],
FV2: [
{
name: "Public",
icon: "/static/assets/icons/public.svg",
description: "Everyone can view this file",
id: "public"
},
{
name: "Anonymous",
icon: "/static/assets/icons/anonymous.svg",
description: "Your username will be hidden",
id: "anonymous"
},
{
name: "Private",
icon: "/static/assets/icons/private.svg",
description: "Nobody but you can view this file",
id: "private"
}
],
AYS: [
{
name: "Yes",
icon: "/static/assets/icons/update.svg",
id: true
}
]
}
export function dfv(optPicker: OptionPicker) {
optPicker.picker("Default file visibility",options.FV).then((exp) => {
if (exp && exp.selected) {
fetch(`/auth/dfv`,{method:"POST", body:JSON.stringify({
defaultFileVisibility: exp.selected
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchAccountData()
})
}
})
}
export function update_all_files(optPicker: OptionPicker) {
optPicker.picker("You sure?",[
{
name: "Yeah",
icon: "/static/assets/icons/update.svg",
description: `This will make all of your files ${get(account)?.defaultFileVisibility || "public"}`,
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/files/manage`,{method:"POST", body:JSON.stringify({
target:get(account)?.files,
action: "changeFileVisibility",
value: get(account)?.defaultFileVisibility
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchAccountData()
})
}
})
}
export function fileOptions(optPicker: OptionPicker, file: FilePointer & {id:string}) {
optPicker.picker(file.filename,[
{
name: file.tag ? "Remove tag" : "Tag file",
icon: `/static/assets/icons/${file.tag ? "tag_remove" : "tag"}.svg`,
description: file.tag || `File has no tag`,
id: "tag"
},
{
name: "Change file visibility",
icon: `/static/assets/icons/${file.visibility||"public"}.svg`,
description: `File is currently ${file.visibility||"public"}`,
id: "changeFileVisibility"
},
{
name: "Delete file",
icon: `/static/assets/icons/admin/delete_file.svg`,
description: ``,
id: "delete"
}
]).then((exp) => {
if (exp && exp.selected) {
switch( exp.selected ) {
case "delete":
fetch(`/files/manage`,{method:"POST", body:JSON.stringify({
target: [ file.id ],
action: "delete",
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchFilePointers();
})
break;
case "changeFileVisibility":
optPicker.picker("Set file visibility", options.FV2).then((exp) => {
if (exp && exp.selected) {
fetch(`/files/manage`, {method: "POST", body: JSON.stringify({
target: [ file.id ],
action: "changeFileVisibility",
value: exp.selected
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchFilePointers();
})
}
})
break;
case "tag":
if (file.tag) {
fetch(`/files/manage`, {method: "POST", body: JSON.stringify({
target: [ file.id ],
action: "setTag"
})}).then(fetchFilePointers)
return
}
optPicker.picker("Enter a tag (max 30char)",[
{
name: "Tag name",
icon: "/static/assets/icons/tag.svg",
id: "tag",
inputSettings: {}
},
{
name: "OK",
icon: "/static/assets/icons/update.svg",
description: "",
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/files/manage`, {method: "POST", body: JSON.stringify({
target: [ file.id ],
action: "setTag",
value: exp.tag || null
})}).then((response) => {
if (response.status != 200) {
optPicker.picker(`${response.status} ${response.headers.get("x-backup-status-message") || response.statusText || ""}`,[])
}
fetchFilePointers();
})
}
})
break
}
}
})
}

View file

@ -1,243 +0,0 @@
<script lang="ts">
import Pulldown from "./Pulldown.svelte"
import { padding_scaleY } from "../transition/padding_scaleY"
import { circIn,circOut } from "svelte/easing"
import { account, fetchAccountData, serverStats, refreshNeeded } from "../stores";
import { fade } from "svelte/transition";
import OptionPicker from "../prompts/OptionPicker.svelte";
import * as accOpts from "../prompts/account";
import * as uplOpts from "../prompts/uploads";
import * as admOpts from "../prompts/admin";
let targetAction: "login"|"create"
let inProgress: boolean
let authError:{status:number,message:string}|undefined
let pwErr: HTMLDivElement
let optPicker: OptionPicker;
// lazy
let username: string
let password: string
let execute = () => {
if (inProgress) return
inProgress = true
fetch(`/auth/${targetAction}`, {
method: "POST",
body: JSON.stringify({
username, password
})
}).then(async (res) => {
inProgress = false
if (res.status != 200) {
authError = await res.json().catch(() => {
return {
status: res.status,
message: res.headers.get("x-backup-status-message") || res.statusText || ""
}
})
} else {
authError = undefined, username = "", password = "";
fetchAccountData();
}
}).catch(() => {})
}
$: {
if (pwErr && authError) {
pwErr.animate({
backgroundColor: ["#885555","#663333"],
easing: "ease-out"
},650)
}
}
// actual account menu
</script>
<Pulldown name="accounts">
<OptionPicker bind:this={optPicker} />
{#if $account}
<div class="loggedIn" transition:fade={{duration:200}}>
<h1>
Hey there, <span class="monospace">@{$account.username}</span>
</h1>
<div class="optPicker">
<div class="category">
<p>Account</p>
</div>
<button on:click={() => accOpts.userChange(optPicker)}>
<img src="/static/assets/icons/change_username.svg" alt="change username">
<p>Change username</p>
</button>
<button on:click={() => ($account?.email ? accOpts.emailPotentialRemove : accOpts.emailChange)(optPicker)}>
<img src="/static/assets/icons/mail.svg" alt="change email">
<p>Change email{#if $account.email}<span class="monospaceText"><br />{$account.email}</span>{/if}</p>
</button>
<button on:click={() => accOpts.pwdChng(optPicker)}>
<img src="/static/assets/icons/change_password.svg" alt="change password">
<p>Change password<span><br />You will be logged out of all sessions</span></p>
</button>
{#if !$account.admin}
<button on:click={() => accOpts.deleteAccount(optPicker)}>
<img src="/static/assets/icons/delete_account.svg" alt="delete account">
<p>Delete account</p>
</button>
{/if}
<div class="category">
<p>Uploads</p>
</div>
<button on:click={() => uplOpts.dfv(optPicker)}>
<img src={`/static/assets/icons/${$account.defaultFileVisibility || "public"}.svg`} alt={$account.defaultFileVisibility || "public"}>
<p>Default file visibility<span><br />Uploads will be <strong>{$account.defaultFileVisibility || "public"}</strong> by default</span></p>
</button>
<button on:click={() => uplOpts.update_all_files(optPicker)}>
<img src="/static/assets/icons/update.svg" alt="update">
<p>Make all of my files {$account.defaultFileVisibility || "public"}<span><br />Matches your default file visibility</span></p>
</button>
<div class="category">
<p>Customization</p>
</div>
<button on:click={() => accOpts.customcss(optPicker)}>
<img src="/static/assets/icons/paint.svg" alt="customcss">
<p>Set custom CSS<span><br />{@html $account.customCSS ? `Using file ID <span class="number">${$account.customCSS}</span>` : "No custom CSS set"}</span></p>
</button>
<button on:click={() => accOpts.embedColor(optPicker)}>
<img src="/static/assets/icons/pound.svg" alt="embedColor">
<p>Set custom embed color<span><br />{@html $account?.embed?.color ? `Using custom color <span class="number">${$account?.embed?.color}</span>` : ""}</span></p>
</button>
<button on:click={() => accOpts.embedSize(optPicker)}>
<img src="/static/assets/icons/image.svg" alt="embedSize">
<p>Set embed image size <span><br />Images currently appear {$account?.embed?.largeImage ? `large` : "small"} in embeds</span></p>
</button>
{#if $refreshNeeded}
<button on:click={() => window.location.reload()} transition:fade={{duration: 200}}>
<img src="/static/assets/icons/refresh.svg" alt="refresh">
<p>Refresh<span><br />Changes were made which require a refresh</span></p>
</button>
{/if}
<div class="category">
<p>Sessions</p>
</div>
<button on:click={() => fetch(`/auth/logout_sessions`,{method:"POST"}).then(() => fetchAccountData())}>
<img src="/static/assets/icons/logout_all.svg" alt="logout_all">
<p>Log out all sessions<span><br />{$account?.sessionCount} session(s) active</span></p>
</button>
<button on:click={() => fetch(`/auth/logout`,{method:"POST"}).then(() => fetchAccountData())}>
<img src="/static/assets/icons/logout.svg" alt="logout">
<p>Log out<span><br />Session expires {new Date($account?.sessionExpires).toLocaleDateString()}</span></p>
</button>
{#if $account.admin}
<div class="category">
<p>Admin</p>
</div>
<button on:click={() => admOpts.deleteAccount(optPicker)}>
<img src="/static/assets/icons/delete_account.svg" alt="delete account">
<p>Delete user account</p>
</button>
<button on:click={() => admOpts.pwdReset(optPicker)}>
<img src="/static/assets/icons/change_password.svg" alt="change password">
<p>Change user password</p>
</button>
<button on:click={() => admOpts.elevateUser(optPicker)}>
<img src="/static/assets/icons/admin/elevate_user.svg" alt="elevate account">
<p>Elevate account to admin</p>
</button>
<button on:click={() => admOpts.chgOwner(optPicker)}>
<img src="/static/assets/icons/link.svg" alt="change file owner">
<p>Change file owner</p>
</button>
<button on:click={() => admOpts.chgId(optPicker)}>
<img src="/static/assets/icons/admin/change_file_id.svg" alt="change file id">
<p>Change file ID<span><br />Potentially buggy, usage not recommended</span></p>
</button>
<button on:click={() => admOpts.delFile(optPicker)}>
<img src="/static/assets/icons/admin/delete_file.svg" alt="delete file">
<p>Delete file</p>
</button>
{/if}
<p style="font-size:12px;color:#AAAAAA;text-align:center;" class="monospace"><br />{$account.id}</p>
</div>
</div>
{:else}
<div class="notLoggedIn" transition:fade={{duration:200}}>
<div class="container_div">
<h1>monofile <span style:color="#999999">accounts</span></h1>
<p class="flavor">Gain control of your uploads.</p>
{#if targetAction}
<div class="fields" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
{#if !$serverStats?.accounts.registrationEnabled && targetAction == "create"}
<div class="pwError">
<div style:background-color="#554C33">
<p>Account registration has been disabled by this instance's owner</p>
</div>
</div>
{/if}
{#if authError}
<div class="pwError" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
<div bind:this={pwErr}>
<p><strong>{authError.status}</strong> {authError.message}</p>
</div>
</div>
{/if}
<input placeholder="username" type="text" bind:value={username}>
<input placeholder="password" type="password" bind:value={password}>
<button on:click={execute}>{@html inProgress ? "<span class=loader><i>•</i> <i>•</i> <i>•</i></span>" : (targetAction=="login" ? "Log in" : "Create account") }</button>
{#if targetAction == "login"}
<button class="flavor" on:click={() => accOpts.forgotPassword(optPicker)}>I forgot my password</button>
{/if}
</div>
{:else}
<div class="lgBtnContainer" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
<button on:click={() => targetAction="login"}>Log in</button>
<button on:click={() => targetAction="create"}>Sign up</button>
</div>
{/if}
</div>
</div>
{/if}
</Pulldown>

View file

@ -1,63 +0,0 @@
<script lang="ts">
import Pulldown from "./Pulldown.svelte";
import { account, fetchFilePointers, files, pulldownManager } from "../stores.js";
import { fade } from "svelte/transition";
import { flip } from "svelte/animate";
import { fileOptions } from "../prompts/uploads";
import OptionPicker from "../prompts/OptionPicker.svelte";
let picker: OptionPicker;
let query = "";
fetchFilePointers();
</script>
<Pulldown name="files">
<OptionPicker bind:this={picker} />
{#if $account?.username}<div class="loggedIn">
<input type="text" placeholder={`Search ${$files.length} file(s)`} class="searchBar" bind:value={query}>
<div class="fileList">
<!-- Probably wildly inefficient but who cares, I just wanna get this over with -->
{#each $files.filter(f => f&&(f.filename.toLowerCase().includes(query.toLowerCase()) || f.id.toLowerCase().includes(query.toLowerCase()) || f.tag?.includes(query.toLowerCase()))) as file (file.id)}
<div class="flFile" transition:fade={{duration:200}} animate:flip={{duration:200}}>
<button class="hitbox" on:click={() => window.open(`/download/${file.id}`)}></button> <!-- this is bad, but I'm lazy -->
<div class="flexCont">
<div class="fileInfo">
<h2>{file.filename}</h2>
<p class="detail">
<img src="/static/assets/icons/{file.visibility || "public"}.svg" alt={file.visibility||"public"} />&nbsp;
<span class="number">{file.id}</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="cd">{file.mime.split(";")[0]}</span>
{#if file.reserved}
<br />
<img src="/static/assets/icons/update.svg" alt="uploading"/>&nbsp;
Uploading...
{/if}
{#if file.tag}
<br />
<img src="/static/assets/icons/tag.svg" alt="tag"/>&nbsp;
<span class="cd">{file.tag}</span>
{/if}
</p>
</div>
<button class="more" on:click={() => fileOptions(picker, file)}>
<img src="/static/assets/icons/more.svg" alt="more" />
</button>
</div>
</div>
{/each}
</div>
</div>
{:else}
<div class="notLoggedIn">
<div style:height="10px" />
<p class="flavor">Log in to view uploads</p>
<button on:click={$pulldownManager.openPulldown("account")}>OK</button>
<div style:height="14px" />
</div>
{/if}
</Pulldown>

View file

@ -1,25 +0,0 @@
<!-- i'm lazy, could probably just use plain html here but hwatever, mgiht make this grab from the server idk -->
<script>
import Pulldown from "./Pulldown.svelte"
let faq = [
{
question : "Are my files compressed on upload?",
answer : "No. Files should stay completely unchanged on download."
},
{
question : "How do I replace a file that I have previously uploaded?",
answer : "You can modify the content of a file that is linked to a file ID by reuploading the file using the same custom ID."
}
]
</script>
<Pulldown name="help">
{#each faq as question}
<div class="faqGroup">
<h2>{question.question}</h2>
<p>{question.answer}</p>
</div>
{/each}
</Pulldown>

View file

@ -1,14 +0,0 @@
<script lang=ts>
import { fade } from "svelte/transition";
export let name: string;
</script>
<div
class="pulldown_display"
data-name={name}
transition:fade={{duration:150}}
>
<slot />
</div>

View file

@ -1,54 +0,0 @@
import { writable } from "svelte/store"
//import type Pulldown from "./pulldowns/Pulldown.svelte"
import type { SvelteComponent } from "svelte"
import type { Account } from "../../server/lib/accounts"
import type { ClientConfiguration } from "../../server/lib/config"
import type { FilePointer } from "../../server/lib/files"
export let refreshNeeded = writable(false)
export let pulldownManager = writable<SvelteComponent>()
export let account = writable<
(Account & { sessionCount: number; sessionExpires: number }) | undefined
>()
export let serverStats = writable<ClientConfiguration | undefined>()
export let files = writable<(FilePointer & { id: string })[]>([])
export let fetchAccountData = function () {
fetch("/auth/me")
.then(async (response) => {
if (response.status == 200) {
account.set(await response.json())
} else {
account.set(undefined)
}
})
.catch((err) => {
console.error(err)
})
}
export let fetchFilePointers = function () {
fetch("/files/list", { cache: "no-cache" })
.then(async (response) => {
if (response.status == 200) {
files.set(await response.json())
} else {
files.set([])
}
})
.catch((err) => {
console.error(err)
})
}
export let refresh_stats = () => {
fetch("/server")
.then(async (data) => {
serverStats.set(await data.json())
})
.catch((err) => {
console.error(err)
})
}
fetchAccountData()

View file

@ -1,23 +0,0 @@
import { circIn, circOut } from "svelte/easing"
export function _void(
node: HTMLElement,
options?: { duration?:number, easingFunc?: (a:number)=>number, prop?:string, rTarg?: "height"|"width"}
) {
const { duration = 300, easingFunc = circIn, prop, rTarg } = options ?? {}
let rect = node.getBoundingClientRect()
return {
duration,
css: (t: number) => {
let eased = easingFunc(t)
return `
white-space: nowrap;
${prop||"height"}: ${(eased)*(rect[rTarg || (prop && prop in rect) ? prop as keyof Omit<DOMRect, "toJSON"> : "height"])}px;
padding: 0px;
opacity:${eased};
overflow: clip;
`
}
}
}

View file

@ -1,21 +0,0 @@
import { circIn, circOut } from "svelte/easing"
function padding_scaleY(node: HTMLElement, options?: { duration?: number, easingFunc?: (a: number) => number, padY?: number, padX?: number, op?: boolean }) {
const { duration = 300, easingFunc = circOut, padY, padX, op } = options ?? {}
let rect = node.getBoundingClientRect()
return {
duration,
css: (t:number) => {
let eased = easingFunc(t)
return `
height: ${eased*(rect.height-(padY||0))}px;
${padX&&padY ? `padding: ${(eased)*(padY)}px ${(padX)}px;` : ""}
${op ? `opacity: ${eased};` : ""}
`
}
}
}
export {padding_scaleY}

View file

@ -1,72 +0,0 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { circOut } from "svelte/easing"
import { _void } from "../transition/_void"
enum UploadTypes {
None,
Files,
Clone
}
let uploadType: UploadTypes = UploadTypes.None
let dispatch = createEventDispatcher();
// file upload
let files: FileList | undefined
$: if (files) {
[...files].forEach(file=>dispatch("addFiles", file))
uploadType = UploadTypes.None
}
// file clone
let cloneUrlTextbox: HTMLInputElement;
let cloneForm: HTMLFormElement;
$: {
if (cloneForm && cloneUrlTextbox) {
cloneForm.addEventListener("submit",(e) => {
e.preventDefault()
if (cloneUrlTextbox.value) {
dispatch("addFiles",cloneUrlTextbox.value)
uploadType = UploadTypes.None;
} else {
cloneUrlTextbox.animate([
{"transform":"translateX(0px)"},
{"transform":"translateX(-3px)"},
{"transform":"translateX(3px)"},
{"transform":"translateX(0px)"}
],100)
}
})
}
}
</script>
<!-- there are 100% better ways to do this but idgaf, it's still easier to manage than <1.3 lmao -->
<div id="add_new_files" transition:_void={{duration:200}}>
<p>
+<span class="add_files_txt">add files</span>
</p>
{#if uploadType == UploadTypes.None}
<div id="file_add_btns" out:_void in:_void={{easingFunc:circOut}}>
<button on:click={() => uploadType = UploadTypes.Files} >upload files...</button>
<button on:click={() => uploadType = UploadTypes.Clone} >clone url...</button>
</div>
{:else}
{#if uploadType == UploadTypes.Files}
<div id="file_add_btns" out:_void in:_void={{easingFunc:circOut}}>
<div class="fileUpload">
<p>click/tap to browse<br/>or drag files into this box</p>
<input type="file" multiple bind:files={files}>
</div>
</div>
{:else if uploadType == UploadTypes.Clone}
<form id="file_add_btns" out:_void in:_void={{easingFunc:circOut}} bind:this={cloneForm}>
<input placeholder="url" type="text" bind:this={cloneUrlTextbox}>
<input type="submit" value="add file" style:flex-basis="30%">
</form>
{/if}
{/if}
</div>