Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/drag and drop #39

Merged
merged 25 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
96a7c94
Disable preloading until investigated further.
EvieePy May 7, 2024
8f60454
Update some docs
EvieePy May 7, 2024
47d48db
Add "/pastes" for API compat.
EvieePy May 7, 2024
ee74403
Possible theme flash fix.
EvieePy May 8, 2024
7d65324
Invalidate theme JS cache.
EvieePy May 8, 2024
5a9a16b
Possible speedups; Removed a fetch call and unnecessary CSS/JS loading.
EvieePy May 8, 2024
68a4473
Sadly this needs to be defered.
EvieePy May 8, 2024
1ad5509
Remove all newlines from filenames
EvieePy May 8, 2024
70e8d58
Add line numbers to single line pastes.
EvieePy May 8, 2024
324aa32
Allow HTML comments.
EvieePy May 8, 2024
20881dc
Added automatic files; removed add file button
EvieePy May 8, 2024
32da2fa
Some CSS adjustments
EvieePy May 9, 2024
18cbb25
Add favicon
EvieePy May 10, 2024
7a524b7
Some CSS changes and fixes to new file behaviour.
EvieePy May 14, 2024
1ca1216
Revert light theme paste background to white
LostLuma May 14, 2024
b7dfb1f
Changes to README
EvieePy May 14, 2024
60c1438
Merge branch 'fix/new-files' of https://github.com/PythonistaGuild/My…
EvieePy May 14, 2024
2dd183e
More README changes.
EvieePy May 14, 2024
bf0563f
Merge branch 'htmx' into fix/new-files
EvieePy May 14, 2024
37758a9
Paste Header style changes
EvieePy May 14, 2024
a2b8b81
Add some annotations style changes
EvieePy May 14, 2024
42dc50b
Various CSS adjustments; better spacing, light theme improvements.
EvieePy May 16, 2024
f5fb454
Return eror if paste has invalid characters.
EvieePy May 16, 2024
f04b125
Add drag and drop files.
EvieePy May 16, 2024
809da2c
Merge branch 'main' into feature/drag-drop
EvieePy May 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import json
from typing import TYPE_CHECKING, Any

import asyncpg
import starlette_plus

from core import CONFIG
Expand Down Expand Up @@ -277,7 +278,11 @@ async def paste_post(self, request: starlette_plus.Request) -> starlette_plus.Re
data["expires"] = expiry
data["password"] = data.get("password")

paste = await self.app.database.create_paste(data=data)
try:
paste = await self.app.database.create_paste(data=data)
except asyncpg.CharacterNotInRepertoireError:
message: str = "File(s)/Filename(s) contain invalid characters or byte sequences."
return starlette_plus.JSONResponse({"error": message}, status_code=400)

to_return: dict[str, Any] = paste.serialize(exclude=["password", "password_ok"])
to_return.pop("files", None)
Expand Down
20 changes: 17 additions & 3 deletions views/htmx.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from typing import TYPE_CHECKING, Any, cast
from urllib.parse import unquote, urlsplit

import asyncpg
import bleach
import humanize
import starlette_plus
Expand Down Expand Up @@ -257,8 +258,14 @@ async def htmx_save(self, request: starlette_plus.Request) -> starlette_plus.Res

inner: dict[str, str | None] = {}

inner["filename"] = names[n] or None
inner["content"] = contents[n]
try:
inner["filename"] = names[n].encode("UTF-8").decode("UTF-8") or None
inner["content"] = contents[n].encode("UTF-8").decode("UTF-8")
except Exception:
return starlette_plus.HTMLResponse(
"""<span id="errorResponse">400: File/Filename contains invalid characters.</span>""",
headers=error_headers,
)
data["files"].append(inner)

if not data["files"]:
Expand All @@ -277,7 +284,14 @@ async def htmx_save(self, request: starlette_plus.Request) -> starlette_plus.Res
data["expires"] = None # TODO: Add this to Frontend...
data["password"] = password or None

paste = await self.app.database.create_paste(data=data)
try:
paste = await self.app.database.create_paste(data=data)
except asyncpg.CharacterNotInRepertoireError:
return starlette_plus.HTMLResponse(
"""<span id="errorResponse">400: File/Filename contains invalid characters.</span>""",
headers=error_headers,
)

to_return: dict[str, Any] = paste.serialize(exclude=["password", "password_ok"])
identifier: str = to_return["id"]

Expand Down
5 changes: 3 additions & 2 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<script src="/static/scripts/initialTheme.js?v=1"></script>
<script src="/static/scripts/themes.js?v=1" defer></script>
<script src="/static/scripts/files.js?v=3" defer></script>
<script src="/static/scripts/dragDrop.js?v=1"></script>

<!-- STYLESHEETS -->
<!-- <link rel="preload" href="static/styles/global.css" as="style" /> -->
Expand Down Expand Up @@ -54,7 +55,7 @@
<form id="content" class="content" hx-swap-oob="true" hx-swap="outerHTML"
hx-include="[name='fileName'] [name='fileContent'] [name='pastePassword']">
<div id="pastecontainer" class="pasteContainer">
<div class="pasteArea" id="__file0" data-position="0">
<div class="pasteArea" id="__file0" data-position="0" ondrop="fileDrop(event, this)" ondragover="fileDragOver(event, this)" ondragenter="fileDragStart(event, this)" ondragleave="fileDragEnd(event, this)">
<div class="pasteHeader">
<textarea name="fileName" class="filenameArea" rows="1" placeholder="Optional Filename..." maxlength="25"
spellcheck="false"></textarea>
Expand All @@ -64,7 +65,7 @@
Filling in all current files will create a new one (Up to 5)" maxlength="300000" onkeyup="addFile(0)"></textarea>
</div>

<div class="pasteArea smallArea" id="__file1" data-position="1">
<div class="pasteArea smallArea" id="__file1" data-position="1" ondrop="fileDrop(event, this)" ondragover="fileDragOver(event, this)" ondragenter="fileDragStart(event, this)" ondragleave="fileDragEnd(event, this)">
<div class="pasteHeader">
<textarea name="fileName" class="filenameArea" rows="1" placeholder="Optional Filename..." maxlength="25"
spellcheck="false"></textarea>
Expand Down
79 changes: 79 additions & 0 deletions web/static/scripts/dragDrop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
let dragCounter = 0;

function fileDragStart(event, target) {
event.preventDefault();
event.stopPropagation();

target.classList.add("dragging");
let pasteAreas = pasteContainer.getElementsByClassName("pasteArea");

for (let area of pasteAreas) {
if (area === target) {
continue;
}
area.classList.remove("dragging");
}

if (target.classList.contains("smallArea")) {
target.classList.remove("smallArea");
}

dragCounter++;
}

function fileDragOver(event, target) {
event.preventDefault();

if (event.dataTransfer.items === 0) { return }

let type = event.dataTransfer.items[0].type;
if (!type) { return }

if (!type.startsWith("text/") && !type.startsWith("application/")) {
target.classList.add("prevented");
event.dataTransfer.dropEffect = "none";
}
}

function fileDragEnd(event, target) {
event.preventDefault();
event.stopPropagation();

dragCounter--;
if (dragCounter !== 0) { return }

target.classList.remove("dragging");
target.classList.remove("prevented");

}

async function fileDrop(event, target) {
event.preventDefault();
event.stopPropagation();

dragCounter = 0;

target.classList.remove("prevented");
target.classList.remove("dragging");

const file = event.dataTransfer.files[0];
const textArea = target.querySelector(".fileContent");
const fileName = target.querySelector(".pasteHeader > .filenameArea");

// Allow double the server limit incase of editing...
if (file.size > 600000) {
return;
}

if (!file.type) { }
else if (!file.type.startsWith("text/") && !file.type.startsWith("application/")) {
return;
}

let name = file.name;
let content = await file.text();
fileName.value = name;
textArea.value = content;

addFile();
}
7 changes: 4 additions & 3 deletions web/static/scripts/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ function addFile(number) {
count += 1;

const pasteHTML = `
<div class="pasteArea smallArea" id="__file${count}" data-position="${count}">
<div class="pasteArea smallArea" id="__file${count}" data-position="${count}" ondrop="fileDrop(event, this)" ondragover="fileDragOver(event, this)" ondragenter="fileDragStart(event, this)" ondragleave="fileDragEnd(event, this)">

<div class="pasteHeader">
<textarea name="fileName" class="filenameArea" rows="1" placeholder="Optional Filename..." maxlength="25"></textarea>
<span class="deleteFile" onclick="deleteFile('__file${count}')">Delete File</span>
Expand Down Expand Up @@ -84,7 +85,8 @@ function deleteFile(identifier) {
if (!canContinue) { return }

const pasteHTML = `
<div class="pasteArea smallArea" id="__file${count}" data-position="${count}">
<div class="pasteArea smallArea" id="__file${count}" data-position="${count}" ondrop="fileDrop(event, this)" ondragover="fileDragOver(event, this)" ondragenter="fileDragStart(event, this)" ondragleave="fileDragEnd(event, this)">

<div class="pasteHeader">
<textarea name="fileName" class="filenameArea" rows="1" placeholder="Optional Filename..." maxlength="25"></textarea>
<span class="deleteFile" onclick="deleteFile('__file${count}')">Delete File</span>
Expand All @@ -93,5 +95,4 @@ function deleteFile(identifier) {
</div>`;

pasteContainer.insertAdjacentHTML("beforeend", pasteHTML);

}
95 changes: 78 additions & 17 deletions web/static/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@
--button--brightness: brightness(0.95);
--button--brightness-hover: brightness(0.85);
--button--brightness-active: brightness(0.95);

.deleteFile {
background-color: var(--color-background);
filter: brightness(0.99);
}

.deleteFile:hover {
cursor: pointer;
filter: brightness(0.97);
}

.deleteFile:active {
filter: brightness(0.99);
}

.savePaste {
background-color: var(--color-background);
filter: brightness(0.99);
}

.savePaste:hover {
cursor: pointer;
filter: brightness(0.97);
}

.savePaste:active {
filter: brightness(0.99);
}
}

[data-theme="dark"] {
Expand Down Expand Up @@ -138,32 +166,60 @@ a {
height: 100%;
background-color: var(--color-background--pastes);
border-radius: 0.25rem;
border: 1px solid transparent;

}

.dragging {
opacity: 0.5;
border: 1px dashed var(--color-accent);
position: relative;
cursor: copy;
background-color: var(--color-background--pastes);
}

.dragging::after {
pointer-events: none;
content: "Drop File to Paste...";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

.prevented {
border: 1px dashed var(--color-error);
cursor: no-drop;
}

.prevented::after {
content: "File is not allowed...";
color: var(--color-error);
font-weight: 700;
font-size: larger;
}

.pasteHeader {
background-color: var(--color-second-paste);
border-radius: 0.25rem 0.2rem 0 0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0.5rem 1rem;
width: 100%;
}

.pasteContainer {
display: flex;
flex-direction: column;
gap: 2rem;
gap: 1rem;
flex-grow: 1;
width: 100%;
max-width: 100%;
border-radius: 0.25rem;
}

.pasteHeader {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 1rem;
width: 100%;
}

input[type="password"] {
background-color: var(--color-background--pastes) !important;
color: var(--color-foreground) !important;
Expand Down Expand Up @@ -212,13 +268,12 @@ input[type="password"] {

.smallArea {
flex-grow: 0;
height: 7rem;
margin: 0 0 6rem 0;
height: 9rem;
}

.smallArea>textarea {
min-height: 7rem;
height: 7rem;
min-height: 4rem;
height: 4rem;
}

textarea::-webkit-resizer {
Expand Down Expand Up @@ -252,7 +307,7 @@ textarea {

.deleteFile {
display: flex;
padding: 1rem;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
background-color: var(--color-background--header);
filter: brightness(0.8);
Expand Down Expand Up @@ -318,7 +373,7 @@ textarea {

.savePaste {
display: flex;
padding: 1rem 4rem;
padding: 0.75rem 4rem;
border-radius: 0.25rem;
background-color: var(--color-background--button);
filter: var(--button--brightness);
Expand Down Expand Up @@ -491,7 +546,12 @@ textarea {
}

.savePaste {
padding: 1rem;
padding: 0.75rem;
font-size: 0.8em;
}

.deleteFile {
padding: 0.5rem 1rem;
font-size: 0.8em;
}

Expand Down Expand Up @@ -524,6 +584,7 @@ textarea {
.header {
padding: 1rem 0.5rem;
}

.pasteArea>textarea {
font-size: 0.8em;
}
Expand Down
Loading