diff --git a/package.json b/package.json index 70124a0ae4..775c997e55 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "8.26.0", + "version": "9.0.0", "clientVersion": "1.0.9358", "codename": "nighthike", "main": "./built/index.js", diff --git a/src/client/app/common/scripts/parse-search-query.ts b/src/client/app/common/scripts/parse-search-query.ts deleted file mode 100644 index 5f6ae3320a..0000000000 --- a/src/client/app/common/scripts/parse-search-query.ts +++ /dev/null @@ -1,53 +0,0 @@ -export default function(qs: string) { - const q = { - text: '' - }; - - qs.split(' ').forEach(x => { - if (/^([a-z_]+?):(.+?)$/.test(x)) { - const [key, value] = x.split(':'); - switch (key) { - case 'user': - q['includeUserUsernames'] = value.split(','); - break; - case 'exclude_user': - q['excludeUserUsernames'] = value.split(','); - break; - case 'follow': - q['following'] = value == 'null' ? null : value == 'true'; - break; - case 'reply': - q['reply'] = value == 'null' ? null : value == 'true'; - break; - case 'renote': - q['renote'] = value == 'null' ? null : value == 'true'; - break; - case 'media': - q['media'] = value == 'null' ? null : value == 'true'; - break; - case 'poll': - q['poll'] = value == 'null' ? null : value == 'true'; - break; - case 'until': - case 'since': - // YYYY-MM-DD - if (/^[0-9]+\-[0-9]+\-[0-9]+$/) { - const [yyyy, mm, dd] = value.split('-'); - q[`${key}_date`] = (new Date(parseInt(yyyy, 10), parseInt(mm, 10) - 1, parseInt(dd, 10))).getTime(); - } - break; - default: - q[key] = value; - break; - } - } else { - q.text += x + ' '; - } - }); - - if (q.text) { - q.text = q.text.trim(); - } - - return q; -} diff --git a/src/client/app/common/views/components/welcome-timeline.vue b/src/client/app/common/views/components/welcome-timeline.vue index d4e7902c7b..b427721d37 100644 --- a/src/client/app/common/views/components/welcome-timeline.vue +++ b/src/client/app/common/views/components/welcome-timeline.vue @@ -63,7 +63,7 @@ export default Vue.extend({ local: true, reply: false, renote: false, - media: false, + file: false, poll: false }).then(notes => { this.notes = notes; diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue index 1ba4a9a447..a61a004a85 100644 --- a/src/client/app/desktop/views/components/note-detail.vue +++ b/src/client/app/desktop/views/components/note-detail.vue @@ -42,8 +42,8 @@ %i18n:@deleted% -
- +
+
@@ -114,7 +114,7 @@ export default Vue.extend({ isRenote(): boolean { return (this.note.renote && this.note.text == null && - this.note.mediaIds.length == 0 && + this.note.fileIds.length == 0 && this.note.poll == null); }, p(): any { diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue index 7592ae3905..1d6b2048ba 100644 --- a/src/client/app/desktop/views/components/notes.note.vue +++ b/src/client/app/desktop/views/components/notes.note.vue @@ -28,8 +28,8 @@ RP:
-
- +
+
%fa:map-marker-alt% 位置情報 @@ -110,7 +110,7 @@ export default Vue.extend({ isRenote(): boolean { return (this.note.renote && this.note.text == null && - this.note.mediaIds.length == 0 && + this.note.fileIds.length == 0 && this.note.poll == null); }, diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue index a1c1207a7b..f19ecf8f9a 100644 --- a/src/client/app/desktop/views/components/notes.vue +++ b/src/client/app/desktop/views/components/notes.vue @@ -122,7 +122,7 @@ export default Vue.extend({ prepend(note, silent = false) { //#region 弾く const isMyNote = note.userId == this.$store.state.i.id; - const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null; + const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null; if (this.$store.state.settings.showMyRenotes === false) { if (isMyNote && isPureRenote) { diff --git a/src/client/app/desktop/views/components/post-form-window.vue b/src/client/app/desktop/views/components/post-form-window.vue index 51a416e281..a88c96d1bf 100644 --- a/src/client/app/desktop/views/components/post-form-window.vue +++ b/src/client/app/desktop/views/components/post-form-window.vue @@ -4,7 +4,7 @@ %fa:map-marker-alt% %i18n:@note% %i18n:@reply% - {{ '%i18n:@attaches%'.replace('{}', media.length) }} + {{ '%i18n:@attaches%'.replace('{}', files.length) }} {{ '%i18n:@uploading-media%'.replace('{}', uploadings.length) }} @@ -14,7 +14,7 @@ :reply="reply" @posted="onPosted" @change-uploadings="onChangeUploadings" - @change-attached-media="onChangeMedia" + @change-attached-files="onChangeFiles" @geo-attached="onGeoAttached" @geo-dettached="onGeoDettached"/>
@@ -29,7 +29,7 @@ export default Vue.extend({ data() { return { uploadings: [], - media: [], + files: [], geo: null }; }, @@ -42,8 +42,8 @@ export default Vue.extend({ onChangeUploadings(files) { this.uploadings = files; }, - onChangeMedia(media) { - this.media = media; + onChangeFiles(files) { + this.files = files; }, onGeoAttached(geo) { this.geo = geo; diff --git a/src/client/app/desktop/views/components/post-form.vue b/src/client/app/desktop/views/components/post-form.vue index 2ca5484610..f6f52c8f1f 100644 --- a/src/client/app/desktop/views/components/post-form.vue +++ b/src/client/app/desktop/views/components/post-form.vue @@ -20,7 +20,7 @@ @keydown="onKeydown" @paste="onPaste" :placeholder="placeholder" v-autocomplete="'text'" > -
+
@@ -188,7 +188,7 @@ export default Vue.extend({ (this.$refs.poll as any).set(draft.data.poll); }); } - this.$emit('change-attached-media', this.files); + this.$emit('change-attached-files', this.files); } } @@ -225,12 +225,12 @@ export default Vue.extend({ attachMedia(driveFile) { this.files.push(driveFile); - this.$emit('change-attached-media', this.files); + this.$emit('change-attached-files', this.files); }, detachMedia(id) { this.files = this.files.filter(x => x.id != id); - this.$emit('change-attached-media', this.files); + this.$emit('change-attached-files', this.files); }, onChangeFile() { @@ -249,7 +249,7 @@ export default Vue.extend({ this.text = ''; this.files = []; this.poll = false; - this.$emit('change-attached-media', this.files); + this.$emit('change-attached-files', this.files); }, onKeydown(e) { @@ -297,7 +297,7 @@ export default Vue.extend({ if (driveFile != null && driveFile != '') { const file = JSON.parse(driveFile); this.files.push(file); - this.$emit('change-attached-media', this.files); + this.$emit('change-attached-files', this.files); e.preventDefault(); } //#endregion @@ -354,7 +354,7 @@ export default Vue.extend({ (this as any).api('notes/create', { text: this.text == '' ? undefined : this.text, - mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined, + fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined, replyId: this.reply ? this.reply.id : undefined, renoteId: this.renote ? this.renote.id : undefined, poll: this.poll ? (this.$refs.poll as any).get() : undefined, @@ -514,7 +514,7 @@ root(isDark) margin-right 8px white-space nowrap - > .medias + > .files margin 0 padding 0 background isDark ? #181b23 : lighten($theme-color, 98%) 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 cb0374b910..6889dc231e 100644 --- a/src/client/app/desktop/views/components/sub-note-content.vue +++ b/src/client/app/desktop/views/components/sub-note-content.vue @@ -7,9 +7,9 @@ RP: ...
-
- ({{ '%i18n:@media-count%'.replace('{}', note.media.length) }}) - +
+ ({{ '%i18n:@media-count%'.replace('{}', note.files.length) }}) +
%i18n:@poll% diff --git a/src/client/app/desktop/views/pages/deck/deck.list-tl.vue b/src/client/app/desktop/views/pages/deck/deck.list-tl.vue index 70048f99e3..e82e76e4d0 100644 --- a/src/client/app/desktop/views/pages/deck/deck.list-tl.vue +++ b/src/client/app/desktop/views/pages/deck/deck.list-tl.vue @@ -68,7 +68,7 @@ export default Vue.extend({ (this as any).api('notes/user-list-timeline', { listId: this.list.id, limit: fetchLimit + 1, - mediaOnly: this.mediaOnly, + withFiles: this.mediaOnly, includeMyRenotes: this.$store.state.settings.showMyRenotes, includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes, includeLocalRenotes: this.$store.state.settings.showLocalRenotes @@ -90,7 +90,7 @@ export default Vue.extend({ listId: this.list.id, limit: fetchLimit + 1, untilId: (this.$refs.timeline as any).tail().id, - mediaOnly: this.mediaOnly, + withFiles: this.mediaOnly, includeMyRenotes: this.$store.state.settings.showMyRenotes, includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes, includeLocalRenotes: this.$store.state.settings.showLocalRenotes @@ -109,7 +109,7 @@ export default Vue.extend({ return promise; }, onNote(note) { - if (this.mediaOnly && note.media.length == 0) return; + if (this.mediaOnly && note.files.length == 0) return; // Prepend a note (this.$refs.timeline as any).prepend(note); diff --git a/src/client/app/desktop/views/pages/deck/deck.note.vue b/src/client/app/desktop/views/pages/deck/deck.note.vue index 2615c0d090..b42df1f347 100644 --- a/src/client/app/desktop/views/pages/deck/deck.note.vue +++ b/src/client/app/desktop/views/pages/deck/deck.note.vue @@ -28,8 +28,8 @@ RP:
-
- +
+
%fa:map-marker-alt% %i18n:@location% @@ -54,11 +54,11 @@
-
- +
+
-
- +
+
@@ -100,7 +100,7 @@ export default Vue.extend({ isRenote(): boolean { return (this.note.renote && this.note.text == null && - this.note.mediaIds.length == 0 && + this.note.fileIds.length == 0 && this.note.poll == null); }, @@ -371,7 +371,7 @@ root(isDark) .mk-url-preview margin-top 8px - > .media + > .files > img display block max-width 100% diff --git a/src/client/app/desktop/views/pages/deck/deck.notes.vue b/src/client/app/desktop/views/pages/deck/deck.notes.vue index f7fca5de92..2e7e30f12a 100644 --- a/src/client/app/desktop/views/pages/deck/deck.notes.vue +++ b/src/client/app/desktop/views/pages/deck/deck.notes.vue @@ -127,7 +127,7 @@ export default Vue.extend({ prepend(note, silent = false) { //#region 弾く const isMyNote = note.userId == this.$store.state.i.id; - const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null; + const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null; if (this.$store.state.settings.showMyRenotes === false) { if (isMyNote && isPureRenote) { diff --git a/src/client/app/desktop/views/pages/deck/deck.tl.vue b/src/client/app/desktop/views/pages/deck/deck.tl.vue index a9e4d489c3..120ceb7fc2 100644 --- a/src/client/app/desktop/views/pages/deck/deck.tl.vue +++ b/src/client/app/desktop/views/pages/deck/deck.tl.vue @@ -96,7 +96,7 @@ export default Vue.extend({ (this.$refs.timeline as any).init(() => new Promise((res, rej) => { (this as any).api(this.endpoint, { limit: fetchLimit + 1, - mediaOnly: this.mediaOnly, + withFiles: this.mediaOnly, includeMyRenotes: this.$store.state.settings.showMyRenotes, includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes, includeLocalRenotes: this.$store.state.settings.showLocalRenotes @@ -117,7 +117,7 @@ export default Vue.extend({ const promise = (this as any).api(this.endpoint, { limit: fetchLimit + 1, - mediaOnly: this.mediaOnly, + withFiles: this.mediaOnly, untilId: (this.$refs.timeline as any).tail().id, includeMyRenotes: this.$store.state.settings.showMyRenotes, includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes, @@ -138,7 +138,7 @@ export default Vue.extend({ }, onNote(note) { - if (this.mediaOnly && note.media.length == 0) return; + if (this.mediaOnly && note.files.length == 0) return; // Prepend a note (this.$refs.timeline as any).prepend(note); diff --git a/src/client/app/desktop/views/pages/user/user.photos.vue b/src/client/app/desktop/views/pages/user/user.photos.vue index 64c537f1ed..c5cd9e24fe 100644 --- a/src/client/app/desktop/views/pages/user/user.photos.vue +++ b/src/client/app/desktop/views/pages/user/user.photos.vue @@ -24,12 +24,12 @@ export default Vue.extend({ mounted() { (this as any).api('users/notes', { userId: this.user.id, - withMedia: true, + withFiles: true, limit: 9 }).then(notes => { notes.forEach(note => { - note.media.forEach(media => { - if (this.images.length < 9) this.images.push(media); + note.files.forEach(file => { + if (this.images.length < 9) this.images.push(file); }); }); this.fetching = false; diff --git a/src/client/app/desktop/views/pages/user/user.timeline.vue b/src/client/app/desktop/views/pages/user/user.timeline.vue index 67987fcb94..54221380a7 100644 --- a/src/client/app/desktop/views/pages/user/user.timeline.vue +++ b/src/client/app/desktop/views/pages/user/user.timeline.vue @@ -66,7 +66,7 @@ export default Vue.extend({ limit: fetchLimit + 1, untilDate: this.date ? this.date.getTime() : undefined, includeReplies: this.mode == 'with-replies', - withMedia: this.mode == 'with-media' + withFiles: this.mode == 'with-media' }).then(notes => { if (notes.length == fetchLimit + 1) { notes.pop(); @@ -86,7 +86,7 @@ export default Vue.extend({ userId: this.user.id, limit: fetchLimit + 1, includeReplies: this.mode == 'with-replies', - withMedia: this.mode == 'with-media', + withFiles: this.mode == 'with-media', untilId: (this.$refs.timeline as any).tail().id }); diff --git a/src/client/app/desktop/views/widgets/trends.vue b/src/client/app/desktop/views/widgets/trends.vue index c33bf2f2f2..aeaab63ac4 100644 --- a/src/client/app/desktop/views/widgets/trends.vue +++ b/src/client/app/desktop/views/widgets/trends.vue @@ -49,7 +49,7 @@ export default define({ offset: this.offset, renote: false, reply: false, - media: false, + file: false, poll: false }).then(notes => { const note = notes ? notes[0] : null; diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue index 786e57bb22..10ff3fcc09 100644 --- a/src/client/app/mobile/views/components/note-detail.vue +++ b/src/client/app/mobile/views/components/note-detail.vue @@ -40,8 +40,8 @@ (%i18n:@deleted%)
-
- +
+
@@ -113,7 +113,7 @@ export default Vue.extend({ isRenote(): boolean { return (this.note.renote && this.note.text == null && - this.note.mediaIds.length == 0 && + this.note.fileIds.length == 0 && this.note.poll == null); }, @@ -369,7 +369,7 @@ root(isDark) > .mk-url-preview margin-top 8px - > .media + > .files > img display block max-width 100% diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue index 258433cb3f..9bd4a83ecb 100644 --- a/src/client/app/mobile/views/components/note.vue +++ b/src/client/app/mobile/views/components/note.vue @@ -28,8 +28,8 @@ RP:
-
- +
+
@@ -90,7 +90,7 @@ export default Vue.extend({ isRenote(): boolean { return (this.note.renote && this.note.text == null && - this.note.mediaIds.length == 0 && + this.note.fileIds.length == 0 && this.note.poll == null); }, @@ -414,7 +414,7 @@ root(isDark) .mk-url-preview margin-top 8px - > .media + > .files > img display block max-width 100% diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue index 714e521c0f..ce2670dc52 100644 --- a/src/client/app/mobile/views/components/notes.vue +++ b/src/client/app/mobile/views/components/notes.vue @@ -125,7 +125,7 @@ export default Vue.extend({ prepend(note, silent = false) { //#region 弾く const isMyNote = note.userId == this.$store.state.i.id; - const isPureRenote = note.renoteId != null && note.text == null && note.mediaIds.length == 0 && note.poll == null; + const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null; if (this.$store.state.settings.showMyRenotes === false) { if (isMyNote && isPureRenote) { diff --git a/src/client/app/mobile/views/components/post-form.vue b/src/client/app/mobile/views/components/post-form.vue index 8b1f7b08c8..644e27cce8 100644 --- a/src/client/app/mobile/views/components/post-form.vue +++ b/src/client/app/mobile/views/components/post-form.vue @@ -200,12 +200,12 @@ export default Vue.extend({ attachMedia(driveFile) { this.files.push(driveFile); - this.$emit('change-attached-media', this.files); + this.$emit('change-attached-files', this.files); }, detachMedia(file) { this.files = this.files.filter(x => x.id != file.id); - this.$emit('change-attached-media', this.files); + this.$emit('change-attached-files', this.files); }, onChangeFile() { @@ -269,7 +269,7 @@ export default Vue.extend({ this.text = ''; this.files = []; this.poll = false; - this.$emit('change-attached-media'); + this.$emit('change-attached-files'); }, post() { @@ -277,7 +277,7 @@ export default Vue.extend({ const viaMobile = this.$store.state.settings.disableViaMobile !== true; (this as any).api('notes/create', { text: this.text == '' ? undefined : this.text, - mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined, + fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined, replyId: this.reply ? this.reply.id : undefined, renoteId: this.renote ? this.renote.id : undefined, poll: this.poll ? (this.$refs.poll as any).get() : undefined, 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 a4ce49786e..4d0aa25f34 100644 --- a/src/client/app/mobile/views/components/sub-note-content.vue +++ b/src/client/app/mobile/views/components/sub-note-content.vue @@ -7,9 +7,9 @@ RP: ...
-
- ({{ '%i18n:@media-count%'.replace('{}', note.media.length) }}) - +
+ ({{ '%i18n:@media-count%'.replace('{}', note.files.length) }}) +
%i18n:@poll% diff --git a/src/client/app/mobile/views/components/user-timeline.vue b/src/client/app/mobile/views/components/user-timeline.vue index 6be675c0a7..7cd23d6655 100644 --- a/src/client/app/mobile/views/components/user-timeline.vue +++ b/src/client/app/mobile/views/components/user-timeline.vue @@ -41,7 +41,7 @@ export default Vue.extend({ (this.$refs.timeline as any).init(() => new Promise((res, rej) => { (this as any).api('users/notes', { userId: this.user.id, - withMedia: this.withMedia, + withFiles: this.withMedia, limit: fetchLimit + 1 }).then(notes => { if (notes.length == fetchLimit + 1) { @@ -62,7 +62,7 @@ export default Vue.extend({ const promise = (this as any).api('users/notes', { userId: this.user.id, - withMedia: this.withMedia, + withFiles: this.withMedia, limit: fetchLimit + 1, untilId: (this.$refs.timeline as any).tail().id }); diff --git a/src/client/app/mobile/views/pages/user/home.photos.vue b/src/client/app/mobile/views/pages/user/home.photos.vue index 73ff1d5173..e9025ec816 100644 --- a/src/client/app/mobile/views/pages/user/home.photos.vue +++ b/src/client/app/mobile/views/pages/user/home.photos.vue @@ -26,7 +26,7 @@ export default Vue.extend({ mounted() { (this as any).api('users/notes', { userId: this.user.id, - withMedia: true, + withFiles: true, limit: 6 }).then(notes => { notes.forEach(note => { diff --git a/src/docs/api/entities/note.yaml b/src/docs/api/entities/note.yaml index cae9a53f82..6654be2b02 100644 --- a/src/docs/api/entities/note.yaml +++ b/src/docs/api/entities/note.yaml @@ -33,19 +33,19 @@ props: ja-JP: "投稿の本文" en-US: "The text of this note" - mediaIds: + fileIds: type: "id(DriveFile)[]" optional: true desc: - ja-JP: "添付されているメディアのID (なければレスポンスでは空配列)" - en-US: "The IDs of the attached media (empty array for response if no media is attached)" + ja-JP: "添付されているファイルのID (なければレスポンスでは空配列)" + en-US: "The IDs of the attached files (empty array for response if no files is attached)" - media: + files: type: "entity(DriveFile)[]" optional: true desc: - ja-JP: "添付されているメディア" - en-US: "The attached media" + ja-JP: "添付されているファイル" + en-US: "The attached files" userId: type: "id(User)" diff --git a/src/misc/get-note-summary.ts b/src/misc/get-note-summary.ts index ec7c74cf9f..3c6f2dd3d6 100644 --- a/src/misc/get-note-summary.ts +++ b/src/misc/get-note-summary.ts @@ -16,9 +16,9 @@ const summarize = (note: any): string => { // 本文 summary += note.text ? note.text : ''; - // メディアが添付されているとき - if (note.media.length != 0) { - summary += ` (${note.media.length}つのメディア)`; + // ファイルが添付されているとき + if (note.files.length != 0) { + summary += ` (${note.files.length}つのファイル)`; } // 投票が添付されているとき diff --git a/src/misc/is-quote.ts b/src/misc/is-quote.ts index 420f03a489..a99b8f6434 100644 --- a/src/misc/is-quote.ts +++ b/src/misc/is-quote.ts @@ -1,5 +1,5 @@ import { INote } from '../models/note'; export default function(note: INote): boolean { - return note.renoteId != null && (note.text != null || note.poll != null || (note.mediaIds != null && note.mediaIds.length > 0)); + return note.renoteId != null && (note.text != null || note.poll != null || (note.fileIds != null && note.fileIds.length > 0)); } diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts index 698ef092a6..215b49b305 100644 --- a/src/models/drive-file.ts +++ b/src/models/drive-file.ts @@ -92,7 +92,7 @@ export async function deleteDriveFile(driveFile: string | mongo.ObjectID | IDriv // このDriveFileを添付しているNoteをすべて削除 await Promise.all(( - await Note.find({ mediaIds: d._id }) + await Note.find({ fileIds: d._id }) ).map(x => deleteNote(x))); // このDriveFileを添付しているMessagingMessageをすべて削除 diff --git a/src/models/note.ts b/src/models/note.ts index 9d2e23d901..98d37caf22 100644 --- a/src/models/note.ts +++ b/src/models/note.ts @@ -6,7 +6,7 @@ import { IUser, pack as packUser } from './user'; import { pack as packApp } from './app'; import PollVote, { deletePollVote } from './poll-vote'; import Reaction, { deleteNoteReaction } from './note-reaction'; -import { pack as packFile } from './drive-file'; +import { pack as packFile, IDriveFile } from './drive-file'; import NoteWatching, { deleteNoteWatching } from './note-watching'; import NoteReaction from './note-reaction'; import Favorite, { deleteFavorite } from './favorite'; @@ -17,9 +17,20 @@ const Note = db.get('notes'); Note.createIndex('uri', { sparse: true, unique: true }); Note.createIndex('userId'); Note.createIndex('tagsLower'); +Note.createIndex('_files.contentType'); Note.createIndex({ createdAt: -1 }); + +// 後方互換性のため +Note.update({}, { + $rename: { + mediaIds: 'fileIds' + } +}, { + multi: true +}); + export default Note; export function isValidText(text: string): boolean { @@ -34,7 +45,7 @@ export type INote = { _id: mongo.ObjectID; createdAt: Date; deletedAt: Date; - mediaIds: mongo.ObjectID[]; + fileIds: mongo.ObjectID[]; replyId: mongo.ObjectID; renoteId: mongo.ObjectID; poll: { @@ -92,6 +103,7 @@ export type INote = { inbox?: string; }; _replyIds?: mongo.ObjectID[]; + _files?: IDriveFile[]; }; /** @@ -271,11 +283,15 @@ export const pack = async ( _note.app = packApp(_note.appId); } - // Populate media - _note.media = hide ? [] : Promise.all(_note.mediaIds.map((fileId: mongo.ObjectID) => + // Populate files + _note.files = hide ? [] : Promise.all(_note.fileIds.map((fileId: mongo.ObjectID) => packFile(fileId) )); + // 後方互換性のため + _note.mediaIds = _note.fileIds; + _note.media = _note.files; + // When requested a detailed note data if (opts.detail) { //#region 重いので廃止 @@ -344,7 +360,7 @@ export const pack = async ( } if (hide) { - _note.mediaIds = []; + _note.fileIds = []; _note.text = null; _note.poll = null; _note.cw = null; diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts index 1dfeebfdf7..97188b44a6 100644 --- a/src/remote/activitypub/models/note.ts +++ b/src/remote/activitypub/models/note.ts @@ -78,11 +78,11 @@ export async function createNote(value: any, resolver?: Resolver, silent = false } //#endergion - // 添付メディア + // 添付ファイル // TODO: attachmentは必ずしもImageではない // TODO: attachmentは必ずしも配列ではない // Noteがsensitiveなら添付もsensitiveにする - const media = note.attachment + const files = note.attachment .map(attach => attach.sensitive = note.sensitive) ? await Promise.all(note.attachment.map(x => resolveImage(actor, x))) : []; @@ -100,7 +100,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false return await post(actor, { createdAt: new Date(note.published), - media, + files: files, reply, renote: undefined, cw: note.summary, diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts index 1d169d3088..6b30324ae7 100644 --- a/src/remote/activitypub/renderer/note.ts +++ b/src/remote/activitypub/renderer/note.ts @@ -8,8 +8,8 @@ import User from '../../../models/user'; import toHtml from '../misc/get-note-html'; export default async function renderNote(note: INote, dive = true): Promise { - const promisedFiles: Promise = note.mediaIds - ? DriveFile.find({ _id: { $in: note.mediaIds } }) + const promisedFiles: Promise = note.fileIds + ? DriveFile.find({ _id: { $in: note.fileIds } }) : Promise.resolve([]); let inReplyTo; diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts index 37df190880..cc7e55b5df 100644 --- a/src/server/activitypub/outbox.ts +++ b/src/server/activitypub/outbox.ts @@ -58,7 +58,7 @@ export default async (ctx: Router.IRouterContext) => { $or: [{ text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }] }] } as any; diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 029bc1a95e..4d15e9483f 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -20,9 +20,9 @@ export default (params: any) => new Promise(async (res, rej) => { const [renote, renoteErr] = $.bool.optional.get(params.renote); if (renoteErr) return rej('invalid renote param'); - // Get 'media' parameter - const [media, mediaErr] = $.bool.optional.get(params.media); - if (mediaErr) return rej('invalid media param'); + // Get 'files' parameter + const [files, filesErr] = $.bool.optional.get(params.files); + if (filesErr) return rej('invalid files param'); // Get 'poll' parameter const [poll, pollErr] = $.bool.optional.get(params.poll); @@ -79,8 +79,8 @@ export default (params: any) => new Promise(async (res, rej) => { query.renoteId = renote ? { $exists: true, $ne: null } : null; } - if (media != undefined) { - query.mediaIds = media ? { $exists: true, $ne: null } : []; + if (files != undefined) { + query.fileIds = files ? { $exists: true, $ne: null } : []; } if (poll != undefined) { diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 04f5f7562e..47b53c943b 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -71,9 +71,15 @@ export const meta = { ref: 'geo' }), + fileIds: $.arr($.type(ID)).optional.unique().range(1, 4).note({ + desc: { + 'ja-JP': '添付するファイル' + } + }), + mediaIds: $.arr($.type(ID)).optional.unique().range(1, 4).note({ desc: { - 'ja-JP': '添付するメディア' + 'ja-JP': '添付するファイル (このパラメータは廃止予定です。代わりに fileIds を使ってください。)' } }), @@ -124,15 +130,16 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async ( } let files: IDriveFile[] = []; - if (ps.mediaIds !== undefined) { + const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null; + if (fileIds != null) { // Fetch files // forEach だと途中でエラーなどがあっても return できないので // 敢えて for を使っています。 - for (const mediaId of ps.mediaIds) { + for (const fileId of fileIds) { // Fetch file // SELECT _id const entity = await DriveFile.findOne({ - _id: mediaId, + _id: fileId, 'metadata.userId': user._id }); @@ -155,7 +162,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async ( if (renote == null) { return rej('renoteee is not found'); - } else if (renote.renoteId && !renote.text && !renote.mediaIds) { + } else if (renote.renoteId && !renote.text && !renote.fileIds) { return rej('cannot renote to renote'); } } @@ -176,7 +183,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async ( } // 返信対象が引用でないRenoteだったらエラー - if (reply.renoteId && !reply.text && !reply.mediaIds) { + if (reply.renoteId && !reply.text && !reply.fileIds) { return rej('cannot reply to renote'); } } @@ -191,13 +198,13 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async ( // テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー if ((ps.text === undefined || ps.text === null) && files === null && renote === null && ps.poll === undefined) { - return rej('text, mediaIds, renoteId or poll is required'); + return rej('text, fileIds, renoteId or poll is required'); } // 投稿を作成 const note = await create(user, { createdAt: new Date(), - media: files, + files: files, poll: ps.poll, text: ps.text, reply, diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 8f7233e308..554245a0f4 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -33,9 +33,9 @@ export default async (params: any, user: ILocalUser) => { throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; } - // Get 'mediaOnly' parameter - const [mediaOnly, mediaOnlyErr] = $.bool.optional.get(params.mediaOnly); - if (mediaOnlyErr) throw 'invalid mediaOnly param'; + // Get 'withFiles' parameter + const [withFiles, withFilesErr] = $.bool.optional.get(params.withFiles); + if (withFilesErr) throw 'invalid withFiles param'; // ミュートしているユーザーを取得 const mutedUserIds = user ? (await Mute.find({ @@ -68,8 +68,8 @@ export default async (params: any, user: ILocalUser) => { }; } - if (mediaOnly) { - query.mediaIds = { $exists: true, $ne: [] }; + if (withFiles) { + query.fileIds = { $exists: true, $ne: [] }; } if (sinceId) { diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 2dbb1190c1..1060792683 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -66,7 +66,7 @@ export const meta = { } }), - mediaOnly: $.bool.optional.note({ + withFiles: $.bool.optional.note({ desc: { 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します' } @@ -164,7 +164,7 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] @@ -180,7 +180,7 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] @@ -196,16 +196,16 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] }); } - if (ps.mediaOnly) { + if (ps.withFiles) { query.$and.push({ - mediaIds: { $exists: true, $ne: [] } + fileIds: { $exists: true, $ne: [] } }); } diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index bbcc6303ca..018e636ab5 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -33,9 +33,9 @@ export default async (params: any, user: ILocalUser) => { throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; } - // Get 'mediaOnly' parameter - const [mediaOnly, mediaOnlyErr] = $.bool.optional.get(params.mediaOnly); - if (mediaOnlyErr) throw 'invalid mediaOnly param'; + // Get 'withFiles' parameter + const [withFiles, withFilesErr] = $.bool.optional.get(params.withFiles); + if (withFilesErr) throw 'invalid withFiles param'; // ミュートしているユーザーを取得 const mutedUserIds = user ? (await Mute.find({ @@ -69,8 +69,8 @@ export default async (params: any, user: ILocalUser) => { }; } - if (mediaOnly) { - query.mediaIds = { $exists: true, $ne: [] }; + if (withFiles) { + query.fileIds = { $exists: true, $ne: [] }; } if (sinceId) { diff --git a/src/server/api/endpoints/notes/search_by_tag.ts b/src/server/api/endpoints/notes/search_by_tag.ts index e092275fe8..317a0726d3 100644 --- a/src/server/api/endpoints/notes/search_by_tag.ts +++ b/src/server/api/endpoints/notes/search_by_tag.ts @@ -247,7 +247,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => if (media != null) { if (media) { push({ - mediaIds: { + fileIds: { $exists: true, $ne: null } @@ -255,11 +255,11 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => } else { push({ $or: [{ - mediaIds: { + fileIds: { $exists: false } }, { - mediaIds: null + fileIds: null }] }); } diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index 099bf2010b..145f648c56 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -67,7 +67,7 @@ export const meta = { } }), - mediaOnly: $.bool.optional.note({ + withFiles: $.bool.optional.note({ desc: { 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します' } @@ -154,7 +154,7 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] @@ -170,7 +170,7 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] @@ -186,16 +186,16 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] }); } - if (ps.mediaOnly) { + if (ps.withFiles) { query.$and.push({ - mediaIds: { $exists: true, $ne: [] } + fileIds: { $exists: true, $ne: [] } }); } diff --git a/src/server/api/endpoints/notes/trend.ts b/src/server/api/endpoints/notes/trend.ts index 7a0a098f28..9f55ed3243 100644 --- a/src/server/api/endpoints/notes/trend.ts +++ b/src/server/api/endpoints/notes/trend.ts @@ -52,7 +52,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) = } if (media != undefined) { - query.mediaIds = media ? { $exists: true, $ne: null } : null; + query.fileIds = media ? { $exists: true, $ne: null } : null; } if (poll != undefined) { diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index a7b43014ed..e00a7de371 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -73,7 +73,7 @@ export const meta = { } }), - mediaOnly: $.bool.optional.note({ + withFiles: $.bool.optional.note({ desc: { 'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します' } @@ -160,7 +160,7 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] @@ -176,7 +176,7 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] @@ -192,16 +192,16 @@ export default async (params: any, user: ILocalUser) => { }, { text: { $ne: null } }, { - mediaIds: { $ne: [] } + fileIds: { $ne: [] } }, { poll: { $ne: null } }] }); } - if (ps.mediaOnly) { + if (ps.withFiles) { query.$and.push({ - mediaIds: { $exists: true, $ne: [] } + fileIds: { $exists: true, $ne: [] } }); } diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index ff7855bde0..d894e52dba 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -27,9 +27,9 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => const [includeReplies = true, includeRepliesErr] = $.bool.optional.get(params.includeReplies); if (includeRepliesErr) return rej('invalid includeReplies param'); - // Get 'withMedia' parameter - const [withMedia = false, withMediaErr] = $.bool.optional.get(params.withMedia); - if (withMediaErr) return rej('invalid withMedia param'); + // Get 'withFiles' parameter + const [withFiles = false, withFilesErr] = $.bool.optional.get(params.withFiles); + if (withFilesErr) return rej('invalid withFiles param'); // Get 'limit' parameter const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); @@ -104,8 +104,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => query.replyId = null; } - if (withMedia) { - query.mediaIds = { + if (withFiles) { + query.fileIds = { $exists: true, $ne: [] }; diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 63e3557828..11e3755863 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -84,7 +84,7 @@ type Option = { text?: string; reply?: INote; renote?: INote; - media?: IDriveFile[]; + files?: IDriveFile[]; geo?: any; poll?: any; viaMobile?: boolean; @@ -135,7 +135,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise< const mentionedUsers = await extractMentionedUsers(tokens); - const note = await insertNote(user, data, tokens, tags, mentionedUsers); + const note = await insertNote(user, data, tags, mentionedUsers); res(note); @@ -309,10 +309,10 @@ async function publish(user: IUser, note: INote, noteObj: any, reply: INote, ren publishToUserLists(note, noteObj); } -async function insertNote(user: IUser, data: Option, tokens: ReturnType, tags: string[], mentionedUsers: IUser[]) { +async function insertNote(user: IUser, data: Option, tags: string[], mentionedUsers: IUser[]) { const insert: any = { createdAt: data.createdAt, - mediaIds: data.media ? data.media.map(file => file._id) : [], + fileIds: data.files ? data.files.map(file => file._id) : [], replyId: data.reply ? data.reply._id : null, renoteId: data.renote ? data.renote._id : null, text: data.text, @@ -347,7 +347,8 @@ async function insertNote(user: IUser, data: Option, tokens: ReturnType