Compare commits
20 Commits
bb7a84259c
...
05d11e0ea5
Author | SHA1 | Date | |
---|---|---|---|
05d11e0ea5 | |||
d030346dde | |||
6016813f4c | |||
4838b7b624 | |||
5c6cb84383 | |||
25b7e723f6 | |||
609562b7fa | |||
a7b8c07952 | |||
e28f6b6974 | |||
af1e08227d | |||
abbdc61e79 | |||
1a3135c6c4 | |||
05a20ff94e | |||
c68762ceac | |||
bed12e2eee | |||
bb6d05be72 | |||
7932152025 | |||
a88f418901 | |||
0d6acdf174 | |||
25582dd1f1 |
@ -24,7 +24,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<i
|
<i
|
||||||
:style="{
|
:style="{
|
||||||
fontSize: `${size}${propUnit}`,
|
...$attrs.style,
|
||||||
|
fontSize: pixi,
|
||||||
color: icolor || 'currentcolor',
|
color: icolor || 'currentcolor',
|
||||||
}"
|
}"
|
||||||
:class="`fa${styleMap[istyle]} fa-${name}`"
|
:class="`fa${styleMap[istyle]} fa-${name}`"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Grid } from "ant-design-vue";
|
import { Grid, ItemType } from "ant-design-vue";
|
||||||
const bp = Grid.useBreakpoint();
|
const bp = Grid.useBreakpoint();
|
||||||
const { data, status } = useAuth();
|
const { data, status } = useAuth();
|
||||||
const itemMap = ref({
|
const itemMap = ref({
|
||||||
@ -22,6 +22,7 @@
|
|||||||
let selected: string[] = [cur.value];
|
let selected: string[] = [cur.value];
|
||||||
|
|
||||||
const clickFn = (minfo) => {
|
const clickFn = (minfo) => {
|
||||||
|
console.log("clicky", minfo);
|
||||||
if (itemMap.value[minfo.key] === undefined) return;
|
if (itemMap.value[minfo.key] === undefined) return;
|
||||||
cur.value = itemMap.value[minfo.key];
|
cur.value = itemMap.value[minfo.key];
|
||||||
selected = [cur.value];
|
selected = [cur.value];
|
||||||
@ -43,12 +44,13 @@
|
|||||||
}"
|
}"
|
||||||
@click="clickFn"
|
@click="clickFn"
|
||||||
:active-key="cur"
|
:active-key="cur"
|
||||||
|
:key="data?.user?._id"
|
||||||
>
|
>
|
||||||
<a-menu-item key="home"> Home </a-menu-item>
|
<a-menu-item key="home"> Home </a-menu-item>
|
||||||
<a-menu-item key="bands"> Bands </a-menu-item>
|
<a-menu-item key="bands"> Bands </a-menu-item>
|
||||||
<a-menu-item key="authors"> Authors </a-menu-item>
|
<a-menu-item key="authors"> Authors </a-menu-item>
|
||||||
<a-menu-item key="forum"> Message Board</a-menu-item>
|
<a-menu-item key="forum"> Message Board</a-menu-item>
|
||||||
<a-sub-menu title="My Stuff" v-if="!!data?.user" key="group/my-stuff">
|
<a-sub-menu :disabled="!data?.user" v-if="!!data?.user" title="My Stuff" key="group/my-stuff">
|
||||||
<a-menu-item key="account"> Account </a-menu-item>
|
<a-menu-item key="account"> Account </a-menu-item>
|
||||||
<a-menu-item key="edit-profile"> Edit Profile </a-menu-item>
|
<a-menu-item key="edit-profile"> Edit Profile </a-menu-item>
|
||||||
<a-menu-item key="profile"> View Profile </a-menu-item>
|
<a-menu-item key="profile"> View Profile </a-menu-item>
|
||||||
@ -60,9 +62,9 @@
|
|||||||
<a-menu-item key="admin" v-if="data?.user?.profile.isAdmin || false"> Admin </a-menu-item>
|
<a-menu-item key="admin" v-if="data?.user?.profile.isAdmin || false"> Admin </a-menu-item>
|
||||||
<a-menu-item key="logout" v-if="!!data?.user"> Logout </a-menu-item>
|
<a-menu-item key="logout" v-if="!!data?.user"> Logout </a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
<div>
|
<div v-if="data?.user">
|
||||||
<nuxt-link to="/new-story">
|
<nuxt-link to="/new-story">
|
||||||
<a-button v-if="data?.user" type="primary" tooltip="Post a New Story">
|
<a-button type="primary" tooltip="Post a New Story">
|
||||||
<!-- <template #icon>
|
<!-- <template #icon>
|
||||||
</template> -->
|
</template> -->
|
||||||
<icon istyle="regular" name="file-plus" />
|
<icon istyle="regular" name="file-plus" />
|
||||||
@ -70,7 +72,7 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="acbut" v-if="!data">
|
<div class="acbut" v-if="!data?.user">
|
||||||
<a-button size="large" @click="() => navigateTo('/auth/login')"> Login </a-button>
|
<a-button size="large" @click="() => navigateTo('/auth/login')"> Login </a-button>
|
||||||
<a-button size="large" type="primary" @click="() => navigateTo('/auth/register')"> Register </a-button>
|
<a-button size="large" type="primary" @click="() => navigateTo('/auth/register')"> Register </a-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
import { ItemType, theme } from "ant-design-vue";
|
import { ItemType, theme } from "ant-design-vue";
|
||||||
import Icon from "../icon.vue";
|
import Icon from "../icon.vue";
|
||||||
import { ISidebarItem } from "@models/sidebarEntry";
|
import { ISidebarItem } from "@models/sidebarEntry";
|
||||||
|
import { NuxtLink } from "#components";
|
||||||
|
|
||||||
const { useToken } = theme;
|
const { useToken } = theme;
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
@ -10,8 +11,8 @@
|
|||||||
const selState = ref<string>("");
|
const selState = ref<string>("");
|
||||||
|
|
||||||
const { data: injecto } = await useApiFetch<ISidebarItem[]>("/sidebar");
|
const { data: injecto } = await useApiFetch<ISidebarItem[]>("/sidebar");
|
||||||
|
let custItems = computed(() => (injecto.value || ([] as ISidebarItem[])).sort((a, b) => a.index - b.index));
|
||||||
let items = reactive<ItemType[]>([
|
/*let items = ref<ItemType[]>([
|
||||||
{
|
{
|
||||||
key: "important",
|
key: "important",
|
||||||
label: h("span", { class: "smallcaps" }, ["Pinned"]),
|
label: h("span", { class: "smallcaps" }, ["Pinned"]),
|
||||||
@ -57,27 +58,24 @@
|
|||||||
name: "sparkles",
|
name: "sparkles",
|
||||||
size: 19,
|
size: 19,
|
||||||
}),
|
}),
|
||||||
children: (injecto.value || ([] as ISidebarItem[]))
|
children: custItems.value.map((b) => ({
|
||||||
.sort((a, b) => a.index - b.index)
|
key: b.url,
|
||||||
.map((b) => ({
|
label: h(
|
||||||
key: b.url,
|
"span",
|
||||||
label: h(
|
{
|
||||||
"span",
|
"data-color": b.color,
|
||||||
{
|
class: "custom-side-item",
|
||||||
"data-color": b.color,
|
},
|
||||||
class: "custom-side-item",
|
[h(NuxtLink, { to: b.url }, [b.linkTitle])],
|
||||||
},
|
),
|
||||||
[h(NuxtLink, { to: b.url }, [b.linkTitle])],
|
})),
|
||||||
),
|
|
||||||
})),
|
|
||||||
} as SubMenuType,
|
} as SubMenuType,
|
||||||
]);
|
]);*/
|
||||||
|
|
||||||
// console.log("wtf", items)
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<!-- <client-only>-->
|
<!-- <client-only>-->
|
||||||
<a-menu
|
<a-menu
|
||||||
|
id="sidebar-menu"
|
||||||
mode="inline"
|
mode="inline"
|
||||||
@select="
|
@select="
|
||||||
({ item, key, selectedKeys }) => {
|
({ item, key, selectedKeys }) => {
|
||||||
@ -89,37 +87,41 @@
|
|||||||
"
|
"
|
||||||
:trigger-sub-menu-action="'click'"
|
:trigger-sub-menu-action="'click'"
|
||||||
v-model:active-key="selState"
|
v-model:active-key="selState"
|
||||||
:items="items"
|
|
||||||
:inline-indent="16"
|
:inline-indent="16"
|
||||||
>
|
>
|
||||||
<!-- <a-sub-menu>
|
<a-sub-menu key="important">
|
||||||
<template #title>
|
<template #icon>
|
||||||
<sidebar-icon>
|
<icon istyle="regular" name="thumbtack" :size="19" />
|
||||||
<template #icon>
|
</template>
|
||||||
<Icon name="sparkles" istyle="regular" :size="19"/>
|
<template #title>
|
||||||
</template>
|
<span class="smallcaps">Pinned</span>
|
||||||
<template #rest>
|
</template>
|
||||||
<div class="smallcaps">
|
<a-menu-item key="/submission-rules">
|
||||||
fun features
|
<template #icon>
|
||||||
</div>
|
<icon istyle="regular" name="memo" :size="15" />
|
||||||
</template>
|
|
||||||
</sidebar-icon>
|
|
||||||
</template>
|
</template>
|
||||||
<a-menu-item>
|
<b :style="{ color: token.colorInfo }">SUBMISSION RULES</b>
|
||||||
<sidebar-icon>
|
</a-menu-item>
|
||||||
<template #icon>
|
<a-menu-item key="/terms">
|
||||||
<icon name="memo" :icolor="token.colorInfo" istyle="regular" :size="13"/>
|
<template #icon>
|
||||||
</template>
|
<icon istyle="regular" name="globe" :size="15" />
|
||||||
<template #rest>
|
</template>
|
||||||
<nuxt-link to="/submission-rules">
|
<b>Terms of Service</b>
|
||||||
<b :style="{ color: token.colorInfo }">
|
</a-menu-item>
|
||||||
submission rules
|
</a-sub-menu>
|
||||||
</b>
|
<a-sub-menu key="fun-features">
|
||||||
</nuxt-link>
|
<template #icon>
|
||||||
</template>
|
<Icon name="sparkles" istyle="regular" :size="19" />
|
||||||
</sidebar-icon>
|
</template>
|
||||||
</a-menu-item>
|
<template #title>
|
||||||
</a-sub-menu> -->
|
<div class="smallcaps">fun features</div>
|
||||||
|
</template>
|
||||||
|
<a-menu-item v-for="item in custItems" :key="item.url">
|
||||||
|
<span :style="{ color: item.color }">
|
||||||
|
<nuxt-link :to="item.url">{{ item.linkTitle }}</nuxt-link>
|
||||||
|
</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-sub-menu>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
<!-- </client-only>-->
|
<!-- </client-only>-->
|
||||||
</template>
|
</template>
|
||||||
@ -127,4 +129,7 @@
|
|||||||
.smallcaps {
|
.smallcaps {
|
||||||
font-variant: small-caps;
|
font-variant: small-caps;
|
||||||
}
|
}
|
||||||
|
#sidebar-menu ul {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -52,8 +52,8 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style>
|
||||||
.ant-list-items > * + * {
|
ul.ant-list-items > * + * {
|
||||||
margin-top: 1.2em;
|
margin-top: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import { Field, FieldArray, useForm, useFieldArray } from "vee-validate";
|
import { Field, FieldArray, useForm, useFieldArray } from "vee-validate";
|
||||||
import { ASpin } from "#components";
|
import { ASpin } from "#components";
|
||||||
import { storySchema } from "@client/storyFormSchema";
|
import { storySchema } from "@client/storyFormSchema";
|
||||||
import { FormStory, defaultChapter } from "@client/types/form/story";
|
import { FormStory, defaultChapter, FormChapter } from "@client/types/form/story";
|
||||||
import { autoEdit, autoSave, debouncedAutoEdit, debouncedAutoSave } from "@client/utils";
|
import { autoEdit, autoSave, debouncedAutoEdit, debouncedAutoSave } from "@client/utils";
|
||||||
|
|
||||||
import findUser from "~/components/findUser.vue";
|
import findUser from "~/components/findUser.vue";
|
||||||
@ -20,17 +20,16 @@
|
|||||||
endpoint: string;
|
endpoint: string;
|
||||||
endpointMethod: "put" | "post";
|
endpointMethod: "put" | "post";
|
||||||
submitText?: string;
|
submitText?: string;
|
||||||
|
draftData?: {
|
||||||
|
endpoint: string;
|
||||||
|
endpointMethod: "put" | "post";
|
||||||
|
};
|
||||||
}>();
|
}>();
|
||||||
let w;
|
|
||||||
onMounted(() => {
|
|
||||||
w = window;
|
|
||||||
});
|
|
||||||
const dc = defaultChapter;
|
const dc = defaultChapter;
|
||||||
// data: FormStory;
|
|
||||||
const sdata = defineModel<FormStory>("data", {
|
const sdata = defineModel<FormStory>("data", {
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
let drag = false;
|
let drag: boolean = false;
|
||||||
const expandos = ref<string[]>([]);
|
const expandos = ref<string[]>([]);
|
||||||
|
|
||||||
function logSubmit(values, actions) {
|
function logSubmit(values, actions) {
|
||||||
@ -46,27 +45,32 @@
|
|||||||
otherBtnInvoked.value = false;
|
otherBtnInvoked.value = false;
|
||||||
await autoSave(values);
|
await autoSave(values);
|
||||||
} else {
|
} else {
|
||||||
const { data: dat } = await useApiFetch(`/story/new`, {
|
const { data: dat } = await useApiFetch<any>(`/story/new`, {
|
||||||
method: "post",
|
method: "post",
|
||||||
body: values,
|
body: values,
|
||||||
});
|
});
|
||||||
if (dat.success) {
|
if (dat.value.success) {
|
||||||
await router.push(`/story/${dat.story._id}/1`);
|
await router.push(`/story/${dat.value.story._id}/1`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await autoEdit(values, props.endpoint, props.endpointMethod);
|
await autoEdit(values, props.endpoint, props.endpointMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function inval({ values, errors, results }) {
|
|
||||||
logSubmit(values, undefined);
|
|
||||||
}
|
|
||||||
const { values, setFieldValue, handleSubmit } = useForm<FormStory>({
|
const { values, setFieldValue, handleSubmit } = useForm<FormStory>({
|
||||||
keepValuesOnUnmount: true,
|
keepValuesOnUnmount: true,
|
||||||
validationSchema: storySchema,
|
validationSchema: storySchema,
|
||||||
initialValues: sdata.value,
|
initialValues: sdata.value,
|
||||||
});
|
});
|
||||||
// const { push, remove, move, fields } = useFieldArray<FormChapter>("chapters");
|
function renumber(update: (idx: number, value: FormChapter) => void) {
|
||||||
|
for (let i = 0; i < values.chapters.length; i++) {
|
||||||
|
const nv = values.chapters[i];
|
||||||
|
nv.index = i + 1;
|
||||||
|
update(i, nv);
|
||||||
|
sdata.value.chapters[i].index = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
const subCb = handleSubmit(onSubmit);
|
const subCb = handleSubmit(onSubmit);
|
||||||
|
|
||||||
const pushHOF = (push) => (e) => {
|
const pushHOF = (push) => (e) => {
|
||||||
@ -96,7 +100,18 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<form data-testid="storyform" @submit="subCb" @change="() => (canDraft ? debouncedAutoSave(values) : debouncedAutoEdit(values, endpoint, endpointMethod))">
|
<form
|
||||||
|
data-testid="storyform"
|
||||||
|
@submit="subCb"
|
||||||
|
@change="
|
||||||
|
() =>
|
||||||
|
canDraft
|
||||||
|
? draftData
|
||||||
|
? debouncedAutoSave(values, draftData.endpoint, draftData.endpointMethod)
|
||||||
|
: debouncedAutoSave(values)
|
||||||
|
: debouncedAutoEdit(values, endpoint, endpointMethod)
|
||||||
|
"
|
||||||
|
>
|
||||||
<!-- <a-form v-bind:model="acData"> -->
|
<!-- <a-form v-bind:model="acData"> -->
|
||||||
|
|
||||||
<Field name="title" v-slot="{ value, field, errorMessage }">
|
<Field name="title" v-slot="{ value, field, errorMessage }">
|
||||||
@ -112,30 +127,26 @@
|
|||||||
</Field>
|
</Field>
|
||||||
<a-divider />
|
<a-divider />
|
||||||
<!-- <test1/> -->
|
<!-- <test1/> -->
|
||||||
<field-array name="chapters" v-slot="{ fields, push, remove, move }">
|
<field-array name="chapters" v-slot="{ fields, push, remove, move, update }">
|
||||||
<client-only :fallback="h(ASpin)">
|
<client-only :fallback="h(ASpin)">
|
||||||
<div>
|
<div>
|
||||||
<div v-for="(element, index) in data.chapters">
|
<div v-for="(element, index) in values.chapters">
|
||||||
<client-only>
|
<client-only>
|
||||||
<Teleport :to="`#chapter-\\[${element.uuidKey}\\]`">
|
<Teleport :to="`#chapter-\\[${element.uuidKey}\\]`">
|
||||||
<a-collapse v-model:active-key="expandos" collapsible="icon">
|
<a-collapse v-model:active-key="expandos" collapsible="icon">
|
||||||
<template #expandIcon="{ isActive }">
|
<template #expandIcon="{ isActive }">
|
||||||
<span :data-testid="`storyform.chapters[${index}].collapse`"> <RightOutlined :rotate="isActive ? 90 : undefined" /></span>
|
<span :data-testid="`storyform.chapters[${index}].collapse`"> <RightOutlined :rotate="isActive ? 90 : undefined" /></span>
|
||||||
</template>
|
</template>
|
||||||
<a-collapse-panel :key="`${element.uuidKey}`">
|
<a-collapse-panel :key="`${element.uuidKey}`" :data-testid="`storyform.chapters[${index}].outer`">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div :data-testid="`storyform.chapters[${index}].header`" style="display: flex; justify-content: space-between">
|
<div :data-testid="`storyform.chapters[${index}].header`" style="display: flex; align-items: center; justify-content: space-between">
|
||||||
<span :data-testid="`storyform.chapters[${index}].titleEl`">{{ values.chapters[index]?.chapterTitle || "Untitled" }}</span>
|
<span :data-testid="`storyform.chapters[${index}].titleEl`">{{ values.chapters[index]?.chapterTitle || "Untitled" }}</span>
|
||||||
<a-button
|
<a-button
|
||||||
@click="
|
@click="
|
||||||
(e) => {
|
(e) => {
|
||||||
// let localFields = toRaw(fields);
|
|
||||||
// log.debug(`${index} | ${element.index}`);
|
|
||||||
// log.debug('fields->', localFields);
|
|
||||||
data.chapters.splice(index, 1);
|
data.chapters.splice(index, 1);
|
||||||
remove(index);
|
remove(index);
|
||||||
// todo renumber
|
renumber(update);
|
||||||
// renumber()
|
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@ -172,6 +183,7 @@
|
|||||||
console.debug(e.moved);
|
console.debug(e.moved);
|
||||||
move(e.moved.oldIndex, e.moved.newIndex);
|
move(e.moved.oldIndex, e.moved.newIndex);
|
||||||
data.chapters = lmove(data.chapters, e.moved.oldIndex, e.moved.newIndex);
|
data.chapters = lmove(data.chapters, e.moved.oldIndex, e.moved.newIndex);
|
||||||
|
renumber(update);
|
||||||
// w.tinymce.remove();
|
// w.tinymce.remove();
|
||||||
// log.debug(toRaw(acData.chapters.map((a) => toRaw(a))));
|
// log.debug(toRaw(acData.chapters.map((a) => toRaw(a))));
|
||||||
}
|
}
|
||||||
@ -191,3 +203,11 @@
|
|||||||
<a-button html-type="submit" v-if="canDraft" @click="() => (otherBtnInvoked = true)"> Save for Later </a-button>
|
<a-button html-type="submit" v-if="canDraft" @click="() => (otherBtnInvoked = true)"> Save for Later </a-button>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
<style>
|
||||||
|
.ant-collapse-item > .ant-collapse-header {
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
.ant-collapse-header.ant-collapse-icon-collapsible-only {
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
console.log(fileField.value);
|
console.log(fileField.value);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('not yet');
|
console.warn('not yet');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
@ -50,67 +50,76 @@
|
|||||||
<i> Nothing here but crickets. </i>
|
<i> Nothing here but crickets. </i>
|
||||||
</template>
|
</template>
|
||||||
<a-layout class="ylayout">
|
<a-layout class="ylayout">
|
||||||
<a-layout-header class="alayhead">
|
<a-layout>
|
||||||
<div style="display: flex; align-items: center; flex-wrap: wrap">
|
<a-layout-header class="alayhead" :style="{ backgroundColor: darkBool ? '#141414' : '#f5f5f5' }">
|
||||||
<div class="siteTitle">Rockfic</div>
|
<div style="display: flex; align-items: center; flex-wrap: wrap">
|
||||||
<div class="stat-block">
|
<div class="siteTitle">Rockfic</div>
|
||||||
<div>
|
<div class="stat-block">
|
||||||
<a-typography-text> Band fiction that rocks </a-typography-text>
|
<div>
|
||||||
<a-typography-text type="secondary"> With {{ totals?.stories || 0 }} stories by {{ totals?.authors || 0 }} authors </a-typography-text>
|
<a-typography-text> Band fiction that rocks </a-typography-text>
|
||||||
</div>
|
<a-typography-text type="secondary"> With {{ totals?.stories || 0 }} stories by {{ totals?.authors || 0 }} authors </a-typography-text>
|
||||||
</div>
|
|
||||||
<navbar v-if="!!bp['md']" :inline="false" />
|
|
||||||
</div>
|
|
||||||
<a-button class="mobileTrigger" type="primary" @click="() => (collapsed = !collapsed)" v-if="!bp['md']">
|
|
||||||
<menu-unfold-outlined v-if="nav" />
|
|
||||||
<menu-fold-outlined v-else />
|
|
||||||
</a-button>
|
|
||||||
</a-layout-header>
|
|
||||||
<a-layout class="mlayout" has-sider style="padding-top: 1em">
|
|
||||||
<a-layout-sider :trigger="null" :collapsed="true" :collapsed-width="0" :collapsible="true" v-model:collapsed="collapsed" v-if="!bp['md']">
|
|
||||||
<navbar inline />
|
|
||||||
</a-layout-sider>
|
|
||||||
<a-layout-content style="flex-grow: 1">
|
|
||||||
<slot />
|
|
||||||
</a-layout-content>
|
|
||||||
<a-layout-sider
|
|
||||||
:zero-width-trigger-style="{
|
|
||||||
background: '#e92662',
|
|
||||||
padding: '1.2em',
|
|
||||||
position: 'fixed',
|
|
||||||
right: 0,
|
|
||||||
borderRadius: '15%',
|
|
||||||
color: 'white',
|
|
||||||
border: '2.4px solid #fffFFF80',
|
|
||||||
top: '75vh',
|
|
||||||
'z-index': 99999999,
|
|
||||||
}"
|
|
||||||
:theme="darko ? 'dark' : 'light'"
|
|
||||||
:breakpoint="'lg'"
|
|
||||||
v-model:collapsed="collapsed"
|
|
||||||
:collapsible="true"
|
|
||||||
:collapsed-width="0"
|
|
||||||
:style="{
|
|
||||||
color: col,
|
|
||||||
height: '100%',
|
|
||||||
position: 'fixed',
|
|
||||||
right: '0px',
|
|
||||||
borderLeft: `2px solid ${darko ? '#fff' : '#ccc'}`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<sidebar-thing />
|
|
||||||
<template #trigger>
|
|
||||||
<div class="outerst" @click="() => (collapsed = !collapsed)">
|
|
||||||
<div :class="sideTriggerVal">
|
|
||||||
<icon istyle="solid" name="chevron-right" :size="30" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<navbar v-if="!!bp['md']" :inline="false" />
|
||||||
</a-layout-sider>
|
</div>
|
||||||
|
<a-button class="mobileTrigger" type="primary" @click="() => (nav = !nav)" v-if="!bp['md']">
|
||||||
|
<menu-unfold-outlined v-if="nav" />
|
||||||
|
<menu-fold-outlined v-else />
|
||||||
|
</a-button>
|
||||||
|
</a-layout-header>
|
||||||
|
<a-layout class="mlayout" has-sider style="padding-top: 1em">
|
||||||
|
<a-layout-sider
|
||||||
|
:trigger="null"
|
||||||
|
:theme="darkBool ? 'dark' : 'light'"
|
||||||
|
:collapsed="nav"
|
||||||
|
:collapsed-width="0"
|
||||||
|
:collapsible="true"
|
||||||
|
v-model:collapsed="collapsed"
|
||||||
|
v-if="!bp['md']"
|
||||||
|
>
|
||||||
|
<navbar inline />
|
||||||
|
</a-layout-sider>
|
||||||
|
<a-layout-content style="flex-grow: 1">
|
||||||
|
<slot />
|
||||||
|
</a-layout-content>
|
||||||
|
<a-layout-sider
|
||||||
|
:zero-width-trigger-style="{
|
||||||
|
background: '#e92662',
|
||||||
|
padding: '1.2em',
|
||||||
|
position: 'fixed',
|
||||||
|
right: 0,
|
||||||
|
borderRadius: '15%',
|
||||||
|
color: 'white',
|
||||||
|
border: '2.4px solid #fffFFF80',
|
||||||
|
top: '75vh',
|
||||||
|
'z-index': 99999999,
|
||||||
|
}"
|
||||||
|
:theme="darko ? 'dark' : 'light'"
|
||||||
|
:breakpoint="'lg'"
|
||||||
|
v-model:collapsed="collapsed"
|
||||||
|
:collapsible="true"
|
||||||
|
:collapsed-width="0"
|
||||||
|
:style="{
|
||||||
|
color: col,
|
||||||
|
right: '-3px',
|
||||||
|
alignSelf: 'stretch',
|
||||||
|
borderLeft: `2px solid ${darko ? '#fff' : '#ccc'}`,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<sidebar-thing />
|
||||||
|
<template #trigger>
|
||||||
|
<div class="outerst" @click="() => (collapsed = !collapsed)">
|
||||||
|
<div :class="sideTriggerVal">
|
||||||
|
<icon istyle="solid" name="chevron-right" :size="30" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-layout-sider>
|
||||||
|
</a-layout>
|
||||||
|
<a-layout-footer style="bottom: 100%">
|
||||||
|
<cfooter />
|
||||||
|
</a-layout-footer>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
<a-layout-footer style="bottom: 100%">
|
|
||||||
<cfooter />
|
|
||||||
</a-layout-footer>
|
|
||||||
</a-layout>
|
</a-layout>
|
||||||
<!-- <div class="mlayout">
|
<!-- <div class="mlayout">
|
||||||
<a-skeleton/>
|
<a-skeleton/>
|
||||||
@ -137,7 +146,7 @@
|
|||||||
|
|
||||||
.sideWrap {
|
.sideWrap {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
right: -1rem;
|
right: -3rem;
|
||||||
width: inherit;
|
width: inherit;
|
||||||
/* display: flex;
|
/* display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -4,14 +4,14 @@ import { useRoute, useRouter } from "#app";
|
|||||||
|
|
||||||
const base = `/user/me`;
|
const base = `/user/me`;
|
||||||
|
|
||||||
export const favourites = (values: (any & { _id: number })[], id: number, remove: boolean, type: "story" | "author") => {
|
export const favourites = async (values: (any & { _id: number })[], id: number, remove: boolean, type: "story" | "author") => {
|
||||||
values?.splice(
|
values?.splice(
|
||||||
values!.findIndex((a) => a._id == id),
|
values!.findIndex((a) => a._id == id),
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
const key = type === "story" ? "stories" : "authors";
|
const key = type === "story" ? "stories" : "authors";
|
||||||
const todo = [id];
|
const todo = [id];
|
||||||
useApiFetch(`${base}/favs`, {
|
await useApiFetch(`${base}/favs`, {
|
||||||
method: "put",
|
method: "put",
|
||||||
body: {
|
body: {
|
||||||
[key]: {
|
[key]: {
|
||||||
@ -22,7 +22,7 @@ export const favourites = (values: (any & { _id: number })[], id: number, remove
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const subscriptions = (
|
export const subscriptions = async (
|
||||||
values: ((any & { _id: number }) | number)[],
|
values: ((any & { _id: number }) | number)[],
|
||||||
id: number,
|
id: number,
|
||||||
action: "hide" | "subscribe" | "unsubscribe",
|
action: "hide" | "subscribe" | "unsubscribe",
|
||||||
@ -36,7 +36,7 @@ export const subscriptions = (
|
|||||||
values!.findIndex((a) => a._id == id || a == id),
|
values!.findIndex((a) => a._id == id || a == id),
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
useApiFetch(`${base}/${action}`, {
|
await useApiFetch(`${base}/${action}`, {
|
||||||
body: {
|
body: {
|
||||||
push: {
|
push: {
|
||||||
[type]: [id],
|
[type]: [id],
|
||||||
@ -46,7 +46,7 @@ export const subscriptions = (
|
|||||||
method: "put",
|
method: "put",
|
||||||
});
|
});
|
||||||
} else if (action == "subscribe") {
|
} else if (action == "subscribe") {
|
||||||
useApiFetch(`${base}/subscriptions`, {
|
await useApiFetch(`${base}/subscriptions`, {
|
||||||
body: {
|
body: {
|
||||||
push: {
|
push: {
|
||||||
[type]: [id],
|
[type]: [id],
|
||||||
@ -56,7 +56,7 @@ export const subscriptions = (
|
|||||||
method: "put",
|
method: "put",
|
||||||
});
|
});
|
||||||
} else if (action == "unsubscribe") {
|
} else if (action == "unsubscribe") {
|
||||||
useApiFetch(`${base}/subscriptions`, {
|
await useApiFetch(`${base}/subscriptions`, {
|
||||||
body: {
|
body: {
|
||||||
pull: {
|
pull: {
|
||||||
[type]: [id],
|
[type]: [id],
|
||||||
|
@ -142,3 +142,4 @@ export const rc: RuntimeConfig & any = {
|
|||||||
},
|
},
|
||||||
app: {},
|
app: {},
|
||||||
};
|
};
|
||||||
|
export const doNotSelect = ["password", "auth", "ipLog"].map((b) => `-${b}`).join(" ");
|
||||||
|
@ -4,7 +4,7 @@ export interface IChapter {
|
|||||||
title: string;
|
title: string;
|
||||||
summary: string;
|
summary: string;
|
||||||
id?: number;
|
id?: number;
|
||||||
// index: number;
|
index: number;
|
||||||
words?: number;
|
words?: number;
|
||||||
notes: string;
|
notes: string;
|
||||||
genre: string[];
|
genre: string[];
|
||||||
@ -25,6 +25,9 @@ export const Chapter = new Schema<IChapter>({
|
|||||||
id: {
|
id: {
|
||||||
type: Number,
|
type: Number,
|
||||||
},
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
summary: {
|
summary: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
|
29
package.json
29
package.json
@ -4,8 +4,9 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt build",
|
"build": "nuxt build",
|
||||||
"dev": "nuxt dev",
|
"dev": "nuxt dev --host 127.0.0.1",
|
||||||
"generate": "nuxt generate",
|
"generate": "nuxt generate",
|
||||||
|
"postinstall": "nuxt prepare",
|
||||||
"preview": "nuxt preview",
|
"preview": "nuxt preview",
|
||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
@ -13,17 +14,27 @@
|
|||||||
"@nuxt/devtools": "latest",
|
"@nuxt/devtools": "latest",
|
||||||
"@nuxtjs/i18n": "^8.0.0-rc.9",
|
"@nuxtjs/i18n": "^8.0.0-rc.9",
|
||||||
"@types/uuid": "^9.0.4",
|
"@types/uuid": "^9.0.4",
|
||||||
"@vue/language-server": "^1.8.27",
|
"@vitejs/plugin-vue": "^5.0.2",
|
||||||
"nuxt": "^3.9.0",
|
"@vitest/browser": "^1.1.2",
|
||||||
|
"@vitest/ui": "^1.1.3",
|
||||||
|
"@vue/language-server": "latest",
|
||||||
|
"@vue/test-utils": "^2.4.3",
|
||||||
|
"happy-dom": "^12.10.3",
|
||||||
|
"jsdom": "^23.0.1",
|
||||||
|
"nuxt": "^3.12.3",
|
||||||
|
"playwright": "^1.40.1",
|
||||||
|
"playwright-core": "^1.40.1",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"tsconfig-to-dual-package": "^1.2.0",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "latest"
|
"typescript": "latest",
|
||||||
|
"unplugin-vue-components": "^0.26.0",
|
||||||
|
"vitest": "^1.4.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design-vue/nuxt": "^1.4.1",
|
"@ant-design-vue/nuxt": "^1.4.1",
|
||||||
"@pinia/nuxt": "^0.4.11",
|
"@pinia/nuxt": "^0.4.11",
|
||||||
"@tinymce/tinymce-vue": "latest",
|
|
||||||
"@sidebase/nuxt-auth": "0.7.0",
|
"@sidebase/nuxt-auth": "0.7.0",
|
||||||
|
"@tinymce/tinymce-vue": "latest",
|
||||||
"@types/jsonwebtoken": "^9.0.3",
|
"@types/jsonwebtoken": "^9.0.3",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/turndown": "^5.0.4",
|
"@types/turndown": "^5.0.4",
|
||||||
@ -33,7 +44,7 @@
|
|||||||
"axios": "^1.5.1",
|
"axios": "^1.5.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"blueimp-md5": "^2.19.0",
|
"blueimp-md5": "^2.19.0",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "latest",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lodash-move": "^1.1.1",
|
"lodash-move": "^1.1.1",
|
||||||
@ -44,13 +55,13 @@
|
|||||||
"mongoose-sequence": "https://github.com/amansingh63/mongoose-sequence",
|
"mongoose-sequence": "https://github.com/amansingh63/mongoose-sequence",
|
||||||
"nitropack": "^2.9.4",
|
"nitropack": "^2.9.4",
|
||||||
"nuxi": "^3.10.0",
|
"nuxi": "^3.10.0",
|
||||||
|
"nuxt-booster": "^3.0.0",
|
||||||
"nuxt-security": "^0.14.4",
|
"nuxt-security": "^0.14.4",
|
||||||
"nuxt-speedkit": "3.0.0-next.26",
|
"nuxt-speedkit": "3.0.0-next.26",
|
||||||
"pinia": "^2.1.6",
|
"pinia": "^2.1.6",
|
||||||
"sanitize-html": "^2.11.0",
|
"sanitize-html": "^2.11.0",
|
||||||
"sharp": "^0.33.2",
|
"sharp": "^0.33.3",
|
||||||
"string-strip-html": "^13.4.3",
|
"string-strip-html": "^13.4.3",
|
||||||
"tinymce": "^6.7.0",
|
|
||||||
"turndown": "^7.1.2",
|
"turndown": "^7.1.2",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vee-validate": "^4.11.7",
|
"vee-validate": "^4.11.7",
|
||||||
|
@ -12,22 +12,24 @@
|
|||||||
},
|
},
|
||||||
middleware: ["auth"],
|
middleware: ["auth"],
|
||||||
});
|
});
|
||||||
|
useHead({
|
||||||
|
title: "Log In",
|
||||||
|
});
|
||||||
|
|
||||||
const formState = reactive<FormState>({
|
const formState = reactive<FormState>({
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
});
|
});
|
||||||
|
const darkRef = inject<Ref<boolean>>("dark");
|
||||||
const onFinish = async (values: any) => {
|
const onFinish = async (values: any) => {
|
||||||
const { signIn, data } = useAuth();
|
const { signIn } = useAuth();
|
||||||
|
|
||||||
let reso: any;
|
|
||||||
try {
|
try {
|
||||||
reso = await signIn(values);
|
await signIn(values, { redirect: true, callbackUrl: "/" });
|
||||||
|
const { data } = useAuth();
|
||||||
await navigateTo({
|
darkRef.value = data.value.user.profile.nightMode;
|
||||||
path: "/",
|
if (darkRef.value) document.body.dataset.theme = "dark";
|
||||||
});
|
await navigateTo("/");
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e.data) {
|
if (e.data) {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
signOut({
|
signOut({
|
||||||
callbackUrl: "/",
|
callbackUrl: "/",
|
||||||
});
|
});
|
||||||
|
const d = inject<Ref<boolean>>("dark");
|
||||||
|
d.value = false;
|
||||||
|
document.body.dataset.theme = undefined;
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<a-typography-title :level="3"> Signed out. 👋 </a-typography-title>
|
<a-typography-title :level="3"> Signed out. 👋 </a-typography-title>
|
||||||
|
@ -6,12 +6,18 @@
|
|||||||
|
|
||||||
const { data: bands } = (await useApiFetch<NonNullable<IBand[]>>("/band/all")) as unknown as { data: Ref<IBand[]> };
|
const { data: bands } = (await useApiFetch<NonNullable<IBand[]>>("/band/all")) as unknown as { data: Ref<IBand[]> };
|
||||||
|
|
||||||
const { data: rd }: { data: any } = useAuth();
|
const {
|
||||||
|
data: { value: rd },
|
||||||
|
getSession,
|
||||||
|
} = useAuth();
|
||||||
|
await getSession({ force: true });
|
||||||
|
let inc = ref<number>(1);
|
||||||
|
const data = ref(rd);
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
await useAuth().getSession({ force: true });
|
await useAuth().getSession({ force: true });
|
||||||
rd.value = useAuth().data.value;
|
data.value = useAuth().data.value;
|
||||||
|
//inc.value += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const hider = subscriptions;
|
const hider = subscriptions;
|
||||||
if (bands.value == null) bands.value = [];
|
if (bands.value == null) bands.value = [];
|
||||||
useHead({
|
useHead({
|
||||||
@ -21,7 +27,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-list v-model:data-source="bands" :grid="bp">
|
<a-list v-model:data-source="bands" :grid="bp">
|
||||||
<template #renderItem="{ item }">
|
<template #renderItem="{ item }">
|
||||||
<a-list-item>
|
<a-list-item :key="item._id + inc">
|
||||||
<a-row :gutter="[5, 5]">
|
<a-row :gutter="[5, 5]">
|
||||||
<a-col>
|
<a-col>
|
||||||
<nuxt-link :to="`/band/${item._id}`">
|
<nuxt-link :to="`/band/${item._id}`">
|
||||||
@ -29,12 +35,12 @@
|
|||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</a-col>
|
</a-col>
|
||||||
<!-- subscribe... -->
|
<!-- subscribe... -->
|
||||||
<a-col v-if="rd && rd.user?._id" style="margin-left: auto">
|
<a-col v-if="data && data.user?._id" style="margin-left: auto">
|
||||||
<a
|
<a
|
||||||
v-if="!rd?.user.subscriptions.bands.includes(item._id)"
|
v-if="!data?.user.subscriptions.bands.includes(item._id)"
|
||||||
@click="
|
@click="
|
||||||
async (e) => {
|
async (e) => {
|
||||||
hider(bands, item._id, 'subscribe', 'bands');
|
await hider(bands, item._id, 'subscribe', 'bands');
|
||||||
await refresh();
|
await refresh();
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@ -45,7 +51,7 @@
|
|||||||
v-else
|
v-else
|
||||||
@click="
|
@click="
|
||||||
async (e) => {
|
async (e) => {
|
||||||
hider(bands, item._id, 'unsubscribe', 'bands');
|
await hider(bands, item._id, 'unsubscribe', 'bands');
|
||||||
await refresh();
|
await refresh();
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@ -53,11 +59,11 @@
|
|||||||
<icon :istyle="'regular'" name="x" :size="12" />
|
<icon :istyle="'regular'" name="x" :size="12" />
|
||||||
</a>
|
</a>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col v-if="rd?.user._id">
|
<a-col v-if="data?.user._id">
|
||||||
<a
|
<a
|
||||||
@click="
|
@click="
|
||||||
async (e) => {
|
async (e) => {
|
||||||
hider(bands, item._id, 'hide', 'bands');
|
await hider(bands, item._id, 'hide', 'bands');
|
||||||
await refresh();
|
await refresh();
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a-typography-title :level="1"> The Latest Fics </a-typography-title>
|
<a-typography-title :level="1" style="margin-top: 0"> The Latest Fics </a-typography-title>
|
||||||
<story-list :last="true" prefix="/latest" />
|
<story-list :last="true" prefix="/latest" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -2,11 +2,12 @@ import mongoose from "mongoose";
|
|||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { User } from "@models/user";
|
import { User } from "@models/user";
|
||||||
import { log } from "@server/logger";
|
import { log } from "@server/logger";
|
||||||
|
import { doNotSelect } from "@server/constants";
|
||||||
|
|
||||||
export default eventHandler(async (event) => {
|
export default eventHandler(async (event) => {
|
||||||
const wrongMsg = "wrong credentials";
|
const wrongMsg = "wrong credentials";
|
||||||
let reqbody = await readBody(event);
|
let reqbody = await readBody(event);
|
||||||
let user = await User.findOne({ username: reqbody.username }).exec();
|
let user = await User.findOne({ username: reqbody.username }).select(doNotSelect).exec();
|
||||||
// log.debug(reqbody, { label: "login/body" });
|
// log.debug(reqbody, { label: "login/body" });
|
||||||
// log.debug("USER -> " + user, { label: "login" });
|
// log.debug("USER -> " + user, { label: "login" });
|
||||||
// log.debug("conn ->" + mongoose.connection, { label: "login" });
|
// log.debug("conn ->" + mongoose.connection, { label: "login" });
|
||||||
@ -30,8 +31,9 @@ export default eventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
let tok = user.generateRefreshToken(useRuntimeConfig().jwt);
|
let tok = user.generateRefreshToken(useRuntimeConfig().jwt);
|
||||||
// setCookie(event, "rockfic_cookie", tok);
|
// setCookie(event, "rockfic_cookie", tok);
|
||||||
|
const fu = user.toObject();
|
||||||
return {
|
return {
|
||||||
user,
|
user: fu,
|
||||||
token: {
|
token: {
|
||||||
refresh: tok,
|
refresh: tok,
|
||||||
access: user.generateAccessToken(useRuntimeConfig().jwt),
|
access: user.generateAccessToken(useRuntimeConfig().jwt),
|
||||||
|
@ -11,6 +11,12 @@ import { messages } from "@server/constants";
|
|||||||
export default eventHandler(async (ev) => {
|
export default eventHandler(async (ev) => {
|
||||||
let os: (Document<unknown, {}, IStory> & IStory) | null = await storyQuerier(ev);
|
let os: (Document<unknown, {}, IStory> & IStory) | null = await storyQuerier(ev);
|
||||||
isLoggedIn(ev);
|
isLoggedIn(ev);
|
||||||
|
if (!os) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 404,
|
||||||
|
message: messages[404],
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!canModify(ev, os)) {
|
if (!canModify(ev, os)) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 403,
|
statusCode: 403,
|
||||||
@ -23,39 +29,31 @@ export default eventHandler(async (ev) => {
|
|||||||
completed: body.completed,
|
completed: body.completed,
|
||||||
coAuthor: !!body.coAuthor ? body.coAuthor : null,
|
coAuthor: !!body.coAuthor ? body.coAuthor : null,
|
||||||
};
|
};
|
||||||
for (const oc of os.chapters) {
|
|
||||||
let filename = `/stories/${oc.id}.txt`;
|
|
||||||
const bucket = getBucket();
|
|
||||||
const curs = bucket.find({ filename }).limit(1);
|
|
||||||
for await (const d of curs) {
|
|
||||||
await bucket.delete(d._id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const cc = os.chapters;
|
|
||||||
os.chapters = [];
|
|
||||||
await os.save();
|
|
||||||
for (const c of body.chapters) {
|
for (const c of body.chapters) {
|
||||||
let idx = cc.findIndex((k) => k.id === c.id);
|
let idx = os.chapters.findIndex((k) => k.id === c.id);
|
||||||
const cont = await bodyHandler(c);
|
const cont = await bodyHandler(c);
|
||||||
if (idx === -1) {
|
if (idx === -1) {
|
||||||
os.chapters!.push({
|
os.chapters!.push({
|
||||||
...modelFormChapter(c),
|
...modelFormChapter(c),
|
||||||
|
words: countWords(cont),
|
||||||
posted: new Date(Date.now()),
|
posted: new Date(Date.now()),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
os.chapters!.push({
|
os.chapters[idx] = {
|
||||||
...modelFormChapter(c),
|
...modelFormChapter(c),
|
||||||
// id: os.chapters[idx].id,
|
id: os.chapters[idx].id,
|
||||||
words: countWords(cont),
|
words: countWords(cont),
|
||||||
posted: cc[idx].posted,
|
posted: os.chapters[idx].posted,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
os.chapters.sort((a, b) => a.index - b.index);
|
||||||
await os.save();
|
await os.save();
|
||||||
for (let i = 0; i < os.chapters.length; i++) {
|
for (let i = 0; i < os.chapters.length; i++) {
|
||||||
const c = os.chapters[i];
|
const c = os.chapters[i];
|
||||||
const cont = await bodyHandler(body.chapters[i]);
|
const cont = await bodyHandler(body.chapters[i]);
|
||||||
await replaceOrUploadContent(c.id ?? c._id, cont);
|
await replaceOrUploadContent(c.id ?? c._id, cont, getBucket());
|
||||||
}
|
}
|
||||||
os = await Story.findOneAndUpdate(
|
os = await Story.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"outDir": "../out",
|
"outDir": "../out",
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"verbatimModuleSyntax": false
|
"verbatimModuleSyntax": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
{
|
{
|
||||||
// https://nuxt.com/docs/guide/concepts/typescript
|
|
||||||
"extends": "./.nuxt/tsconfig.json",
|
"extends": "./.nuxt/tsconfig.json",
|
||||||
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"noImplicitThis": false,
|
"noImplicitThis": false,
|
||||||
"verbatimModuleSyntax": false
|
"verbatimModuleSyntax": false,
|
||||||
// "paths": {
|
"forceConsistentCasingInFileNames": false,
|
||||||
// "@dbconfig": ["./lib/dbconfig.ts"],
|
"allowSyntheticDefaultImports": true
|
||||||
// "@functions": ["./lib/functions.ts"]
|
},
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user