Enhance: 連合向けのノート配信を軽量化 (#13192)
* AP HTML表現をシンプルに * a * CHANGELOG * リンク
This commit is contained in:
parent
c81b61eb2e
commit
e89d760240
|
@ -87,6 +87,7 @@
|
||||||
- Fix: properly handle cc followers
|
- Fix: properly handle cc followers
|
||||||
- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec
|
- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec
|
||||||
- Fix: コントロールパネル->モデレーション->「誰でも新規登録できるようにする」の初期値をONからOFFに変更 #13122
|
- Fix: コントロールパネル->モデレーション->「誰でも新規登録できるようにする」の初期値をONからOFFに変更 #13122
|
||||||
|
- Enhance: 連合向けのノート配信を軽量化 #13192
|
||||||
|
|
||||||
### Service Worker
|
### Service Worker
|
||||||
- Enhance: オフライン表示のデザインを改善・多言語対応
|
- Enhance: オフライン表示のデザインを改善・多言語対応
|
||||||
|
|
|
@ -419,6 +419,10 @@ export class MfmService {
|
||||||
},
|
},
|
||||||
|
|
||||||
text: (node) => {
|
text: (node) => {
|
||||||
|
if (!node.props.text.match(/[\r\n]/)) {
|
||||||
|
return doc.createTextNode(node.props.text);
|
||||||
|
}
|
||||||
|
|
||||||
const el = doc.createElement('span');
|
const el = doc.createElement('span');
|
||||||
const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x));
|
const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x));
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,21 @@ export class ApMfmService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public getNoteHtml(note: MiNote): string | null {
|
public getNoteHtml(note: MiNote, apAppend?: string) {
|
||||||
if (!note.text) return '';
|
let noMisskeyContent = false;
|
||||||
return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers));
|
const srcMfm = (note.text ?? '') + (apAppend ?? '');
|
||||||
|
|
||||||
|
const parsed = mfm.parse(srcMfm);
|
||||||
|
|
||||||
|
if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) {
|
||||||
|
noMisskeyContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = this.mfmService.toHtml(parsed, JSON.parse(note.mentionedRemoteUsers));
|
||||||
|
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
noMisskeyContent,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -389,17 +389,15 @@ export class ApRendererService {
|
||||||
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
let apText = text;
|
let apAppend = '';
|
||||||
|
|
||||||
if (quote) {
|
if (quote) {
|
||||||
apText += `\n\nRE: ${quote}`;
|
apAppend += `\n\nRE: ${quote}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
|
const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
|
||||||
|
|
||||||
const content = this.apMfmService.getNoteHtml(Object.assign({}, note, {
|
const { content, noMisskeyContent } = this.apMfmService.getNoteHtml(note, apAppend);
|
||||||
text: apText,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const emojis = await this.getEmojis(note.emojis);
|
const emojis = await this.getEmojis(note.emojis);
|
||||||
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
|
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
|
||||||
|
@ -412,9 +410,6 @@ export class ApRendererService {
|
||||||
|
|
||||||
const asPoll = poll ? {
|
const asPoll = poll ? {
|
||||||
type: 'Question',
|
type: 'Question',
|
||||||
content: this.apMfmService.getNoteHtml(Object.assign({}, note, {
|
|
||||||
text: text,
|
|
||||||
})),
|
|
||||||
[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
|
[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
|
||||||
[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
|
[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
|
||||||
type: 'Note',
|
type: 'Note',
|
||||||
|
@ -432,11 +427,13 @@ export class ApRendererService {
|
||||||
attributedTo,
|
attributedTo,
|
||||||
summary: summary ?? undefined,
|
summary: summary ?? undefined,
|
||||||
content: content ?? undefined,
|
content: content ?? undefined,
|
||||||
|
...(noMisskeyContent ? {} : {
|
||||||
_misskey_content: text,
|
_misskey_content: text,
|
||||||
source: {
|
source: {
|
||||||
content: text,
|
content: text,
|
||||||
mediaType: 'text/x.misskeymarkdown',
|
mediaType: 'text/x.misskeymarkdown',
|
||||||
},
|
},
|
||||||
|
}),
|
||||||
_misskey_quote: quote,
|
_misskey_quote: quote,
|
||||||
quoteUrl: quote,
|
quoteUrl: quote,
|
||||||
published: this.idService.parse(note.id).date.toISOString(),
|
published: this.idService.parse(note.id).date.toISOString(),
|
||||||
|
|
44
packages/backend/test/unit/ApMfmService.ts
Normal file
44
packages/backend/test/unit/ApMfmService.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import { Test } from '@nestjs/testing';
|
||||||
|
|
||||||
|
import { CoreModule } from '@/core/CoreModule.js';
|
||||||
|
import { ApMfmService } from '@/core/activitypub/ApMfmService.js';
|
||||||
|
import { GlobalModule } from '@/GlobalModule.js';
|
||||||
|
import { MiNote } from '@/models/Note.js';
|
||||||
|
|
||||||
|
describe('ApMfmService', () => {
|
||||||
|
let apMfmService: ApMfmService;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const app = await Test.createTestingModule({
|
||||||
|
imports: [GlobalModule, CoreModule],
|
||||||
|
}).compile();
|
||||||
|
apMfmService = app.get<ApMfmService>(ApMfmService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getNoteHtml', () => {
|
||||||
|
test('Do not provide _misskey_content for simple text', () => {
|
||||||
|
const note: MiNote = {
|
||||||
|
text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com',
|
||||||
|
mentionedRemoteUsers: '[]',
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
|
||||||
|
|
||||||
|
assert.equal(noMisskeyContent, true, 'noMisskeyContent');
|
||||||
|
assert.equal(content, '<p>テキスト <a href="http://misskey.local/tags/タグ" rel="tag">#タグ</a> <a href="http://misskey.local/@mention" class="u-url mention">@mention</a> 🍊 :emoji: <a href="https://example.com">https://example.com</a></p>', 'content');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Provide _misskey_content for MFM', () => {
|
||||||
|
const note: MiNote = {
|
||||||
|
text: '$[tada foo]',
|
||||||
|
mentionedRemoteUsers: '[]',
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
|
||||||
|
|
||||||
|
assert.equal(noMisskeyContent, false, 'noMisskeyContent');
|
||||||
|
assert.equal(content, '<p><i>foo</i></p>', 'content');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -33,6 +33,12 @@ describe('MfmService', () => {
|
||||||
const output = '<p><span>foo<br>bar<br>baz</span></p>';
|
const output = '<p><span>foo<br>bar<br>baz</span></p>';
|
||||||
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
|
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Do not generate unnecessary span', () => {
|
||||||
|
const input = 'foo $[tada bar]';
|
||||||
|
const output = '<p>foo <i>bar</i></p>';
|
||||||
|
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fromHtml', () => {
|
describe('fromHtml', () => {
|
||||||
|
|
Loading…
Reference in a new issue