From f8c414aafca8e91017cf01a65c21b57c88783076 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 21 May 2018 11:08:08 +0900 Subject: #1621 --- src/server/api/endpoints/i/update.ts | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/server/api') diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index b7b25d0f65..6e0c5b8515 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -47,6 +47,11 @@ module.exports = async (params, user, app) => new Promise(async (res, rej) => { if (isBotErr) return rej('invalid isBot param'); if (isBot != null) user.isBot = isBot; + // Get 'isCat' parameter + const [isCat, isCatErr] = $.bool.optional().get(params.isCat); + if (isCatErr) return rej('invalid isCat param'); + if (isCat != null) user.isCat = isCat; + // Get 'autoWatch' parameter const [autoWatch, autoWatchErr] = $.bool.optional().get(params.autoWatch); if (autoWatchErr) return rej('invalid autoWatch param'); @@ -82,6 +87,7 @@ module.exports = async (params, user, app) => new Promise(async (res, rej) => { bannerColor: user.bannerColor, profile: user.profile, isBot: user.isBot, + isCat: user.isCat, settings: user.settings } }); -- cgit v1.2.3-freya From b414068adad6d05f31ee8928de3ba26ab12fd5a6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 22 May 2018 11:39:48 +0900 Subject: Refactor --- src/server/api/endpoints/i/notifications.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/server/api') diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 50ed9b27e8..ba9c47508c 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -96,8 +96,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }); // Serialize - res(await Promise.all(notifications.map(async notification => - await pack(notification)))); + res(await Promise.all(notifications.map(notification => pack(notification)))); // Mark as read all if (notifications.length > 0 && markAsRead) { -- cgit v1.2.3-freya From 89dee86f3917a13d27043c0b744cd5b9f9ef93b9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 22 May 2018 11:45:49 +0900 Subject: Fix bug and remove unnecessary query --- src/server/api/common/read-notification.ts | 9 +++++++++ src/server/api/endpoints/notifications/get_unread_count.ts | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src/server/api') diff --git a/src/server/api/common/read-notification.ts b/src/server/api/common/read-notification.ts index 7b9faf4cf4..cdb87a4114 100644 --- a/src/server/api/common/read-notification.ts +++ b/src/server/api/common/read-notification.ts @@ -1,6 +1,7 @@ import * as mongo from 'mongodb'; import { default as Notification, INotification } from '../../../models/notification'; import publishUserStream from '../../../publishers/stream'; +import Mute from '../../../models/mute'; /** * Mark as read notification(s) @@ -26,6 +27,11 @@ export default ( ? [new mongo.ObjectID(message)] : [(message as INotification)._id]; + const mute = await Mute.find({ + muterId: userId + }); + const mutedUserIds = mute.map(m => m.muteeId); + // Update documents await Notification.update({ _id: { $in: ids }, @@ -42,6 +48,9 @@ export default ( const count = await Notification .count({ notifieeId: userId, + notifierId: { + $nin: mutedUserIds + }, isRead: false }, { limit: 1 diff --git a/src/server/api/endpoints/notifications/get_unread_count.ts b/src/server/api/endpoints/notifications/get_unread_count.ts index 600a80d194..9766366ff1 100644 --- a/src/server/api/endpoints/notifications/get_unread_count.ts +++ b/src/server/api/endpoints/notifications/get_unread_count.ts @@ -9,8 +9,7 @@ import Mute from '../../../../models/mute'; */ module.exports = (params, user) => new Promise(async (res, rej) => { const mute = await Mute.find({ - muterId: user._id, - deletedAt: { $exists: false } + muterId: user._id }); const mutedUserIds = mute.map(m => m.muteeId); -- cgit v1.2.3-freya From 0ba5dc3900b5bc8d86c63e04ab17e2e5652a00d2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 23 May 2018 15:25:15 +0900 Subject: Only show local posts in the timeline of top page --- src/client/app/common/views/components/welcome-timeline.vue | 1 + src/server/api/endpoints/notes.ts | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'src/server/api') diff --git a/src/client/app/common/views/components/welcome-timeline.vue b/src/client/app/common/views/components/welcome-timeline.vue index 6fadb030c3..0f1fc05f59 100644 --- a/src/client/app/common/views/components/welcome-timeline.vue +++ b/src/client/app/common/views/components/welcome-timeline.vue @@ -37,6 +37,7 @@ export default Vue.extend({ fetch(cb?) { this.fetching = true; (this as any).api('notes', { + local: true, reply: false, renote: false, media: false, diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 4ce7613d70..2a276a9582 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -8,6 +8,10 @@ import Note, { pack } from '../../../models/note'; * Get all notes */ module.exports = (params) => new Promise(async (res, rej) => { + // Get 'local' parameter + const [local, localErr] = $.bool.optional().get(params.local); + if (localErr) return rej('invalid local param'); + // Get 'reply' parameter const [reply, replyErr] = $.bool.optional().get(params.reply); if (replyErr) return rej('invalid reply param'); @@ -61,6 +65,10 @@ module.exports = (params) => new Promise(async (res, rej) => { }; } + if (local) { + query._user.host = null; + } + if (reply != undefined) { query.replyId = reply ? { $exists: true, $ne: null } : null; } -- cgit v1.2.3-freya From 6d9d6a677989c2ff163101351bb251e9818418cf Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 23 May 2018 15:46:20 +0900 Subject: oops --- src/server/api/endpoints/notes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/server/api') diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 2a276a9582..6e226d108a 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -66,7 +66,7 @@ module.exports = (params) => new Promise(async (res, rej) => { } if (local) { - query._user.host = null; + query._user = { host: null }; } if (reply != undefined) { -- cgit v1.2.3-freya From eb23be979c3578aecfa3afa05edc0a2e304cb2c4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 23 May 2018 15:47:51 +0900 Subject: oops --- src/server/api/endpoints/notes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/server/api') diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 6e226d108a..21946d1bd3 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -66,7 +66,7 @@ module.exports = (params) => new Promise(async (res, rej) => { } if (local) { - query._user = { host: null }; + query['_user.host'] = null; } if (reply != undefined) { -- cgit v1.2.3-freya From bd434ed02dd69b4a120de5a727a3e5d321f3cbfe Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 25 May 2018 18:41:49 +0900 Subject: Refactor and some fixes --- src/server/api/endpoints/drive/files/create.ts | 9 + src/services/drive/add-file.ts | 427 +++++++++++-------------- 2 files changed, 188 insertions(+), 248 deletions(-) (limited to 'src/server/api') diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts index e9348e4e2f..dd748d6bba 100644 --- a/src/server/api/endpoints/drive/files/create.ts +++ b/src/server/api/endpoints/drive/files/create.ts @@ -1,6 +1,7 @@ /** * Module dependencies */ +import * as fs from 'fs'; import $ from 'cafy'; import ID from '../../../../../cafy-id'; import { validateFileName, pack } from '../../../../../models/drive-file'; import create from '../../../../../services/drive/add-file'; @@ -32,15 +33,23 @@ module.exports = async (file, params, user): Promise => { const [folderId = null, folderIdErr] = $.type(ID).optional().nullable().get(params.folderId); if (folderIdErr) throw 'invalid folderId param'; + function cleanup() { + fs.unlink(file.path, () => {}); + } + try { // Create file const driveFile = await create(user, file.path, name, null, folderId); + cleanup(); + // Serialize return pack(driveFile); } catch (e) { console.error(e); + cleanup(); + throw e; } }; diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index bcd5bee512..8b1d3eef00 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -1,6 +1,5 @@ import { Buffer } from 'buffer'; import * as fs from 'fs'; -import * as tmp from 'tmp'; import * as stream from 'stream'; import * as mongodb from 'mongodb'; @@ -14,8 +13,7 @@ import DriveFile, { IMetadata, getDriveFileBucket, IDriveFile, DriveFileChunk } import DriveFolder from '../../models/drive-folder'; import { pack } from '../../models/drive-file'; import event, { publishDriveStream } from '../../publishers/stream'; -import getAcct from '../../acct/render'; -import { IUser, isLocalUser, isRemoteUser } from '../../models/user'; +import { isLocalUser, IRemoteUser } from '../../models/user'; import DriveFileThumbnail, { getDriveFileThumbnailBucket, DriveFileThumbnailChunk } from '../../models/drive-file-thumbnail'; import genThumbnail from '../../drive/gen-thumbnail'; @@ -25,13 +23,6 @@ const gm = _gm.subClass({ const log = debug('misskey:drive:add-file'); -const tmpFile = (): Promise<[string, any]> => new Promise((resolve, reject) => { - tmp.file((e, path, fd, cleanup) => { - if (e) return reject(e); - resolve([path, cleanup]); - }); -}); - const writeChunks = (name: string, readable: stream.Readable, type: string, metadata: any) => getDriveFileBucket() .then(bucket => new Promise((resolve, reject) => { @@ -55,8 +46,59 @@ const writeThumbnailChunks = (name: string, readable: stream.Readable, originalI readable.pipe(writeStream); })); -const addFile = async ( - user: IUser, +async function deleteOldFile(user: IRemoteUser) { + const oldFile = await DriveFile.findOne({ + _id: { + $nin: [user.avatarId, user.bannerId] + } + }, { + sort: { + _id: 1 + } + }); + + if (oldFile) { + // チャンクをすべて削除 + DriveFileChunk.remove({ + files_id: oldFile._id + }); + + DriveFile.update({ _id: oldFile._id }, { + $set: { + 'metadata.deletedAt': new Date(), + 'metadata.isExpired': true + } + }); + + //#region サムネイルもあれば削除 + const thumbnail = await DriveFileThumbnail.findOne({ + 'metadata.originalId': oldFile._id + }); + + if (thumbnail) { + DriveFileThumbnailChunk.remove({ + files_id: thumbnail._id + }); + + DriveFileThumbnail.remove({ _id: thumbnail._id }); + } + //#endregion + } +} + +/** + * Add file to drive + * + * @param user User who wish to add file + * @param path File path + * @param name Name + * @param comment Comment + * @param folderId Folder ID + * @param force If set to true, forcibly upload the file even if there is a file with the same hash. + * @return Created drive file + */ +export default async function( + user: any, path: string, name: string = null, comment: string = null, @@ -64,55 +106,54 @@ const addFile = async ( force: boolean = false, url: string = null, uri: string = null -): Promise => { - log(`registering ${name} (user: ${getAcct(user)}, path: ${path})`); - - // Calculate hash, get content type and get file size - const [hash, [mime, ext], size] = await Promise.all([ - // hash - ((): Promise => new Promise((res, rej) => { - const readable = fs.createReadStream(path); - const hash = crypto.createHash('md5'); - const chunks = []; - readable - .on('error', rej) - .pipe(hash) - .on('error', rej) - .on('data', (chunk) => chunks.push(chunk)) - .on('end', () => { - const buffer = Buffer.concat(chunks); - res(buffer.toString('hex')); - }); - }))(), - // mime - ((): Promise<[string, string | null]> => new Promise((res, rej) => { - const readable = fs.createReadStream(path); - readable - .on('error', rej) - .once('data', (buffer: Buffer) => { - readable.destroy(); - const type = fileType(buffer); - if (type) { - return res([type.mime, type.ext]); - } else { - // 種類が同定できなかったら application/octet-stream にする - return res(['application/octet-stream', null]); - } - }); - }))(), - // size - ((): Promise => new Promise((res, rej) => { - fs.stat(path, (err, stats) => { - if (err) return rej(err); - res(stats.size); +): Promise { + // Calc md5 hash + const calcHash = new Promise((res, rej) => { + const readable = fs.createReadStream(path); + const hash = crypto.createHash('md5'); + const chunks = []; + readable + .on('error', rej) + .pipe(hash) + .on('error', rej) + .on('data', chunk => chunks.push(chunk)) + .on('end', () => { + const buffer = Buffer.concat(chunks); + res(buffer.toString('hex')); }); - }))() - ]); + }); + + // Detect content type + const detectMime = new Promise<[string, string]>((res, rej) => { + const readable = fs.createReadStream(path); + readable + .on('error', rej) + .once('data', (buffer: Buffer) => { + readable.destroy(); + const type = fileType(buffer); + if (type) { + res([type.mime, type.ext]); + } else { + // 種類が同定できなかったら application/octet-stream にする + res(['application/octet-stream', null]); + } + }); + }); + + // Get file size + const getFileSize = new Promise((res, rej) => { + fs.stat(path, (err, stats) => { + if (err) return rej(err); + res(stats.size); + }); + }); + + const [hash, [mime, ext], size] = await Promise.all([calcHash, detectMime, getFileSize]); log(`hash: ${hash}, mime: ${mime}, ext: ${ext}, size: ${size}`); // detect name - const detectedName: string = name || (ext ? `untitled.${ext}` : 'untitled'); + const detectedName = name || (ext ? `untitled.${ext}` : 'untitled'); if (!force) { // Check if there is a file with the same hash @@ -125,26 +166,70 @@ const addFile = async ( if (much !== null) { log('file with same hash is found'); return much; - } else { - log('file with same hash is not found'); } } - const [wh, averageColor, folder] = await Promise.all([ - // Width and height (when image) - (async () => { - // 画像かどうか - if (!/^image\/.*$/.test(mime)) { - return null; + //#region Check drive usage + const usage = await DriveFile + .aggregate([{ + $match: { + 'metadata.userId': user._id, + 'metadata.deletedAt': { $exists: false } + } + }, { + $project: { + length: true } + }, { + $group: { + _id: null, + usage: { $sum: '$length' } + } + }]) + .then((aggregates: any[]) => { + if (aggregates.length > 0) { + return aggregates[0].usage; + } + return 0; + }); - const imageType = mime.split('/')[1]; + log(`drive usage is ${usage}`); - // 画像でもPNGかJPEGかGIFでないならスキップ - if (imageType != 'png' && imageType != 'jpeg' && imageType != 'gif') { - return null; - } + // If usage limit exceeded + if (usage + size > user.driveCapacity) { + if (isLocalUser(user)) { + throw 'no-free-space'; + } else { + // (アバターまたはバナーを含まず)最も古いファイルを削除する + deleteOldFile(user); + } + } + //#endregion + + const fetchFolder = async () => { + if (!folderId) { + return null; + } + + const driveFolder = await DriveFolder.findOne({ + _id: folderId, + userId: user._id + }); + + if (driveFolder == null) throw 'folder-not-found'; + + return driveFolder; + }; + + const properties = {}; + + let propPromises = []; + const isImage = ['image/jpeg', 'image/gif', 'image/png'].includes(mime); + + if (isImage) { + // Calc width and height + const calcWh = async () => { log('calculate image width and height...'); // Calculate width and height @@ -153,22 +238,12 @@ const addFile = async ( log(`image width and height is calculated: ${size.width}, ${size.height}`); - return [size.width, size.height]; - })(), - // average color (when image) - (async () => { - // 画像かどうか - if (!/^image\/.*$/.test(mime)) { - return null; - } - - const imageType = mime.split('/')[1]; - - // 画像でもPNGかJPEGでないならスキップ - if (imageType != 'png' && imageType != 'jpeg') { - return null; - } + properties['width'] = size.width; + properties['height'] = size.height; + }; + // Calc average color + const calcAvg = async () => { log('calculate average color...'); const info = await prominence(gm(fs.createReadStream(path), name)).identify(); @@ -185,111 +260,17 @@ const addFile = async ( log(`average color is calculated: ${r}, ${g}, ${b}`); - return isTransparent ? [r, g, b, 255] : [r, g, b]; - })(), - // folder - (async () => { - if (!folderId) { - return null; - } - const driveFolder = await DriveFolder.findOne({ - _id: folderId, - userId: user._id - }); - if (!driveFolder) { - throw 'folder-not-found'; - } - return driveFolder; - })(), - // usage checker - (async () => { - // Calculate drive usage - const usage = await DriveFile - .aggregate([{ - $match: { - 'metadata.userId': user._id, - 'metadata.deletedAt': { $exists: false } - } - }, { - $project: { - length: true - } - }, { - $group: { - _id: null, - usage: { $sum: '$length' } - } - }]) - .then((aggregates: any[]) => { - if (aggregates.length > 0) { - return aggregates[0].usage; - } - return 0; - }); - - log(`drive usage is ${usage}`); - - // If usage limit exceeded - if (usage + size > user.driveCapacity) { - if (isLocalUser(user)) { - throw 'no-free-space'; - } else { - //#region (アバターまたはバナーを含まず)最も古いファイルを削除する - const oldFile = await DriveFile.findOne({ - _id: { - $nin: [user.avatarId, user.bannerId] - } - }, { - sort: { - _id: 1 - } - }); - - if (oldFile) { - // チャンクをすべて削除 - DriveFileChunk.remove({ - files_id: oldFile._id - }); - - DriveFile.update({ _id: oldFile._id }, { - $set: { - 'metadata.deletedAt': new Date(), - 'metadata.isExpired': true - } - }); - - //#region サムネイルもあれば削除 - const thumbnail = await DriveFileThumbnail.findOne({ - 'metadata.originalId': oldFile._id - }); - - if (thumbnail) { - DriveFileThumbnailChunk.remove({ - files_id: thumbnail._id - }); - - DriveFileThumbnail.remove({ _id: thumbnail._id }); - } - //#endregion - } - //#endregion - } - } - })() - ]); - - const readable = fs.createReadStream(path); + const value = isTransparent ? [r, g, b, 255] : [r, g, b]; - const properties = {}; + properties['avgColor'] = value; + }; - if (wh) { - properties['width'] = wh[0]; - properties['height'] = wh[1]; + propPromises = [calcWh(), calcAvg()]; } - if (averageColor) { - properties['avgColor'] = averageColor; - } + const [folder] = await Promise.all([fetchFolder(), propPromises]); + + const readable = fs.createReadStream(path); const metadata = { userId: user._id, @@ -309,74 +290,24 @@ const addFile = async ( metadata.uri = uri; } - const file = await (writeChunks(detectedName, readable, mime, metadata) as Promise); + const driveFile = await (writeChunks(detectedName, readable, mime, metadata) as Promise); + + log(`drive file has been created ${driveFile._id}`); + + pack(driveFile).then(packedFile => { + // Publish drive_file_created event + event(user._id, 'drive_file_created', packedFile); + publishDriveStream(user._id, 'file_created', packedFile); + }); try { - const thumb = await genThumbnail(file); + const thumb = await genThumbnail(driveFile); if (thumb) { - await writeThumbnailChunks(detectedName, thumb, file._id); + await writeThumbnailChunks(detectedName, thumb, driveFile._id); } } catch (e) { // noop } - return file; -}; - -/** - * Add file to drive - * - * @param user User who wish to add file - * @param file File path or readableStream - * @param comment Comment - * @param type File type - * @param folderId Folder ID - * @param force If set to true, forcibly upload the file even if there is a file with the same hash. - * @return Object that represents added file - */ -export default (user: any, file: string | stream.Readable, ...args) => new Promise((resolve, reject) => { - const isStream = typeof file === 'object' && typeof file.read === 'function'; - - // Get file path - new Promise<[string, any]>((res, rej) => { - if (typeof file === 'string') { - res([file, null]); - } else if (isStream) { - tmpFile() - .then(([path, cleanup]) => { - const readable: stream.Readable = file; - const writable = fs.createWriteStream(path); - readable - .on('error', rej) - .on('end', () => { - res([path, cleanup]); - }) - .pipe(writable) - .on('error', rej); - }) - .catch(rej); - } else { - rej(new Error('un-compatible file.')); - } - }) - .then(([path, cleanup]) => new Promise((res, rej) => { - addFile(user, path, ...args) - .then(file => { - res(file); - if (cleanup) cleanup(); - }) - .catch(rej); - })) - .then(file => { - log(`drive file has been created ${file._id}`); - - resolve(file); - - pack(file).then(packedFile => { - // Publish drive_file_created event - event(user._id, 'drive_file_created', packedFile); - publishDriveStream(user._id, 'file_created', packedFile); - }); - }) - .catch(reject); -}); + return driveFile; +} -- cgit v1.2.3-freya From 712c0ef27d72e522f8628c349b5ccf950cfeaed6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 25 May 2018 21:05:16 +0900 Subject: Refactor: Use better english --- .../app/desktop/views/components/note-detail.vue | 34 ++++++------ .../app/mobile/views/components/note-detail.vue | 32 +++++------ src/server/api/endpoints.ts | 2 +- src/server/api/endpoints/notes/context.ts | 63 ---------------------- src/server/api/endpoints/notes/conversation.ts | 58 ++++++++++++++++++++ 5 files changed, 92 insertions(+), 97 deletions(-) delete mode 100644 src/server/api/endpoints/notes/context.ts create mode 100644 src/server/api/endpoints/notes/conversation.ts (limited to 'src/server/api') diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue index d5526e5d6a..5b48b7a1ba 100644 --- a/src/client/app/desktop/views/components/note-detail.vue +++ b/src/client/app/desktop/views/components/note-detail.vue @@ -2,16 +2,16 @@
-
- +
+
@@ -107,8 +107,8 @@ export default Vue.extend({ data() { return { - context: [], - contextFetching: false, + conversation: [], + conversationFetching: false, replies: [] }; }, @@ -176,15 +176,15 @@ export default Vue.extend({ }, methods: { - fetchContext() { - this.contextFetching = true; + fetchConversation() { + this.conversationFetching = true; - // Fetch context - (this as any).api('notes/context', { + // Fetch conversation + (this as any).api('notes/conversation', { noteId: this.p.replyId - }).then(context => { - this.contextFetching = false; - this.context = context.reverse(); + }).then(conversation => { + this.conversationFetching = false; + this.conversation = conversation.reverse(); }); }, reply() { @@ -249,7 +249,7 @@ root(isDark) &:disabled color isDark ? #21242b : #ccc - > .context + > .conversation > * border-bottom 1px solid isDark ? #1c2023 : #eef0f2 diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue index c6664a91da..244dbb6c03 100644 --- a/src/client/app/mobile/views/components/note-detail.vue +++ b/src/client/app/mobile/views/components/note-detail.vue @@ -2,15 +2,15 @@
-
- +
+
@@ -99,8 +99,8 @@ export default Vue.extend({ data() { return { - context: [], - contextFetching: false, + conversation: [], + conversationFetching: false, replies: [] }; }, @@ -166,14 +166,14 @@ export default Vue.extend({ methods: { fetchContext() { - this.contextFetching = true; + this.conversationFetching = true; - // Fetch context - (this as any).api('notes/context', { + // Fetch conversation + (this as any).api('notes/conversation', { noteId: this.p.replyId - }).then(context => { - this.contextFetching = false; - this.context = context.reverse(); + }).then(conversation => { + this.conversationFetching = false; + this.conversation = conversation.reverse(); }); }, reply() { @@ -245,7 +245,7 @@ root(isDark) &:disabled color #ccc - > .context + > .conversation > * border-bottom 1px solid isDark ? #1c2023 : #eef0f2 diff --git a/src/server/api/endpoints.ts b/src/server/api/endpoints.ts index 7647c76d3d..892da3540f 100644 --- a/src/server/api/endpoints.ts +++ b/src/server/api/endpoints.ts @@ -482,7 +482,7 @@ const endpoints: Endpoint[] = [ name: 'notes/replies' }, { - name: 'notes/context' + name: 'notes/conversation' }, { name: 'notes/create', diff --git a/src/server/api/endpoints/notes/context.ts b/src/server/api/endpoints/notes/context.ts deleted file mode 100644 index 1cd27250e2..0000000000 --- a/src/server/api/endpoints/notes/context.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Module dependencies - */ -import $ from 'cafy'; import ID from '../../../../cafy-id'; -import Note, { pack } from '../../../../models/note'; - -/** - * Show a context of a note - * - * @param {any} params - * @param {any} user - * @return {Promise} - */ -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'); - - // Get 'limit' parameter - const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); - if (limitErr) return rej('invalid limit param'); - - // Get 'offset' parameter - const [offset = 0, offsetErr] = $.num.optional().min(0).get(params.offset); - if (offsetErr) return rej('invalid offset param'); - - // Lookup note - const note = await Note.findOne({ - _id: noteId - }); - - if (note === null) { - return rej('note not found'); - } - - const context = []; - let i = 0; - - async function get(id) { - i++; - const p = await Note.findOne({ _id: id }); - - if (i > offset) { - context.push(p); - } - - if (context.length == limit) { - return; - } - - if (p.replyId) { - await get(p.replyId); - } - } - - if (note.replyId) { - await get(note.replyId); - } - - // Serialize - res(await Promise.all(context.map(async note => - await pack(note, user)))); -}); diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts new file mode 100644 index 0000000000..02f7229ccf --- /dev/null +++ b/src/server/api/endpoints/notes/conversation.ts @@ -0,0 +1,58 @@ +/** + * Module dependencies + */ +import $ from 'cafy'; import ID from '../../../../cafy-id'; +import Note, { pack } from '../../../../models/note'; + +/** + * Show conversation of 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'); + + // Get 'limit' parameter + const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit); + if (limitErr) return rej('invalid limit param'); + + // Get 'offset' parameter + const [offset = 0, offsetErr] = $.num.optional().min(0).get(params.offset); + if (offsetErr) return rej('invalid offset param'); + + // Lookup note + const note = await Note.findOne({ + _id: noteId + }); + + if (note === null) { + return rej('note not found'); + } + + const conversation = []; + let i = 0; + + async function get(id) { + i++; + const p = await Note.findOne({ _id: id }); + + if (i > offset) { + conversation.push(p); + } + + if (conversation.length == limit) { + return; + } + + if (p.replyId) { + await get(p.replyId); + } + } + + if (note.replyId) { + await get(note.replyId); + } + + // Serialize + res(await Promise.all(conversation.map(note => pack(note, user)))); +}); -- cgit v1.2.3-freya