refactor (backend): port get-note-summary to backend-rs
I removed trim() as it wasn't strictly neccessary
This commit is contained in:
parent
8337863ed3
commit
30969ad817
9 changed files with 123 additions and 71 deletions
13
packages/backend-rs/index.d.ts
vendored
13
packages/backend-rs/index.d.ts
vendored
|
@ -128,7 +128,8 @@ export interface Acct {
|
|||
}
|
||||
export function stringToAcct(acct: string): Acct
|
||||
export function acctToString(acct: Acct): string
|
||||
export interface NoteLike {
|
||||
/** TODO: handle name collisions better */
|
||||
export interface NoteLikeForCheckWordMute {
|
||||
fileIds: Array<string>
|
||||
userId: string | null
|
||||
text: string | null
|
||||
|
@ -136,7 +137,7 @@ export interface NoteLike {
|
|||
renoteId: string | null
|
||||
replyId: string | null
|
||||
}
|
||||
export function checkWordMute(note: NoteLike, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
export function checkWordMute(note: NoteLikeForCheckWordMute, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
export function getFullApAccount(username: string, host?: string | undefined | null): string
|
||||
export function isSelfHost(host?: string | undefined | null): boolean
|
||||
export function isSameOrigin(uri: string): boolean
|
||||
|
@ -147,6 +148,14 @@ export function sqlLikeEscape(src: string): string
|
|||
export function safeForSql(src: string): boolean
|
||||
/** Convert milliseconds to a human readable string */
|
||||
export function formatMilliseconds(milliseconds: number): string
|
||||
/** TODO: handle name collisions better */
|
||||
export interface NoteLikeForGetNoteSummary {
|
||||
fileIds: Array<string>
|
||||
text: string | null
|
||||
cw: string | null
|
||||
hasPoll: boolean
|
||||
}
|
||||
export function getNoteSummary(note: NoteLikeForGetNoteSummary): string
|
||||
export function toMastodonId(firefishId: string): string | null
|
||||
export function fromMastodonId(mastodonId: string): string | null
|
||||
export function fetchMeta(useCache: boolean): Promise<Meta>
|
||||
|
|
|
@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { readEnvironmentConfig, readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding
|
||||
const { readEnvironmentConfig, readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding
|
||||
|
||||
module.exports.readEnvironmentConfig = readEnvironmentConfig
|
||||
module.exports.readServerConfig = readServerConfig
|
||||
|
@ -326,6 +326,7 @@ module.exports.isUnicodeEmoji = isUnicodeEmoji
|
|||
module.exports.sqlLikeEscape = sqlLikeEscape
|
||||
module.exports.safeForSql = safeForSql
|
||||
module.exports.formatMilliseconds = formatMilliseconds
|
||||
module.exports.getNoteSummary = getNoteSummary
|
||||
module.exports.toMastodonId = toMastodonId
|
||||
module.exports.fromMastodonId = fromMastodonId
|
||||
module.exports.fetchMeta = fetchMeta
|
||||
|
|
|
@ -4,7 +4,8 @@ use once_cell::sync::Lazy;
|
|||
use regex::Regex;
|
||||
use sea_orm::{prelude::*, QuerySelect};
|
||||
|
||||
#[crate::export(object)]
|
||||
/// TODO: handle name collisions better
|
||||
#[crate::export(object, js_name = "NoteLikeForCheckWordMute")]
|
||||
pub struct NoteLike {
|
||||
pub file_ids: Vec<String>,
|
||||
pub user_id: Option<String>,
|
||||
|
|
90
packages/backend-rs/src/misc/get_note_summary.rs
Normal file
90
packages/backend-rs/src/misc/get_note_summary.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
/// TODO: handle name collisions better
|
||||
#[crate::export(object, js_name = "NoteLikeForGetNoteSummary")]
|
||||
pub struct NoteLike {
|
||||
pub file_ids: Vec<String>,
|
||||
pub text: Option<String>,
|
||||
pub cw: Option<String>,
|
||||
pub has_poll: bool,
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
pub fn get_note_summary(note: NoteLike) -> String {
|
||||
let mut buf: Vec<String> = vec![];
|
||||
|
||||
if let Some(cw) = note.cw {
|
||||
buf.push(cw)
|
||||
} else if let Some(text) = note.text {
|
||||
buf.push(text)
|
||||
}
|
||||
|
||||
match note.file_ids.len() {
|
||||
0 => (),
|
||||
1 => buf.push("📎".to_string()),
|
||||
n => buf.push(format!("📎 ({})", n)),
|
||||
};
|
||||
|
||||
if note.has_poll {
|
||||
buf.push("📊".to_string())
|
||||
}
|
||||
|
||||
buf.join(" ")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{get_note_summary, NoteLike};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_note_summary() {
|
||||
let note = NoteLike {
|
||||
file_ids: vec![],
|
||||
text: Some("Hello world!".to_string()),
|
||||
cw: None,
|
||||
has_poll: false,
|
||||
};
|
||||
assert_eq!(get_note_summary(note), "Hello world!");
|
||||
|
||||
let note_with_cw = NoteLike {
|
||||
file_ids: vec![],
|
||||
text: Some("Hello world!".to_string()),
|
||||
cw: Some("Content warning".to_string()),
|
||||
has_poll: false,
|
||||
};
|
||||
assert_eq!(get_note_summary(note_with_cw), "Content warning");
|
||||
|
||||
let note_with_file_and_cw = NoteLike {
|
||||
file_ids: vec!["9s7fmcqogiq4igin".to_string()],
|
||||
text: None,
|
||||
cw: Some("Selfie, no ec".to_string()),
|
||||
has_poll: false,
|
||||
};
|
||||
assert_eq!(get_note_summary(note_with_file_and_cw), "Selfie, no ec 📎");
|
||||
|
||||
let note_with_files_only = NoteLike {
|
||||
file_ids: vec![
|
||||
"9s7fmcqogiq4igin".to_string(),
|
||||
"9s7qrld5u14cey98".to_string(),
|
||||
"9s7gebs5zgts4kca".to_string(),
|
||||
"9s5z3e4vefqd29ee".to_string(),
|
||||
],
|
||||
text: None,
|
||||
cw: None,
|
||||
has_poll: false,
|
||||
};
|
||||
assert_eq!(get_note_summary(note_with_files_only), "📎 (4)");
|
||||
|
||||
let note_all = NoteLike {
|
||||
file_ids: vec![
|
||||
"9s7fmcqogiq4igin".to_string(),
|
||||
"9s7qrld5u14cey98".to_string(),
|
||||
"9s7gebs5zgts4kca".to_string(),
|
||||
"9s5z3e4vefqd29ee".to_string(),
|
||||
],
|
||||
text: Some("Hello world!".to_string()),
|
||||
cw: Some("Content warning".to_string()),
|
||||
has_poll: true,
|
||||
};
|
||||
assert_eq!(get_note_summary(note_all), "Content warning 📎 (4) 📊");
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ pub mod convert_host;
|
|||
pub mod emoji;
|
||||
pub mod escape_sql;
|
||||
pub mod format_milliseconds;
|
||||
pub mod get_note_summary;
|
||||
pub mod mastodon_id;
|
||||
pub mod meta;
|
||||
pub mod nyaify;
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
import type { Packed } from "./schema.js";
|
||||
|
||||
/**
|
||||
* 投稿を表す文字列を取得します。
|
||||
* @param {*} note (packされた)投稿
|
||||
*/
|
||||
export const getNoteSummary = (note: Packed<"Note">): string => {
|
||||
if (note.deletedAt) {
|
||||
return "❌";
|
||||
}
|
||||
|
||||
let summary = "";
|
||||
|
||||
// 本文
|
||||
if (note.cw != null) {
|
||||
summary += note.cw;
|
||||
} else {
|
||||
summary += note.text ? note.text : "";
|
||||
}
|
||||
|
||||
// ファイルが添付されているとき
|
||||
if ((note.files || []).length !== 0) {
|
||||
const len = note.files?.length;
|
||||
summary += ` 📎${len !== 1 ? ` (${len})` : ""}`;
|
||||
}
|
||||
|
||||
// 投票が添付されているとき
|
||||
if (note.poll) {
|
||||
summary += " 📊";
|
||||
}
|
||||
|
||||
/*
|
||||
// 返信のとき
|
||||
if (note.replyId) {
|
||||
if (note.reply) {
|
||||
summary += `\n\nRE: ${getNoteSummary(note.reply)}`;
|
||||
} else {
|
||||
summary += '\n\nRE: ...';
|
||||
}
|
||||
}
|
||||
|
||||
// Renoteのとき
|
||||
if (note.renoteId) {
|
||||
if (note.renote) {
|
||||
summary += `\n\nRN: ${getNoteSummary(note.renote)}`;
|
||||
} else {
|
||||
summary += '\n\nRN: ...';
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return summary.trim();
|
||||
};
|
|
@ -28,7 +28,7 @@ export const packedNoteSchema = {
|
|||
},
|
||||
cw: {
|
||||
type: "string",
|
||||
optional: true,
|
||||
optional: false,
|
||||
nullable: true,
|
||||
},
|
||||
userId: {
|
||||
|
@ -98,7 +98,7 @@ export const packedNoteSchema = {
|
|||
},
|
||||
fileIds: {
|
||||
type: "array",
|
||||
optional: true,
|
||||
optional: false,
|
||||
nullable: false,
|
||||
items: {
|
||||
type: "string",
|
||||
|
@ -128,6 +128,11 @@ export const packedNoteSchema = {
|
|||
nullable: false,
|
||||
},
|
||||
},
|
||||
hasPoll: {
|
||||
type: "boolean",
|
||||
optional: false,
|
||||
nullable: false,
|
||||
},
|
||||
poll: {
|
||||
type: "object",
|
||||
optional: true,
|
||||
|
|
|
@ -27,8 +27,7 @@ import {
|
|||
Emojis,
|
||||
GalleryPosts,
|
||||
} from "@/models/index.js";
|
||||
import { stringToAcct } from "backend-rs";
|
||||
import { getNoteSummary } from "@/misc/get-note-summary.js";
|
||||
import { getNoteSummary, stringToAcct } from "backend-rs";
|
||||
import { queues } from "@/queue/queues.js";
|
||||
import { genOpenapiSpec } from "../api/openapi/gen-spec.js";
|
||||
import { urlPreviewHandler } from "./url-preview.js";
|
||||
|
@ -517,8 +516,8 @@ router.get("/notes/:note", async (ctx, next) => {
|
|||
});
|
||||
|
||||
try {
|
||||
if (note) {
|
||||
const _note = await Notes.pack(note);
|
||||
if (note != null) {
|
||||
const packedNote = await Notes.pack(note);
|
||||
|
||||
const profile = await UserProfiles.findOneByOrFail({
|
||||
userId: note.userId,
|
||||
|
@ -526,13 +525,13 @@ router.get("/notes/:note", async (ctx, next) => {
|
|||
const meta = await fetchMeta(true);
|
||||
await ctx.render("note", {
|
||||
...metaToPugArgs(meta),
|
||||
note: _note,
|
||||
note: packedNote,
|
||||
profile,
|
||||
avatarUrl: await Users.getAvatarUrl(
|
||||
await Users.findOneByOrFail({ id: note.userId }),
|
||||
),
|
||||
// TODO: Let locale changeable by instance setting
|
||||
summary: getNoteSummary(_note),
|
||||
summary: getNoteSummary(note),
|
||||
});
|
||||
|
||||
ctx.set("Cache-Control", "public, max-age=15");
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import push from "web-push";
|
||||
import config from "@/config/index.js";
|
||||
import { SwSubscriptions } from "@/models/index.js";
|
||||
import { fetchMeta } from "backend-rs";
|
||||
import { fetchMeta, getNoteSummary } from "backend-rs";
|
||||
import type { Packed } from "@/misc/schema.js";
|
||||
import { getNoteSummary } from "@/misc/get-note-summary.js";
|
||||
|
||||
// Defined also packages/sw/types.ts#L14-L21
|
||||
type pushNotificationsTypes = {
|
||||
|
@ -17,15 +16,15 @@ type pushNotificationsTypes = {
|
|||
|
||||
// プッシュメッセージサーバーには文字数制限があるため、内容を削減します
|
||||
function truncateNotification(notification: Packed<"Notification">): any {
|
||||
if (notification.note) {
|
||||
if (notification.note != null) {
|
||||
return {
|
||||
...notification,
|
||||
note: {
|
||||
...notification.note,
|
||||
// textをgetNoteSummaryしたものに置き換える
|
||||
// replace the text with summary
|
||||
text: getNoteSummary(
|
||||
notification.type === "renote"
|
||||
? (notification.note.renote as Packed<"Note">)
|
||||
notification.type === "renote" && notification.note.renote != null
|
||||
? notification.note.renote
|
||||
: notification.note,
|
||||
),
|
||||
|
||||
|
|
Loading…
Reference in a new issue