Merge branch 'develop' into refactor/push-notification
This commit is contained in:
commit
56f06bae56
|
@ -224,6 +224,7 @@ dependencies = [
|
|||
"napi-derive",
|
||||
"nom-exif",
|
||||
"once_cell",
|
||||
"openssl",
|
||||
"pretty_assertions",
|
||||
"rand",
|
||||
"redis",
|
||||
|
@ -1823,9 +1824,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
|||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
|
@ -2207,6 +2208,15 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.2.3+3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.102"
|
||||
|
@ -2215,6 +2225,7 @@ checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
|||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
@ -2290,9 +2301,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
|
@ -2300,15 +2311,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.9"
|
||||
version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.5.1",
|
||||
"smallvec",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2766,6 +2777,15 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.4"
|
||||
|
@ -4435,7 +4455,7 @@ version = "1.5.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.4.1",
|
||||
"wasite",
|
||||
]
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ idna = "0.5.0"
|
|||
image = "0.25.1"
|
||||
nom-exif = "1.2.0"
|
||||
once_cell = "1.19.0"
|
||||
openssl = "0.10.64"
|
||||
pretty_assertions = "1.4.0"
|
||||
proc-macro2 = "1.0.79"
|
||||
quote = "1.0.36"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
BEGIN;
|
||||
|
||||
DELETE FROM "migrations" WHERE name IN (
|
||||
'AlterAkaType1714099399879',
|
||||
'AddDriveFileUsage1713451569342',
|
||||
'ConvertCwVarcharToText1713225866247',
|
||||
'FixChatFileConstraint1712855579316',
|
||||
|
@ -24,6 +25,13 @@ DELETE FROM "migrations" WHERE name IN (
|
|||
'RemoveNativeUtilsMigration1705877093218'
|
||||
);
|
||||
|
||||
-- alter-aka-type
|
||||
ALTER TABLE "user" RENAME COLUMN "alsoKnownAs" TO "alsoKnownAsOld";
|
||||
ALTER TABLE "user" ADD COLUMN "alsoKnownAs" text;
|
||||
UPDATE "user" SET "alsoKnownAs" = array_to_string("alsoKnownAsOld", ',');
|
||||
COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too';
|
||||
ALTER TABLE "user" DROP COLUMN "alsoKnownAsOld";
|
||||
|
||||
-- AddDriveFileUsage
|
||||
ALTER TABLE "drive_file" DROP COLUMN "usageHint";
|
||||
DROP TYPE "drive_file_usage_hint_enum";
|
||||
|
|
|
@ -19,7 +19,7 @@ deleteAndEditConfirm: Сигурни ли сте, че искате да изт
|
|||
copyUsername: Копиране на потребителското име
|
||||
searchUser: Търсене на потребител
|
||||
reply: Отговор
|
||||
showMore: Покажи още
|
||||
showMore: Показване на повече
|
||||
loadMore: Зареди още
|
||||
followRequestAccepted: Заявката за последване е приета
|
||||
importAndExport: Импорт/експорт на данни
|
||||
|
@ -336,6 +336,10 @@ _pages:
|
|||
title: Заглавие
|
||||
my: Моите страници
|
||||
pageSetting: Настройки на страницата
|
||||
url: Адрес на страницата
|
||||
summary: Кратко обобщение
|
||||
alignCenter: Центриране на елементите
|
||||
variables: Променливи
|
||||
_deck:
|
||||
_columns:
|
||||
notifications: Известия
|
||||
|
@ -398,7 +402,7 @@ sendMessage: Изпращане на съобщение
|
|||
jumpToPrevious: Премини към предишно
|
||||
newer: по-ново
|
||||
older: по-старо
|
||||
showLess: Покажи по-малко
|
||||
showLess: Показване на по-малко
|
||||
youGotNewFollower: те последва
|
||||
receiveFollowRequest: Заявка за последване получена
|
||||
mention: Споменаване
|
||||
|
@ -754,7 +758,7 @@ _feeds:
|
|||
general: Общи
|
||||
metadata: Метаданни
|
||||
disk: Диск
|
||||
featured: Препоръчани
|
||||
featured: Препоръчано
|
||||
yearsOld: на {age} години
|
||||
reload: Опресняване
|
||||
invites: Покани
|
||||
|
@ -940,3 +944,11 @@ showGapBetweenNotesInTimeline: Показване на празнина межд
|
|||
lookup: Поглеждане
|
||||
media: Мултимедия
|
||||
welcomeBackWithName: Добре дошли отново, {name}
|
||||
reduceUiAnimation: Намаляване на UI анимациите
|
||||
clickToFinishEmailVerification: Моля, натиснете [{ok}], за да завършите потвърждаването
|
||||
на ел. поща.
|
||||
_cw:
|
||||
show: Показване на съдържанието
|
||||
remoteFollow: Отдалечено последване
|
||||
messagingUnencryptedInfo: Чатовете във Firefish не са шифровани от край до край. Не
|
||||
споделяйте чувствителна информация през Firefish.
|
||||
|
|
|
@ -1159,9 +1159,9 @@ addRe: "Add \"re:\" at the beginning of comment in reply to a post with a conten
|
|||
confirm: "Confirm"
|
||||
importZip: "Import ZIP"
|
||||
exportZip: "Export ZIP"
|
||||
getQrCode: "Get QR code"
|
||||
getQrCode: "Show QR code"
|
||||
remoteFollow: "Remote follow"
|
||||
remoteFollowUrl: "Remote follow URL"
|
||||
copyRemoteFollowUrl: "Copy remote follow URL"
|
||||
emojiPackCreator: "Emoji pack creator"
|
||||
indexable: "Indexable"
|
||||
indexableDescription: "Allow built-in search to show your public posts"
|
||||
|
|
|
@ -1978,7 +1978,7 @@ importZip: 导入 ZIP
|
|||
exportZip: 导出 ZIP
|
||||
getQrCode: "获取二维码"
|
||||
remoteFollow: "远程关注"
|
||||
remoteFollowUrl: "远程关注 URL"
|
||||
copyRemoteFollowUrl: "复制远程关注 URL"
|
||||
emojiPackCreator: 表情包创建工具
|
||||
objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com/<bucket>/" 而非 "<bucket>.s3.amazonaws.com"
|
||||
的端点 URL。
|
||||
|
|
|
@ -27,6 +27,7 @@ idna = { workspace = true }
|
|||
image = { workspace = true }
|
||||
nom-exif = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
openssl = { workspace = true, features = ["vendored"] }
|
||||
rand = { workspace = true }
|
||||
redis = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
|
|
|
@ -1017,10 +1017,10 @@ export interface User {
|
|||
isDeleted: boolean
|
||||
driveCapacityOverrideMb: number | null
|
||||
movedToUri: string | null
|
||||
alsoKnownAs: string | null
|
||||
speakAsCat: boolean
|
||||
emojiModPerm: UserEmojimodpermEnum
|
||||
isIndexable: boolean
|
||||
alsoKnownAs: Array<string> | null
|
||||
}
|
||||
export interface UserGroup {
|
||||
id: string
|
||||
|
@ -1158,6 +1158,7 @@ export enum PushNotificationKind {
|
|||
ReadAllNotifications = 'readAllNotifications'
|
||||
}
|
||||
export function sendPushNotification(receiverUserId: string, kind: PushNotificationKind, content: any): Promise<void>
|
||||
export function publishToChannelStream(channelId: string, userId: string): void
|
||||
export enum ChatEvent {
|
||||
Message = 'message',
|
||||
Read = 'read',
|
||||
|
@ -1165,6 +1166,30 @@ export enum ChatEvent {
|
|||
Typing = 'typing'
|
||||
}
|
||||
export function publishToChatStream(senderUserId: string, receiverUserId: string, kind: ChatEvent, object: any): void
|
||||
export enum ChatIndexEvent {
|
||||
Message = 'message',
|
||||
Read = 'read'
|
||||
}
|
||||
export function publishToChatIndexStream(userId: string, kind: ChatIndexEvent, object: any): void
|
||||
export interface PackedEmoji {
|
||||
id: string
|
||||
aliases: Array<string>
|
||||
name: string
|
||||
category: string | null
|
||||
host: string | null
|
||||
url: string
|
||||
license: string | null
|
||||
width: number | null
|
||||
height: number | null
|
||||
}
|
||||
export function publishToBroadcastStream(emoji: PackedEmoji): void
|
||||
export interface AbuseUserReportLike {
|
||||
id: string
|
||||
targetUserId: string
|
||||
reporterId: string
|
||||
comment: string
|
||||
}
|
||||
export function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): void
|
||||
export function getTimestamp(id: string): number
|
||||
/**
|
||||
* The generated ID results in the form of `[8 chars timestamp] + [cuid2]`.
|
||||
|
|
|
@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, ChatEvent, publishToChatStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding
|
||||
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding
|
||||
|
||||
module.exports.SECOND = SECOND
|
||||
module.exports.MINUTE = MINUTE
|
||||
|
@ -367,8 +367,13 @@ module.exports.watchNote = watchNote
|
|||
module.exports.unwatchNote = unwatchNote
|
||||
module.exports.PushNotificationKind = PushNotificationKind
|
||||
module.exports.sendPushNotification = sendPushNotification
|
||||
module.exports.publishToChannelStream = publishToChannelStream
|
||||
module.exports.ChatEvent = ChatEvent
|
||||
module.exports.publishToChatStream = publishToChatStream
|
||||
module.exports.ChatIndexEvent = ChatIndexEvent
|
||||
module.exports.publishToChatIndexStream = publishToChatIndexStream
|
||||
module.exports.publishToBroadcastStream = publishToBroadcastStream
|
||||
module.exports.publishToModerationStream = publishToModerationStream
|
||||
module.exports.getTimestamp = getTimestamp
|
||||
module.exports.genId = genId
|
||||
module.exports.genIdAt = genIdAt
|
||||
|
|
|
@ -71,14 +71,14 @@ pub struct Model {
|
|||
pub drive_capacity_override_mb: Option<i32>,
|
||||
#[sea_orm(column_name = "movedToUri")]
|
||||
pub moved_to_uri: Option<String>,
|
||||
#[sea_orm(column_name = "alsoKnownAs", column_type = "Text", nullable)]
|
||||
pub also_known_as: Option<String>,
|
||||
#[sea_orm(column_name = "speakAsCat")]
|
||||
pub speak_as_cat: bool,
|
||||
#[sea_orm(column_name = "emojiModPerm")]
|
||||
pub emoji_mod_perm: UserEmojimodpermEnum,
|
||||
#[sea_orm(column_name = "isIndexable")]
|
||||
pub is_indexable: bool,
|
||||
#[sea_orm(column_name = "alsoKnownAs")]
|
||||
pub also_known_as: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
@ -13,7 +13,7 @@ pub fn initialize_logger() {
|
|||
"info" => Level::INFO,
|
||||
"debug" => Level::DEBUG,
|
||||
"trace" => Level::TRACE,
|
||||
_ => Level::INFO,
|
||||
_ => Level::INFO, // Fallback
|
||||
});
|
||||
} else if let Some(levels) = &CONFIG.log_level {
|
||||
// `logLevel` config is Deprecated
|
||||
|
@ -27,13 +27,25 @@ pub fn initialize_logger() {
|
|||
builder = builder.with_max_level(Level::WARN);
|
||||
} else if levels.contains(&"error".to_string()) {
|
||||
builder = builder.with_max_level(Level::ERROR);
|
||||
} else {
|
||||
// Fallback
|
||||
builder = builder.with_max_level(Level::INFO);
|
||||
}
|
||||
} else {
|
||||
// Fallback
|
||||
builder = builder.with_max_level(Level::INFO);
|
||||
};
|
||||
|
||||
let subscriber = builder.with_level(true).pretty().finish();
|
||||
let subscriber = builder
|
||||
.without_time()
|
||||
.with_level(true)
|
||||
.with_ansi(true)
|
||||
.with_target(true)
|
||||
.with_thread_names(true)
|
||||
.with_line_number(true)
|
||||
.log_internal_errors(true)
|
||||
.compact()
|
||||
.finish();
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber).expect("Failed to initialize the logger");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
pub mod antenna;
|
||||
pub mod channel;
|
||||
pub mod chat;
|
||||
pub mod chat_index;
|
||||
pub mod custom_emoji;
|
||||
pub mod moderation;
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use crate::database::redis_conn;
|
||||
|
@ -10,9 +14,9 @@ pub enum Stream {
|
|||
#[strum(serialize = "internal")]
|
||||
Internal,
|
||||
#[strum(serialize = "broadcast")]
|
||||
Broadcast,
|
||||
#[strum(to_string = "adminStream:{user_id}")]
|
||||
Admin { user_id: String },
|
||||
CustomEmoji,
|
||||
#[strum(to_string = "adminStream:{moderator_id}")]
|
||||
Moderation { moderator_id: String },
|
||||
#[strum(to_string = "user:{user_id}")]
|
||||
User { user_id: String },
|
||||
#[strum(to_string = "channelStream:{channel_id}")]
|
||||
|
@ -37,7 +41,7 @@ pub enum Stream {
|
|||
#[strum(to_string = "messagingStream:{group_id}")]
|
||||
GroupChat { group_id: String },
|
||||
#[strum(to_string = "messagingIndexStream:{user_id}")]
|
||||
MessagingIndex { user_id: String },
|
||||
ChatIndex { user_id: String },
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -57,7 +61,7 @@ pub fn publish_to_stream(
|
|||
) -> Result<(), Error> {
|
||||
let message = if let Some(kind) = kind {
|
||||
format!(
|
||||
"{{ \"type\": \"{}\", \"body\": {} }}",
|
||||
"{{\"type\":\"{}\",\"body\":{}}}",
|
||||
kind,
|
||||
value.unwrap_or("null".to_string()),
|
||||
)
|
||||
|
@ -67,10 +71,7 @@ pub fn publish_to_stream(
|
|||
|
||||
redis_conn()?.publish(
|
||||
&CONFIG.host,
|
||||
format!(
|
||||
"{{ \"channel\": \"{}\", \"message\": {} }}",
|
||||
stream, message,
|
||||
),
|
||||
format!("{{\"channel\":\"{}\",\"message\":{}}}", stream, message),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -84,10 +85,10 @@ mod unit_test {
|
|||
#[test]
|
||||
fn channel_to_string() {
|
||||
assert_eq!(Stream::Internal.to_string(), "internal");
|
||||
assert_eq!(Stream::Broadcast.to_string(), "broadcast");
|
||||
assert_eq!(Stream::CustomEmoji.to_string(), "broadcast");
|
||||
assert_eq!(
|
||||
Stream::Admin {
|
||||
user_id: "9tb42br63g5apjcq".to_string()
|
||||
Stream::Moderation {
|
||||
moderator_id: "9tb42br63g5apjcq".to_string()
|
||||
}
|
||||
.to_string(),
|
||||
"adminStream:9tb42br63g5apjcq"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
use crate::service::stream::{publish_to_stream, Error, Stream};
|
||||
|
||||
#[crate::export(js_name = "publishToChannelStream")]
|
||||
pub fn publish(channel_id: String, user_id: String) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::Channel { channel_id },
|
||||
Some("typing".to_string()),
|
||||
Some(format!("\"{}\"", user_id)),
|
||||
)
|
||||
}
|
|
@ -13,12 +13,15 @@ pub enum ChatEvent {
|
|||
Typing,
|
||||
}
|
||||
|
||||
// We want to merge `kind` and `object` into a single enum
|
||||
// https://github.com/napi-rs/napi-rs/issues/2036
|
||||
|
||||
#[crate::export(js_name = "publishToChatStream")]
|
||||
pub fn publish(
|
||||
sender_user_id: String,
|
||||
receiver_user_id: String,
|
||||
kind: ChatEvent,
|
||||
object: &serde_json::Value, // TODO?: change this to enum
|
||||
object: &serde_json::Value,
|
||||
) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::Chat {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
use crate::service::stream::{publish_to_stream, Error, Stream};
|
||||
|
||||
#[derive(strum::Display)]
|
||||
#[crate::export(string_enum = "camelCase")]
|
||||
pub enum ChatIndexEvent {
|
||||
#[strum(serialize = "message")]
|
||||
Message,
|
||||
#[strum(serialize = "read")]
|
||||
Read,
|
||||
}
|
||||
|
||||
// We want to merge `kind` and `object` into a single enum
|
||||
// https://github.com/napi-rs/napi-rs/issues/2036
|
||||
|
||||
#[crate::export(js_name = "publishToChatIndexStream")]
|
||||
pub fn publish(
|
||||
user_id: String,
|
||||
kind: ChatIndexEvent,
|
||||
object: &serde_json::Value,
|
||||
) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::ChatIndex { user_id },
|
||||
Some(kind.to_string()),
|
||||
Some(serde_json::to_string(object)?),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
use crate::service::stream::{publish_to_stream, Error, Stream};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// TODO: define schema type in other place
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object)]
|
||||
pub struct PackedEmoji {
|
||||
pub id: String,
|
||||
pub aliases: Vec<String>,
|
||||
pub name: String,
|
||||
pub category: Option<String>,
|
||||
pub host: Option<String>,
|
||||
pub url: String,
|
||||
pub license: Option<String>,
|
||||
pub width: Option<i32>,
|
||||
pub height: Option<i32>,
|
||||
}
|
||||
|
||||
#[crate::export(js_name = "publishToBroadcastStream")]
|
||||
pub fn publish(emoji: &PackedEmoji) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::CustomEmoji,
|
||||
Some("emojiAdded".to_string()),
|
||||
Some(format!("{{\"emoji\":{}}}", serde_json::to_string(emoji)?)),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
use crate::service::stream::{publish_to_stream, Error, Stream};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[crate::export(object)]
|
||||
pub struct AbuseUserReportLike {
|
||||
pub id: String,
|
||||
pub target_user_id: String,
|
||||
pub reporter_id: String,
|
||||
pub comment: String,
|
||||
}
|
||||
|
||||
#[crate::export(js_name = "publishToModerationStream")]
|
||||
pub fn publish(moderator_id: String, report: &AbuseUserReportLike) -> Result<(), Error> {
|
||||
publish_to_stream(
|
||||
&Stream::Moderation { moderator_id },
|
||||
Some("newAbuseUserReport".to_string()),
|
||||
Some(serde_json::to_string(report)?),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class AlterAkaType1714099399879 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" RENAME COLUMN "alsoKnownAs" TO "alsoKnownAsOld"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ADD COLUMN "alsoKnownAs" character varying(512)[]`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "user" SET "alsoKnownAs" = string_to_array("alsoKnownAsOld", ',')::character varying[]`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "user" SET "alsoKnownAs" = NULL WHERE "alsoKnownAs" = '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'`,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "alsoKnownAsOld"`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" RENAME COLUMN "alsoKnownAs" TO "alsoKnownAsOld"`,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "user" ADD COLUMN "alsoKnownAs" text`);
|
||||
await queryRunner.query(
|
||||
`UPDATE "user" SET "alsoKnownAs" = array_to_string("alsoKnownAsOld", ',')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'`,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "alsoKnownAsOld"`);
|
||||
}
|
||||
}
|
|
@ -88,7 +88,9 @@ export class User {
|
|||
})
|
||||
public movedToUri: string | null;
|
||||
|
||||
@Column("simple-array", {
|
||||
@Column("varchar", {
|
||||
length: 512,
|
||||
array: true,
|
||||
nullable: true,
|
||||
comment: "URIs the user is known as too",
|
||||
})
|
||||
|
|
|
@ -2,9 +2,14 @@ import {
|
|||
publishMainStream,
|
||||
publishGroupMessagingStream,
|
||||
} from "@/services/stream.js";
|
||||
import { publishToChatStream, ChatEvent } from "backend-rs";
|
||||
import { publishMessagingIndexStream } from "@/services/stream.js";
|
||||
import { sendPushNotification, PushNotificationKind } from "backend-rs";
|
||||
import {
|
||||
publishToChatStream,
|
||||
publishToChatIndexStream,
|
||||
sendPushNotification,
|
||||
ChatEvent,
|
||||
ChatIndexEvent,
|
||||
PushNotificationKind,
|
||||
} from "backend-rs";
|
||||
import type { User, IRemoteUser } from "@/models/entities/user.js";
|
||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
||||
import { MessagingMessages, UserGroupJoinings, Users } from "@/models/index.js";
|
||||
|
@ -55,7 +60,7 @@ export async function readUserMessagingMessage(
|
|||
|
||||
// Publish event
|
||||
publishToChatStream(otherpartyId, userId, ChatEvent.Read, messageIds);
|
||||
publishMessagingIndexStream(userId, "read", messageIds);
|
||||
publishToChatIndexStream(userId, ChatIndexEvent.Read, messageIds);
|
||||
|
||||
if (!(await Users.getHasUnreadMessagingMessage(userId))) {
|
||||
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
|
||||
|
@ -129,7 +134,7 @@ export async function readGroupMessagingMessage(
|
|||
ids: reads,
|
||||
userId: userId,
|
||||
});
|
||||
publishMessagingIndexStream(userId, "read", reads);
|
||||
publishToChatIndexStream(userId, ChatIndexEvent.Read, reads);
|
||||
|
||||
if (!(await Users.getHasUnreadMessagingMessage(userId))) {
|
||||
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Emojis, DriveFiles } from "@/models/index.js";
|
||||
import { type ImageSize, genId, getImageSizeFromUrl } from "backend-rs";
|
||||
import {
|
||||
type ImageSize,
|
||||
genId,
|
||||
getImageSizeFromUrl,
|
||||
publishToBroadcastStream,
|
||||
} from "backend-rs";
|
||||
import { insertModerationLog } from "@/services/insert-moderation-log.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import rndstr from "rndstr";
|
||||
import { publishBroadcastStream } from "@/services/stream.js";
|
||||
import { db } from "@/db/postgre.js";
|
||||
import { apiLogger } from "@/server/api/logger.js";
|
||||
import { inspect } from "node:util";
|
||||
|
@ -75,9 +79,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
|
||||
await db.queryResultCache!.remove(["meta_emojis"]);
|
||||
|
||||
publishBroadcastStream("emojiAdded", {
|
||||
emoji: await Emojis.pack(emoji.id),
|
||||
});
|
||||
publishToBroadcastStream(await Emojis.pack(emoji));
|
||||
|
||||
insertModerationLog(me, "addEmoji", {
|
||||
emojiId: emoji.id,
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import define from "@/server/api/define.js";
|
||||
import { Emojis } from "@/models/index.js";
|
||||
import { type ImageSize, genId, getImageSizeFromUrl } from "backend-rs";
|
||||
import {
|
||||
type ImageSize,
|
||||
genId,
|
||||
getImageSizeFromUrl,
|
||||
publishToBroadcastStream,
|
||||
} from "backend-rs";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
|
||||
import { publishBroadcastStream } from "@/services/stream.js";
|
||||
import { db } from "@/db/postgre.js";
|
||||
import { apiLogger } from "@/server/api/logger.js";
|
||||
import { inspect } from "node:util";
|
||||
|
@ -102,9 +106,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
|
||||
await db.queryResultCache!.remove(["meta_emojis"]);
|
||||
|
||||
publishBroadcastStream("emojiAdded", {
|
||||
emoji: await Emojis.pack(copied.id),
|
||||
});
|
||||
publishToBroadcastStream(await Emojis.pack(copied));
|
||||
|
||||
return {
|
||||
id: copied.id,
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import * as mfm from "mfm-js";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
import { publishAdminStream } from "@/services/stream.js";
|
||||
import { AbuseUserReports, UserProfiles, Users } from "@/models/index.js";
|
||||
import { genId } from "backend-rs";
|
||||
import { genId, publishToModerationStream } from "backend-rs";
|
||||
import { sendEmail } from "@/services/send-email.js";
|
||||
import { fetchMeta } from "backend-rs";
|
||||
import { getUser } from "@/server/api/common/getters.js";
|
||||
import { ApiError } from "@/server/api/error.js";
|
||||
import define from "@/server/api/define.js";
|
||||
|
@ -86,9 +84,8 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
],
|
||||
});
|
||||
|
||||
const meta = await fetchMeta(true);
|
||||
for (const moderator of moderators) {
|
||||
publishAdminStream(moderator.id, "newAbuseUserReport", {
|
||||
publishToModerationStream(moderator.id, {
|
||||
id: report.id,
|
||||
targetUserId: report.targetUserId,
|
||||
reporterId: report.reporterId,
|
||||
|
|
|
@ -14,11 +14,12 @@ import {
|
|||
} from "@/models/index.js";
|
||||
import type { AccessToken } from "@/models/entities/access-token.js";
|
||||
import type { UserProfile } from "@/models/entities/user-profile.js";
|
||||
import { publishGroupMessagingStream } from "@/services/stream.js";
|
||||
import {
|
||||
publishChannelStream,
|
||||
publishGroupMessagingStream,
|
||||
} from "@/services/stream.js";
|
||||
import { publishToChatStream, ChatEvent } from "backend-rs";
|
||||
publishToChannelStream,
|
||||
publishToChatStream,
|
||||
ChatEvent,
|
||||
} from "backend-rs";
|
||||
import type { UserGroup } from "@/models/entities/user-group.js";
|
||||
import type { Packed } from "@/misc/schema.js";
|
||||
import { readNotification } from "@/server/api/common/read-notification.js";
|
||||
|
@ -512,9 +513,9 @@ export default class Connection {
|
|||
}
|
||||
}
|
||||
|
||||
private typingOnChannel(channel: ChannelModel["id"]) {
|
||||
private typingOnChannel(channelId: ChannelModel["id"]) {
|
||||
if (this.user) {
|
||||
publishChannelStream(channel, "typing", this.user.id);
|
||||
publishToChannelStream(channelId, this.user.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,14 @@ import {
|
|||
genId,
|
||||
sendPushNotification,
|
||||
publishToChatStream,
|
||||
publishToChatIndexStream,
|
||||
toPuny,
|
||||
PushNotificationKind,
|
||||
ChatEvent,
|
||||
ChatIndexEvent,
|
||||
PushNotificationKind,
|
||||
} from "backend-rs";
|
||||
import type { MessagingMessage } from "@/models/entities/messaging-message.js";
|
||||
import {
|
||||
publishMessagingIndexStream,
|
||||
publishMainStream,
|
||||
publishGroupMessagingStream,
|
||||
} from "@/services/stream.js";
|
||||
|
@ -63,7 +64,11 @@ export async function createMessage(
|
|||
ChatEvent.Message,
|
||||
messageObj,
|
||||
);
|
||||
publishMessagingIndexStream(message.userId, "message", messageObj);
|
||||
publishToChatIndexStream(
|
||||
message.userId,
|
||||
ChatIndexEvent.Message,
|
||||
messageObj,
|
||||
);
|
||||
publishMainStream(message.userId, "messagingMessage", messageObj);
|
||||
}
|
||||
|
||||
|
@ -75,7 +80,11 @@ export async function createMessage(
|
|||
ChatEvent.Message,
|
||||
messageObj,
|
||||
);
|
||||
publishMessagingIndexStream(recipientUser.id, "message", messageObj);
|
||||
publishToChatIndexStream(
|
||||
recipientUser.id,
|
||||
ChatIndexEvent.Message,
|
||||
messageObj,
|
||||
);
|
||||
publishMainStream(recipientUser.id, "messagingMessage", messageObj);
|
||||
}
|
||||
} else if (recipientGroup) {
|
||||
|
@ -87,7 +96,11 @@ export async function createMessage(
|
|||
userGroupId: recipientGroup.id,
|
||||
});
|
||||
for (const joining of joinings) {
|
||||
publishMessagingIndexStream(joining.userId, "message", messageObj);
|
||||
publishToChatIndexStream(
|
||||
joining.userId,
|
||||
ChatIndexEvent.Message,
|
||||
messageObj,
|
||||
);
|
||||
publishMainStream(joining.userId, "messagingMessage", messageObj);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,18 +5,18 @@ import type { UserList } from "@/models/entities/user-list.js";
|
|||
import type { UserGroup } from "@/models/entities/user-group.js";
|
||||
import { config } from "@/config.js";
|
||||
// import type { Antenna } from "@/models/entities/antenna.js";
|
||||
import type { Channel } from "@/models/entities/channel.js";
|
||||
// import type { Channel } from "@/models/entities/channel.js";
|
||||
import type {
|
||||
StreamChannels,
|
||||
AdminStreamTypes,
|
||||
// AdminStreamTypes,
|
||||
// AntennaStreamTypes,
|
||||
BroadcastTypes,
|
||||
ChannelStreamTypes,
|
||||
// BroadcastTypes,
|
||||
// ChannelStreamTypes,
|
||||
DriveStreamTypes,
|
||||
GroupMessagingStreamTypes,
|
||||
InternalStreamTypes,
|
||||
MainStreamTypes,
|
||||
MessagingIndexStreamTypes,
|
||||
// MessagingIndexStreamTypes,
|
||||
// MessagingStreamTypes,
|
||||
NoteStreamTypes,
|
||||
UserListStreamTypes,
|
||||
|
@ -64,16 +64,17 @@ class Publisher {
|
|||
);
|
||||
};
|
||||
|
||||
public publishBroadcastStream = <K extends keyof BroadcastTypes>(
|
||||
type: K,
|
||||
value?: BroadcastTypes[K],
|
||||
): void => {
|
||||
this.publish(
|
||||
"broadcast",
|
||||
type,
|
||||
typeof value === "undefined" ? null : value,
|
||||
);
|
||||
};
|
||||
/* ported to backend-rs */
|
||||
// public publishBroadcastStream = <K extends keyof BroadcastTypes>(
|
||||
// type: K,
|
||||
// value?: BroadcastTypes[K],
|
||||
// ): void => {
|
||||
// this.publish(
|
||||
// "broadcast",
|
||||
// type,
|
||||
// typeof value === "undefined" ? null : value,
|
||||
// );
|
||||
// };
|
||||
|
||||
public publishMainStream = <K extends keyof MainStreamTypes>(
|
||||
userId: User["id"],
|
||||
|
@ -110,17 +111,18 @@ class Publisher {
|
|||
});
|
||||
};
|
||||
|
||||
public publishChannelStream = <K extends keyof ChannelStreamTypes>(
|
||||
channelId: Channel["id"],
|
||||
type: K,
|
||||
value?: ChannelStreamTypes[K],
|
||||
): void => {
|
||||
this.publish(
|
||||
`channelStream:${channelId}`,
|
||||
type,
|
||||
typeof value === "undefined" ? null : value,
|
||||
);
|
||||
};
|
||||
/* ported to backend-rs */
|
||||
// public publishChannelStream = <K extends keyof ChannelStreamTypes>(
|
||||
// channelId: Channel["id"],
|
||||
// type: K,
|
||||
// value?: ChannelStreamTypes[K],
|
||||
// ): void => {
|
||||
// this.publish(
|
||||
// `channelStream:${channelId}`,
|
||||
// type,
|
||||
// typeof value === "undefined" ? null : value,
|
||||
// );
|
||||
// };
|
||||
|
||||
public publishUserListStream = <K extends keyof UserListStreamTypes>(
|
||||
listId: UserList["id"],
|
||||
|
@ -175,35 +177,37 @@ class Publisher {
|
|||
);
|
||||
};
|
||||
|
||||
public publishMessagingIndexStream = <
|
||||
K extends keyof MessagingIndexStreamTypes,
|
||||
>(
|
||||
userId: User["id"],
|
||||
type: K,
|
||||
value?: MessagingIndexStreamTypes[K],
|
||||
): void => {
|
||||
this.publish(
|
||||
`messagingIndexStream:${userId}`,
|
||||
type,
|
||||
typeof value === "undefined" ? null : value,
|
||||
);
|
||||
};
|
||||
/* ported to backend-rs */
|
||||
// public publishMessagingIndexStream = <
|
||||
// K extends keyof MessagingIndexStreamTypes,
|
||||
// >(
|
||||
// userId: User["id"],
|
||||
// type: K,
|
||||
// value?: MessagingIndexStreamTypes[K],
|
||||
// ): void => {
|
||||
// this.publish(
|
||||
// `messagingIndexStream:${userId}`,
|
||||
// type,
|
||||
// typeof value === "undefined" ? null : value,
|
||||
// );
|
||||
// };
|
||||
|
||||
public publishNotesStream = (note: Note): void => {
|
||||
this.publish("notesStream", null, note);
|
||||
};
|
||||
|
||||
public publishAdminStream = <K extends keyof AdminStreamTypes>(
|
||||
userId: User["id"],
|
||||
type: K,
|
||||
value?: AdminStreamTypes[K],
|
||||
): void => {
|
||||
this.publish(
|
||||
`adminStream:${userId}`,
|
||||
type,
|
||||
typeof value === "undefined" ? null : value,
|
||||
);
|
||||
};
|
||||
/* ported to backend-rs */
|
||||
// public publishAdminStream = <K extends keyof AdminStreamTypes>(
|
||||
// userId: User["id"],
|
||||
// type: K,
|
||||
// value?: AdminStreamTypes[K],
|
||||
// ): void => {
|
||||
// this.publish(
|
||||
// `adminStream:${userId}`,
|
||||
// type,
|
||||
// typeof value === "undefined" ? null : value,
|
||||
// );
|
||||
// };
|
||||
}
|
||||
|
||||
const publisher = new Publisher();
|
||||
|
@ -212,17 +216,16 @@ export default publisher;
|
|||
|
||||
export const publishInternalEvent = publisher.publishInternalEvent;
|
||||
export const publishUserEvent = publisher.publishUserEvent;
|
||||
export const publishBroadcastStream = publisher.publishBroadcastStream;
|
||||
// export const publishBroadcastStream = publisher.publishBroadcastStream;
|
||||
export const publishMainStream = publisher.publishMainStream;
|
||||
export const publishDriveStream = publisher.publishDriveStream;
|
||||
export const publishNoteStream = publisher.publishNoteStream;
|
||||
export const publishNotesStream = publisher.publishNotesStream;
|
||||
export const publishChannelStream = publisher.publishChannelStream;
|
||||
// export const publishChannelStream = publisher.publishChannelStream;
|
||||
export const publishUserListStream = publisher.publishUserListStream;
|
||||
// export const publishAntennaStream = publisher.publishAntennaStream;
|
||||
// export const publishMessagingStream = publisher.publishMessagingStream;
|
||||
export const publishGroupMessagingStream =
|
||||
publisher.publishGroupMessagingStream;
|
||||
export const publishMessagingIndexStream =
|
||||
publisher.publishMessagingIndexStream;
|
||||
export const publishAdminStream = publisher.publishAdminStream;
|
||||
// export const publishMessagingIndexStream = publisher.publishMessagingIndexStream;
|
||||
// export const publishAdminStream = publisher.publishAdminStream;
|
||||
|
|
|
@ -113,6 +113,7 @@
|
|||
:detailed="true"
|
||||
:detailed-view="detailedView"
|
||||
:parent-id="appearNote.id"
|
||||
:is-long-judger="isLongJudger"
|
||||
@push="(e) => router.push(notePage(e))"
|
||||
@focusfooter="footerEl!.focus()"
|
||||
@expanded="(e) => setPostExpanded(e)"
|
||||
|
@ -325,6 +326,7 @@ const props = defineProps<{
|
|||
collapsedReply?: boolean;
|
||||
hideFooter?: boolean;
|
||||
hideEmojiViewer?: boolean;
|
||||
isLongJudger?: (note: entities.Note) => boolean;
|
||||
}>();
|
||||
|
||||
const inChannel = inject("inChannel", null);
|
||||
|
|
|
@ -60,12 +60,14 @@
|
|||
class="content"
|
||||
:note="removeReplyTo(notification.note.renote)"
|
||||
:hide-emoji-viewer="true"
|
||||
:is-long-judger="isLongJudger"
|
||||
/>
|
||||
<XNote
|
||||
v-else
|
||||
class="content"
|
||||
:note="removeReplyTo(notification.note)"
|
||||
:hide-emoji-viewer="true"
|
||||
:is-long-judger="isLongJudger"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -120,6 +122,15 @@ const userleft = ref(props.notification.users.length - users.value.length);
|
|||
let readObserver: IntersectionObserver | undefined;
|
||||
let connection: Connection<Channels["main"]> | null = null;
|
||||
|
||||
function isLongJudger(note: entities.Note) {
|
||||
return (
|
||||
note.text != null &&
|
||||
(note.text.split("\n").length > 5 ||
|
||||
note.text.length > 300 ||
|
||||
note.files.length > 4)
|
||||
);
|
||||
}
|
||||
|
||||
function getText() {
|
||||
let res = "";
|
||||
switch (props.notification.type) {
|
||||
|
|
|
@ -196,13 +196,26 @@ import { i18n } from "@/i18n";
|
|||
import { defaultStore } from "@/store";
|
||||
import icon from "@/scripts/icon";
|
||||
|
||||
const props = defineProps<{
|
||||
note: entities.Note;
|
||||
parentId?: string;
|
||||
conversation?: entities.Note[];
|
||||
detailed?: boolean;
|
||||
detailedView?: boolean;
|
||||
}>();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
note: entities.Note;
|
||||
parentId?: string;
|
||||
conversation?: entities.Note[];
|
||||
detailed?: boolean;
|
||||
detailedView?: boolean;
|
||||
isLongJudger?: (note: entities.Note) => boolean;
|
||||
}>(),
|
||||
{
|
||||
isLongJudger: (note: entities.Note) => {
|
||||
return (
|
||||
note.text != null &&
|
||||
(note.text.split("\n").length > 10 ||
|
||||
note.text.length > 800 ||
|
||||
note.files.length > 4)
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: "push", v): void;
|
||||
|
@ -216,10 +229,7 @@ const showMoreButton = ref<HTMLElement>();
|
|||
const isLong =
|
||||
!props.detailedView &&
|
||||
props.note.cw == null &&
|
||||
((props.note.text != null &&
|
||||
(props.note.text.split("\n").length > 10 ||
|
||||
props.note.text.length > 800)) ||
|
||||
props.note.files.length > 4);
|
||||
props.isLongJudger(props.note);
|
||||
const collapsed = ref(props.note.cw == null && isLong);
|
||||
const urls = props.note.text
|
||||
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
|
||||
|
|
|
@ -57,7 +57,8 @@ if (!remoteAccountId.result) {
|
|||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const subscribeUri = data.links.find(
|
||||
(link: { rel: string; }) => link.rel === "http://ostatus.org/schema/1.0/subscribe",
|
||||
(link: { rel: string }) =>
|
||||
link.rel === "http://ostatus.org/schema/1.0/subscribe",
|
||||
).template;
|
||||
window.location.href = subscribeUri.replace(
|
||||
"{uri}",
|
||||
|
|
|
@ -255,6 +255,27 @@ export function getUserMenu(user, router: Router = mainRouter) {
|
|||
router.push(`/user-info/${user.id}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: `${icon("ph-share")}`,
|
||||
text: i18n.ts.share,
|
||||
type: "parent",
|
||||
children: [
|
||||
{
|
||||
icon: "ph-qr-code ph-bold ph-lg",
|
||||
text: i18n.ts.getQrCode,
|
||||
action: () => {
|
||||
os.displayQrCode(`https://${host}/follow-me?acct=${user.username}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: `${icon("ph-hand-waving")}`,
|
||||
text: i18n.ts.copyRemoteFollowUrl,
|
||||
action: () => {
|
||||
copyToClipboard(`https://${host}/follow-me?acct=${user.username}`);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: `${icon("ph-newspaper")}`,
|
||||
text: i18n.ts._feeds.copyFeed,
|
||||
|
@ -281,13 +302,6 @@ export function getUserMenu(user, router: Router = mainRouter) {
|
|||
copyToClipboard(`https://${host}/@${user.username}.json`);
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: `${icon("ph-hand-waving")}`,
|
||||
text: i18n.ts.remoteFollowUrl,
|
||||
action: () => {
|
||||
copyToClipboard(`https://${host}/follow-me?acct=${user.username}`);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -306,13 +320,6 @@ export function getUserMenu(user, router: Router = mainRouter) {
|
|||
},
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
icon: "ph-qr-code ph-bold ph-lg",
|
||||
text: i18n.ts.getQrCode,
|
||||
action: () => {
|
||||
os.displayQrCode(`https://${host}/follow-me?acct=${user.username}`);
|
||||
},
|
||||
},
|
||||
isSignedIn(me) && me.id !== user.id
|
||||
? {
|
||||
type: "link",
|
||||
|
|
Loading…
Reference in New Issue