full release edit capabilities oh my goodness gracious

Signed-off-by: ari melody <ari@arimelody.me>
This commit is contained in:
ari melody 2024-08-31 01:30:30 +01:00
parent 34cddcfdb2
commit 604e2a4a7c
25 changed files with 1043 additions and 202 deletions

View file

@ -86,7 +86,7 @@ a img {
}
.card {
margin-bottom: 2em;
margin-bottom: 1em;
}
.card h2 {

View file

@ -0,0 +1,68 @@
/**
* Creates a "magic" reorderable list from `container`.
* This function is absolute magic and I love it
*
* Example:
* ```html
* <ul id="list">
* <li>Item 1</li>
* <li>Item 2</li>
* <li>Item 3</li>
* </ul>
* ```
* ```js
* // javascript
* makeMagicList(document.getElementById("list"), "li");
* ```
*
* @param {HTMLElement} container The parent container to use as a list.
* @param {string} itemSelector The selector name of list item elements.
* @param {Function} callback A function to call after each reordering.
*/
export function makeMagicList(container, itemSelector, callback) {
if (!container)
throw new Error("container not provided");
if (!itemSelector)
throw new Error("itemSelector not provided");
container.querySelectorAll(itemSelector).forEach(item => {
item.draggable = true;
item.addEventListener("dragstart", () => { item.classList.add("moving") });
item.addEventListener("dragend", () => { item.classList.remove("moving") });
item.querySelectorAll("input").forEach(el => {
el.addEventListener("mousedown", () => { item.draggable = false });
el.addEventListener("mouseup", () => { item.draggable = true });
el.addEventListener("dragstart", e => { e.stopPropagation() });
});
});
var lastCursorY;
container.addEventListener("dragover", event => {
const dragging = container.querySelector(itemSelector + ".moving");
if (!dragging) return;
let cursorY = event.touches ? event.touches[0].clientY : event.clientY;
// don't bother processing if we haven't moved
if (lastCursorY === cursorY) return
lastCursorY = cursorY;
// get the element positioned ahead of the cursor
const notMoving = [...container.querySelectorAll(itemSelector + ":not(.moving)")];
const afterElement = notMoving.reduce((previous, current) => {
const box = current.getBoundingClientRect();
const offset = cursorY - box.top - box.height / 2;
if (offset < 0 && offset > previous.offset)
return { offset: offset, element: current };
return previous;
}, { offset: Number.NEGATIVE_INFINITY }).element;
if (afterElement) {
container.insertBefore(dragging, afterElement);
} else {
container.appendChild(dragging);
}
if (callback) callback();
});
}

View file

@ -1,3 +1,9 @@
input[type="text"] {
font-size: inherit;
font-family: inherit;
color: inherit;
}
#release {
margin-bottom: 1em;
padding: 1.5em;
@ -12,10 +18,6 @@
.release-artwork {
width: 200px;
display: flex;
justify-content: center;
align-items: start;
}
.release-artwork img {
@ -28,6 +30,7 @@
}
.release-info {
width: 0;
margin: 0;
flex-grow: 1;
display: flex;
@ -38,6 +41,28 @@
margin: 0;
}
#title {
width: 100%;
margin: 0 -.2em;
padding: 0 .2em;
font-weight: bold;
border-radius: 4px;
border: 1px solid transparent;
background: transparent;
outline: none;
}
#title:hover {
background: #ffffff;
border-color: #80808080;
}
#title:active,
#title:focus {
background: #ffffff;
border-color: #808080;
}
.release-title small {
opacity: .75;
}
@ -71,6 +96,7 @@
border: none;
background: none;
outline: none;
resize: vertical;
}
.release-info table td:has(select),
.release-info table td:has(input),
@ -126,6 +152,10 @@ button[disabled] {
cursor: not-allowed !important;
}
a.delete {
color: #d22828;
}
.release-actions {
margin-top: auto;
display: flex;
@ -134,90 +164,6 @@ button[disabled] {
justify-content: right;
}
.card.credits .credit {
margin-bottom: .5em;
padding: .5em;
display: flex;
flex-direction: row;
align-items: center;
gap: 1em;
border-radius: .5em;
background: #f8f8f8f8;
border: 1px solid #808080;
}
.card.credits .credit .artist-avatar {
border-radius: .5em;
}
.card.credits .credit .artist-name {
font-weight: bold;
}
.card.credits .credit .artist-role small {
font-size: inherit;
opacity: .66;
}
.track {
margin-bottom: 1em;
padding: 1em;
display: flex;
flex-direction: column;
gap: .5em;
border-radius: .5em;
background: #f8f8f8f8;
border: 1px solid #808080;
}
.card h2.track-title {
margin: 0;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.card-title a.button {
text-decoration: none;
}
.track-id {
width: fit-content;
font-family: "Monaspace Argon", monospace;
font-size: .8em;
font-style: italic;
line-height: 1em;
user-select: all;
-webkit-user-select: all;
}
.track-album {
margin-left: auto;
font-style: italic;
font-size: .75em;
opacity: .5;
}
.track-album.empty {
color: #ff2020;
opacity: 1;
}
.track-description {
font-style: italic;
}
.track-lyrics {
max-height: 10em;
overflow-y: scroll;
}
.track .empty {
opacity: 0.75;
}
dialog {
width: min(720px, calc(100% - 2em));
padding: 2em;
@ -245,13 +191,15 @@ dialog div.dialog-actions {
gap: .5em;
}
dialog#editcredits ul {
margin: 0;
padding: 0;
list-style: none;
.card-title a.button {
text-decoration: none;
}
dialog#editcredits .credit>div {
/*
* RELEASE CREDITS
*/
.card.credits .credit {
margin-bottom: .5em;
padding: .5em;
display: flex;
@ -264,24 +212,70 @@ dialog#editcredits .credit>div {
border: 1px solid #808080;
}
dialog#editcredits .credit p {
margin: 0;
}
dialog#editcredits .credit .artist-avatar {
.card.credits .credit .artist-avatar {
border-radius: .5em;
}
dialog#editcredits .credit .credit-info {
.card.credits .credit .artist-name {
font-weight: bold;
}
.card.credits .credit .artist-role small {
font-size: inherit;
opacity: .66;
}
#editcredits ul {
margin: 0;
padding: 0;
list-style: none;
}
#editcredits .credit>div {
margin-bottom: .5em;
padding: .5em;
display: flex;
flex-direction: row;
align-items: center;
gap: 1em;
border-radius: .5em;
background: #f8f8f8f8;
border: 1px solid #808080;
}
#editcredits .credit {
transition: transform .2s ease-out, opacity .2s;
}
#editcredits .credit.moving {
transform: scale(1.05);
opacity: .5;
}
#editcredits .credit p {
margin: 0;
}
#editcredits .credit .artist-avatar {
border-radius: .5em;
}
#editcredits .credit .credit-info {
width: 100%;
}
dialog#editcredits .credit .credit-info .credit-attribute {
#editcredits .credit .credit-info .credit-attribute {
width: 100%;
display: flex;
}
dialog#editcredits .credit .credit-info .credit-attribute input[type="text"] {
#editcredits .credit .credit-info .credit-attribute label {
display: flex;
align-items: center;
}
#editcredits .credit .credit-info .credit-attribute input[type="text"] {
margin-left: .25em;
padding: .2em .4em;
flex-grow: 1;
@ -291,15 +285,255 @@ dialog#editcredits .credit .credit-info .credit-attribute input[type="text"] {
color: inherit;
}
dialog#editcredits .credit .artist-name {
#editcredits .credit .artist-name {
font-weight: bold;
}
dialog#editcredits .credit .artist-role small {
#editcredits .credit .artist-role small {
font-size: inherit;
opacity: .66;
}
dialog#editcredits .credit button.delete {
#editcredits .credit button.delete {
margin-left: auto;
}
#addcredit ul {
padding: 0;
list-style: none;
background: #f8f8f8;
}
#addcredit ul li.new-artist {
padding: .5em;
display: flex;
gap: .5em;
cursor: pointer;
}
#addcredit ul li.new-artist:nth-child(even) {
background: #f0f0f0;
}
#addcredit ul li.new-artist:hover {
background: #e0e0e0;
}
#addcredit .new-artist .artist-id {
opacity: .5;
}
/*
* RELEASE LINKS
*/
.card.links {
display: flex;
gap: .2em;
}
.card.links a.button[data-name="spotify"] {
background-color: #8cff83
}
.card.links a.button[data-name="applemusic"] {
background-color: #8cd9ff
}
.card.links a.button[data-name="soundcloud"] {
background-color: #fdaa6d
}
.card.links a.button[data-name="youtube"] {
background-color: #ff6e6e
}
#editlinks table {
width: 100%;
}
#editlinks tr {
display: flex;
}
#editlinks th {
padding: 0 .1em;
display: flex;
align-items: center;
text-align: left;
}
#editlinks tr:nth-child(odd) {
background: #f8f8f8;
}
#editlinks tr th,
#editlinks tr td {
height: 2em;
}
#editlinks tr td {
padding: 0;
}
#editlinks tr.link {
transition: transform .2s ease-out, opacity .2s;
}
#editlinks tr.link.moving {
transform: scale(1.05);
opacity: .5;
}
#editlinks tr .grabber {
width: 2em;
display: flex;
justify-content: center;
cursor: pointer;
}
#editlinks tr .grabber img {
width: 1em;
pointer-events: none;
}
#editlinks tr .link-name {
width: 8em;
}
#editlinks tr .link-url {
flex-grow: 1;
}
#editlinks td a.delete {
display: flex;
height: 100%;
align-items: center;
padding: 0 .5em;
}
#editlinks td input[type="text"] {
width: calc(100% - .6em);
height: 100%;
padding: 0 .3em;
border: none;
outline: none;
cursor: pointer;
background: none;
}
#editlinks td input[type="text"]:hover {
background: #0001;
}
#editlinks td input[type="text"]:focus {
outline: 1px solid #808080;
}
/*
* RELEASE TRACKS
*/
.card.tracks .track {
margin-bottom: 1em;
padding: 1em;
display: flex;
flex-direction: column;
gap: .5em;
border-radius: .5em;
background: #f8f8f8f8;
border: 1px solid #808080;
}
.card.tracks h2.track-title {
margin: 0;
display: flex;
gap: .5em;
}
.card.tracks h2.track-title .track-number {
opacity: .5;
}
.card.tracks .track-album {
margin-left: auto;
font-style: italic;
font-size: .75em;
opacity: .5;
}
.card.tracks .track-album.empty {
color: #ff2020;
opacity: 1;
}
.card.tracks .track-description {
font-style: italic;
}
.card.tracks .track-lyrics {
max-height: 10em;
overflow-y: scroll;
}
.card.tracks .track .empty {
opacity: 0.75;
}
#edittracks ul {
padding: 0;
list-style: none;
}
#edittracks .track {
transition: transform .2s ease-out, opacity .2s;
}
#edittracks .track.moving {
transform: scale(1.05);
opacity: .5;
}
#edittracks .track div {
padding: .5em;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
cursor: pointer;
}
#edittracks .track div:active {
cursor: move;
}
#edittracks .track:nth-child(even) {
background: #f0f0f0;
}
#edittracks .track-number {
min-width: 1em;
display: inline-block;
opacity: .5;
}
#edittracks .track-name {
margin: 0;
}
#addtrack ul {
padding: 0;
list-style: none;
background: #f8f8f8;
}
#addtrack ul li.new-track {
padding: .5em;
display: flex;
gap: .5em;
cursor: pointer;
}
#addtrack ul li.new-track:nth-child(even) {
background: #f0f0f0;
}
#addtrack ul li.new-track:hover {
background: #e0e0e0;
}

View file

@ -1,7 +1,9 @@
import Stateful from "/script/silver.min.js"
const releaseID = document.getElementById("release").dataset.id;
const artwork_input = document.getElementById("artwork");
const title_input = document.getElementById("title");
const artwork_img = document.getElementById("artwork");
const artwork_input = document.getElementById("artwork-file");
const type_input = document.getElementById("type");
const desc_input = document.getElementById("description");
const date_input = document.getElementById("release-date");
@ -10,20 +12,22 @@ const buylink_input = document.getElementById("buylink");
const vis_input = document.getElementById("visibility");
const save_btn = document.getElementById("save");
let token = atob(localStorage.getItem("arime-token"));
var artwork_data = artwork_img.attributes.src.value;
let edited = new Stateful(false);
var token = atob(localStorage.getItem("arime-token"));
let release_data = update_data(undefined);
var edited = new Stateful(false);
var release_data = update_data(undefined);
function update_data(old) {
let release_data = {
var release_data = {
visible: vis_input.value === "true",
title: undefined,
title: title_input.value,
description: desc_input.value,
type: type_input.value,
releaseDate: date_input.value,
artwork: artwork_input.attributes.src.value,
artwork: artwork_data,
buyname: buyname_input.value,
buylink: buylink_input.value,
};
@ -38,8 +42,6 @@ function update_data(old) {
function save_release() {
console.table(release_data);
edited.set(false);
(async () => {
const res = await fetch(
"/api/v1/music/" + releaseID, {
@ -61,15 +63,29 @@ function save_release() {
location = location;
})();
}
window.save_release = save_release;
edited.onUpdate(edited => {
save_btn.disabled = !edited;
})
artwork_input.addEventListener("click", () => {
title_input.addEventListener("change", () => {
release_data = update_data(release_data);
});
artwork_img.addEventListener("click", () => {
artwork_input.addEventListener("change", () => {
if (artwork_input.files.length > 0) {
const reader = new FileReader();
reader.onload = e => {
const data = e.target.result;
artwork_img.src = data;
artwork_data = data;
release_data = update_data(release_data);
};
reader.readAsDataURL(artwork_input.files[0]);
}
});
artwork_input.click();
});
type_input.addEventListener("change", () => {
release_data = update_data(release_data);
});

24
admin/static/index.js Normal file
View file

@ -0,0 +1,24 @@
const newReleaseBtn = document.getElementById("create-release");
newReleaseBtn.addEventListener("click", event => {
event.preventDefault();
const id = prompt("Enter an ID for this release:");
if (id == null || id == "") return;
fetch("/api/v1/music", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({id})
}).then(res => {
if (res.ok) location = "/admin/release/" + id;
else {
res.text().then(err => {
alert("Request failed: " + err);
console.error(err);
});
}
}).catch(err => {
alert("Failed to create release. Check the console for details.");
console.error(err);
});
});