fix
This commit is contained in:
parent
e1eee2872d
commit
23508dc56e
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class NoteUpdateAtHistory1696318192428 {
|
||||||
|
name = 'NoteUpdateAtHistory1696318192428'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note" ADD "updatedAtHistory" TIMESTAMP WITH TIME ZONE array`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note" DROP "updatedAtHistory"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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,
|
||||||
|
updatedAtHistory: note.updatedAtHistory ? note.updatedAtHistory.map(x => x.toISOString()) : undefined,
|
||||||
noteEditHistory: note.noteEditHistory.length ? note.noteEditHistory : 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),
|
||||||
|
|
|
@ -19,6 +19,12 @@ export class MiNote {
|
||||||
})
|
})
|
||||||
public updatedAt: Date | null;
|
public updatedAt: Date | null;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
array: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
public updatedAtHistory: Date[] | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 3000,
|
length: 3000,
|
||||||
array: true,
|
array: true,
|
||||||
|
|
|
@ -22,6 +22,15 @@ export const packedNoteSchema = {
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
format: 'date-time',
|
format: 'date-time',
|
||||||
},
|
},
|
||||||
|
updatedAtHistory: {
|
||||||
|
type: 'array',
|
||||||
|
optional: true, nullable: true,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
},
|
||||||
noteEditHistory: {
|
noteEditHistory: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
optional: true, nullable: false,
|
optional: true, nullable: false,
|
||||||
|
|
|
@ -77,7 +77,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
v-if="appearNote.text"
|
v-if="appearNote.text"
|
||||||
:parsedNodes="parsed"
|
:parsedNodes="parsed"
|
||||||
:text="appearNote.text"
|
:text="appearNote.text"
|
||||||
:author="appearNote.user"
|
|
||||||
:nyaize="'respect'"
|
:nyaize="'respect'"
|
||||||
:emojiUrls="appearNote.emojis"
|
:emojiUrls="appearNote.emojis"
|
||||||
:enableEmojiMenu="true"
|
:enableEmojiMenu="true"
|
||||||
|
@ -88,7 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkLoading v-if="translating" mini/>
|
<MkLoading v-if="translating" mini/>
|
||||||
<div v-else-if="translation">
|
<div v-else-if="translation">
|
||||||
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
<b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b>
|
||||||
<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
<Mfm :text="translation.text" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="appearNote.files && appearNote.files.length > 0">
|
<div v-if="appearNote.files && appearNote.files.length > 0">
|
||||||
|
@ -143,12 +142,11 @@ 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>
|
||||||
<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>
|
<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>
|
||||||
<div v-if="tab === 'replies'">
|
<div v-if="tab === 'replies'">
|
||||||
<div v-if="!repliesLoaded" style="padding: 16px">
|
<div v-if="!repliesLoaded" style="padding: 16px">
|
||||||
|
@ -186,26 +184,39 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'history'" :class="$style.tab_history">
|
<div v-else-if="tab === 'history'" :class="$style.tab_history">
|
||||||
<div style="display: grid;">
|
<div style="display: grid;">
|
||||||
<div v-for="text in appearNote.noteEditHistory.reverse()" :key="text">
|
<div v-for="(text, index) in appearNote.noteEditHistory" :key="text" :class="$style.historyRoot">
|
||||||
<MkNotePreview :class="$style.historyNote" :text="text"/>
|
<MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/>
|
||||||
|
<div :class="$style.historyMain">
|
||||||
|
<div :class="$style.historyHeader">
|
||||||
|
<MkUserName :user="appearNote.user" :nowrap="true"/>
|
||||||
|
<MkTime :class="$style.updatedAt" :time="appearNote.updatedAtHistory![index]"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<Mfm :text="text.trim()" :author="appearNote.user" :i="$i"/>
|
||||||
|
</div>
|
||||||
|
<CodeDiff
|
||||||
|
:oldString="appearNote.noteEditHistory[index - 1] || ''"
|
||||||
|
:newString="text"
|
||||||
|
:trim="true"
|
||||||
|
:hideHeader="true"
|
||||||
|
diffStyle="char"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="appearNote.noteEditHistory == null" class="_fullinfo">
|
||||||
|
<img :src="infoImageUrl" class="_ghost"/>
|
||||||
|
<div>{{ i18n.ts.nothing }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
import {computed, inject, onMounted, provide, ref, shallowRef, watch} from 'vue';
|
import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue';
|
||||||
import * as mfm from 'mfm-js';
|
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';
|
||||||
|
@ -241,6 +252,13 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import MkPagination, { type Paging } from '@/components/MkPagination.vue';
|
import MkPagination, { type Paging } from '@/components/MkPagination.vue';
|
||||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
import { infoImageUrl, instance } from '@/instance.js';
|
||||||
|
import MkPostForm from '@/components/MkPostFormSimple.vue';
|
||||||
|
import { deviceKind } from '@/scripts/device-kind.js';
|
||||||
|
|
||||||
|
const MOBILE_THRESHOLD = 500;
|
||||||
|
const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -250,38 +268,37 @@ const inChannel = inject('inChannel', null);
|
||||||
|
|
||||||
const note = ref(deepClone(props.note));
|
const note = ref(deepClone(props.note));
|
||||||
|
|
||||||
|
|
||||||
let gaming = ref('');
|
let gaming = ref('');
|
||||||
|
|
||||||
const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode'));
|
const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode'));
|
||||||
const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));
|
const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));
|
||||||
if (darkMode.value && gamingMode.value == true) {
|
if (darkMode.value && gamingMode.value == true) {
|
||||||
gaming.value = 'dark';
|
gaming.value = 'dark';
|
||||||
} else if (!darkMode.value && gamingMode.value == true) {
|
} else if (!darkMode.value && gamingMode.value == true) {
|
||||||
gaming.value = 'light';
|
gaming.value = 'light';
|
||||||
} else {
|
} else {
|
||||||
gaming.value = '';
|
gaming.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(darkMode, () => {
|
watch(darkMode, () => {
|
||||||
if (darkMode.value && gamingMode.value == true) {
|
if (darkMode.value && gamingMode.value == true) {
|
||||||
gaming.value = 'dark';
|
gaming.value = 'dark';
|
||||||
} else if (!darkMode.value && gamingMode.value == true) {
|
} else if (!darkMode.value && gamingMode.value == true) {
|
||||||
gaming.value = 'light';
|
gaming.value = 'light';
|
||||||
} else {
|
} else {
|
||||||
gaming.value = '';
|
gaming.value = '';
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
watch(gamingMode, () => {
|
watch(gamingMode, () => {
|
||||||
if (darkMode.value && gamingMode.value == true) {
|
if (darkMode.value && gamingMode.value == true) {
|
||||||
gaming.value = 'dark';
|
gaming.value = 'dark';
|
||||||
} else if (!darkMode.value && gamingMode.value == true) {
|
} else if (!darkMode.value && gamingMode.value == true) {
|
||||||
gaming.value = 'light';
|
gaming.value = 'light';
|
||||||
} else {
|
} else {
|
||||||
gaming.value = '';
|
gaming.value = '';
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
// plugin
|
// plugin
|
||||||
if (noteViewInterruptors.length > 0) {
|
if (noteViewInterruptors.length > 0) {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -831,12 +848,32 @@ function loadConversation() {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
.noteFooterButton {
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.historyRoot {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: clip;
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
|
||||||
.noteFooterButton {
|
.historyMain {
|
||||||
&:not(:last-child) {
|
flex: 1;
|
||||||
margin-right: 12px;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
.historyHeader {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
font-weight: bold;
|
||||||
|
width: 100%;
|
||||||
|
overflow: clip;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.historyNote {
|
.historyNote {
|
||||||
|
@ -845,6 +882,12 @@ function loadConversation() {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.updatedAt {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
.muted {
|
.muted {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
Loading…
Reference in New Issue