upd: improve note edit table & improve previous version view

Closes transfem-org/Sharkey#105
This commit is contained in:
Mar0xy 2023-10-22 13:16:30 +02:00
parent 6b3b805a3e
commit d50e81e475
No known key found for this signature in database
GPG key ID: 56569BBE47D2C828
8 changed files with 463 additions and 101 deletions

View file

@ -0,0 +1,13 @@
export class AlterNoteEdit1697970083000 {
name = "AlterNoteEdit1697970083000";
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "note_edit" RENAME COLUMN "text" TO "newText"`);
await queryRunner.query(`ALTER TABLE "note_edit" ADD COLUMN "oldText" text`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "note_edit" RENAME COLUMN "newText" TO "text"`);
await queryRunner.query(`ALTER TABLE "note_edit" DROP COLUMN "oldText"`);
}
}

View file

@ -387,104 +387,109 @@ export class NoteEditService implements OnApplicationShutdown {
update.hasPoll = !!data.poll;
}
await this.noteEditRepository.insert({
id: this.idService.gen(),
noteId: oldnote.id,
text: data.text || undefined,
cw: data.cw,
fileIds: undefined,
updatedAt: new Date(),
});
const note = new MiNote({
id: oldnote.id,
updatedAt: data.updatedAt ? data.updatedAt : new Date(),
fileIds: data.files ? data.files.map(file => file.id) : [],
replyId: data.reply ? data.reply.id : null,
renoteId: data.renote ? data.renote.id : null,
channelId: data.channel ? data.channel.id : null,
threadId: data.reply
? data.reply.threadId
? data.reply.threadId
: data.reply.id
: null,
name: data.name,
text: data.text,
hasPoll: data.poll != null,
cw: data.cw ?? null,
tags: tags.map(tag => normalizeForSearch(tag)),
emojis,
reactions: oldnote.reactions,
userId: user.id,
localOnly: data.localOnly!,
reactionAcceptance: data.reactionAcceptance,
visibility: data.visibility as any,
visibleUserIds: data.visibility === 'specified'
? data.visibleUsers
? data.visibleUsers.map(u => u.id)
: []
: [],
attachedFileTypes: data.files ? data.files.map(file => file.type) : [],
// 以下非正規化データ
replyUserId: data.reply ? data.reply.userId : null,
replyUserHost: data.reply ? data.reply.userHost : null,
renoteUserId: data.renote ? data.renote.userId : null,
renoteUserHost: data.renote ? data.renote.userHost : null,
userHost: user.host,
});
if (data.uri != null) note.uri = data.uri;
if (data.url != null) note.url = data.url;
if (mentionedUsers.length > 0) {
note.mentions = mentionedUsers.map(u => u.id);
const profiles = await this.userProfilesRepository.findBy({ userId: In(note.mentions) });
note.mentionedRemoteUsers = JSON.stringify(mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u)).map(u => {
const profile = profiles.find(p => p.userId === u.id);
const url = profile != null ? profile.url : null;
return {
uri: u.uri,
url: url ?? undefined,
username: u.username,
host: u.host,
} as IMentionedRemoteUsers[0];
}));
}
if (data.poll != null) {
// Start transaction
await this.db.transaction(async transactionalEntityManager => {
await transactionalEntityManager.update(MiNote, oldnote.id, note);
const poll = new MiPoll({
noteId: note.id,
choices: data.poll!.choices,
expiresAt: data.poll!.expiresAt,
multiple: data.poll!.multiple,
votes: new Array(data.poll!.choices.length).fill(0),
noteVisibility: note.visibility,
userId: user.id,
userHost: user.host,
});
if (!oldnote.hasPoll) {
await transactionalEntityManager.insert(MiPoll, poll);
} else {
await transactionalEntityManager.update(MiPoll, oldnote.id, poll);
}
if (Object.keys(update).length > 0) {
await this.noteEditRepository.insert({
id: this.idService.gen(),
noteId: oldnote.id,
oldText: update.text ? oldnote.text : undefined,
newText: update.text || undefined,
cw: update.cw || undefined,
fileIds: undefined,
updatedAt: new Date(),
});
const note = new MiNote({
id: oldnote.id,
updatedAt: data.updatedAt ? data.updatedAt : new Date(),
fileIds: data.files ? data.files.map(file => file.id) : [],
replyId: data.reply ? data.reply.id : null,
renoteId: data.renote ? data.renote.id : null,
channelId: data.channel ? data.channel.id : null,
threadId: data.reply
? data.reply.threadId
? data.reply.threadId
: data.reply.id
: null,
name: data.name,
text: data.text,
hasPoll: data.poll != null,
cw: data.cw ?? null,
tags: tags.map(tag => normalizeForSearch(tag)),
emojis,
reactions: oldnote.reactions,
userId: user.id,
localOnly: data.localOnly!,
reactionAcceptance: data.reactionAcceptance,
visibility: data.visibility as any,
visibleUserIds: data.visibility === 'specified'
? data.visibleUsers
? data.visibleUsers.map(u => u.id)
: []
: [],
attachedFileTypes: data.files ? data.files.map(file => file.type) : [],
// 以下非正規化データ
replyUserId: data.reply ? data.reply.userId : null,
replyUserHost: data.reply ? data.reply.userHost : null,
renoteUserId: data.renote ? data.renote.userId : null,
renoteUserHost: data.renote ? data.renote.userHost : null,
userHost: user.host,
});
if (data.uri != null) note.uri = data.uri;
if (data.url != null) note.url = data.url;
if (mentionedUsers.length > 0) {
note.mentions = mentionedUsers.map(u => u.id);
const profiles = await this.userProfilesRepository.findBy({ userId: In(note.mentions) });
note.mentionedRemoteUsers = JSON.stringify(mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u)).map(u => {
const profile = profiles.find(p => p.userId === u.id);
const url = profile != null ? profile.url : null;
return {
uri: u.uri,
url: url ?? undefined,
username: u.username,
host: u.host,
} as IMentionedRemoteUsers[0];
}));
}
if (data.poll != null) {
// Start transaction
await this.db.transaction(async transactionalEntityManager => {
await transactionalEntityManager.update(MiNote, oldnote.id, note);
const poll = new MiPoll({
noteId: note.id,
choices: data.poll!.choices,
expiresAt: data.poll!.expiresAt,
multiple: data.poll!.multiple,
votes: new Array(data.poll!.choices.length).fill(0),
noteVisibility: note.visibility,
userId: user.id,
userHost: user.host,
});
if (!oldnote.hasPoll) {
await transactionalEntityManager.insert(MiPoll, poll);
} else {
await transactionalEntityManager.update(MiPoll, oldnote.id, poll);
}
});
} else {
await this.notesRepository.update(oldnote.id, note);
}
setImmediate('post edited', { signal: this.#shutdownController.signal }).then(
() => this.postNoteEdited(note, user, data, silent, tags!, mentionedUsers!),
() => { /* aborted, ignore this */ },
);
return note;
} else {
await this.notesRepository.update(oldnote.id, note);
return oldnote;
}
setImmediate('post edited', { signal: this.#shutdownController.signal }).then(
() => this.postNoteEdited(note, user, data, silent, tags!, mentionedUsers!),
() => { /* aborted, ignore this */ },
);
return note;
}
@bindThis

View file

@ -24,7 +24,12 @@ export class NoteEdit {
@Column("text", {
nullable: true,
})
public text: string | null;
public oldText: string | null;
@Column("text", {
nullable: true,
})
public newText: string | null;
@Column("varchar", {
length: 512,

View file

@ -26,7 +26,12 @@ export const packedNoteEdit = {
nullable: false,
format: "id",
},
text: {
oldText: {
type: "string",
optional: true,
nullable: true,
},
newText: {
type: "string",
optional: true,
nullable: true,

View file

@ -51,7 +51,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
for (const edit of edits) {
editArray.push({
updatedAt: new Date(edit.updatedAt).toLocaleString('UTC', { hour: 'numeric', minute: 'numeric', second: 'numeric', year: 'numeric', month: 'short', day: 'numeric' }),
text: edit.text,
text: edit.oldText,
});
}