mirror of
https://github.com/paricafe/misskey.git
synced 2025-01-31 09:10:16 -06:00
feat(wip): update note attachments
This commit is contained in:
parent
21e1598584
commit
3d09a7cbb6
5 changed files with 88 additions and 24 deletions
|
@ -123,6 +123,8 @@ export interface NoteEventTypes {
|
|||
updatedAt: string;
|
||||
tags?: string[];
|
||||
emojis?: Record<string, string>;
|
||||
fileIds?: string[];
|
||||
files?: Packed<'DriveFile'>[];
|
||||
};
|
||||
reacted: {
|
||||
reaction: string;
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Injectable, Inject } from '@nestjs/common';
|
|||
import * as mfm from 'mfm-js';
|
||||
import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js';
|
||||
import type { MiNote } from '@/models/Note.js';
|
||||
import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/_.js';
|
||||
import type { InstancesRepository, MiDriveFile, NotesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { RelayService } from '@/core/RelayService.js';
|
||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
|
@ -32,8 +32,14 @@ import { extractHashtags } from "@/misc/extract-hashtags.js";
|
|||
import { extractCustomEmojisFromMfm } from "@/misc/extract-custom-emojis-from-mfm.js";
|
||||
import { UtilityService } from "@/core/UtilityService.js";
|
||||
import { CustomEmojiService } from "@/core/CustomEmojiService.js";
|
||||
import { awaitAll } from "@/misc/prelude/await-all.js";
|
||||
import type { DriveFileEntityService } from "@/core/entities/DriveFileEntityService.js";
|
||||
|
||||
type Option = Pick<MiNote, 'text' | 'cw' | 'updatedAt'> & {
|
||||
type Option = {
|
||||
updatedAt?: Date | null;
|
||||
text: string | null;
|
||||
files?: MiDriveFile[] | null;
|
||||
cw: string | null;
|
||||
apHashtags?: string[] | null;
|
||||
apEmojis?: string[] | null;
|
||||
}
|
||||
|
@ -54,6 +60,7 @@ export class NoteUpdateService {
|
|||
private instancesRepository: InstancesRepository,
|
||||
|
||||
private customEmojiService: CustomEmojiService,
|
||||
private driveFileEntityService: DriveFileEntityService,
|
||||
private userEntityService: UserEntityService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
private globalEventService: GlobalEventService,
|
||||
|
@ -76,21 +83,19 @@ export class NoteUpdateService {
|
|||
* Update note
|
||||
* @param user Note creator
|
||||
* @param note Note to update
|
||||
* @param ps New note info
|
||||
* @param data New note info
|
||||
*/
|
||||
async update(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, ps: Option, quiet = false, updater?: MiUser) {
|
||||
if (!ps.updatedAt) {
|
||||
async update(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, data: Option, quiet = false, updater?: MiUser) {
|
||||
if (!data.updatedAt) {
|
||||
throw new Error('update time is required');
|
||||
}
|
||||
|
||||
if (note.history && note.history.findIndex(h => h.createdAt === ps.updatedAt?.toISOString()) !== -1) {
|
||||
if (note.history && note.history.findIndex(h => h.createdAt === data.updatedAt?.toISOString()) !== -1) {
|
||||
// Same history already exists, skip this
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse tags & emojis
|
||||
const data = ps;
|
||||
|
||||
const meta = await this.metaService.fetch();
|
||||
|
||||
let tags = data.apHashtags;
|
||||
|
@ -114,25 +119,28 @@ export class NoteUpdateService {
|
|||
|
||||
tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32);
|
||||
|
||||
const newNote = {
|
||||
const newNote: MiNote = {
|
||||
...note,
|
||||
|
||||
// Overwrite updated fields
|
||||
text: ps.text,
|
||||
cw: ps.cw,
|
||||
updatedAt: ps.updatedAt,
|
||||
text: data.text,
|
||||
cw: data.cw,
|
||||
updatedAt: data.updatedAt,
|
||||
tags,
|
||||
emojis,
|
||||
fileIds: data.files ? data.files.map(file => file.id) : [],
|
||||
};
|
||||
|
||||
if (!quiet) {
|
||||
this.globalEventService.publishNoteStream(note.id, 'updated', {
|
||||
cw: ps.cw,
|
||||
text: ps.text ?? '', // prevent null
|
||||
updatedAt: ps.updatedAt.toISOString(),
|
||||
this.globalEventService.publishNoteStream(note.id, 'updated', await awaitAll({
|
||||
fileIds: newNote.fileIds,
|
||||
files: this.driveFileEntityService.packManyByIds(newNote.fileIds),
|
||||
cw: data.cw,
|
||||
text: data.text ?? '', // prevent null
|
||||
updatedAt: data.updatedAt.toISOString(),
|
||||
tags: tags.length > 0 ? tags : undefined,
|
||||
emojis: note.userHost != null ? await this.customEmojiService.populateEmojis(emojis, note.userHost) : undefined,
|
||||
});
|
||||
emojis: note.userHost != null ? this.customEmojiService.populateEmojis(emojis, note.userHost) : undefined,
|
||||
}));
|
||||
|
||||
if (this.userEntityService.isLocalUser(user) && !note.localOnly) {
|
||||
const content = this.apRendererService.addContext(
|
||||
|
@ -151,7 +159,7 @@ export class NoteUpdateService {
|
|||
cw: note.cw,
|
||||
text: note.text,
|
||||
}];
|
||||
if (note.updatedAt && note.updatedAt >= ps.updatedAt) {
|
||||
if (note.updatedAt && note.updatedAt >= data.updatedAt) {
|
||||
// Previous version, just update history
|
||||
history.sort((h1, h2) => new Date(h1.createdAt).getTime() - new Date(h2.createdAt).getTime()); // earliest -> latest
|
||||
|
||||
|
@ -166,10 +174,11 @@ export class NoteUpdateService {
|
|||
|
||||
// Update note info
|
||||
await this.notesRepository.update({ id: note.id }, {
|
||||
updatedAt: ps.updatedAt,
|
||||
updatedAt: data.updatedAt,
|
||||
fileIds: newNote.fileIds,
|
||||
history,
|
||||
cw: ps.cw,
|
||||
text: ps.text,
|
||||
cw: data.cw,
|
||||
text: data.text,
|
||||
tags,
|
||||
emojis,
|
||||
});
|
||||
|
|
|
@ -370,6 +370,15 @@ export class ApNoteService {
|
|||
|
||||
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser;
|
||||
|
||||
// 添付ファイル
|
||||
const files: MiDriveFile[] = [];
|
||||
|
||||
for (const attach of toArray(note.attachment)) {
|
||||
attach.sensitive ??= note.sensitive;
|
||||
const file = await this.apImageService.resolveImage(actor, attach);
|
||||
if (file) files.push(file);
|
||||
}
|
||||
|
||||
const emojis = await this.extractEmojis(note.tag ?? [], actor.host).catch(e => {
|
||||
this.logger.info(`extractEmojis: ${e}`);
|
||||
return [];
|
||||
|
@ -378,6 +387,7 @@ export class ApNoteService {
|
|||
const apEmojis = emojis.map(emoji => emoji.name);
|
||||
|
||||
await this.noteUpdateService.update(actor, originNote, {
|
||||
files,
|
||||
cw,
|
||||
text,
|
||||
apHashtags,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import ms from 'ms';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { UsersRepository } from '@/models/_.js';
|
||||
import type { DriveFilesRepository, MiDriveFile, UsersRepository } from '@/models/_.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
|
@ -33,6 +33,12 @@ export const meta = {
|
|||
code: 'NO_SUCH_NOTE',
|
||||
id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474',
|
||||
},
|
||||
|
||||
noSuchFile: {
|
||||
message: 'Some files are not found.',
|
||||
code: 'NO_SUCH_FILE',
|
||||
id: 'b6992544-63e7-67f0-fa7f-32444b1b5306',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
@ -46,6 +52,20 @@ export const paramDef = {
|
|||
maxLength: MAX_NOTE_TEXT_LENGTH,
|
||||
nullable: false,
|
||||
},
|
||||
fileIds: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 1,
|
||||
maxItems: 16,
|
||||
items: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
mediaIds: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 1,
|
||||
maxItems: 16,
|
||||
items: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
cw: { type: 'string', nullable: true, maxLength: 100 },
|
||||
},
|
||||
required: ['noteId', 'text', 'cw'],
|
||||
|
@ -57,6 +77,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
@Inject(DI.driveFilesRepository)
|
||||
private driveFilesRepository: DriveFilesRepository,
|
||||
|
||||
private getterService: GetterService,
|
||||
private noteUpdateService: NoteUpdateService,
|
||||
) {
|
||||
|
@ -70,7 +93,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchNote);
|
||||
}
|
||||
|
||||
if (note.text === ps.text && note.cw === ps.cw) {
|
||||
let files: MiDriveFile[] = [];
|
||||
const fileIds = ps.fileIds ?? ps.mediaIds ?? null;
|
||||
if (fileIds != null) {
|
||||
files = await this.driveFilesRepository.createQueryBuilder('file')
|
||||
.where('file.userId = :userId AND file.id IN (:...fileIds)', {
|
||||
userId: me.id,
|
||||
fileIds,
|
||||
})
|
||||
.orderBy('array_position(ARRAY[:...fileIds], "id"::text)')
|
||||
.setParameters({ fileIds })
|
||||
.getMany();
|
||||
|
||||
if (files.length !== fileIds.length) {
|
||||
throw new ApiError(meta.errors.noSuchFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (note.text === ps.text && note.cw === ps.cw && note.fileIds === fileIds) {
|
||||
// The same as old note, nothing to do
|
||||
return;
|
||||
}
|
||||
|
@ -79,6 +119,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
text: ps.text,
|
||||
cw: ps.cw,
|
||||
updatedAt: new Date(),
|
||||
files,
|
||||
}, false, me);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -89,6 +89,8 @@ export function useNoteCapture(props: {
|
|||
note.value.text = body.text;
|
||||
note.value.tags = body.tags;
|
||||
note.value.emojis = body.emojis;
|
||||
note.value.fileIds = body.fileIds;
|
||||
note.value.files = body.files;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue