wip
This commit is contained in:
@@ -283,7 +283,7 @@ export class ChatService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async userHistory(meId: MiUser['id'], limit: number) {
|
public async userHistory(meId: MiUser['id'], limit: number): Promise<MiChatMessage[]> {
|
||||||
const history: MiChatMessage[] = [];
|
const history: MiChatMessage[] = [];
|
||||||
|
|
||||||
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
||||||
@@ -324,7 +324,7 @@ export class ChatService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async roomHistory(meId: MiUser['id'], limit: number) {
|
public async roomHistory(meId: MiUser['id'], limit: number): Promise<MiChatMessage[]> {
|
||||||
/*
|
/*
|
||||||
const rooms = await this.userRoomJoiningsRepository.findBy({
|
const rooms = await this.userRoomJoiningsRepository.findBy({
|
||||||
userId: meId,
|
userId: meId,
|
||||||
@@ -359,4 +359,24 @@ export class ChatService {
|
|||||||
return history;
|
return history;
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async getUserReadStateMap(userId: MiUser['id'], otherIds: MiUser['id'][]) {
|
||||||
|
const readStateMap: Record<MiUser['id'], boolean> = {};
|
||||||
|
|
||||||
|
const redisPipeline = this.redisClient.pipeline();
|
||||||
|
|
||||||
|
for (const otherId of otherIds) {
|
||||||
|
redisPipeline.get(`newChatMessageExists:${userId}:${otherId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const markers = await redisPipeline.exec();
|
||||||
|
|
||||||
|
for (let i = 0; i < otherIds.length; i++) {
|
||||||
|
const marker = markers[i][1];
|
||||||
|
readStateMap[otherIds[i]] = marker == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return readStateMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -46,6 +46,10 @@ export const packedChatMessageSchema = {
|
|||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
ref: 'DriveFile',
|
ref: 'DriveFile',
|
||||||
},
|
},
|
||||||
|
isRead: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
@@ -48,7 +48,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const history = ps.room ? await this.chatService.roomHistory(me.id, ps.limit) : await this.chatService.userHistory(me.id, ps.limit);
|
const history = ps.room ? await this.chatService.roomHistory(me.id, ps.limit) : await this.chatService.userHistory(me.id, ps.limit);
|
||||||
|
|
||||||
return await this.chatMessageEntityService.packMany(history, me);
|
const packedMessages = await this.chatMessageEntityService.packMany(history, me);
|
||||||
|
|
||||||
|
if (ps.room) {
|
||||||
|
} else {
|
||||||
|
const otherIds = history.map(m => m.fromUserId === me.id ? m.toUserId! : m.fromUserId!);
|
||||||
|
const readStateMap = await this.chatService.getUserReadStateMap(me.id, otherIds);
|
||||||
|
|
||||||
|
for (const message of packedMessages) {
|
||||||
|
const otherId = message.fromUserId === me.id ? message.toUserId! : message.fromUserId!;
|
||||||
|
message.isRead = readStateMap[otherId] ?? false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packedMessages;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,34 +10,34 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkButton primary :class="$style.start" @click="start"><i class="ti ti-plus"></i> {{ i18n.ts.startChat }}</MkButton>
|
<MkButton primary :class="$style.start" @click="start"><i class="ti ti-plus"></i> {{ i18n.ts.startChat }}</MkButton>
|
||||||
|
|
||||||
<div v-if="messages.length > 0" :class="$style.history">
|
<div v-if="history.length > 0" :class="$style.history">
|
||||||
<MkA
|
<MkA
|
||||||
v-for="(message, i) in messages"
|
v-for="item in history"
|
||||||
:key="message.id"
|
:key="item.id"
|
||||||
:class="[$style.message, { [$style.isMe]: message.isMe, [$style.isRead]: message.isRead }]"
|
:class="[$style.message, { [$style.isMe]: item.isMe, [$style.isRead]: item.message.isRead }]"
|
||||||
class="_panel"
|
class="_panel"
|
||||||
:to="message.roomId ? `/chat/room/${message.roomId}` : `/chat/user/${message.otherId}`"
|
:to="item.message.roomId ? `/chat/room/${item.message.roomId}` : `/chat/user/${item.other.id}`"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<MkAvatar :class="$style.avatar" :user="message.user" indicator link preview/>
|
<MkAvatar :class="$style.avatar" :user="item.other" indicator link preview/>
|
||||||
<header v-if="message.roomId">
|
<header v-if="item.message.roomId">
|
||||||
<span class="name">{{ message.room.name }}</span>
|
<span class="name">{{ item.message.room.name }}</span>
|
||||||
<MkTime :time="message.createdAt" class="time"/>
|
<MkTime :time="item.message.createdAt" class="time"/>
|
||||||
</header>
|
</header>
|
||||||
<header v-else>
|
<header v-else>
|
||||||
<span class="name"><MkUserName :user="message.other"/></span>
|
<span class="name"><MkUserName :user="item.other"/></span>
|
||||||
<span class="username">@{{ acct(message.other) }}</span>
|
<span class="username">@{{ acct(item.other) }}</span>
|
||||||
<MkTime :time="message.createdAt" class="time"/>
|
<MkTime :time="item.message.createdAt" class="time"/>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<p class="text"><span v-if="message.isMe" :class="$style.iSaid">{{ i18n.ts.you }}:</span>{{ message.text }}</p>
|
<p class="text"><span v-if="item.isMe" :class="$style.iSaid">{{ i18n.ts.you }}:</span>{{ item.message.text }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!fetching && messages.length == 0" class="_fullinfo">
|
<div v-if="!fetching && history.length == 0" class="_fullinfo">
|
||||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||||
<div>{{ $ts.noHistory }}</div>
|
<div>{{ i18n.ts.noHistory }}</div>
|
||||||
</div>
|
</div>
|
||||||
<MkLoading v-if="fetching"/>
|
<MkLoading v-if="fetching"/>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,11 +46,48 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
import { ensureSignin } from '@/i.js';
|
||||||
|
|
||||||
|
const $i = ensureSignin();
|
||||||
|
|
||||||
|
const fetching = ref(true);
|
||||||
|
const history = ref<{
|
||||||
|
id: string;
|
||||||
|
message: Misskey.entities.ChatMessage;
|
||||||
|
other: Misskey.entities.ChatMessage['fromUser'] | Misskey.entities.ChatMessage['toUser'] | null;
|
||||||
|
isMe: boolean;
|
||||||
|
}[]>([]);
|
||||||
|
|
||||||
|
async function fetchHistory() {
|
||||||
|
fetching.value = true;
|
||||||
|
|
||||||
|
const [userMessages, groupMessages] = await Promise.all([
|
||||||
|
misskeyApi('messaging/history', { group: false }),
|
||||||
|
misskeyApi('messaging/history', { group: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
history.value = [...userMessages, ...groupMessages]
|
||||||
|
.toSorted((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||||
|
.map(m => ({
|
||||||
|
id: m.id,
|
||||||
|
message: m,
|
||||||
|
other: m.room == null ? (m.fromUserId === $i.id ? m.toUser : m.fromUser) : null,
|
||||||
|
isMe: m.fromUserId === $i.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
fetching.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchHistory();
|
||||||
|
});
|
||||||
|
|
||||||
const headerActions = computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user