diff options
Diffstat (limited to 'src')
29 files changed, 261 insertions, 55 deletions
diff --git a/src/client/app/common/views/pages/page/page.block.vue b/src/client/app/common/views/components/page/page.block.vue index 1c421fc2c0..1c421fc2c0 100644 --- a/src/client/app/common/views/pages/page/page.block.vue +++ b/src/client/app/common/views/components/page/page.block.vue diff --git a/src/client/app/common/views/pages/page/page.button.vue b/src/client/app/common/views/components/page/page.button.vue index 4dc6570019..87112aca0d 100644 --- a/src/client/app/common/views/pages/page/page.button.vue +++ b/src/client/app/common/views/components/page/page.button.vue @@ -49,7 +49,7 @@ export default Vue.extend({ <style lang="stylus" scoped> .kudkigyw display inline-block - min-width 300px + min-width 200px max-width 450px margin 8px 0 </style> diff --git a/src/client/app/common/views/pages/page/page.counter.vue b/src/client/app/common/views/components/page/page.counter.vue index 8d55319fe9..8d55319fe9 100644 --- a/src/client/app/common/views/pages/page/page.counter.vue +++ b/src/client/app/common/views/components/page/page.counter.vue diff --git a/src/client/app/common/views/pages/page/page.if.vue b/src/client/app/common/views/components/page/page.if.vue index 417ef0c553..417ef0c553 100644 --- a/src/client/app/common/views/pages/page/page.if.vue +++ b/src/client/app/common/views/components/page/page.if.vue diff --git a/src/client/app/common/views/pages/page/page.image.vue b/src/client/app/common/views/components/page/page.image.vue index 1285445eb0..1285445eb0 100644 --- a/src/client/app/common/views/pages/page/page.image.vue +++ b/src/client/app/common/views/components/page/page.image.vue diff --git a/src/client/app/common/views/pages/page/page.number-input.vue b/src/client/app/common/views/components/page/page.number-input.vue index 31da37330a..31da37330a 100644 --- a/src/client/app/common/views/pages/page/page.number-input.vue +++ b/src/client/app/common/views/components/page/page.number-input.vue diff --git a/src/client/app/common/views/pages/page/page.post.vue b/src/client/app/common/views/components/page/page.post.vue index cb695e21e9..cb695e21e9 100644 --- a/src/client/app/common/views/pages/page/page.post.vue +++ b/src/client/app/common/views/components/page/page.post.vue diff --git a/src/client/app/common/views/pages/page/page.section.vue b/src/client/app/common/views/components/page/page.section.vue index 03c009d9c3..03c009d9c3 100644 --- a/src/client/app/common/views/pages/page/page.section.vue +++ b/src/client/app/common/views/components/page/page.section.vue diff --git a/src/client/app/common/views/pages/page/page.switch.vue b/src/client/app/common/views/components/page/page.switch.vue index 53695f1b36..53695f1b36 100644 --- a/src/client/app/common/views/pages/page/page.switch.vue +++ b/src/client/app/common/views/components/page/page.switch.vue diff --git a/src/client/app/common/views/pages/page/page.text-input.vue b/src/client/app/common/views/components/page/page.text-input.vue index cf917dd5a8..cf917dd5a8 100644 --- a/src/client/app/common/views/pages/page/page.text-input.vue +++ b/src/client/app/common/views/components/page/page.text-input.vue diff --git a/src/client/app/common/views/pages/page/page.text.vue b/src/client/app/common/views/components/page/page.text.vue index 326fd39050..326fd39050 100644 --- a/src/client/app/common/views/pages/page/page.text.vue +++ b/src/client/app/common/views/components/page/page.text.vue diff --git a/src/client/app/common/views/pages/page/page.textarea-input.vue b/src/client/app/common/views/components/page/page.textarea-input.vue index eece59fefb..eece59fefb 100644 --- a/src/client/app/common/views/pages/page/page.textarea-input.vue +++ b/src/client/app/common/views/components/page/page.textarea-input.vue diff --git a/src/client/app/common/views/pages/page/page.textarea.vue b/src/client/app/common/views/components/page/page.textarea.vue index 03c8542cb0..03c8542cb0 100644 --- a/src/client/app/common/views/pages/page/page.textarea.vue +++ b/src/client/app/common/views/components/page/page.textarea.vue diff --git a/src/client/app/common/views/pages/page/page.vue b/src/client/app/common/views/components/page/page.vue index a93d5316d5..99e627fd89 100644 --- a/src/client/app/common/views/pages/page/page.vue +++ b/src/client/app/common/views/components/page/page.vue @@ -1,6 +1,6 @@ <template> -<div v-if="page" class="iroscrza" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners, center: page.alignCenter }" :style="{ fontFamily: page.font }" :key="path"> - <header> +<div class="iroscrza" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners, center: page.alignCenter }" :style="{ fontFamily: page.font }"> + <header v-if="showTitle"> <div class="title">{{ page.title }}</div> </header> @@ -8,9 +8,13 @@ <x-block v-for="child in page.content" :value="child" @input="v => updateBlock(v)" :page="page" :script="script" :key="child.id" :h="2"/> </div> - <footer> + <footer v-if="showFooter"> <small>@{{ page.user.username }}</small> - <router-link v-if="$store.getters.isSignedIn && $store.state.i.id === page.userId" :to="`/i/pages/edit/${page.id}`">{{ $t('edit-this-page') }}</router-link> + <template v-if="$store.getters.isSignedIn && $store.state.i.id === page.userId"> + <router-link :to="`/i/pages/edit/${page.id}`">{{ $t('edit-this-page') }}</router-link> + <a v-if="$store.state.i.pinnedPageId === page.id" @click="pin(false)">{{ $t('unpin-this-page') }}</a> + <a v-else @click="pin(true)">{{ $t('pin-this-page') }}</a> + </template> <router-link :to="`./${page.name}/view-source`">{{ $t('view-source') }}</router-link> <div class="like"> <button @click="unlike()" v-if="page.isLiked" :title="$t('unlike')"><fa :icon="faHeartS"/></button> @@ -25,7 +29,7 @@ import Vue from 'vue'; import i18n from '../../../../i18n'; import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons'; -import { faHeart, faStickyNote } from '@fortawesome/free-regular-svg-icons'; +import { faHeart } from '@fortawesome/free-regular-svg-icons'; import XBlock from './page.block.vue'; import { ASEvaluator } from '../../../../../../misc/aiscript/evaluator'; import { collectPageVars } from '../../../scripts/collect-page-vars'; @@ -69,64 +73,43 @@ export default Vue.extend({ }, props: { - pageName: { - type: String, + page: { + type: Object, required: true }, - username: { - type: String, - required: true + showTitle: { + type: Boolean, + required: false, + default: true + }, + showFooter: { + type: Boolean, + required: false, + default: false }, }, data() { return { - page: null, script: null, faHeartS, faHeart }; }, - computed: { - path(): string { - return this.username + '/' + this.pageName; - } - }, - - watch: { - path() { - this.fetch(); - } - }, - created() { - this.fetch(); + const pageVars = this.getPageVars(); + this.script = new Script(this.page, new ASEvaluator(this.page.variables, pageVars, { + randomSeed: Math.random(), + user: this.page.user, + visitor: this.$store.state.i, + page: this.page, + url: url + }), e => { + console.dir(e); + }); }, methods: { - fetch() { - this.$root.api('pages/show', { - name: this.pageName, - username: this.username, - }).then(page => { - this.page = page; - this.$emit('init', { - title: this.page.title, - icon: faStickyNote - }); - const pageVars = this.getPageVars(); - this.script = new Script(this.page, new ASEvaluator(this.page.variables, pageVars, { - randomSeed: Math.random(), - user: page.user, - visitor: this.$store.state.i, - page: page, - url: url - }), e => { - console.dir(e); - }); - }); - }, - getPageVars() { return collectPageVars(this.page.content); }, @@ -147,6 +130,17 @@ export default Vue.extend({ this.page.isLiked = false; this.page.likedCount--; }); + }, + + pin(pin) { + this.$root.api('i/update', { + pinnedPageId: pin ? this.page.id : null, + }).then(() => { + this.$root.dialog({ + type: 'success', + splash: true + }); + }); } } }); diff --git a/src/client/app/common/views/deck/deck.page-column.vue b/src/client/app/common/views/deck/deck.page-column.vue new file mode 100644 index 0000000000..0ef391a51d --- /dev/null +++ b/src/client/app/common/views/deck/deck.page-column.vue @@ -0,0 +1,69 @@ +<template> +<x-column> + <template #header> + <fa :icon="faStickyNote"/>{{ page ? page.name : '' }} + </template> + + <div v-if="page"> + <x-page :page="page" :key="page.id"/> + </div> +</x-column> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import { faStickyNote } from '@fortawesome/free-regular-svg-icons'; +import i18n from '../../../i18n'; +import XColumn from './deck.column.vue'; +import XPage from '../../../common/views/components/page/page.vue'; + +export default Vue.extend({ + i18n: i18n(), + + components: { + XColumn, + XPage + }, + + props: { + pageName: { + type: String, + required: true + }, + username: { + type: String, + required: true + }, + }, + + data() { + return { + page: null, + faStickyNote + }; + }, + + watch: { + $route: 'fetch' + }, + + created() { + this.fetch(); + }, + + methods: { + fetch() { + this.$root.api('pages/show', { + name: this.pageName, + username: this.username, + }).then(page => { + this.page = page; + this.$emit('init', { + title: this.page.title, + icon: faStickyNote + }); + }); + } + } +}); +</script> diff --git a/src/client/app/common/views/deck/deck.user-column.home.vue b/src/client/app/common/views/deck/deck.user-column.home.vue index 56b117a7dd..9fb50a6672 100644 --- a/src/client/app/common/views/deck/deck.user-column.home.vue +++ b/src/client/app/common/views/deck/deck.user-column.home.vue @@ -1,5 +1,11 @@ <template> <div> + <ui-container v-if="user.pinnedPage" :body-togglable="true"> + <template #header><fa icon="thumbtack"/> {{ $t('pinned-page') }}</template> + <div> + <x-page :page="user.pinnedPage" :key="user.pinnedPage.id" :show-title="!user.pinnedPage.hideTitleWhenPinned"/> + </div> + </ui-container> <ui-container v-if="user.pinnedNotes && user.pinnedNotes.length > 0" :body-togglable="true"> <template #header><fa icon="thumbtack"/> {{ $t('pinned-notes') }}</template> <div> @@ -48,6 +54,7 @@ export default Vue.extend({ components: { XNotes, + XPage: () => import('../../../common/views/components/page/page.vue').then(m => m.default), }, props: { diff --git a/src/client/app/common/views/pages/page-editor/page-editor.vue b/src/client/app/common/views/pages/page-editor/page-editor.vue index ebe0f4688d..ade7d86991 100644 --- a/src/client/app/common/views/pages/page-editor/page-editor.vue +++ b/src/client/app/common/views/pages/page-editor/page-editor.vue @@ -35,6 +35,8 @@ <option value="sans-serif">{{ $t('fontSansSerif') }}</option> </ui-select> + <ui-switch v-model="hideTitleWhenPinned">{{ $t('hide-title-when-pinned') }}</ui-switch> + <div class="eyeCatch"> <ui-button v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('set-eye-catching-image') }}</ui-button> <div v-else-if="eyeCatchingImage"> @@ -140,6 +142,7 @@ export default Vue.extend({ font: 'sans-serif', content: [], alignCenter: false, + hideTitleWhenPinned: false, variables: [], aiScript: null, showOptions: false, @@ -192,6 +195,7 @@ export default Vue.extend({ this.currentName = this.page.name; this.summary = this.page.summary; this.font = this.page.font; + this.hideTitleWhenPinned = this.page.hideTitleWhenPinned; this.alignCenter = this.page.alignCenter; this.content = this.page.content; this.variables = this.page.variables; @@ -223,6 +227,7 @@ export default Vue.extend({ name: this.name.trim(), summary: this.summary, font: this.font, + hideTitleWhenPinned: this.hideTitleWhenPinned, alignCenter: this.alignCenter, content: this.content, variables: this.variables, @@ -240,6 +245,7 @@ export default Vue.extend({ name: this.name.trim(), summary: this.summary, font: this.font, + hideTitleWhenPinned: this.hideTitleWhenPinned, alignCenter: this.alignCenter, content: this.content, variables: this.variables, diff --git a/src/client/app/common/views/pages/page.vue b/src/client/app/common/views/pages/page.vue new file mode 100644 index 0000000000..d1c4c2be43 --- /dev/null +++ b/src/client/app/common/views/pages/page.vue @@ -0,0 +1,63 @@ +<template> +<x-page v-if="page" :page="page" :key="page.id" :show-footer="true"/> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import { faStickyNote } from '@fortawesome/free-regular-svg-icons'; +import XPage from '../components/page/page.vue'; + +export default Vue.extend({ + components: { + XPage + }, + + props: { + pageName: { + type: String, + required: true + }, + username: { + type: String, + required: true + }, + }, + + data() { + return { + page: null, + }; + }, + + computed: { + path(): string { + return this.username + '/' + this.pageName; + } + }, + + watch: { + path() { + this.fetch(); + } + }, + + created() { + this.fetch(); + }, + + methods: { + fetch() { + this.$root.api('pages/show', { + name: this.pageName, + username: this.username, + }).then(page => { + this.page = page; + this.$emit('init', { + title: this.page.title, + icon: faStickyNote + }); + }); + }, + } +}); +</script> diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts index 8065241714..1a4be33020 100644 --- a/src/client/app/desktop/script.ts +++ b/src/client/app/desktop/script.ts @@ -148,6 +148,7 @@ init(async (launch, os) => { { path: '/i/groups', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-groups.vue').then(m => m.default) }) }, { path: '/i/groups/:groupId', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-group-editor.vue').then(m => m.default), groupId: route.params.groupId }) }, { path: '/i/follow-requests', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/follow-requests.vue').then(m => m.default) }) }, + { path: '/@:username/pages/:pageName', name: 'page', props: true, component: () => import('../common/views/deck/deck.page-column.vue').then(m => m.default) }, ]} : { path: '/', component: MkHome, children: [ { path: '', name: 'index', component: MkHomeTimeline }, @@ -171,12 +172,11 @@ init(async (launch, os) => { { path: '/i/follow-requests', component: () => import('../common/views/pages/follow-requests.vue').then(m => m.default) }, { path: '/i/pages/new', component: () => import('../common/views/pages/page-editor/page-editor.vue').then(m => m.default) }, { path: '/i/pages/edit/:pageId', component: () => import('../common/views/pages/page-editor/page-editor.vue').then(m => m.default), props: route => ({ initPageId: route.params.pageId }) }, - { path: '/@:user/pages/:page', component: () => import('../common/views/pages/page/page.vue').then(m => m.default), props: route => ({ pageName: route.params.page, username: route.params.user }) }, + { path: '/@:user/pages/:page', component: () => import('../common/views/pages/page.vue').then(m => m.default), props: route => ({ pageName: route.params.page, username: route.params.user }) }, { path: '/@:user/pages/:pageName/view-source', component: () => import('../common/views/pages/page-editor/page-editor.vue').then(m => m.default), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) }, ]}, { path: '/i/pages/new', component: () => import('../common/views/pages/page-editor/page-editor.vue').then(m => m.default) }, { path: '/i/pages/edit/:pageId', component: () => import('../common/views/pages/page-editor/page-editor.vue').then(m => m.default), props: route => ({ initPageId: route.params.pageId }) }, - { path: '/@:user/pages/:page', component: () => import('../common/views/pages/page/page.vue').then(m => m.default), props: route => ({ pageName: route.params.page, username: route.params.user }) }, { path: '/@:user/pages/:pageName/view-source', component: () => import('../common/views/pages/page-editor/page-editor.vue').then(m => m.default), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) }, { path: '/i/messaging/group/:group', component: MkMessagingRoom }, { path: '/i/messaging/:user', component: MkMessagingRoom }, diff --git a/src/client/app/desktop/views/home/user/user.home.vue b/src/client/app/desktop/views/home/user/user.home.vue index ec533efd3e..c47e0a0771 100644 --- a/src/client/app/desktop/views/home/user/user.home.vue +++ b/src/client/app/desktop/views/home/user/user.home.vue @@ -1,5 +1,6 @@ <template> <div class="lnctpgve"> + <x-page v-if="user.pinnedPage" :page="user.pinnedPage" :key="user.pinnedPage.id" :show-title="!user.pinnedPage.hideTitleWhenPinned"/> <mk-note-detail v-for="n in user.pinnedNotes" :key="n.id" :note="n" :compact="true"/> <!--<mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>--> <div class="activity"> @@ -21,13 +22,15 @@ import i18n from '../../../../i18n'; import XTimeline from './user.timeline.vue'; import XPhotos from './user.photos.vue'; import XActivity from '../../../../common/views/components/activity.vue'; +import XPage from '../../../../common/views/components/page/page.vue'; export default Vue.extend({ i18n: i18n(), components: { XTimeline, XPhotos, - XActivity + XActivity, + XPage, }, props: { user: { diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts index d04662cc1f..6222409931 100644 --- a/src/client/app/mobile/script.ts +++ b/src/client/app/mobile/script.ts @@ -129,6 +129,7 @@ init((launch, os) => { { path: '/i/groups', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-groups.vue').then(m => m.default) }) }, { path: '/i/groups/:groupId', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-group-editor.vue').then(m => m.default), groupId: route.params.groupId }) }, { path: '/i/follow-requests', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/follow-requests.vue').then(m => m.default) }) }, + { path: '/@:username/pages/:pageName', name: 'page', props: true, component: () => import('../common/views/deck/deck.page-column.vue').then(m => m.default) }, ]}] : [ { path: '/', name: 'index', component: MkIndex }, @@ -163,7 +164,7 @@ init((launch, os) => { { path: 'following', component: () => import('../common/views/pages/following.vue').then(m => m.default) }, { path: 'followers', component: () => import('../common/views/pages/followers.vue').then(m => m.default) }, ]}, - { path: '/@:user/pages/:page', component: UI, props: route => ({ component: () => import('../common/views/pages/page/page.vue').then(m => m.default), pageName: route.params.page, username: route.params.user }) }, + { path: '/@:user/pages/:page', component: UI, props: route => ({ component: () => import('../common/views/pages/page.vue').then(m => m.default), pageName: route.params.page, username: route.params.user }) }, { path: '/@:user/pages/:pageName/view-source', component: UI, props: route => ({ component: () => import('../common/views/pages/page-editor/page-editor.vue').then(m => m.default), initUser: route.params.user, initPageName: route.params.pageName }) }, { path: '/notes/:note', component: MkNote }, { path: '/authorize-follow', component: MkFollow }, diff --git a/src/client/app/mobile/views/pages/user/home.vue b/src/client/app/mobile/views/pages/user/home.vue index 1d7b0a4e6d..316b2a12fe 100644 --- a/src/client/app/mobile/views/pages/user/home.vue +++ b/src/client/app/mobile/views/pages/user/home.vue @@ -1,5 +1,6 @@ <template> <div class="wojmldye"> + <x-page class="page" v-if="user.pinnedPage" :page="user.pinnedPage" :key="user.pinnedPage.id" :show-title="!user.pinnedPage.hideTitleWhenPinned"/> <mk-note-detail class="note" v-for="n in user.pinnedNotes" :key="n.id" :note="n" :compact="true"/> <ui-container :body-togglable="true"> <template #header><fa :icon="['far', 'comments']"/>{{ $t('recent-notes') }}</template> @@ -33,6 +34,7 @@ export default Vue.extend({ components: { XNotes, XPhotos, + XPage: () => import('../../../../common/views/components/page/page.vue').then(m => m.default), XActivity: () => import('../../../../common/views/components/activity.vue').then(m => m.default) }, props: ['user'], @@ -53,6 +55,12 @@ export default Vue.extend({ <style lang="stylus" scoped> .wojmldye + > .page + margin 0 0 8px 0 + + @media (min-width 500px) + margin 0 0 16px 0 + > .note margin 0 0 8px 0 diff --git a/src/models/entities/page.ts b/src/models/entities/page.ts index 05015ba175..2163f9997f 100644 --- a/src/models/entities/page.ts +++ b/src/models/entities/page.ts @@ -40,6 +40,11 @@ export class Page { @Column('boolean') public alignCenter: boolean; + @Column('boolean', { + default: false + }) + public hideTitleWhenPinned: boolean; + @Column('varchar', { length: 32, }) diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts index 4a588ebfbf..61e80049c3 100644 --- a/src/models/entities/user-profile.ts +++ b/src/models/entities/user-profile.ts @@ -1,6 +1,7 @@ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; import { id } from '../id'; import { User } from './user'; +import { Page } from './page'; @Entity() export class UserProfile { @@ -118,6 +119,18 @@ export class UserProfile { }) public carefulBot: boolean; + @Column({ + ...id(), + nullable: true + }) + public pinnedPageId: Page['id'] | null; + + @OneToOne(type => Page, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public pinnedPage: Page | null; + //#region Linking @Column('boolean', { default: false, diff --git a/src/models/repositories/page.ts b/src/models/repositories/page.ts index 33126274a1..1335ada73f 100644 --- a/src/models/repositories/page.ts +++ b/src/models/repositories/page.ts @@ -71,6 +71,7 @@ export class PageRepository extends Repository<Page> { title: page.title, name: page.name, summary: page.summary, + hideTitleWhenPinned: page.hideTitleWhenPinned, alignCenter: page.alignCenter, font: page.font, eyeCatchingImageId: page.eyeCatchingImageId, diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 06da74197f..4e85fd7b93 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import { EntityRepository, Repository, In } from 'typeorm'; import { User, ILocalUser, IRemoteUser } from '../entities/user'; -import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings } from '..'; +import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages } from '..'; import { ensure } from '../../prelude/ensure'; import config from '../../config'; import { SchemaType } from '../../misc/schema'; @@ -155,6 +155,8 @@ export class UserRepository extends Repository<User> { pinnedNotes: Notes.packMany(pins.map(pin => pin.noteId), meId, { detail: true }), + pinnedPageId: profile!.pinnedPageId, + pinnedPage: profile!.pinnedPageId ? Pages.pack(profile!.pinnedPageId, meId) : null, twoFactorEnabled: profile!.twoFactorEnabled, usePasswordLessLogin: profile!.usePasswordLessLogin, securityKeys: profile!.twoFactorEnabled diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 10521d12d8..a454cdb940 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -10,7 +10,7 @@ import extractHashtags from '../../../../misc/extract-hashtags'; import * as langmap from 'langmap'; import { updateHashtag } from '../../../../services/update-hashtag'; import { ApiError } from '../../error'; -import { Users, DriveFiles, UserProfiles } from '../../../../models'; +import { Users, DriveFiles, UserProfiles, Pages } from '../../../../models'; import { User } from '../../../../models/entities/user'; import { UserProfile } from '../../../../models/entities/user-profile'; import { ensure } from '../../../../prelude/ensure'; @@ -125,6 +125,13 @@ export const meta = { 'ja-JP': 'アップロードするメディアをデフォルトで「閲覧注意」として設定するか' } }, + + pinnedPageId: { + validator: $.optional.nullable.type(ID), + desc: { + 'ja-JP': 'ピン留めするページID' + } + } }, errors: { @@ -150,7 +157,13 @@ export const meta = { message: 'The file specified as a banner is not an image.', code: 'BANNER_NOT_AN_IMAGE', id: '75aedb19-2afd-4e6d-87fc-67941256fa60' - } + }, + + noSuchPage: { + message: 'No such page.', + code: 'NO_SUCH_PAGE', + id: '8e01b590-7eb9-431b-a239-860e086c408e' + }, } }; @@ -203,6 +216,16 @@ export default define(meta, async (ps, user, app) => { } } + if (ps.pinnedPageId) { + const page = await Pages.findOne(ps.pinnedPageId); + + if (page == null || page.userId !== user.id) throw new ApiError(meta.errors.noSuchPage); + + profileUpdates.pinnedPageId = page.id; + } else if (ps.pinnedPageId === null) { + profileUpdates.pinnedPageId = null; + } + //#region emojis/tags let emojis = [] as string[]; diff --git a/src/server/api/endpoints/pages/create.ts b/src/server/api/endpoints/pages/create.ts index ffe0d38ea6..a49a5d37b8 100644 --- a/src/server/api/endpoints/pages/create.ts +++ b/src/server/api/endpoints/pages/create.ts @@ -57,6 +57,11 @@ export const meta = { validator: $.optional.bool, default: false }, + + hideTitleWhenPinned: { + validator: $.optional.bool, + default: false + }, }, res: { @@ -100,6 +105,7 @@ export default define(meta, async (ps, user) => { userId: user.id, visibility: 'public', alignCenter: ps.alignCenter, + hideTitleWhenPinned: ps.hideTitleWhenPinned, font: ps.font })); diff --git a/src/server/api/endpoints/pages/update.ts b/src/server/api/endpoints/pages/update.ts index 8ee34fc3ba..9daf5e9cae 100644 --- a/src/server/api/endpoints/pages/update.ts +++ b/src/server/api/endpoints/pages/update.ts @@ -61,6 +61,10 @@ export const meta = { alignCenter: { validator: $.optional.bool, }, + + hideTitleWhenPinned: { + validator: $.optional.bool, + }, }, errors: { @@ -113,6 +117,7 @@ export default define(meta, async (ps, user) => { content: ps.content, variables: ps.variables, alignCenter: ps.alignCenter === undefined ? page.alignCenter : ps.alignCenter, + hideTitleWhenPinned: ps.hideTitleWhenPinned === undefined ? page.hideTitleWhenPinned : ps.hideTitleWhenPinned, font: ps.font === undefined ? page.font : ps.font, eyeCatchingImageId: ps.eyeCatchingImageId === null ? null |