mirror of https://github.com/raftario/filite.git
Redesign UI
This commit is contained in:
parent
efefe24fbe
commit
d6bc31d8ca
|
@ -17,8 +17,6 @@ A simple, light and standalone pastebin, URL shortener and file-sharing service
|
|||
|
||||
* A tracking tool. No stats are stored to increase speed and reduce resource usage, if this is what you are looking for this tool is not for you.
|
||||
|
||||
![Screenshot of filite UI](screenshot.png)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Get the binary either from the [releases page](https://github.com/raftario/filite/releases) or [using Cargo](https://crates.io/crates/filite)
|
||||
|
@ -59,6 +57,7 @@ pool_size = 4
|
|||
files_dir = "files"
|
||||
# Max allowed size for file uploads, in bytes
|
||||
max_filesize = 10000000
|
||||
|
||||
# Highlight.js configuration
|
||||
[highlight]
|
||||
# Theme to use
|
||||
|
|
|
@ -3,77 +3,449 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/spectre.css/0.5.8/spectre.min.css"
|
||||
integrity="sha256-J24PZiunX9uL1Sdmbe6YT9kNuV5lfVxj3A6Kij5UP6k="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/spectre.css/0.5.8/spectre-icons.min.css"
|
||||
integrity="sha256-LxdDS9G94ArUz2UYVPo5FhSeD4owwcBFAQv2Nl1dNUU="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<style>
|
||||
{{ css }}
|
||||
div[id$="-form"]:not(.active) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<title>filite</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<div id="files-tab" role="tab"><span>Files</span></div>
|
||||
<div id="links-tab" class="active" role="tab">
|
||||
<span>Links</span>
|
||||
</div>
|
||||
<div id="texts-tab" role="tab"><span>Texts</span></div>
|
||||
</nav>
|
||||
<main>
|
||||
<div id="files-form">
|
||||
<label for="files-url">URL</label>
|
||||
<input
|
||||
id="files-url"
|
||||
class="mb1"
|
||||
type="text"
|
||||
placeholder="a1b2c3"
|
||||
required
|
||||
/>
|
||||
<label for="files-file">File</label>
|
||||
<input id="files-file" type="file" hidden />
|
||||
<div class="mb2">
|
||||
<button id="files-browse">Browse</button
|
||||
><input id="files-value" type="text" required />
|
||||
<nav class="container mb-2 pb-2">
|
||||
<div class="columns">
|
||||
<div
|
||||
class="column col-sm-12 col-md-10 col-lg-8 col-6 col-mx-auto"
|
||||
>
|
||||
<ul class="tab tab-block">
|
||||
<li id="files-tab" class="tab-item">
|
||||
<a href="#">Files</a>
|
||||
</li>
|
||||
<li id="links-tab" class="tab-item active">
|
||||
<a href="#">Links</a>
|
||||
</li>
|
||||
<li id="texts-tab" class="tab-item">
|
||||
<a href="#">Texts</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button id="files-submit" disabled>Submit</button>
|
||||
</div>
|
||||
<div id="links-form" class="active">
|
||||
<label for="links-url">URL</label>
|
||||
<input
|
||||
id="links-url"
|
||||
class="mb1"
|
||||
type="text"
|
||||
placeholder="a1b2c3"
|
||||
required
|
||||
/>
|
||||
<label for="links-forward">Forward</label>
|
||||
<input
|
||||
id="links-forward"
|
||||
class="mb2"
|
||||
type="url"
|
||||
placeholder="http://example.com/"
|
||||
required
|
||||
/>
|
||||
<button id="links-submit" disabled>Submit</button>
|
||||
</div>
|
||||
<div id="texts-form">
|
||||
<label for="texts-url">URL</label>
|
||||
<input
|
||||
id="texts-url"
|
||||
class="mb1"
|
||||
type="text"
|
||||
placeholder="a1b2c3"
|
||||
required
|
||||
/>
|
||||
<label for="texts-contents">Contents</label>
|
||||
<textarea
|
||||
id="texts-contents"
|
||||
class="mb2"
|
||||
placeholder="Hello, World!"
|
||||
required
|
||||
></textarea>
|
||||
<button id="texts-submit" disabled>Submit</button>
|
||||
</nav>
|
||||
<main class="container mt-2 pt-2">
|
||||
<div class="columns">
|
||||
<div
|
||||
id="files-form"
|
||||
class="column col-sm-12 col-md-10 col-lg-8 col-6 col-mx-auto"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="files-url">URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">/f/</span>
|
||||
<input
|
||||
id="files-url"
|
||||
class="form-input"
|
||||
type="text"
|
||||
placeholder="a1b2c3"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary input-group-btn"
|
||||
id="files-submit"
|
||||
disabled
|
||||
>
|
||||
<i class="icon icon-upload"></i>
|
||||
</button>
|
||||
</div>
|
||||
<p class="form-input-hint">Press space to randomize</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="files-file">File</label>
|
||||
<input
|
||||
class="form-input"
|
||||
id="files-file"
|
||||
type="file"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="links-form"
|
||||
class="column col-sm-12 col-md-10 col-lg-8 col-6 col-mx-auto active"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="links-url">URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">/l/</span>
|
||||
<input
|
||||
id="links-url"
|
||||
class="form-input"
|
||||
type="text"
|
||||
placeholder="a1b2c3"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary input-group-btn"
|
||||
id="links-submit"
|
||||
disabled
|
||||
>
|
||||
<i class="icon icon-upload"></i>
|
||||
</button>
|
||||
</div>
|
||||
<p class="form-input-hint">Press space to randomize</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="links-forward"
|
||||
>Forward</label
|
||||
>
|
||||
<input
|
||||
id="links-forward"
|
||||
class="form-input"
|
||||
type="url"
|
||||
placeholder="http://example.com/"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="texts-form"
|
||||
class="column col-sm-12 col-md-10 col-lg-8 col-6 col-mx-auto"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="texts-url">URL</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">/t/</span>
|
||||
<input
|
||||
id="texts-url"
|
||||
class="form-input"
|
||||
type="text"
|
||||
placeholder="a1b2c3"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary input-group-btn"
|
||||
id="texts-submit"
|
||||
disabled
|
||||
>
|
||||
<i class="icon icon-upload"></i>
|
||||
</button>
|
||||
</div>
|
||||
<p class="form-input-hint">Press space to randomize</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="texts-contents"
|
||||
>Contents</label
|
||||
>
|
||||
<textarea
|
||||
id="texts-contents"
|
||||
class="form-input"
|
||||
placeholder="Hello, World!"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-switch">
|
||||
<input id="texts-highlight" type="checkbox" />
|
||||
<i class="form-icon"></i> Syntax highlighting
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div id="modal" class="modal">
|
||||
<a id="modal-bg" href="#" class="modal-overlay"></a>
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title h6">Success</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="content">
|
||||
<div class="form-group">
|
||||
<div class="has-icon-right">
|
||||
<input
|
||||
id="modal-input"
|
||||
type="url"
|
||||
class="form-input"
|
||||
/>
|
||||
<i class="form-icon icon icon-copy"></i>
|
||||
</div>
|
||||
<p class="form-input-hint" id="modal-hint">
|
||||
Click to copy to clipboard
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
{{ js }}
|
||||
const tabs = {
|
||||
files: [
|
||||
document.querySelector("#files-tab"),
|
||||
document.querySelector("#files-form"),
|
||||
],
|
||||
links: [
|
||||
document.querySelector("#links-tab"),
|
||||
document.querySelector("#links-form"),
|
||||
],
|
||||
texts: [
|
||||
document.querySelector("#texts-tab"),
|
||||
document.querySelector("#texts-form"),
|
||||
],
|
||||
};
|
||||
|
||||
const inputs = {
|
||||
files: [
|
||||
document.querySelector("#files-url"),
|
||||
document.querySelector("#files-file"),
|
||||
document.querySelector("#files-submit"),
|
||||
],
|
||||
links: [
|
||||
document.querySelector("#links-url"),
|
||||
document.querySelector("#links-forward"),
|
||||
document.querySelector("#links-submit"),
|
||||
],
|
||||
texts: [
|
||||
document.querySelector("#texts-url"),
|
||||
document.querySelector("#texts-contents"),
|
||||
document.querySelector("#texts-highlight"),
|
||||
document.querySelector("#texts-submit"),
|
||||
],
|
||||
};
|
||||
|
||||
const used = {
|
||||
files: [],
|
||||
links: [],
|
||||
texts: [],
|
||||
};
|
||||
|
||||
let baseUrl = `${location.protocol}//${location.host}${location.pathname}`;
|
||||
if (!baseUrl.endsWith("/")) {
|
||||
baseUrl += "/";
|
||||
}
|
||||
|
||||
const modal = {
|
||||
self: document.querySelector("#modal"),
|
||||
input: document.querySelector("#modal-input"),
|
||||
bg: document.querySelector("#modal-bg"),
|
||||
hint: document.querySelector("#modal-hint"),
|
||||
};
|
||||
const openModal = (text) => {
|
||||
modal.input.value = text;
|
||||
modal.hint.innerText = "Click to copy to clipboard";
|
||||
modal.self.classList.add("active");
|
||||
};
|
||||
const closeModal = () => {
|
||||
modal.hint.innerText = "Copied to clipboard";
|
||||
setTimeout(() => {
|
||||
modal.self.classList.remove("active");
|
||||
modal.input.value = "";
|
||||
}, 1000);
|
||||
};
|
||||
modal.input.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
modal.input.select();
|
||||
document.execCommand("copy");
|
||||
closeModal();
|
||||
};
|
||||
modal.bg.onclick = closeModal;
|
||||
|
||||
const fetchUsed = () => {
|
||||
fetch(`${baseUrl}f`)
|
||||
.then((response) => response.json())
|
||||
.then((json) => (used.files = json));
|
||||
fetch(`${baseUrl}l`)
|
||||
.then((response) => response.json())
|
||||
.then((json) => (used.links = json));
|
||||
fetch(`${baseUrl}t`)
|
||||
.then((response) => response.json())
|
||||
.then((json) => (used.texts = json));
|
||||
};
|
||||
fetchUsed();
|
||||
|
||||
const randomUrl = () => {
|
||||
return Math.floor(Math.random() * 2147483647).toString(36);
|
||||
};
|
||||
|
||||
for (const group in tabs) {
|
||||
tabs[group][0].onclick = () => {
|
||||
const active = document.querySelectorAll(".active");
|
||||
for (const el of active) {
|
||||
el.classList.remove("active");
|
||||
}
|
||||
for (const el of tabs[group]) {
|
||||
el.classList.add("active");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for (const group in inputs) {
|
||||
const submitButton = inputs[group][inputs[group].length - 1];
|
||||
|
||||
const urlInput = inputs[group][0];
|
||||
urlInput.addEventListener("input", (e) => {
|
||||
if (urlInput.value[urlInput.value.length - 1] === " ") {
|
||||
urlInput.value = randomUrl();
|
||||
checkValidity();
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
urlInput.value = urlInput.value
|
||||
.replace(/[^0-9A-Za-z]/g, "")
|
||||
.toLowerCase();
|
||||
if (parseInt(urlInput.value, 36) > 2147483647) {
|
||||
urlInput.setCustomValidity(
|
||||
"Base 36 integer below or equal to zik0zj"
|
||||
);
|
||||
} else {
|
||||
urlInput.setCustomValidity("");
|
||||
}
|
||||
});
|
||||
|
||||
const checkValidity = () => {
|
||||
if (
|
||||
used[group].some(
|
||||
(x) => x.id === parseInt(urlInput.value, 36)
|
||||
)
|
||||
) {
|
||||
urlInput.classList.add("conflict");
|
||||
} else {
|
||||
urlInput.classList.remove("conflict");
|
||||
}
|
||||
submitButton.disabled = inputs[group].some(
|
||||
(input) =>
|
||||
input.validity != undefined && !input.validity.valid
|
||||
);
|
||||
};
|
||||
checkValidity();
|
||||
|
||||
for (const input of inputs[group].filter(
|
||||
(input) =>
|
||||
input instanceof HTMLInputElement ||
|
||||
input instanceof HTMLTextAreaElement
|
||||
)) {
|
||||
input.addEventListener("input", () => checkValidity());
|
||||
input.addEventListener("change", () => checkValidity());
|
||||
}
|
||||
|
||||
const clearInputs = () => {
|
||||
for (const input of inputs[group].filter(
|
||||
(input) =>
|
||||
input instanceof HTMLInputElement ||
|
||||
input instanceof HTMLTextAreaElement
|
||||
)) {
|
||||
input.value = "";
|
||||
}
|
||||
submitButton.disabled = true;
|
||||
};
|
||||
|
||||
if (group === "files") {
|
||||
submitButton.addEventListener("click", () => {
|
||||
const filesFileInput = inputs.files[1];
|
||||
const file = filesFileInput.files[0];
|
||||
|
||||
if (!file) {
|
||||
alert(new Error("No file selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = () => {
|
||||
const id = urlInput.value;
|
||||
const url = `${baseUrl}f/${id}`;
|
||||
|
||||
const base64 = btoa(fileReader.result);
|
||||
const filename = file.name;
|
||||
let status;
|
||||
fetch(url, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ base64, filename }),
|
||||
})
|
||||
.then((response) => {
|
||||
status = response.status;
|
||||
return response.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (status !== 201) {
|
||||
throw new Error(text);
|
||||
} else {
|
||||
openModal(url);
|
||||
clearInputs();
|
||||
fetchUsed();
|
||||
}
|
||||
})
|
||||
.catch((error) => alert(error));
|
||||
};
|
||||
fileReader.readAsBinaryString(file);
|
||||
});
|
||||
} else if (group === "links") {
|
||||
submitButton.addEventListener("click", () => {
|
||||
const id = urlInput.value;
|
||||
const forward = inputs.links[1].value;
|
||||
|
||||
const url = `${baseUrl}l/${id}`;
|
||||
let status;
|
||||
fetch(url, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ forward }),
|
||||
})
|
||||
.then((response) => {
|
||||
status = response.status;
|
||||
return response.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (status !== 201) {
|
||||
throw new Error(text);
|
||||
} else {
|
||||
openModal(url);
|
||||
clearInputs();
|
||||
fetchUsed();
|
||||
}
|
||||
})
|
||||
.catch((error) => alert(error));
|
||||
});
|
||||
} else if (group === "texts") {
|
||||
submitButton.addEventListener("click", () => {
|
||||
const id = urlInput.value;
|
||||
const contents = inputs.texts[1].value;
|
||||
const highlight = inputs.texts[2].checked;
|
||||
|
||||
const url = `${baseUrl}t/${id}`;
|
||||
let status;
|
||||
fetch(url, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ contents, highlight }),
|
||||
})
|
||||
.then((response) => {
|
||||
status = response.status;
|
||||
return response.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (status !== 201) {
|
||||
throw new Error(text);
|
||||
} else {
|
||||
openModal(url);
|
||||
clearInputs();
|
||||
fetchUsed();
|
||||
}
|
||||
})
|
||||
.catch((error) => alert(error));
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,252 +0,0 @@
|
|||
const tabs = {
|
||||
files: [
|
||||
document.querySelector("#files-tab"),
|
||||
document.querySelector("#files-form"),
|
||||
],
|
||||
links: [
|
||||
document.querySelector("#links-tab"),
|
||||
document.querySelector("#links-form"),
|
||||
],
|
||||
texts: [
|
||||
document.querySelector("#texts-tab"),
|
||||
document.querySelector("#texts-form"),
|
||||
],
|
||||
};
|
||||
|
||||
const inputs = {
|
||||
files: [
|
||||
document.querySelector("#files-url"),
|
||||
document.querySelector("#files-file"),
|
||||
document.querySelector("#files-submit"),
|
||||
],
|
||||
links: [
|
||||
document.querySelector("#links-url"),
|
||||
document.querySelector("#links-forward"),
|
||||
document.querySelector("#links-submit"),
|
||||
],
|
||||
texts: [
|
||||
document.querySelector("#texts-url"),
|
||||
document.querySelector("#texts-contents"),
|
||||
document.querySelector("#texts-submit"),
|
||||
],
|
||||
};
|
||||
|
||||
const used = {
|
||||
files: [],
|
||||
links: [],
|
||||
texts: [],
|
||||
};
|
||||
|
||||
let baseUrl = `${location.protocol}//${location.host}${location.pathname}`;
|
||||
if (!baseUrl.endsWith("/")) {
|
||||
baseUrl += "/";
|
||||
}
|
||||
|
||||
const fetchUsed = () => {
|
||||
fetch(`${baseUrl}f`)
|
||||
.then((response) => response.json())
|
||||
.then((json) => (used.files = json));
|
||||
fetch(`${baseUrl}l`)
|
||||
.then((response) => response.json())
|
||||
.then((json) => (used.links = json));
|
||||
fetch(`${baseUrl}t`)
|
||||
.then((response) => response.json())
|
||||
.then((json) => (used.texts = json));
|
||||
};
|
||||
fetchUsed();
|
||||
|
||||
const randomUrl = () => {
|
||||
return Math.floor(Math.random() * 2147483647).toString(36);
|
||||
};
|
||||
|
||||
const updateClipboard = (data) => {
|
||||
navigator.permissions
|
||||
.query({ name: "clipboard-write" })
|
||||
.then((result) => {
|
||||
if (result.state === "denied") {
|
||||
throw new Error();
|
||||
}
|
||||
})
|
||||
.then(() => navigator.clipboard.writeText(data))
|
||||
.then(() => alert("URL copied to clipboard"))
|
||||
.catch(() => alert(data));
|
||||
};
|
||||
|
||||
for (const group in tabs) {
|
||||
tabs[group][0].onclick = () => {
|
||||
const active = document.querySelectorAll(".active");
|
||||
for (const el of active) {
|
||||
el.classList.remove("active");
|
||||
}
|
||||
for (const el of tabs[group]) {
|
||||
el.classList.add("active");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for (const group in inputs) {
|
||||
const submitButton = inputs[group][inputs[group].length - 1];
|
||||
|
||||
const urlInput = inputs[group][0];
|
||||
urlInput.addEventListener("input", (e) => {
|
||||
if (urlInput.value[urlInput.value.length - 1] === " ") {
|
||||
urlInput.value = randomUrl();
|
||||
checkValidity();
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
urlInput.value = urlInput.value
|
||||
.replace(/[^0-9A-Za-z]/g, "")
|
||||
.toLowerCase();
|
||||
if (parseInt(urlInput.value, 36) > 2147483647) {
|
||||
urlInput.setCustomValidity(
|
||||
"Base 36 integer below or equal to zik0zj"
|
||||
);
|
||||
} else {
|
||||
urlInput.setCustomValidity("");
|
||||
}
|
||||
});
|
||||
|
||||
const checkValidity = () => {
|
||||
if (used[group].some((x) => x.id === parseInt(urlInput.value, 36))) {
|
||||
urlInput.classList.add("conflict");
|
||||
} else {
|
||||
urlInput.classList.remove("conflict");
|
||||
}
|
||||
submitButton.disabled = inputs[group].some(
|
||||
(input) => input.validity != undefined && !input.validity.valid
|
||||
);
|
||||
};
|
||||
|
||||
for (const input of inputs[group].filter(
|
||||
(input) =>
|
||||
input instanceof HTMLInputElement ||
|
||||
input instanceof HTMLTextAreaElement
|
||||
)) {
|
||||
input.addEventListener("input", () => checkValidity());
|
||||
input.addEventListener("change", () => checkValidity());
|
||||
}
|
||||
|
||||
const clearInputs = () => {
|
||||
for (const input of inputs[group].filter(
|
||||
(input) =>
|
||||
input instanceof HTMLInputElement ||
|
||||
input instanceof HTMLTextAreaElement
|
||||
)) {
|
||||
input.value = "";
|
||||
}
|
||||
submitButton.disabled = true;
|
||||
};
|
||||
|
||||
if (group === "files") {
|
||||
const filesFileInput = inputs.files[1];
|
||||
const filesBrowseButton = document.querySelector("#files-browse");
|
||||
const filesValueInput = document.querySelector("#files-value");
|
||||
filesFileInput.addEventListener("change", () => {
|
||||
filesValueInput.value = filesFileInput.files[0].name || "";
|
||||
});
|
||||
filesBrowseButton.onclick = () => {
|
||||
filesFileInput.click();
|
||||
};
|
||||
filesValueInput.onfocus = (e) => {
|
||||
e.preventDefault();
|
||||
filesValueInput.blur();
|
||||
return false;
|
||||
};
|
||||
|
||||
submitButton.addEventListener("click", () => {
|
||||
const file = filesFileInput.files[0];
|
||||
|
||||
if (!file) {
|
||||
alert(new Error("No file selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = () => {
|
||||
const id = urlInput.value;
|
||||
const url = `${baseUrl}f/${id}`;
|
||||
|
||||
const base64 = btoa(fileReader.result);
|
||||
const filename = file.name;
|
||||
let status;
|
||||
fetch(url, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ base64, filename }),
|
||||
})
|
||||
.then((response) => {
|
||||
status = response.status;
|
||||
return response.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (status !== 201) {
|
||||
throw new Error(text);
|
||||
} else {
|
||||
updateClipboard(url);
|
||||
clearInputs();
|
||||
filesValueInput.value = "";
|
||||
fetchUsed();
|
||||
}
|
||||
})
|
||||
.catch((error) => alert(error));
|
||||
};
|
||||
fileReader.readAsBinaryString(file);
|
||||
});
|
||||
} else if (group === "links") {
|
||||
submitButton.addEventListener("click", () => {
|
||||
const id = urlInput.value;
|
||||
const forward = inputs.links[1].value;
|
||||
|
||||
const url = `${baseUrl}l/${id}`;
|
||||
let status;
|
||||
fetch(url, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ forward }),
|
||||
})
|
||||
.then((response) => {
|
||||
status = response.status;
|
||||
return response.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (status !== 201) {
|
||||
throw new Error(text);
|
||||
} else {
|
||||
updateClipboard(url);
|
||||
clearInputs();
|
||||
fetchUsed();
|
||||
}
|
||||
})
|
||||
.catch((error) => alert(error));
|
||||
});
|
||||
} else if (group === "texts") {
|
||||
submitButton.addEventListener("click", () => {
|
||||
const id = urlInput.value;
|
||||
const contents = inputs.texts[1].value;
|
||||
|
||||
const url = `${baseUrl}t/${id}`;
|
||||
let status;
|
||||
fetch(url, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ contents, highlight: true }),
|
||||
})
|
||||
.then((response) => {
|
||||
status = response.status;
|
||||
return response.text();
|
||||
})
|
||||
.then((text) => {
|
||||
if (status !== 201) {
|
||||
throw new Error(text);
|
||||
} else {
|
||||
updateClipboard(url);
|
||||
clearInputs();
|
||||
fetchUsed();
|
||||
}
|
||||
})
|
||||
.catch((error) => alert(error));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
html,
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template:
|
||||
[r1s] "nav" 2rem [r1e]
|
||||
[r2s] "main" auto [r2e]
|
||||
/ auto;
|
||||
background-color: #002b36;
|
||||
color: #839496;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #586e75;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
grid-area: nav;
|
||||
background-color: #073642;
|
||||
}
|
||||
|
||||
nav div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
font-size: 1rem;
|
||||
border-bottom: 0.0625rem solid #586e75;
|
||||
}
|
||||
|
||||
nav div:hover {
|
||||
background-color: #002b36;
|
||||
color: #93a1a1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
nav div.active {
|
||||
background-color: #002b36;
|
||||
color: #93a1a1;
|
||||
border-top: 0.0625rem solid #586e75;
|
||||
border-right: 0.0625rem solid #586e75;
|
||||
border-left: 0.0625rem solid #586e75;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-area: main;
|
||||
}
|
||||
|
||||
main div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
main div.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
main div label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
padding-left: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
main div input[type="text"],
|
||||
main div input[type="url"] {
|
||||
display: block;
|
||||
padding: 0.5rem 1rem;
|
||||
width: calc(75vw - 2rem);
|
||||
background-color: #073642;
|
||||
color: #839496;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
main div textarea {
|
||||
display: block;
|
||||
padding: 0.5rem 1rem;
|
||||
width: calc(75vw - 2rem);
|
||||
height: 25vh;
|
||||
background-color: #073642;
|
||||
color: #839496;
|
||||
font-family: monospace;
|
||||
font-size: 0.5rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
main div input[type="text"]:invalid,
|
||||
main div input[type="url"]:invalid,
|
||||
main div textarea:invalid {
|
||||
border: 0.0625rem solid #dc322f;
|
||||
}
|
||||
|
||||
.conflict {
|
||||
border: 0.0625rem solid #b58900 !important;
|
||||
}
|
||||
|
||||
main div button {
|
||||
display: block;
|
||||
padding: 0.5rem 1rem;
|
||||
width: 75vw;
|
||||
background-color: #073642;
|
||||
color: #839496;
|
||||
font-size: 1rem;
|
||||
border: 0.0625rem solid #586e75;
|
||||
}
|
||||
|
||||
main div button:hover,
|
||||
main div button:focus {
|
||||
background-color: #002b36;
|
||||
color: #93a1a1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
main div button:disabled {
|
||||
background-color: #073642;
|
||||
color: #586e75;
|
||||
border: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
main div div {
|
||||
display: flex;
|
||||
width: 75vw;
|
||||
}
|
||||
|
||||
main div div button {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
main div div input {
|
||||
flex: 1;
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #073642;
|
||||
}
|
||||
|
||||
.mb1 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.mb2 {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
@media screen and (max-height: 720px) {
|
||||
.mb1 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mb2 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 33 KiB |
|
@ -219,39 +219,17 @@ macro_rules! random_id {
|
|||
|
||||
#[cfg(feature = "dev")]
|
||||
lazy_static! {
|
||||
static ref RESOURCES_DIR: PathBuf = {
|
||||
let mut ressources_dir = PathBuf::new();
|
||||
ressources_dir.push(get_env!("CARGO_MANIFEST_DIR"));
|
||||
ressources_dir.push("resources");
|
||||
ressources_dir
|
||||
};
|
||||
static ref HTML_PATH: PathBuf = {
|
||||
let mut html_path = RESOURCES_DIR.clone();
|
||||
html_path.push("index.html");
|
||||
html_path
|
||||
};
|
||||
static ref JS_PATH: PathBuf = {
|
||||
let mut js_path = RESOURCES_DIR.clone();
|
||||
js_path.push("script.js");
|
||||
js_path
|
||||
};
|
||||
static ref CSS_PATH: PathBuf = {
|
||||
let mut css_path = RESOURCES_DIR.clone();
|
||||
css_path.push("style.css");
|
||||
css_path
|
||||
static ref INDEX_PATH: PathBuf = {
|
||||
let mut index_path = PathBuf::new();
|
||||
index_path.push(get_env!("CARGO_MANIFEST_DIR"));
|
||||
index_path.push("resources");
|
||||
index_path.push("index.html");
|
||||
index_path
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "dev"))]
|
||||
lazy_static! {
|
||||
static ref INDEX_CONTENTS: String = {
|
||||
let html = include_str!("../resources/index.html");
|
||||
let js = include_str!("../resources/script.js");
|
||||
let css = include_str!("../resources/style.css");
|
||||
|
||||
html.replace("{{ js }}", js).replace("{{ css }}", css)
|
||||
};
|
||||
}
|
||||
static INDEX_CONTENTS: &str = include_str!("../resources/index.html");
|
||||
|
||||
static HIGHLIGHT_CONTENTS: &str = include_str!("../resources/highlight.html");
|
||||
const HIGHLIGHT_LANGUAGE: &str = r#"<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/languages/{{ language }}.min.js"></script>"#;
|
||||
|
@ -269,15 +247,11 @@ pub async fn index(
|
|||
let contents = {
|
||||
#[cfg(feature = "dev")]
|
||||
{
|
||||
let html = fs::read_to_string(&*HTML_PATH).expect("Can't read index.html");
|
||||
let js = fs::read_to_string(&*JS_PATH).expect("Can't read script.js");
|
||||
let css = fs::read_to_string(&*CSS_PATH).expect("Can't read style.css");
|
||||
|
||||
html.replace("{{ js }}", &js).replace("{{ css }}", &css)
|
||||
fs::read_to_string(&*INDEX_PATH).expect("Can't read index.html")
|
||||
}
|
||||
#[cfg(not(feature = "dev"))]
|
||||
{
|
||||
(&*INDEX_CONTENTS).clone()
|
||||
INDEX_CONTENTS.to_owned()
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -670,7 +644,7 @@ pub mod texts {
|
|||
)
|
||||
}
|
||||
|
||||
/// PUT a new text entry
|
||||
/// POST a new text entry
|
||||
pub async fn post(
|
||||
request: HttpRequest,
|
||||
body: web::Json<PutPostText>,
|
||||
|
|
Loading…
Reference in New Issue