ピン留めを解除することができるようにしたり

This commit is contained in:
syuilo 2018-09-24 16:26:12 +09:00
parent 7b4c307c46
commit 59d67d3140
No known key found for this signature in database
GPG key ID: BDC4C49D06AB9D69
6 changed files with 128 additions and 46 deletions

View file

@ -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: "投稿元で見る"

View file

@ -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', {

View file

@ -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);
}); });

View 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);
});

View file

@ -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) {

View file

@ -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);
});
}
} }
/** /**