ピン留めを解除することができるようにしたり
This commit is contained in:
		
							parent
							
								
									7b4c307c46
								
							
						
					
					
						commit
						59d67d3140
					
				
					 6 changed files with 128 additions and 46 deletions
				
			
		| 
						 | 
					@ -328,6 +328,7 @@ common/views/components/note-menu.vue:
 | 
				
			||||||
  copy-link: "リンクをコピー"
 | 
					  copy-link: "リンクをコピー"
 | 
				
			||||||
  favorite: "お気に入り"
 | 
					  favorite: "お気に入り"
 | 
				
			||||||
  pin: "ピン留め"
 | 
					  pin: "ピン留め"
 | 
				
			||||||
 | 
					  unpin: "ピン留め解除"
 | 
				
			||||||
  delete: "削除"
 | 
					  delete: "削除"
 | 
				
			||||||
  delete-confirm: "この投稿を削除しますか?"
 | 
					  delete-confirm: "この投稿を削除しますか?"
 | 
				
			||||||
  remote: "投稿元で見る"
 | 
					  remote: "投稿元で見る"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,11 +28,19 @@ export default Vue.extend({
 | 
				
			||||||
			}];
 | 
								}];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (this.note.userId == this.$store.state.i.id) {
 | 
								if (this.note.userId == this.$store.state.i.id) {
 | 
				
			||||||
				items.push({
 | 
									if (this.$store.state.i.pinnedNoteIds.includes(this.note.id)) {
 | 
				
			||||||
					icon: '%fa:thumbtack%',
 | 
										items.push({
 | 
				
			||||||
					text: '%i18n:@pin%',
 | 
											icon: '%fa:thumbtack%',
 | 
				
			||||||
					action: this.pin
 | 
											text: '%i18n:@unpin%',
 | 
				
			||||||
				});
 | 
											action: this.unpin
 | 
				
			||||||
 | 
										});
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										items.push({
 | 
				
			||||||
 | 
											icon: '%fa:thumbtack%',
 | 
				
			||||||
 | 
											text: '%i18n:@pin%',
 | 
				
			||||||
 | 
											action: this.pin
 | 
				
			||||||
 | 
										});
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) {
 | 
								if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) {
 | 
				
			||||||
| 
						 | 
					@ -56,6 +64,7 @@ export default Vue.extend({
 | 
				
			||||||
			return items;
 | 
								return items;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		detail() {
 | 
							detail() {
 | 
				
			||||||
			this.$router.push(`/notes/${ this.note.id }`);
 | 
								this.$router.push(`/notes/${ this.note.id }`);
 | 
				
			||||||
| 
						 | 
					@ -73,6 +82,14 @@ export default Vue.extend({
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							unpin() {
 | 
				
			||||||
 | 
								(this as any).api('i/unpin', {
 | 
				
			||||||
 | 
									noteId: this.note.id
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									this.destroyDom();
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		del() {
 | 
							del() {
 | 
				
			||||||
			if (!window.confirm('%i18n:@delete-confirm%')) return;
 | 
								if (!window.confirm('%i18n:@delete-confirm%')) return;
 | 
				
			||||||
			(this as any).api('notes/delete', {
 | 
								(this as any).api('notes/delete', {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,35 @@
 | 
				
			||||||
import * as mongo from 'mongodb';
 | 
					 | 
				
			||||||
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
 | 
					import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
 | 
				
			||||||
import User, { ILocalUser } from '../../../../models/user';
 | 
					import User, { ILocalUser } from '../../../../models/user';
 | 
				
			||||||
import Note from '../../../../models/note';
 | 
					import Note from '../../../../models/note';
 | 
				
			||||||
import { pack } from '../../../../models/user';
 | 
					import { pack } from '../../../../models/user';
 | 
				
			||||||
import { deliverPinnedChange } from '../../../../services/i/pin';
 | 
					import { deliverPinnedChange } from '../../../../services/i/pin';
 | 
				
			||||||
 | 
					import getParams from '../../get-params';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const meta = {
 | 
				
			||||||
 | 
						desc: {
 | 
				
			||||||
 | 
							'ja-JP': '指定した投稿をピン留めします。'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requireCredential: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kind: 'account-write',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params: {
 | 
				
			||||||
 | 
							noteId: $.type(ID).note({
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									'ja-JP': '対象の投稿のID'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Pin note
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export default async (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
					export default async (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
				
			||||||
	// Get 'noteId' parameter
 | 
						const [ps, psErr] = getParams(meta, params);
 | 
				
			||||||
	const [noteId, noteIdErr] = $.type(ID).get(params.noteId);
 | 
						if (psErr) return rej(psErr);
 | 
				
			||||||
	if (noteIdErr) return rej('invalid noteId param');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Fetch pinee
 | 
						// Fetch pinee
 | 
				
			||||||
	const note = await Note.findOne({
 | 
						const note = await Note.findOne({
 | 
				
			||||||
		_id: noteId,
 | 
							_id: ps.noteId,
 | 
				
			||||||
		userId: user._id
 | 
							userId: user._id
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,21 +37,17 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
 | 
				
			||||||
		return rej('note not found');
 | 
							return rej('note not found');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let addedId: mongo.ObjectID;
 | 
					 | 
				
			||||||
	let removedId: mongo.ObjectID;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const pinnedNoteIds = user.pinnedNoteIds || [];
 | 
						const pinnedNoteIds = user.pinnedNoteIds || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pinnedNoteIds.length > 5) {
 | 
				
			||||||
 | 
							return rej('cannot pin more notes');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pinnedNoteIds.some(id => id.equals(note._id))) {
 | 
						if (pinnedNoteIds.some(id => id.equals(note._id))) {
 | 
				
			||||||
		return rej('already exists');
 | 
							return rej('already exists');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pinnedNoteIds.unshift(note._id);
 | 
						pinnedNoteIds.unshift(note._id);
 | 
				
			||||||
	addedId = note._id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (pinnedNoteIds.length > 5) {
 | 
					 | 
				
			||||||
		removedId = pinnedNoteIds.pop();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await User.update(user._id, {
 | 
						await User.update(user._id, {
 | 
				
			||||||
		$set: {
 | 
							$set: {
 | 
				
			||||||
| 
						 | 
					@ -45,14 +55,13 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Serialize
 | 
					 | 
				
			||||||
	const iObj = await pack(user, user, {
 | 
						const iObj = await pack(user, user, {
 | 
				
			||||||
		detail: true
 | 
							detail: true
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Send Add/Remove to followers
 | 
					 | 
				
			||||||
	deliverPinnedChange(user._id, removedId, addedId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Send response
 | 
						// Send response
 | 
				
			||||||
	res(iObj);
 | 
						res(iObj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send Add to followers
 | 
				
			||||||
 | 
						deliverPinnedChange(user._id, note._id, true);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/server/api/endpoints/i/unpin.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/server/api/endpoints/i/unpin.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,57 @@
 | 
				
			||||||
 | 
					import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
 | 
				
			||||||
 | 
					import User, { ILocalUser } from '../../../../models/user';
 | 
				
			||||||
 | 
					import Note from '../../../../models/note';
 | 
				
			||||||
 | 
					import { pack } from '../../../../models/user';
 | 
				
			||||||
 | 
					import { deliverPinnedChange } from '../../../../services/i/pin';
 | 
				
			||||||
 | 
					import getParams from '../../get-params';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const meta = {
 | 
				
			||||||
 | 
						desc: {
 | 
				
			||||||
 | 
							'ja-JP': '指定した投稿のピン留めを解除します。'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requireCredential: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kind: 'account-write',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params: {
 | 
				
			||||||
 | 
							noteId: $.type(ID).note({
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									'ja-JP': '対象の投稿のID'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default async (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						const [ps, psErr] = getParams(meta, params);
 | 
				
			||||||
 | 
						if (psErr) return rej(psErr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Fetch unpinee
 | 
				
			||||||
 | 
						const note = await Note.findOne({
 | 
				
			||||||
 | 
							_id: ps.noteId,
 | 
				
			||||||
 | 
							userId: user._id
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (note === null) {
 | 
				
			||||||
 | 
							return rej('note not found');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const pinnedNoteIds = (user.pinnedNoteIds || []).filter(id => !id.equals(note._id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						await User.update(user._id, {
 | 
				
			||||||
 | 
							$set: {
 | 
				
			||||||
 | 
								pinnedNoteIds: pinnedNoteIds
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const iObj = await pack(user, user, {
 | 
				
			||||||
 | 
							detail: true
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send response
 | 
				
			||||||
 | 
						res(iObj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send Remove to followers
 | 
				
			||||||
 | 
						deliverPinnedChange(user._id, note._id, false);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ import $ from 'cafy'; import ID from '../../../../../misc/cafy-id';
 | 
				
			||||||
import Favorite from '../../../../../models/favorite';
 | 
					import Favorite from '../../../../../models/favorite';
 | 
				
			||||||
import Note from '../../../../../models/note';
 | 
					import Note from '../../../../../models/note';
 | 
				
			||||||
import { ILocalUser } from '../../../../../models/user';
 | 
					import { ILocalUser } from '../../../../../models/user';
 | 
				
			||||||
 | 
					import getParams from '../../../get-params';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	desc: {
 | 
						desc: {
 | 
				
			||||||
| 
						 | 
					@ -11,17 +12,24 @@ export const meta = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kind: 'favorite-write'
 | 
						kind: 'favorite-write',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params: {
 | 
				
			||||||
 | 
							noteId: $.type(ID).note({
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									'ja-JP': '対象の投稿のID'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
					export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
				
			||||||
	// Get 'noteId' parameter
 | 
						const [ps, psErr] = getParams(meta, params);
 | 
				
			||||||
	const [noteId, noteIdErr] = $.type(ID).get(params.noteId);
 | 
						if (psErr) return rej(psErr);
 | 
				
			||||||
	if (noteIdErr) return rej('invalid noteId param');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get favoritee
 | 
						// Get favoritee
 | 
				
			||||||
	const note = await Note.findOne({
 | 
						const note = await Note.findOne({
 | 
				
			||||||
		_id: noteId
 | 
							_id: ps.noteId
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (note === null) {
 | 
						if (note === null) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import renderRemove from '../../remote/activitypub/renderer/remove';
 | 
				
			||||||
import packAp from '../../remote/activitypub/renderer';
 | 
					import packAp from '../../remote/activitypub/renderer';
 | 
				
			||||||
import { deliver } from '../../queue';
 | 
					import { deliver } from '../../queue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function deliverPinnedChange(userId: mongo.ObjectID, oldId?: mongo.ObjectID, newId?: mongo.ObjectID) {
 | 
					export async function deliverPinnedChange(userId: mongo.ObjectID, noteId: mongo.ObjectID, isAddition: boolean) {
 | 
				
			||||||
	const user = await User.findOne({
 | 
						const user = await User.findOne({
 | 
				
			||||||
		_id: userId
 | 
							_id: userId
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
| 
						 | 
					@ -20,21 +20,11 @@ export async function deliverPinnedChange(userId: mongo.ObjectID, oldId?: mongo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const target = `${config.url}/users/${user._id}/collections/featured`;
 | 
						const target = `${config.url}/users/${user._id}/collections/featured`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (oldId) {
 | 
						const item = `${config.url}/notes/${noteId}`;
 | 
				
			||||||
		const oldItem = `${config.url}/notes/${oldId}`;
 | 
						const content = packAp(isAddition ? renderAdd(user, target, item) : renderRemove(user, target, item));
 | 
				
			||||||
		const content = packAp(renderRemove(user, target, oldItem));
 | 
						queue.forEach(inbox => {
 | 
				
			||||||
		queue.forEach(inbox => {
 | 
							deliver(user, content, inbox);
 | 
				
			||||||
			deliver(user, content, inbox);
 | 
						});
 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (newId) {
 | 
					 | 
				
			||||||
		const newItem = `${config.url}/notes/${newId}`;
 | 
					 | 
				
			||||||
		const content = packAp(renderAdd(user, target, newItem));
 | 
					 | 
				
			||||||
		queue.forEach(inbox => {
 | 
					 | 
				
			||||||
			deliver(user, content, inbox);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue