Moed pages/scene to pages/scenes/@sceneId for consistency.
This commit is contained in:
1168
pages/scenes/@sceneId/+Page.vue
Normal file
1168
pages/scenes/@sceneId/+Page.vue
Normal file
File diff suppressed because it is too large
Load Diff
63
pages/scenes/@sceneId/+onBeforeRender.js
Normal file
63
pages/scenes/@sceneId/+onBeforeRender.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { redirect, render } from 'vike/abort';
|
||||
import { getIdsBySlug } from '#/src/cache.js';
|
||||
import { getRandomCampaigns } from '#/src/campaigns.js';
|
||||
import { fetchScenesById } from '#/src/scenes.js';
|
||||
|
||||
function getTitle(scene) {
|
||||
if (scene.title) {
|
||||
return scene.title;
|
||||
}
|
||||
|
||||
if (scene.actors.length > 0) {
|
||||
return `Scene with ${scene.actors.map((actor) => actor.name).join(', ')}`;
|
||||
}
|
||||
|
||||
return 'Scene';
|
||||
}
|
||||
|
||||
export async function onBeforeRender(pageContext) {
|
||||
if (pageContext._pageId === '/pages/scene/edit' && !pageContext.user) {
|
||||
throw redirect(`/login?r=${encodeURIComponent(pageContext.urlOriginal)}`);
|
||||
}
|
||||
|
||||
const [scene] = await fetchScenesById([Number(pageContext.routeParams.sceneId)], {
|
||||
reqUser: pageContext.user,
|
||||
includeAssets: true,
|
||||
includePartOf: true,
|
||||
actorStashes: true,
|
||||
restriction: pageContext.restriction,
|
||||
});
|
||||
|
||||
if (!scene) {
|
||||
throw render(404, `Cannot find scene '${pageContext.routeParams.sceneId}'.`);
|
||||
}
|
||||
|
||||
const [campaigns, tagIds] = await Promise.all([
|
||||
getRandomCampaigns([
|
||||
{
|
||||
minRatio: 1.5,
|
||||
entityIds: [scene.channel.id, scene.network?.id].filter(Boolean),
|
||||
allowRandomFallback: false,
|
||||
},
|
||||
], { tagFilter: pageContext.tagFilter }),
|
||||
getIdsBySlug([
|
||||
'anal',
|
||||
'creampie',
|
||||
'dp',
|
||||
'facial',
|
||||
], 'tags', true),
|
||||
]);
|
||||
|
||||
return {
|
||||
pageContext: {
|
||||
title: getTitle(scene),
|
||||
pageProps: {
|
||||
scene,
|
||||
tagIds,
|
||||
},
|
||||
campaigns: {
|
||||
scene: campaigns[0],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
1
pages/scenes/@sceneId/+route.js
Normal file
1
pages/scenes/@sceneId/+route.js
Normal file
@@ -0,0 +1 @@
|
||||
export default '/scene/@sceneId/*';
|
||||
637
pages/scenes/@sceneId/edit/+Page.vue
Normal file
637
pages/scenes/@sceneId/edit/+Page.vue
Normal file
@@ -0,0 +1,637 @@
|
||||
<script setup>
|
||||
import { format } from 'date-fns';
|
||||
import { computed, inject, ref, useId } from 'vue';
|
||||
|
||||
import EditActors from '#/components/edit/actors.vue';
|
||||
import EditMovies from '#/components/edit/movies.vue';
|
||||
import EditTags from '#/components/edit/tags.vue';
|
||||
import Checkbox from '#/components/form/checkbox.vue';
|
||||
|
||||
import { post } from '#/src/api.js';
|
||||
import verifyAbility from '#/utils/verify-ability.js';
|
||||
|
||||
const pageContext = inject('pageContext');
|
||||
|
||||
const user = pageContext.user;
|
||||
const scene = ref(pageContext.pageProps.scene);
|
||||
const applyTooltipId = useId();
|
||||
|
||||
const fields = computed(() => [
|
||||
{
|
||||
key: 'actors',
|
||||
type: 'actors',
|
||||
value: scene.value.actors,
|
||||
},
|
||||
{
|
||||
key: 'tags',
|
||||
type: 'tags',
|
||||
value: scene.value.tags.toSorted((tagA, tagB) => tagA.name.localeCompare(tagB.name)),
|
||||
simplify: false,
|
||||
note: 'Actor-specific tags should only be used where confusion is reasonable, such as group scenes in which some perform anal, and some don\'t.',
|
||||
},
|
||||
{
|
||||
key: 'url',
|
||||
label: 'URL',
|
||||
type: 'string',
|
||||
value: scene.value.url,
|
||||
enabled: verifyAbility(user, 'scene', 'update'),
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
type: 'string',
|
||||
value: scene.value.title,
|
||||
note: 'Do not correct language errors unless source was updated.',
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
type: 'text',
|
||||
value: scene.value.description,
|
||||
note: 'Do not correct language errors unless source was updated.',
|
||||
},
|
||||
{
|
||||
key: 'date',
|
||||
type: 'date',
|
||||
value: {
|
||||
date: scene.value.date
|
||||
? format(scene.value.date, 'yyyy-MM-dd hh:mm')
|
||||
: null,
|
||||
precision: scene.value.datePrecision,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'duration',
|
||||
type: 'duration',
|
||||
value: [Math.floor(scene.value.duration / 3600), Math.floor((scene.value.duration % 3600) / 60), scene.value.duration % 60],
|
||||
simplify: false,
|
||||
},
|
||||
{
|
||||
key: 'productionDate',
|
||||
label: 'production date',
|
||||
type: 'date',
|
||||
value: {
|
||||
date: scene.value.productionDate
|
||||
? format(scene.value.productionDate, 'yyyy-MM-dd hh:mm')
|
||||
: null,
|
||||
precision: scene.value.productionDatePrecision, // not currently implemented
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'movies',
|
||||
type: 'movies',
|
||||
value: scene.value.movies,
|
||||
},
|
||||
{
|
||||
key: 'comment',
|
||||
type: 'text',
|
||||
placeholder: 'Do NOT use this field to summarize and clarify your revision. This field is for permanent notes and comments regarding the scene or database entry itself.',
|
||||
value: scene.value.comment,
|
||||
enabled: verifyAbility(user, 'scene', 'update'),
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
type: 'checkbox',
|
||||
checkboxLabel: 'Remove this scene from the database',
|
||||
value: false,
|
||||
enabled: verifyAbility(user, 'scene', 'delete'),
|
||||
},
|
||||
].filter((field) => field.enabled !== false));
|
||||
|
||||
function simplifyArray(field) {
|
||||
if (Array.isArray(field.value) && field.simplify !== false) {
|
||||
return field.value.map((item) => item.hash || item.id);
|
||||
}
|
||||
|
||||
return field.value;
|
||||
}
|
||||
|
||||
const editing = ref(new Set());
|
||||
const edits = ref(Object.fromEntries(fields.value.map((field) => [field.key, simplifyArray(field)])));
|
||||
const comment = ref(null);
|
||||
const apply = ref(user.role !== 'user');
|
||||
const submitted = ref(false);
|
||||
|
||||
const userCanDelete = user.abilities.some((ability) => ability.subject === 'scene' && ability.action === 'delete');
|
||||
const isApplyDisabled = computed(() => editing.value.size === 0 || (edits.value.delete && !userCanDelete));
|
||||
|
||||
const keyMap = {
|
||||
date: {
|
||||
date: 'date',
|
||||
precision: 'datePrecision',
|
||||
},
|
||||
productionDate: {
|
||||
date: 'productionDate',
|
||||
precision: 'productionDatePrecision',
|
||||
},
|
||||
};
|
||||
|
||||
function toggleField(item) {
|
||||
if (editing.value.has(item.key)) {
|
||||
editing.value.delete(item.key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
editing.value.add(item.key);
|
||||
}
|
||||
|
||||
const timeUnits = ['h', 'm', 's'];
|
||||
|
||||
function setDuration(unit, event) {
|
||||
edits.value.duration[timeUnits.indexOf(unit)] = Number(event.target.value);
|
||||
}
|
||||
|
||||
function setDelete(checked) {
|
||||
edits.value.delete = checked;
|
||||
|
||||
if (!userCanDelete) {
|
||||
apply.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
try {
|
||||
await post('/revisions/scenes', {
|
||||
sceneId: scene.value.id,
|
||||
edits: {
|
||||
...Object.fromEntries(Array.from(editing.value).flatMap((key) => {
|
||||
if (edits.value[key] && typeof edits.value[key] === 'object' && !Array.isArray(edits.value[key])) {
|
||||
return Object.entries(edits.value[key]).map(([valueKey, value]) => [keyMap[key]?.[valueKey] || valueKey, value]);
|
||||
}
|
||||
|
||||
return [[key, edits.value[key]]];
|
||||
})),
|
||||
// duration: edits.value.duration
|
||||
duration: editing.value.has('duration') && edits.value.duration
|
||||
? (((edits.value.duration[0] || 0) * 3600) + ((edits.value.duration[1] || 0) * 60) + (edits.value.duration[2] || 0)) || null
|
||||
: undefined,
|
||||
},
|
||||
comment: comment.value,
|
||||
apply: apply.value,
|
||||
}, {
|
||||
successFeedback: 'Your revision has been submitted for approval.',
|
||||
appendErrorMessage: true,
|
||||
});
|
||||
|
||||
editing.value = new Set();
|
||||
edits.value = {};
|
||||
comment.value = null;
|
||||
|
||||
submitted.value = true;
|
||||
|
||||
// scene.value = await get(`/scenes/${scene.value.id}`);
|
||||
}
|
||||
catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="editor">
|
||||
<p
|
||||
v-if="submitted"
|
||||
class="submitted"
|
||||
>
|
||||
<template v-if="apply">Your revision has been submitted. Thank you for your contribution!</template>
|
||||
<template v-else>Your revision has been submitted for review. Thank you for your contribution!</template>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
:href="`/scene/${scene.id}/${scene.slug}`"
|
||||
class="link"
|
||||
>Return to scene</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a
|
||||
:href="`/scene/edit/${scene.id}`"
|
||||
class="link"
|
||||
>Make another edit</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a
|
||||
:href="`/scene/revs/${scene.id}/${scene.slug}`"
|
||||
class="link"
|
||||
>Go to scene revisions</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a
|
||||
:href="`/user/${user.username}/revisions/scenes`"
|
||||
class="link"
|
||||
>Go to user revisions</a>
|
||||
</li>
|
||||
|
||||
<li v-if="user.role !== 'user'">
|
||||
<a
|
||||
href="/admin/revisions/scenes"
|
||||
class="link"
|
||||
>Go to revisions admin</a>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<form
|
||||
v-else
|
||||
@submit.prevent
|
||||
>
|
||||
<div class="editor-header">
|
||||
<h2 class="heading ellipsis">Edit scene #{{ scene.id }} - {{ scene.title }}</h2>
|
||||
|
||||
<a
|
||||
:href="`/scene/${scene.id}/${scene.slug}`"
|
||||
target="_blank"
|
||||
class="link noshrink"
|
||||
>Go to scene</a>
|
||||
</div>
|
||||
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="item in fields"
|
||||
:key="`item-${item.key}`"
|
||||
class="row"
|
||||
>
|
||||
<div class="item-header">
|
||||
<div class="key">
|
||||
{{ item.label || item.key }}
|
||||
|
||||
<Icon
|
||||
v-if="item.note"
|
||||
v-tooltip="{ content: item.note, ariaId: `field-note-${item.key}` }"
|
||||
icon="info2"
|
||||
class="item-note noselect"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="item-actions">
|
||||
<Icon
|
||||
v-if="!item.forced"
|
||||
icon="pencil5"
|
||||
class="noselect"
|
||||
:class="{ active: editing.has(item.key) }"
|
||||
@click="toggleField(item)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="value"
|
||||
:class="{ disabled: !editing.has(item.key) }"
|
||||
>
|
||||
<EditActors
|
||||
v-if="item.type === 'actors'"
|
||||
:scene="scene"
|
||||
:item="item"
|
||||
:edits="edits"
|
||||
:editing="editing"
|
||||
@actors="(actors) => { edits.actors = actors; }"
|
||||
/>
|
||||
|
||||
<EditTags
|
||||
v-if="item.type === 'tags'"
|
||||
:scene="scene"
|
||||
:item="item"
|
||||
:edits="edits"
|
||||
:editing="editing"
|
||||
@tags="(tags) => { edits.tags = tags; }"
|
||||
/>
|
||||
|
||||
<EditMovies
|
||||
v-if="item.type === 'movies'"
|
||||
:scene="scene"
|
||||
:item="item"
|
||||
:edits="edits"
|
||||
:editing="editing"
|
||||
@movies="(movies) => { edits.movies = movies; }"
|
||||
/>
|
||||
|
||||
<input
|
||||
v-if="item.type === 'string'"
|
||||
v-model="edits[item.key]"
|
||||
class="string input"
|
||||
:disabled="!editing.has(item.key)"
|
||||
>
|
||||
|
||||
<textarea
|
||||
v-if="item.type === 'text'"
|
||||
v-model="edits[item.key]"
|
||||
:placeholder="item.placeholder"
|
||||
rows="3"
|
||||
class="text input"
|
||||
:disabled="!editing.has(item.key)"
|
||||
/>
|
||||
|
||||
<Checkbox
|
||||
v-if="item.type === 'checkbox'"
|
||||
:label="item.checkboxLabel"
|
||||
:checked="edits[item.key]"
|
||||
:disabled="!editing.has(item.key)"
|
||||
class="checkbox delete"
|
||||
@change="(checked) => setDelete(checked)"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="item.type === 'date'"
|
||||
class="date"
|
||||
>
|
||||
<input
|
||||
v-model="edits[item.key].date"
|
||||
type="datetime-local"
|
||||
class="date input"
|
||||
:disabled="!editing.has(item.key)"
|
||||
>
|
||||
|
||||
<select
|
||||
v-if="item.value.precision"
|
||||
v-model="edits[item.key].precision"
|
||||
class="input"
|
||||
:disabled="!editing.has(item.key)"
|
||||
>
|
||||
<option value="minute">Minute</option>
|
||||
<option value="hour">Hour</option>
|
||||
<option value="day">Day</option>
|
||||
<option value="month">Month</option>
|
||||
<option value="year">Year</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="item.type === 'duration'"
|
||||
class="duration"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
class="input"
|
||||
:value="edits[item.key]?.[0] ?? item.value[0]"
|
||||
min="0"
|
||||
max="100"
|
||||
:disabled="!editing.has(item.key)"
|
||||
@change="setDuration('h', $event)"
|
||||
>H
|
||||
|
||||
<input
|
||||
type="number"
|
||||
class="input"
|
||||
:value="edits[item.key]?.[1] ?? item.value[1]"
|
||||
min="0"
|
||||
max="59"
|
||||
:disabled="!editing.has(item.key)"
|
||||
@change="setDuration('m', $event)"
|
||||
>M
|
||||
|
||||
<input
|
||||
type="number"
|
||||
class="input"
|
||||
:value="edits[item.key]?.[2] ?? item.value[2]"
|
||||
min="0"
|
||||
max="59"
|
||||
:disabled="!editing.has(item.key)"
|
||||
@change="setDuration('s', $event)"
|
||||
>S
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="editor-footer">
|
||||
<div class="comment">
|
||||
<textarea
|
||||
v-model="comment"
|
||||
rows="3"
|
||||
placeholder="Please provide verifiable information supporting your edits."
|
||||
class="text input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="editor-actions">
|
||||
<Checkbox
|
||||
v-if="user.role !== 'user'"
|
||||
v-tooltip="isApplyDisabled && editing.has('delete') ? { content: 'Delete must be approved by an admin', ariaId: applyTooltipId } : null"
|
||||
label="Approve and apply immediately"
|
||||
:checked="apply"
|
||||
:disabled="isApplyDisabled"
|
||||
@change="(checked) => apply = checked"
|
||||
/>
|
||||
|
||||
<!-- we don't want the return key to submit the form -->
|
||||
<button
|
||||
class="button button-primary"
|
||||
type="button"
|
||||
:disabled="editing.size === 0"
|
||||
@click="submit"
|
||||
>
|
||||
<template v-if="apply">Submit</template>
|
||||
<template v-else>Submit for review</template>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.editor {
|
||||
flex-grow: 1;
|
||||
background: var(--background-dark-10);
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.heading {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: .25rem 1rem;
|
||||
|
||||
.value.disabled {
|
||||
color: var(--glass);
|
||||
}
|
||||
}
|
||||
|
||||
.key {
|
||||
width: 8rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-transform: capitalize;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.input {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex-grow: 1;
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
|
||||
&:disabled {
|
||||
color: var(--glass-strong-10);
|
||||
background: none;
|
||||
border: solid 1px var(--glass-weak-30);
|
||||
}
|
||||
}
|
||||
|
||||
.duration {
|
||||
.input {
|
||||
width: 5rem;
|
||||
margin-right: .25rem;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: .75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.date {
|
||||
.input {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
.icon {
|
||||
padding: .25rem 1rem;
|
||||
fill: var(--glass);
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
fill: var(--text);
|
||||
}
|
||||
|
||||
&.active {
|
||||
fill: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-note {
|
||||
fill: var(--glass);
|
||||
padding: .5rem .75rem;
|
||||
cursor: help;
|
||||
|
||||
&:hover {
|
||||
fill: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
.editor-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1rem 1rem 0 1rem;
|
||||
border-top: solid 1px var(--primary-light-30);
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.comment {
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
resize: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox.delete {
|
||||
display: inline-flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.value.disabled .delete {
|
||||
:deep(.check-checkbox) + .check {
|
||||
background: var(--glass-weak-30);
|
||||
}
|
||||
}
|
||||
|
||||
.value:not(.disabled) .delete {
|
||||
:deep(.check-checkbox:checked) + .check {
|
||||
background: var(--error);
|
||||
}
|
||||
}
|
||||
|
||||
.editor-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
margin: .5rem 0;
|
||||
|
||||
.button {
|
||||
padding: .5rem 1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.submitted {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.delete-title {
|
||||
display: block;
|
||||
margin-top: .5rem;
|
||||
max-width: 25rem;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.dialog-section {
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media(--small) {
|
||||
.row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
|
||||
.key {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1
pages/scenes/@sceneId/edit/+route.js
Normal file
1
pages/scenes/@sceneId/edit/+route.js
Normal file
@@ -0,0 +1 @@
|
||||
export default '/scene/edit/@sceneId/*';
|
||||
62
pages/scenes/@sceneId/revisions/+Page.vue
Normal file
62
pages/scenes/@sceneId/revisions/+Page.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
|
||||
import Revisions from '#/components/edit/revisions.vue';
|
||||
|
||||
const pageContext = inject('pageContext');
|
||||
const scene = pageContext.pageProps.scene;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="revs-header">
|
||||
<h2 class="heading">Revisions for "{{ scene.title }}"</h2>
|
||||
|
||||
<div class="revs-actions">
|
||||
<a
|
||||
:href="`/scene/edit/${scene.id}/${scene.slug}`"
|
||||
class="link"
|
||||
>Edit scene</a>
|
||||
|
||||
<a
|
||||
:href="`/scene/${scene.id}/${scene.slug}`"
|
||||
target="_blank"
|
||||
class="link"
|
||||
>Go to scene</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Revisions context="scene" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
padding: 1rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.revs-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.heading {
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.revs-actions {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@media(--compact) {
|
||||
.revs-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
34
pages/scenes/@sceneId/revisions/+onBeforeRender.js
Normal file
34
pages/scenes/@sceneId/revisions/+onBeforeRender.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { fetchSceneRevisions, fetchScenesById } from '#/src/scenes.js';
|
||||
|
||||
export async function onBeforeRender(pageContext) {
|
||||
const [scene] = await fetchScenesById([Number(pageContext.routeParams.sceneId)], {
|
||||
reqUser: pageContext.user,
|
||||
includeAssets: true,
|
||||
includePartOf: true,
|
||||
actorStashes: true,
|
||||
});
|
||||
|
||||
const {
|
||||
revisions,
|
||||
actors,
|
||||
tags,
|
||||
movies,
|
||||
} = await fetchSceneRevisions(null, {
|
||||
sceneId: scene.id,
|
||||
isFinalized: true,
|
||||
limit: 100,
|
||||
}, pageContext.user);
|
||||
|
||||
return {
|
||||
pageContext: {
|
||||
title: `Revs for '${scene.title}'`,
|
||||
pageProps: {
|
||||
scene,
|
||||
revisions,
|
||||
actors,
|
||||
tags,
|
||||
movies,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
19
pages/scenes/@sceneId/revisions/+route.js
Normal file
19
pages/scenes/@sceneId/revisions/+route.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { match } from 'path-to-regexp';
|
||||
|
||||
const path = '/scene/revs/:sceneId/:slug?';
|
||||
const urlMatch = match(path, { decode: decodeURIComponent });
|
||||
|
||||
export default (pageContext) => {
|
||||
const matched = urlMatch(pageContext.urlPathname);
|
||||
|
||||
if (matched) {
|
||||
return {
|
||||
routeParams: {
|
||||
sceneId: matched.params.sceneId,
|
||||
domain: 'scenes',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
Reference in New Issue
Block a user