mirror of
https://codeberg.org/yeentown/barkey.git
synced 2025-07-07 20:44:34 +00:00
feat: render quote note with quote-inline
class for ap compatibility (#15818)
This commit is contained in:
parent
d5fe6e36ae
commit
f454e820bd
3 changed files with 31 additions and 11 deletions
|
@ -6,7 +6,7 @@
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as parse5 from 'parse5';
|
import * as parse5 from 'parse5';
|
||||||
import { Window, XMLSerializer } from 'happy-dom';
|
import { type Document, type HTMLParagraphElement, Window, XMLSerializer } from 'happy-dom';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { intersperse } from '@/misc/prelude/array.js';
|
import { intersperse } from '@/misc/prelude/array.js';
|
||||||
|
@ -23,6 +23,8 @@ type ChildNode = DefaultTreeAdapterMap['childNode'];
|
||||||
const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
|
const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
|
||||||
const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/;
|
const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/;
|
||||||
|
|
||||||
|
export type Appender = (document: Document, body: HTMLParagraphElement) => void;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MfmService {
|
export class MfmService {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -267,7 +269,7 @@ export class MfmService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) {
|
public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = [], additionalAppenders: Appender[] = []) {
|
||||||
if (nodes == null) {
|
if (nodes == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -492,6 +494,10 @@ export class MfmService {
|
||||||
|
|
||||||
appendChildren(nodes, body);
|
appendChildren(nodes, body);
|
||||||
|
|
||||||
|
for (const additionalAppender of additionalAppenders) {
|
||||||
|
additionalAppender(doc, body);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove the unnecessary namespace
|
// Remove the unnecessary namespace
|
||||||
const serialized = new XMLSerializer().serializeToString(body).replace(/^\s*<p xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">/, '<p>');
|
const serialized = new XMLSerializer().serializeToString(body).replace(/^\s*<p xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">/, '<p>');
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import * as mfm from 'mfm-js';
|
import * as mfm from 'mfm-js';
|
||||||
import { MfmService } from '@/core/MfmService.js';
|
import { MfmService, Appender } from '@/core/MfmService.js';
|
||||||
import type { MiNote } from '@/models/Note.js';
|
import type { MiNote } from '@/models/Note.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { extractApHashtagObjects } from './models/tag.js';
|
import { extractApHashtagObjects } from './models/tag.js';
|
||||||
|
@ -25,17 +25,17 @@ export class ApMfmService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public getNoteHtml(note: Pick<MiNote, 'text' | 'mentionedRemoteUsers'>, apAppend?: string) {
|
public getNoteHtml(note: Pick<MiNote, 'text' | 'mentionedRemoteUsers'>, additionalAppender: Appender[] = []) {
|
||||||
let noMisskeyContent = false;
|
let noMisskeyContent = false;
|
||||||
const srcMfm = (note.text ?? '') + (apAppend ?? '');
|
const srcMfm = (note.text ?? '');
|
||||||
|
|
||||||
const parsed = mfm.parse(srcMfm);
|
const parsed = mfm.parse(srcMfm);
|
||||||
|
|
||||||
if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) {
|
if (!additionalAppender.length && parsed.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) {
|
||||||
noMisskeyContent = true;
|
noMisskeyContent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = this.mfmService.toHtml(parsed, JSON.parse(note.mentionedRemoteUsers));
|
const content = this.mfmService.toHtml(parsed, JSON.parse(note.mentionedRemoteUsers), additionalAppender);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content,
|
content,
|
||||||
|
|
|
@ -19,7 +19,7 @@ import type { MiEmoji } from '@/models/Emoji.js';
|
||||||
import type { MiPoll } from '@/models/Poll.js';
|
import type { MiPoll } from '@/models/Poll.js';
|
||||||
import type { MiPollVote } from '@/models/PollVote.js';
|
import type { MiPollVote } from '@/models/PollVote.js';
|
||||||
import { UserKeypairService } from '@/core/UserKeypairService.js';
|
import { UserKeypairService } from '@/core/UserKeypairService.js';
|
||||||
import { MfmService } from '@/core/MfmService.js';
|
import { MfmService, type Appender } from '@/core/MfmService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
||||||
import type { MiUserKeypair } from '@/models/UserKeypair.js';
|
import type { MiUserKeypair } from '@/models/UserKeypair.js';
|
||||||
|
@ -430,10 +430,24 @@ export class ApRendererService {
|
||||||
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
let apAppend = '';
|
const apAppend: Appender[] = [];
|
||||||
|
|
||||||
if (quote) {
|
if (quote) {
|
||||||
apAppend += `\n\nRE: ${quote}`;
|
// Append quote link as `<br><br><span class="quote-inline">RE: <a href="...">...</a></span>`
|
||||||
|
// the claas name `quote-inline` is used in non-misskey clients for styling quote notes.
|
||||||
|
// For compatibility, the span part should be kept as possible.
|
||||||
|
apAppend.push((doc, body) => {
|
||||||
|
body.appendChild(doc.createElement('br'));
|
||||||
|
body.appendChild(doc.createElement('br'));
|
||||||
|
const span = doc.createElement('span');
|
||||||
|
span.className = 'quote-inline';
|
||||||
|
span.appendChild(doc.createTextNode('RE: '));
|
||||||
|
const link = doc.createElement('a');
|
||||||
|
link.setAttribute('href', quote);
|
||||||
|
link.textContent = quote;
|
||||||
|
span.appendChild(link);
|
||||||
|
body.appendChild(span);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
|
const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
|
||||||
|
|
Loading…
Add table
Reference in a new issue