wip
This commit is contained in:
parent
678b718c24
commit
8785621fcf
|
@ -593,4 +593,35 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
relations: ['user'],
|
relations: ['user'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async ogogogo(noteIds: MiNote['id'][]) {
|
||||||
|
if (noteIds.length === 0) return [];
|
||||||
|
|
||||||
|
const notes = await this.notesRepository.find({
|
||||||
|
where: {
|
||||||
|
id: In(noteIds),
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
reactions: true,
|
||||||
|
reactionAndUserPairCache: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(notes);
|
||||||
|
|
||||||
|
const bufferedReactionsMap = this.meta.enableReactionsBuffering ? await this.reactionsBufferingService.getMany(noteIds) : null;
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const note of notes) {
|
||||||
|
const bufferedReactions = bufferedReactionsMap?.get(note.id);
|
||||||
|
const reactionAndUserPairCache = note.reactionAndUserPairCache.concat(bufferedReactions.pairs.map(x => x.join('/')));
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
id: note.id,
|
||||||
|
reactions: this.reactionService.convertLegacyReactions(this.reactionsBufferingService.mergeReactions(note.reactions, bufferedReactions.deltas ?? {})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
|
import { ApiError } from '../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['notes'],
|
||||||
|
|
||||||
|
requireCredential: false,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
noteIds: { type: 'array', items: { type: 'string', format: 'misskey:id' }, maxItems: 100, minItems: 1 },
|
||||||
|
},
|
||||||
|
required: ['noteIds'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -514,7 +514,10 @@ function react(): void {
|
||||||
misskeyApi('notes/reactions/create', {
|
misskeyApi('notes/reactions/create', {
|
||||||
noteId: appearNote.value.id,
|
noteId: appearNote.value.id,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
|
}).then(() => {
|
||||||
|
// 別にthenを待たなくても良いかも(楽観的更新)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) {
|
if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) {
|
||||||
claimAchievement('reactWithoutRead');
|
claimAchievement('reactWithoutRead');
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,32 @@
|
||||||
|
|
||||||
import { onUnmounted } from 'vue';
|
import { onUnmounted } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import type { Ref, ShallowRef } from 'vue';
|
import type { Ref, ShallowRef } from 'vue';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
import { store } from '@/store.js';
|
import { store } from '@/store.js';
|
||||||
|
|
||||||
export function useNoteCapture(props: {
|
const noteEvents = new EventEmitter<{
|
||||||
|
reacted: Misskey.entities.Note;
|
||||||
|
unreacted: Misskey.entities.Note;
|
||||||
|
pollVoted: Misskey.entities.Note;
|
||||||
|
deleted: Misskey.entities.Note;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const capturedNoteIdMapForPolling = new Map<string, number>();
|
||||||
|
|
||||||
|
const POLLING_INTERVAL = 1000 * 10;
|
||||||
|
|
||||||
|
window.setInterval(() => {
|
||||||
|
const ids = [...capturedNoteIdMapForPolling.keys()];
|
||||||
|
if (ids.length === 0) return;
|
||||||
|
if (window.document.hidden) return;
|
||||||
|
|
||||||
|
console.log('Polling notes', ids);
|
||||||
|
}, POLLING_INTERVAL);
|
||||||
|
|
||||||
|
function pseudoNoteCapture(props: {
|
||||||
rootEl: ShallowRef<HTMLElement | undefined>;
|
rootEl: ShallowRef<HTMLElement | undefined>;
|
||||||
note: Ref<Misskey.entities.Note>;
|
note: Ref<Misskey.entities.Note>;
|
||||||
pureNote: Ref<Misskey.entities.Note>;
|
pureNote: Ref<Misskey.entities.Note>;
|
||||||
|
@ -18,7 +38,34 @@ export function useNoteCapture(props: {
|
||||||
}) {
|
}) {
|
||||||
const note = props.note;
|
const note = props.note;
|
||||||
const pureNote = props.pureNote;
|
const pureNote = props.pureNote;
|
||||||
const connection = $i && store.s.realtimeMode ? useStream() : null;
|
|
||||||
|
function onReacted(): void {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedNoteIdMapForPolling.has(note.value.id)) {
|
||||||
|
capturedNoteIdMapForPolling.set(note.value.id, capturedNoteIdMapForPolling.get(note.value.id)! + 1);
|
||||||
|
} else {
|
||||||
|
capturedNoteIdMapForPolling.set(note.value.id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
capturedNoteIdMapForPolling.set(note.value.id, capturedNoteIdMapForPolling.get(note.value.id)! - 1);
|
||||||
|
if (capturedNoteIdMapForPolling.get(note.value.id) === 0) {
|
||||||
|
capturedNoteIdMapForPolling.delete(note.value.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function realtimeNoteCapture(props: {
|
||||||
|
rootEl: ShallowRef<HTMLElement | undefined>;
|
||||||
|
note: Ref<Misskey.entities.Note>;
|
||||||
|
pureNote: Ref<Misskey.entities.Note>;
|
||||||
|
isDeletedRef: Ref<boolean>;
|
||||||
|
}): void {
|
||||||
|
const note = props.note;
|
||||||
|
const pureNote = props.pureNote;
|
||||||
|
const connection = useStream();
|
||||||
|
|
||||||
function onStreamNoteUpdated(noteData): void {
|
function onStreamNoteUpdated(noteData): void {
|
||||||
const { type, id, body } = noteData;
|
const { type, id, body } = noteData;
|
||||||
|
@ -85,26 +132,22 @@ export function useNoteCapture(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
function capture(withHandler = false): void {
|
function capture(withHandler = false): void {
|
||||||
if (connection) {
|
// TODO: このノートがストリーミング経由で流れてきた場合のみ sr する
|
||||||
// TODO: このノートがストリーミング経由で流れてきた場合のみ sr する
|
connection.send(window.document.body.contains(props.rootEl.value ?? null as Node | null) ? 'sr' : 's', { id: note.value.id });
|
||||||
connection.send(window.document.body.contains(props.rootEl.value ?? null as Node | null) ? 'sr' : 's', { id: note.value.id });
|
if (pureNote.value.id !== note.value.id) connection.send('s', { id: pureNote.value.id });
|
||||||
if (pureNote.value.id !== note.value.id) connection.send('s', { id: pureNote.value.id });
|
if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated);
|
||||||
if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function decapture(withHandler = false): void {
|
function decapture(withHandler = false): void {
|
||||||
if (connection) {
|
connection.send('un', {
|
||||||
|
id: note.value.id,
|
||||||
|
});
|
||||||
|
if (pureNote.value.id !== note.value.id) {
|
||||||
connection.send('un', {
|
connection.send('un', {
|
||||||
id: note.value.id,
|
id: pureNote.value.id,
|
||||||
});
|
});
|
||||||
if (pureNote.value.id !== note.value.id) {
|
|
||||||
connection.send('un', {
|
|
||||||
id: pureNote.value.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (withHandler) connection.off('noteUpdated', onStreamNoteUpdated);
|
|
||||||
}
|
}
|
||||||
|
if (withHandler) connection.off('noteUpdated', onStreamNoteUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStreamConnected() {
|
function onStreamConnected() {
|
||||||
|
@ -112,14 +155,23 @@ export function useNoteCapture(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
capture(true);
|
capture(true);
|
||||||
if (connection) {
|
connection.on('_connected_', onStreamConnected);
|
||||||
connection.on('_connected_', onStreamConnected);
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
decapture(true);
|
decapture(true);
|
||||||
if (connection) {
|
connection.off('_connected_', onStreamConnected);
|
||||||
connection.off('_connected_', onStreamConnected);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useNoteCapture(props: {
|
||||||
|
rootEl: ShallowRef<HTMLElement | undefined>;
|
||||||
|
note: Ref<Misskey.entities.Note>;
|
||||||
|
pureNote: Ref<Misskey.entities.Note>;
|
||||||
|
isDeletedRef: Ref<boolean>;
|
||||||
|
}) {
|
||||||
|
if ($i && store.s.realtimeMode) {
|
||||||
|
realtimeNoteCapture(props);
|
||||||
|
} else {
|
||||||
|
pseudoNoteCapture(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue