Added visibility toggle to stash tile. Collapsed stash page directory structure.

This commit is contained in:
DebaucheryLibrarian 2024-03-26 03:00:50 +01:00
parent f6b50cc732
commit ce4b9e7d40
15 changed files with 127 additions and 30 deletions

View File

@ -1,14 +1,16 @@
<template> <template>
<div v-if="is404"> <div class="page">
<h1>404 Page Not Found</h1> <div v-if="is404">
<h1>404 Not Found</h1>
<p v-if="abortReason">{{ abortReason }}</p> <p v-if="abortReason">{{ abortReason }}</p>
<p v-else>This page could not be found.</p> <p v-else>This page could not be found.</p>
</div> </div>
<div v-else> <div v-else>
<h1>500 Internal Error</h1> <h1>500 Internal Error</h1>
<p>Something went wrong.</p> <p>Something went wrong.</p>
</div>
</div> </div>
</template> </template>
@ -25,3 +27,10 @@ defineProps({
const pageContext = inject('pageContext'); const pageContext = inject('pageContext');
const { abortReason } = pageContext; const { abortReason } = pageContext;
</script> </script>
<style scoped>
.page {
display: flex;
justify-content: center;
}
</style>

View File

@ -16,18 +16,34 @@
:key="`stash-${stash.id}`" :key="`stash-${stash.id}`"
> >
<div class="stash"> <div class="stash">
<a <div class="stash-header">
:href="`/stash/${profile.username}/${stash.slug}`" <a
class="stash-name nolink" :href="`/stash/${profile.username}/${stash.slug}`"
> class="stash-name ellipsis nolink"
{{ stash.name }} >
<span class="ellipsis">{{ stash.name }}</span>
<Icon
v-if="stash.primary"
icon="heart7"
class="primary"
/>
</a>
<Icon <Icon
v-if="stash.primary" v-if="profile.id === user?.id && stash.public"
icon="heart7" icon="eye"
class="primary" class="public noselect"
@click="setStashPublic(stash, false)"
/> />
</a>
<Icon
v-else-if="profile.id === user?.id"
icon="eye-blocked"
class="public noselect"
@click="setStashPublic(stash, true)"
/>
</div>
<div class="stash-counts"> <div class="stash-counts">
<a <a
@ -52,14 +68,48 @@
</template> </template>
<script setup> <script setup>
import { inject } from 'vue'; import { ref, inject } from 'vue';
import { get, patch } from '#/src/api.js';
import events from '#/src/events.js';
const pageContext = inject('pageContext'); const pageContext = inject('pageContext');
const profile = pageContext.pageProps.profile; const profile = ref(pageContext.pageProps.profile);
const user = pageContext.user;
const done = ref(true);
function abbreviateNumber(number) { function abbreviateNumber(number) {
return number?.toLocaleString('en-US', { notation: 'compact' }) || 0; return number?.toLocaleString('en-US', { notation: 'compact' }) || 0;
} }
async function setStashPublic(stash, isPublic) {
if (done.value === false) {
return;
}
try {
done.value = false;
await patch(`/stashes/${stash.id}`, { public: isPublic });
profile.value = await get(`/users/${profile.value.id}`);
events.emit('feedback', {
type: isPublic ? 'success' : 'remove',
message: isPublic
? `Stash '${stash.name}' is public`
: `Stash '${stash.name}' is private`,
});
} catch (error) {
console.error(error);
events.emit('feedback', {
type: 'error',
message: 'Failed to update stash',
});
}
done.value = true;
}
</script> </script>
<style scoped> <style scoped>
@ -73,11 +123,12 @@ function abbreviateNumber(number) {
.username { .username {
margin: 0; margin: 0;
font-size: 1.25rem;
} }
.avatar { .avatar {
width: 2rem; width: 1.5rem;
height: 2rem; height: 1.5rem;
border-radius: .25rem; border-radius: .25rem;
margin-right: 1rem; margin-right: 1rem;
} }
@ -99,11 +150,30 @@ function abbreviateNumber(number) {
} }
} }
.stash-name { .stash-header {
display: flex; display: flex;
padding: .5rem; align-items: stretch;
border-bottom: solid 1px var(--shadow-weak-30); border-bottom: solid 1px var(--shadow-weak-30);
font-weight: bold; font-weight: bold;
}
.icon.public {
display: flex;
height: auto;
padding: 0 .75rem;
fill: var(--shadow);
&:hover {
cursor: pointer;
fill: var(--shadow-strong-30);
}
}
.stash-name {
display: flex;
flex-grow: 1;
padding: .5rem;
overflow: hidden;
.icon { .icon {
margin-left: .75rem; margin-left: .75rem;
@ -111,6 +181,7 @@ function abbreviateNumber(number) {
.icon.primary { .icon.primary {
fill: var(--primary); fill: var(--primary);
transform: translateY(1px);
} }
} }

View File

@ -3,7 +3,7 @@ import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
import { fetchUser } from '#/src/users.js'; import { fetchUser } from '#/src/users.js';
export async function onBeforeRender(pageContext) { export async function onBeforeRender(pageContext) {
const profile = await fetchUser(pageContext.routeParams.username); const profile = await fetchUser(pageContext.routeParams.username, {}, pageContext.user);
if (!profile) { if (!profile) {
throw render(404, `Cannot find user '${pageContext.routeParams.username}'.`); throw render(404, `Cannot find user '${pageContext.routeParams.username}'.`);

View File

@ -25,20 +25,20 @@ export function curateUser(user, assets = {}) {
} }
function whereUser(builder, userId, options = {}) { function whereUser(builder, userId, options = {}) {
if (typeof userId === 'number') { if (Number.isNaN(Number(userId))) {
builder.where('users.id', userId);
}
if (typeof userId === 'string') {
builder.where(knex.raw('lower(users.username)'), userId.toLowerCase()); builder.where(knex.raw('lower(users.username)'), userId.toLowerCase());
if (options.email) { if (options.email) {
builder.orWhere(knex.raw('lower(users.email)'), userId.toLowerCase()); builder.orWhere(knex.raw('lower(users.email)'), userId.toLowerCase());
} }
return;
} }
builder.where('users.id', Number(userId));
} }
export async function fetchUser(userId, options = {}) { export async function fetchUser(userId, options = {}, reqUser) {
const user = await knex('users') const user = await knex('users')
.select(knex.raw('users.*, users_roles.abilities as role_abilities')) .select(knex.raw('users.*, users_roles.abilities as role_abilities'))
.modify((builder) => whereUser(builder, userId, options)) .modify((builder) => whereUser(builder, userId, options))
@ -52,6 +52,11 @@ export async function fetchUser(userId, options = {}) {
const stashes = await knex('stashes') const stashes = await knex('stashes')
.where('user_id', user.id) .where('user_id', user.id)
.modify((builder) => {
if (reqUser?.id !== user.id) {
builder.where('public', true);
}
})
.leftJoin('stashes_meta', 'stashes_meta.stash_id', 'stashes.id'); .leftJoin('stashes_meta', 'stashes_meta.stash_id', 'stashes.id');
if (options.raw) { if (options.raw) {

View File

@ -24,6 +24,10 @@ import {
signupApi, signupApi,
} from './auth.js'; } from './auth.js';
import {
fetchUserApi
} from './users.js';
import { import {
createStashApi, createStashApi,
removeStashApi, removeStashApi,
@ -96,6 +100,7 @@ export default async function initServer() {
router.delete('/api/session', logoutApi); router.delete('/api/session', logoutApi);
// USERS // USERS
router.get('/api/users/:userId', fetchUserApi);
router.post('/api/users', signupApi); router.post('/api/users', signupApi);
// STASHES // STASHES

7
src/web/users.js Normal file
View File

@ -0,0 +1,7 @@
import { fetchUser } from '../users.js';
export async function fetchUserApi(req, res) {
const user = await fetchUser(req.params.userId, {}, req.user);
res.send(user);
}