diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-04-26 13:00:10 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-04-26 13:00:10 +0900 |
| commit | 17fff8c66525d62721bd1fa29d11360908904ec3 (patch) | |
| tree | 4f22894599833ba4ef7f5b15da0bc1571cc66eb8 /src | |
| parent | Merge branch 'develop' (diff) | |
| parent | 12.79.2 (diff) | |
| download | misskey-17fff8c66525d62721bd1fa29d11360908904ec3.tar.gz misskey-17fff8c66525d62721bd1fa29d11360908904ec3.tar.bz2 misskey-17fff8c66525d62721bd1fa29d11360908904ec3.zip | |
Merge branch 'develop'
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/components/captcha.vue | 26 | ||||
| -rw-r--r-- | src/client/components/drive.file.vue | 2 | ||||
| -rw-r--r-- | src/client/components/drive.folder.vue | 2 | ||||
| -rw-r--r-- | src/client/components/drive.vue | 2 | ||||
| -rw-r--r-- | src/client/components/post-form-attaches.vue | 2 | ||||
| -rw-r--r-- | src/client/pages/gallery/edit.vue | 168 | ||||
| -rw-r--r-- | src/client/pages/gallery/new.vue | 110 | ||||
| -rw-r--r-- | src/client/pages/gallery/post.vue | 10 | ||||
| -rw-r--r-- | src/client/pages/mentions.vue | 1 | ||||
| -rw-r--r-- | src/client/pages/user/index.vue | 6 | ||||
| -rw-r--r-- | src/client/router.ts | 3 | ||||
| -rw-r--r-- | src/client/scripts/theme.ts | 2 | ||||
| -rw-r--r-- | src/client/themes/d-astro.json5 | 76 | ||||
| -rw-r--r-- | src/client/themes/l-light.json5 | 2 | ||||
| -rw-r--r-- | src/client/themes/l-vivid.json5 | 82 | ||||
| -rw-r--r-- | src/client/ui/deck/mentions-column.vue | 1 | ||||
| -rw-r--r-- | src/docs/it-IT/reversi-bot.md | 2 | ||||
| -rw-r--r-- | src/server/api/endpoints/gallery/posts/delete.ts | 40 | ||||
| -rw-r--r-- | src/server/api/endpoints/gallery/posts/update.ts | 81 | ||||
| -rw-r--r-- | src/server/web/index.ts | 20 |
20 files changed, 496 insertions, 142 deletions
diff --git a/src/client/components/captcha.vue b/src/client/components/captcha.vue index 26215df09d..5da8ede3b9 100644 --- a/src/client/components/captcha.vue +++ b/src/client/components/captcha.vue @@ -6,7 +6,7 @@ </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, PropType } from 'vue'; type Captcha = { render(container: string | Node, options: { @@ -32,7 +32,7 @@ declare global { export default defineComponent({ props: { provider: { - type: String, + type: String as PropType<CaptchaProvider>, required: true, }, sitekey: { @@ -51,19 +51,25 @@ export default defineComponent({ }, computed: { - loaded() { - return !!window[this.provider as CaptchaProvider]; + variable(): string { + switch (this.provider) { + case 'hcaptcha': return 'hcaptcha'; + case 'recaptcha': return 'grecaptcha'; + } + }, + loaded(): boolean { + return !!window[this.variable]; }, - src() { + src(): string { const endpoint = ({ hcaptcha: 'https://hcaptcha.com/1', recaptcha: 'https://www.recaptcha.net/recaptcha', - } as Record<PropertyKey, unknown>)[this.provider]; + } as Record<CaptchaProvider, string>)[this.provider]; - return `${typeof endpoint == 'string' ? endpoint : 'about:invalid'}/api.js?render=explicit`; + return `${typeof endpoint === 'string' ? endpoint : 'about:invalid'}/api.js?render=explicit`; }, - captcha() { - return window[this.provider as CaptchaProvider] || {} as unknown as Captcha; + captcha(): Captcha { + return window[this.variable] || {} as unknown as Captcha; }, }, @@ -94,7 +100,7 @@ export default defineComponent({ methods: { reset() { - this.captcha?.reset(); + if (this.captcha?.reset) this.captcha.reset(); }, requestRender() { if (this.captcha.render && this.$refs.captcha instanceof Element) { diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue index 293465e428..37b1afc1b3 100644 --- a/src/client/components/drive.file.vue +++ b/src/client/components/drive.file.vue @@ -81,7 +81,7 @@ export default defineComponent({ getMenu() { return [{ text: this.$ts.rename, - icon: faICursor, + icon: 'fas fa-i-cursor', action: this.rename }, { text: this.file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive, diff --git a/src/client/components/drive.folder.vue b/src/client/components/drive.folder.vue index 6ddd392527..4c09e7775a 100644 --- a/src/client/components/drive.folder.vue +++ b/src/client/components/drive.folder.vue @@ -247,7 +247,7 @@ export default defineComponent({ } }, null, { text: this.$ts.rename, - icon: faICursor, + icon: 'fas fa-i-cursor', action: this.rename }, null, { text: this.$ts.delete, diff --git a/src/client/components/drive.vue b/src/client/components/drive.vue index bd5bd8008b..06f9cf7806 100644 --- a/src/client/components/drive.vue +++ b/src/client/components/drive.vue @@ -614,7 +614,7 @@ export default defineComponent({ type: 'label' }, this.folder ? { text: this.$ts.renameFolder, - icon: faICursor, + icon: 'fas fa-i-cursor', action: () => { this.renameFolder(this.folder); } } : undefined, this.folder ? { text: this.$ts.deleteFolder, diff --git a/src/client/components/post-form-attaches.vue b/src/client/components/post-form-attaches.vue index eba7e7af36..f832ea87b5 100644 --- a/src/client/components/post-form-attaches.vue +++ b/src/client/components/post-form-attaches.vue @@ -93,7 +93,7 @@ export default defineComponent({ if (this.menu) return; this.menu = os.modalMenu([{ text: this.$ts.renameFile, - icon: faICursor, + icon: 'fas fa-i-cursor', action: () => { this.rename(file) } }, { text: file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive, diff --git a/src/client/pages/gallery/edit.vue b/src/client/pages/gallery/edit.vue new file mode 100644 index 0000000000..cd6a0defdd --- /dev/null +++ b/src/client/pages/gallery/edit.vue @@ -0,0 +1,168 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormInput v-model:value="title"> + <span>{{ $ts.title }}</span> + </FormInput> + + <FormTextarea v-model:value="description" :max="500"> + <span>{{ $ts.description }}</span> + </FormTextarea> + + <FormGroup> + <div v-for="file in files" :key="file.id" class="_formItem _formPanel wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }"> + <div class="name">{{ file.name }}</div> + <button class="remove _button" @click="remove(file)" v-tooltip="$ts.remove"><i class="fas fa-times"></i></button> + </div> + <FormButton @click="selectFile" primary><i class="fas fa-plus"></i> {{ $ts.attachFile }}</FormButton> + </FormGroup> + + <FormSwitch v-model:value="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch> + + <FormButton v-if="postId" @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <FormButton v-else @click="save" primary><i class="fas fa-save"></i> {{ $ts.publish }}</FormButton> + + <FormButton v-if="postId" @click="del" danger><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { computed, defineComponent } from 'vue'; +import FormButton from '@client/components/form/button.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormTextarea from '@client/components/form/textarea.vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormTuple from '@client/components/form/tuple.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import { selectFile } from '@client/scripts/select-file'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; + +export default defineComponent({ + components: { + FormButton, + FormInput, + FormTextarea, + FormSwitch, + FormBase, + FormGroup, + FormSuspense, + }, + + props: { + postId: { + type: String, + required: false, + default: null, + } + }, + + data() { + return { + [symbols.PAGE_INFO]: computed(() => this.postId ? { + title: this.$ts.edit, + icon: 'fas fa-pencil-alt' + } : { + title: this.$ts.postToGallery, + icon: 'fas fa-pencil-alt' + }), + init: null, + files: [], + description: null, + title: null, + isSensitive: false, + } + }, + + watch: { + postId: { + handler() { + this.init = () => this.postId ? os.api('gallery/posts/show', { + postId: this.postId + }).then(post => { + this.files = post.files; + this.title = post.title; + this.description = post.description; + this.isSensitive = post.isSensitive; + }) : Promise.resolve(null); + }, + immediate: true, + } + }, + + methods: { + selectFile(e) { + selectFile(e.currentTarget || e.target, null, true).then(files => { + this.files = this.files.concat(files); + }); + }, + + remove(file) { + this.files = this.files.filter(f => f.id !== file.id); + }, + + async save() { + if (this.postId) { + await os.apiWithDialog('gallery/posts/update', { + postId: this.postId, + title: this.title, + description: this.description, + fileIds: this.files.map(file => file.id), + isSensitive: this.isSensitive, + }); + this.$router.push(`/gallery/${this.postId}`); + } else { + const post = await os.apiWithDialog('gallery/posts/create', { + title: this.title, + description: this.description, + fileIds: this.files.map(file => file.id), + isSensitive: this.isSensitive, + }); + this.$router.push(`/gallery/${post.id}`); + } + }, + + async del() { + const { canceled } = await os.dialog({ + type: 'warning', + text: this.$ts.deleteConfirm, + showCancelButton: true + }); + if (canceled) return; + await os.apiWithDialog('gallery/posts/delete', { + postId: this.postId, + }); + this.$router.push(`/gallery`); + } + } +}); +</script> + +<style lang="scss" scoped> +.wqugxsfx { + height: 200px; + background-size: contain; + background-position: center; + background-repeat: no-repeat; + position: relative; + + > .name { + position: absolute; + top: 8px; + left: 9px; + padding: 8px; + background: var(--panel); + } + + > .remove { + position: absolute; + top: 8px; + right: 9px; + padding: 8px; + background: var(--panel); + } +} +</style> diff --git a/src/client/pages/gallery/new.vue b/src/client/pages/gallery/new.vue deleted file mode 100644 index 3f9756df8e..0000000000 --- a/src/client/pages/gallery/new.vue +++ /dev/null @@ -1,110 +0,0 @@ -<template> -<FormBase> - <FormInput v-model:value="title"> - <span>{{ $ts.title }}</span> - </FormInput> - - <FormTextarea v-model:value="description" :max="500"> - <span>{{ $ts.description }}</span> - </FormTextarea> - - <FormGroup> - <div v-for="file in files" :key="file.id" class="_formItem _formPanel wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }"> - <div class="name">{{ file.name }}</div> - <button class="remove _button" @click="remove(file)" v-tooltip="$ts.remove"><i class="fas fa-times"></i></button> - </div> - <FormButton @click="selectFile" primary><i class="fas fa-plus"></i> {{ $ts.attachFile }}</FormButton> - </FormGroup> - - <FormSwitch v-model:value="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch> - - <FormButton @click="publish" primary><i class="fas fa-save"></i> {{ $ts.publish }}</FormButton> -</FormBase> -</template> - -<script lang="ts"> -import { defineComponent } from 'vue'; -import FormButton from '@client/components/form/button.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormTuple from '@client/components/form/tuple.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import { selectFile } from '@client/scripts/select-file'; -import * as os from '@client/os'; -import * as symbols from '@client/symbols'; - -export default defineComponent({ - components: { - FormButton, - FormInput, - FormTextarea, - FormSwitch, - FormBase, - FormGroup, - }, - - data() { - return { - [symbols.PAGE_INFO]: { - title: this.$ts.postToGallery, - icon: 'fas fa-pencil-alt' - }, - files: [], - description: null, - title: null, - isSensitive: false, - } - }, - - methods: { - selectFile(e) { - selectFile(e.currentTarget || e.target, null, true).then(files => { - this.files = this.files.concat(files); - }); - }, - - remove(file) { - this.files = this.files.filter(f => f.id !== file.id); - }, - - async publish() { - const post = await os.apiWithDialog('gallery/posts/create', { - title: this.title, - description: this.description, - fileIds: this.files.map(file => file.id), - isSensitive: this.isSensitive, - }); - - this.$router.push(`/gallery/${post.id}`); - } - } -}); -</script> - -<style lang="scss" scoped> -.wqugxsfx { - height: 200px; - background-size: contain; - background-position: center; - background-repeat: no-repeat; - position: relative; - - > .name { - position: absolute; - top: 8px; - left: 9px; - padding: 8px; - background: var(--panel); - } - - > .remove { - position: absolute; - top: 8px; - right: 9px; - padding: 8px; - background: var(--panel); - } -} -</style> diff --git a/src/client/pages/gallery/post.vue b/src/client/pages/gallery/post.vue index 9bd102cee2..703506a78d 100644 --- a/src/client/pages/gallery/post.vue +++ b/src/client/pages/gallery/post.vue @@ -19,6 +19,7 @@ <MkButton class="button" @click="like()" v-else v-tooltip="$ts._gallery.like"><i class="far fa-heart"></i><span class="count" v-if="post.likedCount > 0">{{ post.likedCount }}</span></MkButton> </div> <div class="other"> + <button v-if="$i && $i.id === post.user.id" class="_button" @click="edit" v-tooltip="$ts.edit" v-click-anime><i class="fas fa-pencil-alt fa-fw"></i></button> <button class="_button" @click="shareWithNote" v-tooltip="$ts.shareWithNote" v-click-anime><i class="fas fa-retweet fa-fw"></i></button> <button class="_button" @click="share" v-tooltip="$ts.share" v-click-anime><i class="fas fa-share-alt fa-fw"></i></button> </div> @@ -84,6 +85,11 @@ export default defineComponent({ title: this.post.title, text: this.post.description, }, + actions: [{ + icon: 'fas fa-pencil-alt', + text: this.$ts.edit, + handler: this.edit + }] } : null), otherPostsPagination: { endpoint: 'users/gallery/posts', @@ -154,6 +160,10 @@ export default defineComponent({ this.post.likedCount--; }); }, + + edit() { + this.$router.push(`/gallery/${this.post.id}/edit`); + } } }); </script> diff --git a/src/client/pages/mentions.vue b/src/client/pages/mentions.vue index a12993ebb8..798d3e342d 100644 --- a/src/client/pages/mentions.vue +++ b/src/client/pages/mentions.vue @@ -25,7 +25,6 @@ export default defineComponent({ endpoint: 'notes/mentions', limit: 10, }, - faAt }; }, diff --git a/src/client/pages/user/index.vue b/src/client/pages/user/index.vue index a1fe7ec09f..717ca5d8fe 100644 --- a/src/client/pages/user/index.vue +++ b/src/client/pages/user/index.vue @@ -161,15 +161,15 @@ </dl> </div> <div class="status"> - <MkA :to="userPage(user)" :class="{ active: page === 'index' }"> + <MkA :to="userPage(user)" :class="{ active: page === 'index' }" v-click-anime> <b>{{ number(user.notesCount) }}</b> <span>{{ $ts.notes }}</span> </MkA> - <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }"> + <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }" v-click-anime> <b>{{ number(user.followingCount) }}</b> <span>{{ $ts.following }}</span> </MkA> - <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }"> + <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }" v-click-anime> <b>{{ number(user.followersCount) }}</b> <span>{{ $ts.followers }}</span> </MkA> diff --git a/src/client/router.ts b/src/client/router.ts index 5371bf17d9..8dcc1d1eb4 100644 --- a/src/client/router.ts +++ b/src/client/router.ts @@ -38,7 +38,8 @@ export const router = createRouter({ { path: '/pages/new', component: page('page-editor/page-editor') }, { path: '/pages/edit/:pageId', component: page('page-editor/page-editor'), props: route => ({ initPageId: route.params.pageId }) }, { path: '/gallery', component: page('gallery/index') }, - { path: '/gallery/new', component: page('gallery/new') }, + { path: '/gallery/new', component: page('gallery/edit') }, + { path: '/gallery/:postId/edit', component: page('gallery/edit'), props: route => ({ postId: route.params.postId }) }, { path: '/gallery/:postId', component: page('gallery/post'), props: route => ({ postId: route.params.postId }) }, { path: '/channels', component: page('channels') }, { path: '/channels/new', component: page('channel-editor') }, diff --git a/src/client/scripts/theme.ts b/src/client/scripts/theme.ts index b0bf620a7d..09441c8a76 100644 --- a/src/client/scripts/theme.ts +++ b/src/client/scripts/theme.ts @@ -18,9 +18,11 @@ export const builtinThemes = [ require('@client/themes/l-light.json5'), require('@client/themes/l-apricot.json5'), require('@client/themes/l-rainy.json5'), + require('@client/themes/l-vivid.json5'), require('@client/themes/d-dark.json5'), require('@client/themes/d-persimmon.json5'), + require('@client/themes/d-astro.json5'), require('@client/themes/d-black.json5'), ] as Theme[]; diff --git a/src/client/themes/d-astro.json5 b/src/client/themes/d-astro.json5 new file mode 100644 index 0000000000..a88e949c3c --- /dev/null +++ b/src/client/themes/d-astro.json5 @@ -0,0 +1,76 @@ +{ + id: '080a01c5-377d-4fbb-88cc-6bb5d04977ea', + base: 'dark', + name: 'Mi Astro', + author: 'syuilo', + props: { + bg: '#232125', + fg: '#efdab9', + cwBg: '#687390', + cwFg: '#393f4f', + link: '#78b0a0', + warn: '#ecb637', + badge: '#31b1ce', + error: '#ec4137', + focus: ':alpha<0.3<@accent', + navBg: '@panel', + navFg: '@fg', + panel: '#2a272b', + accent: '#81c08b', + header: ':alpha<0.7<@bg', + infoBg: '#253142', + infoFg: '#fff', + renote: '#659CC8', + shadow: 'rgba(0, 0, 0, 0.3)', + divider: 'rgba(255, 255, 255, 0.1)', + hashtag: '#ff9156', + mention: '#ffd152', + modalBg: 'rgba(0, 0, 0, 0.5)', + success: '#86b300', + buttonBg: 'rgba(255, 255, 255, 0.05)', + acrylicBg: ':alpha<0.5<@bg', + cwHoverBg: '#707b97', + indicator: '@accent', + mentionMe: '#fb5d38', + messageBg: ':lighten<5<@bg', + navActive: '@accent', + infoWarnBg: '#42321c', + infoWarnFg: '#ffbd3e', + navHoverFg: ':lighten<17<@fg', + dateLabelFg: '@fg', + inputBorder: '#959da2', + panelBorder: 'rgba(0, 0, 0, 0)', + panelShadow: '" 0 8px 24px rgba(0, 0, 0, 0.12)', + accentDarken: ':darken<10<@accent', + acrylicPanel: ':alpha<0.5<@panel', + navIndicator: '@accent', + accentLighten: ':lighten<10<@accent', + buttonHoverBg: 'rgba(255, 255, 255, 0.1)', + driveFolderBg: ':alpha<0.3<@accent', + fgHighlighted: ':lighten<3<@fg', + panelHeaderBg: ':lighten<3<@panel', + panelHeaderFg: '@fg', + htmlThemeColor: '@bg', + panelHighlight: ':lighten<3<@panel', + listItemHoverBg: 'rgba(255, 255, 255, 0.03)', + scrollbarHandle: 'rgba(255, 255, 255, 0.2)', + wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', + panelHeaderDivider: 'rgba(0, 0, 0, 0)', + scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', + X2: ':darken<2<@panel', + X3: 'rgba(255, 255, 255, 0.05)', + X4: 'rgba(255, 255, 255, 0.1)', + X5: 'rgba(255, 255, 255, 0.05)', + X6: 'rgba(255, 255, 255, 0.15)', + X7: 'rgba(255, 255, 255, 0.05)', + X8: ':lighten<5<@accent', + X9: ':darken<5<@accent', + X10: ':alpha<0.4<@accent', + X11: 'rgba(0, 0, 0, 0.3)', + X12: 'rgba(255, 255, 255, 0.1)', + X13: 'rgba(255, 255, 255, 0.15)', + X14: ':alpha<0.5<@navBg', + X15: ':alpha<0<@panel', + X16: ':alpha<0.7<@panel', + }, +} diff --git a/src/client/themes/l-light.json5 b/src/client/themes/l-light.json5 index fdc1700b95..27a973c88a 100644 --- a/src/client/themes/l-light.json5 +++ b/src/client/themes/l-light.json5 @@ -10,7 +10,7 @@ props: { bg: '#f9f9f9', fg: '#676767', - divider: 'rgb(223, 223, 223)', + divider: '#e8e8e8', header: ':alpha<0.7<@panel', navBg: '#fff', panel: '#fff', diff --git a/src/client/themes/l-vivid.json5 b/src/client/themes/l-vivid.json5 new file mode 100644 index 0000000000..a21e053b0a --- /dev/null +++ b/src/client/themes/l-vivid.json5 @@ -0,0 +1,82 @@ +{ + id: '6128c2a9-5c54-43fe-a47d-17942356470b', + + name: 'Mi Vivid', + author: 'syuilo', + + base: 'light', + + props: { + bg: '#fafafa', + fg: '#444', + cwBg: '#b1b9c1', + cwFg: '#fff', + link: '#ff9400', + warn: '#ecb637', + badge: '#31b1ce', + error: '#ec4137', + focus: ':alpha<0.3<@accent', + navBg: '@panel', + navFg: '@fg', + panel: '#fff', + accent: '#008cff', + header: ':alpha<0.7<@panel', + infoBg: '#e5f5ff', + infoFg: '#72818a', + renote: '@accent', + shadow: 'rgba(0, 0, 0, 0.1)', + divider: 'rgba(0, 0, 0, 0.08)', + hashtag: '#92d400', + mention: '@accent', + modalBg: 'rgba(0, 0, 0, 0.3)', + success: '#86b300', + buttonBg: 'rgba(0, 0, 0, 0.05)', + acrylicBg: ':alpha<0.5<@bg', + cwHoverBg: '#bbc4ce', + indicator: '@accent', + mentionMe: '@mention', + messageBg: '@panel', + navActive: '@accent', + infoWarnBg: '#fff0db', + infoWarnFg: '#573c08', + navHoverFg: ':darken<17<@fg', + dateLabelFg: '@fg', + inputBorder: '#dae0e4', + panelBorder: 'rgba(0, 0, 0, 0)', + panelShadow: '" 0 8px 24px rgb(21 43 75 / 8%)', + accentDarken: ':darken<10<@accent', + acrylicPanel: ':alpha<0.5<@panel', + navIndicator: '@accent', + accentLighten: ':lighten<10<@accent', + buttonHoverBg: 'rgba(0, 0, 0, 0.1)', + driveFolderBg: ':alpha<0.3<@accent', + fgHighlighted: ':darken<3<@fg', + fgTransparent: ':alpha<0.5<@fg', + panelHeaderBg: ':lighten<3<@panel', + panelHeaderFg: '@fg', + htmlThemeColor: '@bg', + panelHighlight: ':darken<3<@panel', + listItemHoverBg: 'rgba(0, 0, 0, 0.03)', + scrollbarHandle: 'rgba(0, 0, 0, 0.2)', + wallpaperOverlay: 'rgba(255, 255, 255, 0.5)', + fgTransparentWeak: ':alpha<0.75<@fg', + panelHeaderDivider: '@divider', + scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)', + X2: ':darken<2<@panel', + X3: 'rgba(0, 0, 0, 0.05)', + X4: 'rgba(0, 0, 0, 0.1)', + X5: 'rgba(0, 0, 0, 0.05)', + X6: 'rgba(0, 0, 0, 0.25)', + X7: 'rgba(0, 0, 0, 0.05)', + X8: ':lighten<5<@accent', + X9: ':darken<5<@accent', + X10: ':alpha<0.4<@accent', + X11: 'rgba(0, 0, 0, 0.1)', + X12: 'rgba(0, 0, 0, 0.1)', + X13: 'rgba(0, 0, 0, 0.15)', + X14: ':alpha<0.5<@navBg', + X15: ':alpha<0<@panel', + X16: ':alpha<0.7<@panel', + X17: ':alpha<0.8<@bg', + }, +} diff --git a/src/client/ui/deck/mentions-column.vue b/src/client/ui/deck/mentions-column.vue index 053ef918f0..c625bb3ea1 100644 --- a/src/client/ui/deck/mentions-column.vue +++ b/src/client/ui/deck/mentions-column.vue @@ -36,7 +36,6 @@ export default defineComponent({ endpoint: 'notes/mentions', limit: 10, }, - faAt } }, diff --git a/src/docs/it-IT/reversi-bot.md b/src/docs/it-IT/reversi-bot.md index 7ab2a7212e..00d4a18a8e 100644 --- a/src/docs/it-IT/reversi-bot.md +++ b/src/docs/it-IT/reversi-bot.md @@ -110,7 +110,7 @@ y = Math.floor(pos / mapWidth) ``` ### フォームコントロールの種類 -#### スイッチ +#### Interruttore type: `switch` スイッチを表示します。何かの機能をオン/オフさせたい場合に有用です。 ##### プロパティ diff --git a/src/server/api/endpoints/gallery/posts/delete.ts b/src/server/api/endpoints/gallery/posts/delete.ts new file mode 100644 index 0000000000..8b54828b20 --- /dev/null +++ b/src/server/api/endpoints/gallery/posts/delete.ts @@ -0,0 +1,40 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { ApiError } from '../../../error'; +import { GalleryPosts } from '../../../../../models'; +import { ID } from '@/misc/cafy-id'; + +export const meta = { + tags: ['gallery'], + + requireCredential: true as const, + + kind: 'write:gallery', + + params: { + postId: { + validator: $.type(ID), + }, + }, + + errors: { + noSuchPost: { + message: 'No such post.', + code: 'NO_SUCH_POST', + id: 'ae52f367-4bd7-4ecd-afc6-5672fff427f5' + }, + } +}; + +export default define(meta, async (ps, user) => { + const post = await GalleryPosts.findOne({ + id: ps.postId, + userId: user.id, + }); + + if (post == null) { + throw new ApiError(meta.errors.noSuchPost); + } + + await GalleryPosts.delete(post.id); +}); diff --git a/src/server/api/endpoints/gallery/posts/update.ts b/src/server/api/endpoints/gallery/posts/update.ts new file mode 100644 index 0000000000..c8bb8d48c9 --- /dev/null +++ b/src/server/api/endpoints/gallery/posts/update.ts @@ -0,0 +1,81 @@ +import $ from 'cafy'; +import * as ms from 'ms'; +import define from '../../../define'; +import { ID } from '../../../../../misc/cafy-id'; +import { DriveFiles, GalleryPosts } from '../../../../../models'; +import { GalleryPost } from '../../../../../models/entities/gallery-post'; +import { ApiError } from '../../../error'; + +export const meta = { + tags: ['gallery'], + + requireCredential: true as const, + + kind: 'write:gallery', + + limit: { + duration: ms('1hour'), + max: 300 + }, + + params: { + postId: { + validator: $.type(ID), + }, + + title: { + validator: $.str.min(1), + }, + + description: { + validator: $.optional.nullable.str, + }, + + fileIds: { + validator: $.arr($.type(ID)).unique().range(1, 32), + }, + + isSensitive: { + validator: $.optional.bool, + default: false, + }, + }, + + res: { + type: 'object' as const, + optional: false as const, nullable: false as const, + ref: 'GalleryPost', + }, + + errors: { + + } +}; + +export default define(meta, async (ps, user) => { + const files = (await Promise.all(ps.fileIds.map(fileId => + DriveFiles.findOne({ + id: fileId, + userId: user.id + }) + ))).filter(file => file != null); + + if (files.length === 0) { + throw new Error(); + } + + await GalleryPosts.update({ + id: ps.postId, + userId: user.id, + }, { + updatedAt: new Date(), + title: ps.title, + description: ps.description, + isSensitive: ps.isSensitive, + fileIds: files.map(file => file.id) + }); + + const post = await GalleryPosts.findOneOrFail(ps.postId); + + return await GalleryPosts.pack(post, user); +}); diff --git a/src/server/web/index.ts b/src/server/web/index.ts index c3b184088b..cc343063af 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -252,7 +252,7 @@ router.get('/users/:user', async ctx => { }); // Note -router.get('/notes/:note', async ctx => { +router.get('/notes/:note', async (ctx, next) => { const note = await Notes.findOne(ctx.params.note); if (note) { @@ -277,11 +277,11 @@ router.get('/notes/:note', async ctx => { return; } - ctx.status = 404; + await next(); }); // Page -router.get('/@:user/pages/:page', async ctx => { +router.get('/@:user/pages/:page', async (ctx, next) => { const { username, host } = parseAcct(ctx.params.user); const user = await Users.findOne({ usernameLower: username.toLowerCase(), @@ -314,12 +314,12 @@ router.get('/@:user/pages/:page', async ctx => { return; } - ctx.status = 404; + await next(); }); // Clip // TODO: 非publicなclipのハンドリング -router.get('/clips/:clip', async ctx => { +router.get('/clips/:clip', async (ctx, next) => { const clip = await Clips.findOne({ id: ctx.params.clip, }); @@ -339,11 +339,11 @@ router.get('/clips/:clip', async ctx => { return; } - ctx.status = 404; + await next(); }); // Gallery post -router.get('/gallery/:post', async ctx => { +router.get('/gallery/:post', async (ctx, next) => { const post = await GalleryPosts.findOne(ctx.params.post); if (post) { @@ -362,11 +362,11 @@ router.get('/gallery/:post', async ctx => { return; } - ctx.status = 404; + await next(); }); // Channel -router.get('/channels/:channel', async ctx => { +router.get('/channels/:channel', async (ctx, next) => { const channel = await Channels.findOne({ id: ctx.params.channel, }); @@ -384,7 +384,7 @@ router.get('/channels/:channel', async ctx => { return; } - ctx.status = 404; + await next(); }); //#endregion |