parent
588d6acfb7
commit
e1eee2872d
16
packages/backend/migration/1696044626209-noteEditHistory.js
Normal file
16
packages/backend/migration/1696044626209-noteEditHistory.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class NoteEditHistory1696044626209 {
|
||||||
|
name = 'NoteEditHistory1696044626209'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note" ADD "noteEditHistory" varchar(3000) array DEFAULT '{}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note" DROP "noteEditHistory"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -324,6 +324,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
id: note.id,
|
id: note.id,
|
||||||
createdAt: this.idService.parse(note.id).date.toISOString(),
|
createdAt: this.idService.parse(note.id).date.toISOString(),
|
||||||
updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined,
|
updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined,
|
||||||
|
noteEditHistory: note.noteEditHistory.length ? note.noteEditHistory : undefined,
|
||||||
userId: note.userId,
|
userId: note.userId,
|
||||||
user: this.userEntityService.pack(note.user ?? note.userId, me),
|
user: this.userEntityService.pack(note.user ?? note.userId, me),
|
||||||
text: text,
|
text: text,
|
||||||
|
|
|
@ -19,6 +19,13 @@ export class MiNote {
|
||||||
})
|
})
|
||||||
public updatedAt: Date | null;
|
public updatedAt: Date | null;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 3000,
|
||||||
|
array: true,
|
||||||
|
default: '{}',
|
||||||
|
})
|
||||||
|
public noteEditHistory: string[];
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
|
|
|
@ -22,6 +22,10 @@ export const packedNoteSchema = {
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
},
|
},
|
||||||
|
noteEditHistory: {
|
||||||
|
type: 'array',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
deletedAt: {
|
deletedAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
|
|
|
@ -143,10 +143,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
<div :class="$style.tabs">
|
<div :class="$style.tabs">
|
||||||
|
|
||||||
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'replies'}]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button>
|
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'replies'}]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button>
|
||||||
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'renotes'}]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button>
|
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'renotes'}]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button>
|
||||||
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'reactions'}]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button>
|
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'reactions'}]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button>
|
||||||
</div>
|
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'history' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light']" @click="tab = 'history'"><i class="ti ti-pencil"></i> {{ i18n.ts.edited }}</button>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="tab === 'replies'">
|
<div v-if="tab === 'replies'">
|
||||||
<div v-if="!repliesLoaded" style="padding: 16px">
|
<div v-if="!repliesLoaded" style="padding: 16px">
|
||||||
|
@ -182,8 +184,24 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="tab === 'history'" :class="$style.tab_history">
|
||||||
|
<div style="display: grid;">
|
||||||
|
<div v-for="text in appearNote.noteEditHistory.reverse()" :key="text">
|
||||||
|
<MkNotePreview :class="$style.historyNote" :text="text"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div><div v-else />
|
</div>
|
||||||
|
<div v-else class="_panel" :class="$style.muted" @click="muted = false">
|
||||||
|
<I18n :src="i18n.ts.userSaysSomething" tag="small">
|
||||||
|
<template #name>
|
||||||
|
<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
|
||||||
|
<MkUserName :user="appearNote.user"/>
|
||||||
|
</MkA>
|
||||||
|
</template>
|
||||||
|
</I18n>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -192,6 +210,7 @@ import * as mfm from 'mfm-js';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||||
|
import MkNotePreview from '@/components/MkNotePreview.vue';
|
||||||
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
|
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
|
||||||
import MkMediaList from '@/components/MkMediaList.vue';
|
import MkMediaList from '@/components/MkMediaList.vue';
|
||||||
import MkCwButton from '@/components/MkCwButton.vue';
|
import MkCwButton from '@/components/MkCwButton.vue';
|
||||||
|
@ -748,6 +767,9 @@ function loadConversation() {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab_history {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
.reactionTabs {
|
.reactionTabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
@ -817,6 +839,12 @@ function loadConversation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.historyNote {
|
||||||
|
padding-top: 10px;
|
||||||
|
min-height: 75px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.muted {
|
.muted {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
Loading…
Reference in a new issue