wip: clip
This commit is contained in:
		
							parent
							
								
									d53c55ecb5
								
							
						
					
					
						commit
						8e8459fa55
					
				
					 6 changed files with 193 additions and 4 deletions
				
			
		|  | @ -215,6 +215,7 @@ imageUrl: "画像URL" | |||
| remove: "削除" | ||||
| removed: "削除しました" | ||||
| removeAreYouSure: "「{x}」を削除しますか?" | ||||
| deleteAreYouSure: "「{x}」を削除しますか?" | ||||
| resetAreYouSure: "リセットしますか?" | ||||
| saved: "保存しました" | ||||
| messaging: "チャット" | ||||
|  |  | |||
							
								
								
									
										139
									
								
								src/client/pages/clip.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/client/pages/clip.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,139 @@ | |||
| <template> | ||||
| <div v-if="clip" class="_section"> | ||||
| 	<div class="okzinsic _content _panel _vMargin"> | ||||
| 		<div class="description" v-if="clip.description"> | ||||
| 			<Mfm :text="clip.description" :is-note="false" :i="$store.state.i"/> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 
 | ||||
| 	<XNotes class="_content _vMargin" :pagination="pagination" :detail="true"/> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { computed, defineComponent } from 'vue'; | ||||
| import { faEllipsisH, faPaperclip, faPencilAlt, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; | ||||
| import MkContainer from '@/components/ui/container.vue'; | ||||
| import XPostForm from '@/components/post-form.vue'; | ||||
| import XNotes from '@/components/notes.vue'; | ||||
| import * as os from '@/os'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		MkContainer, | ||||
| 		XPostForm, | ||||
| 		XNotes, | ||||
| 	}, | ||||
| 
 | ||||
| 	props: { | ||||
| 		clipId: { | ||||
| 			type: String, | ||||
| 			required: true | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			INFO: computed(() => this.clip ? { | ||||
| 				title: this.clip.name, | ||||
| 				icon: faPaperclip, | ||||
| 				action: { | ||||
| 					icon: faEllipsisH, | ||||
| 					handler: this.menu | ||||
| 				} | ||||
| 			} : null), | ||||
| 			clip: null, | ||||
| 			pagination: { | ||||
| 				endpoint: 'clips/notes', | ||||
| 				limit: 10, | ||||
| 				params: () => ({ | ||||
| 					clipId: this.clipId, | ||||
| 				}) | ||||
| 			}, | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		isOwned(): boolean { | ||||
| 			return this.$store.getters.isSignedIn && this.clip && (this.$store.state.i.id === this.clip.userId); | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	watch: { | ||||
| 		clipId: { | ||||
| 			async handler() { | ||||
| 				this.clip = await os.api('clips/show', { | ||||
| 					clipId: this.clipId, | ||||
| 				}); | ||||
| 			}, | ||||
| 			immediate: true | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	created() { | ||||
| 
 | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		menu(ev) { | ||||
| 			os.modalMenu([this.isOwned ? { | ||||
| 				icon: faPencilAlt, | ||||
| 				text: this.$t('edit'), | ||||
| 				action: async () => { | ||||
| 					const { canceled, result } = await os.form(this.clip.name, { | ||||
| 						name: { | ||||
| 							type: 'string', | ||||
| 							label: this.$t('name'), | ||||
| 							default: this.clip.name | ||||
| 						}, | ||||
| 						description: { | ||||
| 							type: 'string', | ||||
| 							required: false, | ||||
| 							multiline: true, | ||||
| 							label: this.$t('description'), | ||||
| 							default: this.clip.description | ||||
| 						}, | ||||
| 						isPublic: { | ||||
| 							type: 'boolean', | ||||
| 							label: this.$t('public'), | ||||
| 							default: this.clip.isPublic | ||||
| 						} | ||||
| 					}); | ||||
| 					if (canceled) return; | ||||
| 
 | ||||
| 					os.apiWithDialog('clips/update', { | ||||
| 						clipId: this.clip.id, | ||||
| 						...result | ||||
| 					}); | ||||
| 				} | ||||
| 			} : undefined, this.isOwned ? { | ||||
| 				icon: faTrashAlt, | ||||
| 				text: this.$t('delete'), | ||||
| 				danger: true, | ||||
| 				action: async () => { | ||||
| 					const { canceled } = await os.dialog({ | ||||
| 						type: 'warning', | ||||
| 						text: this.$t('deleteAreYouSure', { x: this.clip.name }), | ||||
| 						showCancelButton: true | ||||
| 					}); | ||||
| 					if (canceled) return; | ||||
| 
 | ||||
| 					await os.apiWithDialog('clips/delete', { | ||||
| 						clipId: this.clip.id, | ||||
| 					}); | ||||
| 				} | ||||
| 			} : undefined], ev.currentTarget || ev.target); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .okzinsic { | ||||
| 	position: relative; | ||||
| 
 | ||||
| 	> .description { | ||||
| 		padding: 16px; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  | @ -1,10 +1,13 @@ | |||
| <template> | ||||
| <div class="_section"> | ||||
| <div class="_section qtcaoidl"> | ||||
| 	<MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $t('add') }}</MkButton> | ||||
| 
 | ||||
| 	<div class="_content"> | ||||
| 		<MkPagination :pagination="pagination" #default="{items}" ref="list"> | ||||
| 			<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`">{{ item.name }}</MkA> | ||||
| 		<MkPagination :pagination="pagination" #default="{items}" ref="list" class="list"> | ||||
| 			<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _vMargin"> | ||||
| 				<b>{{ item.name }}</b> | ||||
| 				<div v-if="item.description" class="description">{{ item.description }}</div> | ||||
| 			</MkA> | ||||
| 		</MkPagination> | ||||
| 	</div> | ||||
| </div> | ||||
|  | @ -76,3 +79,26 @@ export default defineComponent({ | |||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .qtcaoidl { | ||||
| 	> .add { | ||||
| 		margin: 0 auto 16px auto; | ||||
| 	} | ||||
| 
 | ||||
| 	> ._content { | ||||
| 		> .list { | ||||
| 			> .item { | ||||
| 				display: block; | ||||
| 				padding: 16px; | ||||
| 
 | ||||
| 				> .description { | ||||
| 					margin-top: 8px; | ||||
| 					padding-top: 8px; | ||||
| 					border-top: solid 1px var(--divider); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ export const router = createRouter({ | |||
| 		{ path: '/channels/new', component: page('channel-editor') }, | ||||
| 		{ path: '/channels/:channelId/edit', component: page('channel-editor'), props: true }, | ||||
| 		{ path: '/channels/:channelId', component: page('channel'), props: route => ({ channelId: route.params.channelId }) }, | ||||
| 		{ path: '/clips/:clipId', component: page('clip'), props: route => ({ clipId: route.params.clipId }) }, | ||||
| 		{ path: '/my/notifications', component: page('notifications') }, | ||||
| 		{ path: '/my/favorites', component: page('favorites') }, | ||||
| 		{ path: '/my/messages', component: page('messages') }, | ||||
|  |  | |||
|  | @ -15,8 +15,10 @@ export class ClipRepository extends Repository<Clip> { | |||
| 		return { | ||||
| 			id: clip.id, | ||||
| 			createdAt: clip.createdAt.toISOString(), | ||||
| 			userId: clip.userId, | ||||
| 			name: clip.name, | ||||
| 			description: clip.description, | ||||
| 			isPublic: clip.isPublic, | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
|  | @ -38,6 +40,11 @@ export const packedClipSchema = { | |||
| 			format: 'date-time', | ||||
| 			description: 'The date that the Clip was created.' | ||||
| 		}, | ||||
| 		userId: { | ||||
| 			type: 'string' as const, | ||||
| 			optional: false as const, nullable: false as const, | ||||
| 			format: 'id', | ||||
| 		}, | ||||
| 		name: { | ||||
| 			type: 'string' as const, | ||||
| 			optional: false as const, nullable: false as const, | ||||
|  | @ -48,5 +55,10 @@ export const packedClipSchema = { | |||
| 			optional: false as const, nullable: true as const, | ||||
| 			description: 'The description of the Clip.' | ||||
| 		}, | ||||
| 		isPublic: { | ||||
| 			type: 'boolean' as const, | ||||
| 			optional: false as const, nullable: false as const, | ||||
| 			description: 'Whether this Clip is public.', | ||||
| 		}, | ||||
| 	}, | ||||
| }; | ||||
|  |  | |||
|  | @ -18,6 +18,14 @@ export const meta = { | |||
| 
 | ||||
| 		name: { | ||||
| 			validator: $.str.range(1, 100), | ||||
| 		}, | ||||
| 
 | ||||
| 		isPublic: { | ||||
| 			validator: $.optional.bool | ||||
| 		}, | ||||
| 
 | ||||
| 		description: { | ||||
| 			validator: $.optional.nullable.str.range(1, 2048) | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -42,7 +50,9 @@ export default define(meta, async (ps, user) => { | |||
| 	} | ||||
| 
 | ||||
| 	await Clips.update(clip.id, { | ||||
| 		name: ps.name | ||||
| 		name: ps.name, | ||||
| 		description: ps.description, | ||||
| 		isPublic: ps.isPublic, | ||||
| 	}); | ||||
| 
 | ||||
| 	return await Clips.pack(clip.id); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue