Added stash menu with remove and rename.

This commit is contained in:
2024-03-27 00:06:03 +01:00
parent e4638324f7
commit c018f54a12
22 changed files with 620 additions and 623 deletions

View File

@@ -1,4 +1,5 @@
import { parse } from '@brillout/json-serializer/parse'; /* eslint-disable-line import/extensions */
import events from '#/src/events.js';
const postHeaders = {
mode: 'cors',
@@ -18,39 +19,69 @@ function getQuery(data) {
return `?${encodeURI(decodeURIComponent(new URLSearchParams(curatedQuery).toString()))}`; // recode so commas aren't encoded
}
export async function get(path, query = {}) {
function showFeedback(isSuccess, options = {}, errorMessage) {
if (!isSuccess && typeof options.errorFeedback) {
events.emit('feedback', {
type: 'error',
message: options.appendErrorMessage && errorMessage
? `${options.errorFeedback}: ${errorMessage}`
: options.errorFeedback,
});
}
if (isSuccess && options.successFeedback) {
events.emit('feedback', {
type: 'success',
message: options.successFeedback,
});
}
if (isSuccess && options.undoFeedback) {
events.emit('feedback', {
type: 'undo',
message: options.undoFeedback,
});
}
}
export async function get(path, query = {}, options = {}) {
const res = await fetch(`/api${path}${getQuery(query)}`);
const body = parse(await res.text());
if (res.ok) {
showFeedback(true, options);
return body;
}
showFeedback(false, options, body.statusMessage);
throw new Error(body.statusMessage);
}
export async function post(path, data, { query } = {}) {
const res = await fetch(`/api${path}${getQuery(query)}`, {
export async function post(path, data, options = {}) {
const res = await fetch(`/api${path}${getQuery(options.query)}`, {
method: 'POST',
body: JSON.stringify(data),
...postHeaders,
});
if (res.status === 204) {
showFeedback(true, options);
return null;
}
const body = parse(await res.text());
if (res.ok) {
showFeedback(true, options);
return body;
}
showFeedback(false, options, body.statusMessage);
throw new Error(body.statusMessage);
}
export async function patch(path, data, { query } = {}) {
const res = await fetch(`/api${path}${getQuery(query)}`, {
export async function patch(path, data, options = {}) {
const res = await fetch(`/api${path}${getQuery(options.query)}`, {
method: 'PATCH',
body: JSON.stringify(data),
...postHeaders,
@@ -63,28 +94,33 @@ export async function patch(path, data, { query } = {}) {
const body = parse(await res.text());
if (res.ok) {
showFeedback(true, options);
return body;
}
showFeedback(false, options, body.statusMessage);
throw new Error(body.statusMessage);
}
export async function del(path, { data, query } = {}) {
const res = await fetch(`/api${path}${getQuery(query)}`, {
export async function del(path, options = {}) {
const res = await fetch(`/api${path}${getQuery(options.query)}`, {
method: 'DELETE',
body: JSON.stringify(data),
body: JSON.stringify(options.data),
...postHeaders,
});
if (res.status === 204) {
showFeedback(true, options);
return null;
}
const body = parse(await res.text());
if (res.ok) {
showFeedback(true, options);
return body;
}
showFeedback(false, options, body.statusMessage);
throw new Error(body.statusMessage);
}

View File

@@ -19,7 +19,7 @@ export function curateStash(stash, assets = {}) {
id: stash.id,
name: stash.name,
slug: stash.slug,
primary: stash.primary,
isPrimary: stash.primary,
public: stash.public,
createdAt: stash.created_at,
stashedScenes: stash.stashed_scenes ?? null,
@@ -38,10 +38,10 @@ export function curateStash(stash, assets = {}) {
function curateStashEntry(stash, user) {
const curatedStashEntry = {
user_id: user.id,
name: stash.name,
slug: slugify(stash.name),
public: false,
user_id: user?.id || undefined,
name: stash.name || undefined,
slug: slugify(stash.name) || undefined,
public: stash.public ?? false,
};
return curatedStashEntry;
@@ -94,26 +94,30 @@ export async function fetchStashes(domain, itemId, sessionUser) {
return stashes.map((stash) => curateStash(stash));
}
function verifyStashName(stash) {
if (!stash.name) {
throw new HttpError('Stash name required', 400);
}
if (stash.name.length < config.stashes.nameLength[0]) {
throw new HttpError('Stash name is too short', 400);
}
if (stash.name.length > config.stashes.nameLength[1]) {
throw new HttpError('Stash name is too long', 400);
}
if (!config.stashes.namePattern.test(stash.name)) {
throw new HttpError('Stash name contains invalid characters', 400);
}
}
export async function createStash(newStash, sessionUser) {
if (!sessionUser) {
throw new HttpError('You are not authenthicated', 401);
}
if (!newStash.name) {
throw new HttpError('Stash name required', 400);
}
if (newStash.name.length < config.stashes.nameLength[0]) {
throw new HttpError('Stash name is too short', 400);
}
if (newStash.name.length > config.stashes.nameLength[1]) {
throw new HttpError('Stash name is too long', 400);
}
if (!config.stashes.namePattern.test(newStash.name)) {
throw new HttpError('Stash name contains invalid characters', 400);
}
verifyStashName(newStash);
try {
const stash = await knex('stashes')
@@ -130,24 +134,36 @@ export async function createStash(newStash, sessionUser) {
}
}
export async function updateStash(stashId, newStash, sessionUser) {
export async function updateStash(stashId, updatedStash, sessionUser) {
if (!sessionUser) {
throw new HttpError('You are not authenthicated', 401);
}
const stash = await knex('stashes')
.where({
id: stashId,
user_id: sessionUser.id,
})
.update(newStash)
.returning('*');
if (!stash) {
throw new HttpError('You are not authorized to modify this stash', 403);
if (updatedStash.name) {
verifyStashName(updatedStash);
}
return curateStash(stash);
try {
const stash = await knex('stashes')
.where({
id: stashId,
user_id: sessionUser.id,
})
.update(curateStashEntry(updatedStash))
.returning('*');
if (!stash) {
throw new HttpError('You are not authorized to modify this stash', 403);
}
return curateStash(stash);
} catch (error) {
if (error.routine === '_bt_check_unique') {
throw new HttpError('Stash name should be unique', 409);
}
throw error;
}
}
export async function removeStash(stashId, sessionUser) {

View File

@@ -18,7 +18,7 @@ export function curateUser(user, assets = {}) {
avatar: `/media/avatars/${user.id}_${user.username}.png`,
createdAt: user.created_at,
stashes: curatedStashes,
primaryStash: curatedStashes.find((stash) => stash.primary),
primaryStash: curatedStashes.find((stash) => stash.isPrimary),
};
return curatedUser;

View File

@@ -1,21 +0,0 @@
export function curateUser(user, assets = {}) {
if (!user) {
return null;
}
const curatedStashes = assets.stashes?.filter(Boolean).map((stash) => curateStash(stash)) || [];
const curatedUser = {
id: user.id,
username: user.username,
email: user.email,
emailVerified: user.email_verified,
identityVerified: user.identity_verified,
avatar: `/media/avatars/${user.id}_${user.username}.png`,
createdAt: user.created_at,
stashes: curatedStashes,
primaryStash: curatedStashes.find((stash) => stash.primary),
};
return curatedUser;
}