summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-05-28 14:39:46 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-05-28 14:39:46 +0900
commitceda2ca89661d1bd3889453997fe0868a8c31e9d (patch)
tree94a3a12fc7230abdd9f82d77188fec345dfaf9cd /src
parent:art: (diff)
downloadsharkey-ceda2ca89661d1bd3889453997fe0868a8c31e9d.tar.gz
sharkey-ceda2ca89661d1bd3889453997fe0868a8c31e9d.tar.bz2
sharkey-ceda2ca89661d1bd3889453997fe0868a8c31e9d.zip
Implement delete note
Diffstat (limited to 'src')
-rw-r--r--src/client/app/common/views/components/note-menu.vue10
-rw-r--r--src/client/app/desktop/views/components/note-detail.sub.vue1
-rw-r--r--src/client/app/desktop/views/components/note-detail.vue1
-rw-r--r--src/client/app/desktop/views/components/notes.note.vue3
-rw-r--r--src/client/app/desktop/views/components/sub-note-content.vue5
-rw-r--r--src/client/app/mobile/views/components/note-detail.vue3
-rw-r--r--src/client/app/mobile/views/components/note.vue3
-rw-r--r--src/client/app/mobile/views/components/sub-note-content.vue5
-rw-r--r--src/remote/activitypub/kernel/delete/note.ts10
-rw-r--r--src/remote/activitypub/renderer/delete.ts4
-rw-r--r--src/renderers/get-note-summary.ts4
-rw-r--r--src/server/api/endpoints.ts5
-rw-r--r--src/server/api/endpoints/notes/delete.ts26
-rw-r--r--src/services/note/delete.ts44
14 files changed, 109 insertions, 15 deletions
diff --git a/src/client/app/common/views/components/note-menu.vue b/src/client/app/common/views/components/note-menu.vue
index fb95055049..a400610a2b 100644
--- a/src/client/app/common/views/components/note-menu.vue
+++ b/src/client/app/common/views/components/note-menu.vue
@@ -4,6 +4,7 @@
<div class="popover" :class="{ compact }" ref="popover">
<button @click="favorite">%i18n:@favorite%</button>
<button v-if="note.userId == $store.state.i.id" @click="pin">%i18n:@pin%</button>
+ <button v-if="note.userId == $store.state.i.id" @click="del">%i18n:@delete%</button>
<a v-if="note.uri" :href="note.uri" target="_blank">%i18n:@remote%</a>
</div>
</div>
@@ -59,6 +60,15 @@ export default Vue.extend({
});
},
+ del() {
+ if (!window.confirm('%i18n:@delete-confirm%')) return;
+ (this as any).api('notes/delete', {
+ noteId: this.note.id
+ }).then(() => {
+ this.$destroy();
+ });
+ },
+
favorite() {
(this as any).api('notes/favorites/create', {
noteId: this.note.id
diff --git a/src/client/app/desktop/views/components/note-detail.sub.vue b/src/client/app/desktop/views/components/note-detail.sub.vue
index 0471c70ee7..00e54ff1a6 100644
--- a/src/client/app/desktop/views/components/note-detail.sub.vue
+++ b/src/client/app/desktop/views/components/note-detail.sub.vue
@@ -16,6 +16,7 @@
<div class="body">
<div class="text">
<span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+ <span v-if="note.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
<mk-note-html v-if="note.text" :text="note.text" :i="$store.state.i"/>
</div>
<div class="media" v-if="note.mediaIds.length > 0">
diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index e64990b4ce..63b2150110 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -39,6 +39,7 @@
<div class="body">
<div class="text">
<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+ <span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
<mk-note-html v-if="p.text" :text="p.text" :i="$store.state.i"/>
</div>
<div class="media" v-if="p.media.length > 0">
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index c4ad67c2f8..f293ffacfb 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -41,7 +41,8 @@
</p>
<div class="content" v-show="p.cw == null || showContent">
<div class="text">
- <span v-if="p.isHidden" style="opacity: 0.5">(この投稿は非公開です)</span>
+ <span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+ <span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
<a class="reply" v-if="p.reply">%fa:reply%</a>
<mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="$store.state.i" :class="$style.text"/>
<a class="rp" v-if="p.renote">RP:</a>
diff --git a/src/client/app/desktop/views/components/sub-note-content.vue b/src/client/app/desktop/views/components/sub-note-content.vue
index 8aa32cec73..46e1b802b9 100644
--- a/src/client/app/desktop/views/components/sub-note-content.vue
+++ b/src/client/app/desktop/views/components/sub-note-content.vue
@@ -1,13 +1,14 @@
<template>
<div class="mk-sub-note-content">
<div class="body">
- <span v-if="note.isHidden" style="opacity: 0.5">%i18n:@hidden%</span>
+ <span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+ <span v-if="note.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
<a class="reply" v-if="note.replyId">%fa:reply%</a>
<mk-note-html :text="note.text" :i="$store.state.i"/>
<a class="rp" v-if="note.renoteId" :href="`/note:${note.renoteId}`">RP: ...</a>
</div>
<details v-if="note.media.length > 0">
- <summary>({{ note.media.length }}%i18n:@media%)</summary>
+ <summary>({{ '%i18n:@media-count%'.replace('{}', note.media.length) }})</summary>
<mk-media-list :media-list="note.media"/>
</details>
<details v-if="note.poll">
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index 91b8c20111..8ab766e1dc 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -36,7 +36,8 @@
</header>
<div class="body">
<div class="text">
- <span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@hidden%)</span>
+ <span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
+ <span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
<mk-note-html v-if="p.text" :text="p.text" :i="$store.state.i"/>
</div>
<div class="tags" v-if="p.tags && p.tags.length > 0">
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index 35e1c24b07..997d83a6fe 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -41,7 +41,8 @@
</p>
<div class="content" v-show="p.cw == null || showContent">
<div class="text">
- <span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@hidden%)</span>
+ <span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
+ <span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
<a class="reply" v-if="p.reply">%fa:reply%</a>
<mk-note-html v-if="p.text && !canHideText(p)" :text="p.text" :i="$store.state.i" :class="$style.text"/>
<a class="rp" v-if="p.renote != null">RP:</a>
diff --git a/src/client/app/mobile/views/components/sub-note-content.vue b/src/client/app/mobile/views/components/sub-note-content.vue
index 023dec70d2..4ad90b97df 100644
--- a/src/client/app/mobile/views/components/sub-note-content.vue
+++ b/src/client/app/mobile/views/components/sub-note-content.vue
@@ -1,13 +1,14 @@
<template>
<div class="mk-sub-note-content">
<div class="body">
- <span v-if="note.isHidden" style="opacity: 0.5">(%i18n:@hidden%)</span>
+ <span v-if="note.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
+ <span v-if="note.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
<a class="reply" v-if="note.replyId">%fa:reply%</a>
<mk-note-html v-if="note.text" :text="note.text" :i="$store.state.i"/>
<a class="rp" v-if="note.renoteId">RP: ...</a>
</div>
<details v-if="note.media.length > 0">
- <summary>({{ note.media.length }}個のメディア)</summary>
+ <summary>({{ '%i18n:@media-count%'.replace('{}', note.media.length) }})</summary>
<mk-media-list :media-list="note.media"/>
</details>
<details v-if="note.poll">
diff --git a/src/remote/activitypub/kernel/delete/note.ts b/src/remote/activitypub/kernel/delete/note.ts
index b2868f69a3..1951982f69 100644
--- a/src/remote/activitypub/kernel/delete/note.ts
+++ b/src/remote/activitypub/kernel/delete/note.ts
@@ -2,6 +2,7 @@ import * as debug from 'debug';
import Note from '../../../../models/note';
import { IRemoteUser } from '../../../../models/user';
+import deleteNode from '../../../../services/note/delete';
const log = debug('misskey:activitypub');
@@ -18,12 +19,5 @@ export default async function(actor: IRemoteUser, uri: string): Promise<void> {
throw new Error('投稿を削除しようとしているユーザーは投稿の作成者ではありません');
}
- Note.update({ _id: note._id }, {
- $set: {
- deletedAt: new Date(),
- text: null,
- mediaIds: [],
- poll: null
- }
- });
+ await deleteNode(actor, note);
}
diff --git a/src/remote/activitypub/renderer/delete.ts b/src/remote/activitypub/renderer/delete.ts
new file mode 100644
index 0000000000..d15cb447e6
--- /dev/null
+++ b/src/remote/activitypub/renderer/delete.ts
@@ -0,0 +1,4 @@
+export default object => ({
+ type: 'Delete',
+ object
+});
diff --git a/src/renderers/get-note-summary.ts b/src/renderers/get-note-summary.ts
index 643e2d09ba..ec7c74cf9f 100644
--- a/src/renderers/get-note-summary.ts
+++ b/src/renderers/get-note-summary.ts
@@ -3,6 +3,10 @@
* @param {*} note (packされた)投稿
*/
const summarize = (note: any): string => {
+ if (note.deletedAt) {
+ return '(削除された投稿)';
+ }
+
if (note.isHidden) {
return '(非公開の投稿)';
}
diff --git a/src/server/api/endpoints.ts b/src/server/api/endpoints.ts
index 892da3540f..908d9574a5 100644
--- a/src/server/api/endpoints.ts
+++ b/src/server/api/endpoints.ts
@@ -495,6 +495,11 @@ const endpoints: Endpoint[] = [
kind: 'note-write'
},
{
+ name: 'notes/delete',
+ withCredential: true,
+ kind: 'note-write'
+ },
+ {
name: 'notes/renotes'
},
{
diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts
new file mode 100644
index 0000000000..9bbb1541d6
--- /dev/null
+++ b/src/server/api/endpoints/notes/delete.ts
@@ -0,0 +1,26 @@
+import $ from 'cafy'; import ID from '../../../../cafy-id';
+import Note from '../../../../models/note';
+import deleteNote from '../../../../services/note/delete';
+
+/**
+ * Delete a note
+ */
+module.exports = (params, user) => new Promise(async (res, rej) => {
+ // Get 'noteId' parameter
+ const [noteId, noteIdErr] = $.type(ID).get(params.noteId);
+ if (noteIdErr) return rej('invalid noteId param');
+
+ // Fetch note
+ const note = await Note.findOne({
+ _id: noteId,
+ userId: user._id
+ });
+
+ if (note === null) {
+ return rej('note not found');
+ }
+
+ await deleteNote(user, note);
+
+ res();
+});
diff --git a/src/services/note/delete.ts b/src/services/note/delete.ts
new file mode 100644
index 0000000000..793f0090be
--- /dev/null
+++ b/src/services/note/delete.ts
@@ -0,0 +1,44 @@
+import Note, { INote } from '../../models/note';
+import { IUser, isLocalUser } from '../../models/user';
+import { publishNoteStream } from '../../publishers/stream';
+import renderDelete from '../../remote/activitypub/renderer/delete';
+import pack from '../../remote/activitypub/renderer';
+import { deliver } from '../../queue';
+import Following from '../../models/following';
+import renderNote from '../../remote/activitypub/renderer/note';
+
+/**
+ * 投稿を削除します。
+ * @param user 投稿者
+ * @param note 投稿
+ */
+export default async function(user: IUser, note: INote) {
+ await Note.update({
+ _id: note._id,
+ userId: user._id
+ }, {
+ $set: {
+ deletedAt: new Date(),
+ text: null,
+ mediaIds: [],
+ poll: null
+ }
+ });
+
+ publishNoteStream(note._id, 'deleted');
+
+ //#region ローカルの投稿なら削除アクティビティを配送
+ if (isLocalUser(user)) {
+ const content = pack(renderDelete(await renderNote(note)));
+
+ const followings = await Following.find({
+ followeeId: user._id,
+ '_follower.host': { $ne: null }
+ });
+
+ followings.forEach(following => {
+ deliver(user, content, following._follower.inbox);
+ });
+ }
+ //#endregion
+}