Compare commits

..

No commits in common. "7b69f8367a3336f2c5a6d03aa7fc03fc2bbdb67c" and "9a986b3497e09acae8fa336f0b65f08364dfe480" have entirely different histories.

9 changed files with 45 additions and 73 deletions

View File

@ -55,7 +55,6 @@ import { MetaService } from '@/core/MetaService.js';
import { SearchService } from '@/core/SearchService.js';
import { FeaturedService } from '@/core/FeaturedService.js';
import { RedisTimelineService } from '@/core/RedisTimelineService.js';
import { nyaize } from '@/misc/nyaize.js';
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@ -224,10 +223,7 @@ export class NoteCreateService implements OnApplicationShutdown {
host: MiUser['host'];
createdAt: MiUser['createdAt'];
isBot: MiUser['isBot'];
isCat: MiUser['isCat'];
}, data: Option, silent = false): Promise<MiNote> {
let patsedText: mfm.MfmNode[] | null = null;
// チャンネル外にリプライしたら対象のスコープに合わせる
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
@ -306,25 +302,6 @@ export class NoteCreateService implements OnApplicationShutdown {
data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH);
}
data.text = data.text.trim();
if (user.isCat) {
patsedText = patsedText ?? mfm.parse(data.text);
function nyaizeNode(node: mfm.MfmNode) {
if (node.type === 'quote') return;
if (node.type === 'text') {
node.props.text = nyaize(node.props.text);
}
if (node.children) {
for (const child of node.children) {
nyaizeNode(child);
}
}
}
for (const node of patsedText) {
nyaizeNode(node);
}
data.text = mfm.toString(patsedText);
}
} else {
data.text = null;
}
@ -335,7 +312,7 @@ export class NoteCreateService implements OnApplicationShutdown {
// Parse MFM if needed
if (!tags || !emojis || !mentionedUsers) {
const tokens = patsedText ?? (data.text ? mfm.parse(data.text)! : []);
const tokens = data.text ? mfm.parse(data.text)! : [];
const cwTokens = data.cw ? mfm.parse(data.cw)! : [];
const choiceTokens = data.poll && data.poll.choices
? concat(data.poll.choices.map(choice => mfm.parse(choice)!))
@ -577,7 +554,7 @@ export class NoteCreateService implements OnApplicationShutdown {
}
// Pack the note
const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true });
const noteObj = await this.noteEntityService.pack(note);
this.globalEventService.publishNotesStream(noteObj);

View File

@ -5,9 +5,11 @@
import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
import * as mfm from 'mfm-js';
import { ModuleRef } from '@nestjs/core';
import { DI } from '@/di-symbols.js';
import type { Packed } from '@/misc/json-schema.js';
import { nyaize } from '@/misc/nyaize.js';
import { awaitAll } from '@/misc/prelude/await-all.js';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
@ -16,7 +18,6 @@ import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepos
import { bindThis } from '@/decorators.js';
import { isNotNull } from '@/misc/is-not-null.js';
import { DebounceLoader } from '@/misc/loader.js';
import { IdService } from '@/core/IdService.js';
import type { OnModuleInit } from '@nestjs/common';
import type { CustomEmojiService } from '../CustomEmojiService.js';
import type { ReactionService } from '../ReactionService.js';
@ -29,7 +30,6 @@ export class NoteEntityService implements OnModuleInit {
private driveFileEntityService: DriveFileEntityService;
private customEmojiService: CustomEmojiService;
private reactionService: ReactionService;
private idService: IdService;
private noteLoader = new DebounceLoader(this.findNoteOrFail);
constructor(
@ -68,7 +68,6 @@ export class NoteEntityService implements OnModuleInit {
this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService');
this.customEmojiService = this.moduleRef.get('CustomEmojiService');
this.reactionService = this.moduleRef.get('ReactionService');
this.idService = this.moduleRef.get('IdService');
}
@bindThis
@ -170,11 +169,11 @@ export class NoteEntityService implements OnModuleInit {
}
@bindThis
public async populateMyReaction(noteId: MiNote['id'], meId: MiUser['id'], _hint_?: {
private async populateMyReaction(note: MiNote, meId: MiUser['id'], _hint_?: {
myReactions: Map<MiNote['id'], MiNoteReaction | null>;
}) {
if (_hint_?.myReactions) {
const reaction = _hint_.myReactions.get(noteId);
const reaction = _hint_.myReactions.get(note.id);
if (reaction) {
return this.reactionService.convertLegacyReaction(reaction.reaction);
} else if (reaction === null) {
@ -183,14 +182,14 @@ export class NoteEntityService implements OnModuleInit {
// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
}
// パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない
if (this.idService.parse(noteId).date.getTime() + 2000 > Date.now()) {
// パフォーマンスのためノートが作成されてから1秒以上経っていない場合はリアクションを取得しない
if (note.createdAt.getTime() + 1000 > Date.now()) {
return undefined;
}
const reaction = await this.noteReactionsRepository.findOneBy({
userId: meId,
noteId: noteId,
noteId: note.id,
});
if (reaction) {
@ -358,11 +357,30 @@ export class NoteEntityService implements OnModuleInit {
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
...(meId ? {
myReaction: this.populateMyReaction(note.id, meId, options?._hint_),
myReaction: this.populateMyReaction(note, meId, options?._hint_),
} : {}),
} : {}),
});
if (packed.user.isCat && packed.text) {
const tokens = packed.text ? mfm.parse(packed.text) : [];
function nyaizeNode(node: mfm.MfmNode) {
if (node.type === 'quote') return;
if (node.type === 'text') {
node.props.text = nyaize(node.props.text);
}
if (node.children) {
for (const child of node.children) {
nyaizeNode(child);
}
}
}
for (const node of tokens) {
nyaizeNode(node);
}
packed.text = mfm.toString(tokens);
}
if (!opts.skipHide) {
await this.hideNote(packed, meId);
}
@ -385,8 +403,8 @@ export class NoteEntityService implements OnModuleInit {
const myReactionsMap = new Map<MiNote['id'], MiNoteReaction | null>();
if (meId) {
const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
// パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない
const targets = [...notes.filter(n => n.createdAt.getTime() + 2000 < Date.now()).map(n => n.id), ...renoteIds];
// パフォーマンスのためノートが作成されてから1秒以上経っていない場合はリアクションを取得しない
const targets = [...notes.filter(n => n.createdAt.getTime() + 1000 < Date.now()).map(n => n.id), ...renoteIds];
const myReactions = await this.noteReactionsRepository.findBy({
userId: meId,
noteId: In(targets),

View File

@ -38,6 +38,8 @@ class ChannelChannel extends Channel {
private async onNote(note: Packed<'Note'>) {
if (note.channelId !== this.channelId) return;
// TODO: ZQT: populate my reaction
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
@ -45,11 +47,6 @@ class ChannelChannel extends Channel {
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.user && note.renoteId && !note.text) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id);
note.renote!.myReaction = myRenoteReaction;
}
this.connection.cacheNote(note);
this.send('note', note);

View File

@ -52,6 +52,8 @@ class GlobalTimelineChannel extends Channel {
if (note.visibility !== 'public') return;
if (note.channelId != null) return;
// TODO: ZQT: populate my reaction
// 関係ない返信は除外
if (note.reply && !this.following[note.userId]?.withReplies) {
const reply = note.reply;
@ -71,11 +73,6 @@ class GlobalTimelineChannel extends Channel {
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.user && note.renoteId && !note.text) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id);
note.renote!.myReaction = myRenoteReaction;
}
this.connection.cacheNote(note);
this.send('note', note);

View File

@ -43,6 +43,8 @@ class HashtagChannel extends Channel {
const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
if (!matched) return;
// TODO: ZQT: populate my reaction
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
@ -50,11 +52,6 @@ class HashtagChannel extends Channel {
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.user && note.renoteId && !note.text) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id);
note.renote!.myReaction = myRenoteReaction;
}
this.connection.cacheNote(note);
this.send('note', note);

View File

@ -55,6 +55,8 @@ class HomeTimelineChannel extends Channel {
if (!Object.hasOwn(this.following, note.userId)) return;
} else if (note.visibility === 'specified') {
if (!note.visibleUserIds!.includes(this.user!.id)) return;
} else {
// TODO: ZQT: populate my reaction
}
// 関係ない返信は除外
@ -73,11 +75,6 @@ class HomeTimelineChannel extends Channel {
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.user && note.renoteId && !note.text) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id);
note.renote!.myReaction = myRenoteReaction;
}
this.connection.cacheNote(note);
this.send('note', note);

View File

@ -66,6 +66,8 @@ class HybridTimelineChannel extends Channel {
if (!Object.hasOwn(this.following, note.userId)) return;
} else if (note.visibility === 'specified') {
if (!note.visibleUserIds!.includes(this.user!.id)) return;
} else {
// TODO: ZQT: populate my reaction
}
// Ignore notes from instances the user has muted
@ -87,11 +89,6 @@ class HybridTimelineChannel extends Channel {
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.user && note.renoteId && !note.text) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id);
note.renote!.myReaction = myRenoteReaction;
}
this.connection.cacheNote(note);
this.send('note', note);

View File

@ -54,6 +54,8 @@ class LocalTimelineChannel extends Channel {
if (note.visibility !== 'public') return;
if (note.channelId != null && !this.followingChannels.has(note.channelId)) return;
// TODO: ZQT: populate my reaction
// 関係ない返信は除外
if (note.reply && this.user && !this.following[note.userId]?.withReplies && !this.withReplies) {
const reply = note.reply;
@ -70,11 +72,6 @@ class LocalTimelineChannel extends Channel {
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.user && note.renoteId && !note.text) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id);
note.renote!.myReaction = myRenoteReaction;
}
this.connection.cacheNote(note);
this.send('note', note);

View File

@ -86,6 +86,8 @@ class UserListChannel extends Channel {
if (!Object.hasOwn(this.following, note.userId)) return;
} else if (note.visibility === 'specified') {
if (!note.visibleUserIds!.includes(this.user!.id)) return;
} else {
// TODO: ZQT: populate my reaction
}
// 関係ない返信は除外
@ -102,13 +104,6 @@ class UserListChannel extends Channel {
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.user && note.renoteId && !note.text) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id);
note.renote!.myReaction = myRenoteReaction;
}
this.connection.cacheNote(note);
this.send('note', note);
}