Merge branch 'develop' into iceshrimp_mastodon
This commit is contained in:
commit
c630187b2e
37 changed files with 336 additions and 97 deletions
|
@ -90,6 +90,8 @@ By submitting this issue, you agree to follow our [Contribution Guidelines](http
|
|||
However, we are currently so understaffed that it is virtually impossible to
|
||||
respond to every single proposal. So, feel free to implement it if there is no response
|
||||
for more than a week or there is a thumbs-up emoji reaction from the project maintainer(s).
|
||||
|
||||
Many thanks for your involvement!
|
||||
-->
|
||||
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ By submitting this issue, you agree to follow our [Contribution Guidelines](http
|
|||
However, we are currently so understaffed that it is virtually impossible to
|
||||
respond to every single proposal. So, feel free to implement it if there is no response
|
||||
for more than a week or there is a thumbs-up emoji reaction from the project maintainer(s).
|
||||
|
||||
Many thanks for your involvement!
|
||||
-->
|
||||
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ By submitting this issue, you agree to follow our [Contribution Guidelines](http
|
|||
However, we are currently so understaffed that it is virtually impossible to
|
||||
respond to every single proposal. So, feel free to implement it if there is no response
|
||||
for more than a week or there is a thumbs-up emoji reaction from the project maintainer(s).
|
||||
|
||||
Many thanks for your involvement!
|
||||
-->
|
||||
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
|
||||
## Contribution Guidelines
|
||||
By submitting this merge request, you agree to follow our [Contribution Guidelines](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md)
|
||||
- [ ] This closes issue #0000 (please substitute the number)
|
||||
- [ ] This is a minor bug fix or refactoring
|
||||
|
||||
- [ ] This closes #0000 (please substitute the issue number or open a new one unless this is a minor fix/refactor)
|
||||
- [ ] I agree to follow this project's Contribution Guidelines
|
||||
- [ ] I have made sure to test this merge request
|
||||
- [ ] I have made sure to run `pnpm run format` before submitting this merge request
|
||||
|
|
|
@ -4,6 +4,7 @@ Breaking changes are indicated by the :warning: icon.
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Added `readCatLanguage` field to the response of `i` and request of `i/update` (optional).
|
||||
- The old Mastodon API has been replaced with a new implementation based on Iceshrimp’s.
|
||||
- :warning: The new API uses a new format to manage Mastodon sessions in the database, whereas old implementation uses Misskey sessions. All previous client app and token registrations will not work with the new API. All clients need to be re-registered and all users need to re-authenticate.
|
||||
- :warning: All IDs (of statuses/notes, notifications, users, etc.) will be using the alphanumerical format, aligning with the Firefish/Misskey API. The old numerical IDs will not work when queried against the new API.
|
||||
|
|
|
@ -7,8 +7,9 @@ Critical security updates are indicated by the :warning: icon.
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Fix bugs
|
||||
- Mastodon API implementation was ported from Iceshrimp, with added Firefish extensions including push notifications, post languages, schedule post support, and more. (#10880)
|
||||
- Add ability to disable the cat language conversion (nyaification)
|
||||
- Fix bugs
|
||||
|
||||
### Acknowledgement
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ DELETE FROM "migrations" WHERE name IN (
|
|||
'SwSubscriptionAccessToken1709395223611',
|
||||
'UserProfileMentions1711075007936',
|
||||
'ClientCredentials1713108561474',
|
||||
'TurnOffCatLanguage1720107645050',
|
||||
'RefactorScheduledPosts1716804636187',
|
||||
'RemoveEnumTypenameSuffix1716462794927',
|
||||
'CreateScheduledNote1714728200194',
|
||||
|
@ -52,6 +53,9 @@ ALTER TABLE "user_profile" DROP COLUMN "mentions";
|
|||
-- client-credential-support
|
||||
ALTER TABLE "access_token" ALTER COLUMN "userId" SET NOT NULL;
|
||||
|
||||
-- turn-off-cat-language
|
||||
ALTER TABLE "user" DROP COLUMN "readCatLanguage";
|
||||
|
||||
-- refactor-scheduled-post
|
||||
CREATE TABLE "scheduled_note" (
|
||||
"id" character varying(32) NOT NULL PRIMARY KEY,
|
||||
|
|
|
@ -10,7 +10,15 @@ Please take a look at #10947.
|
|||
|
||||
### For all users
|
||||
|
||||
This is not related to the recent changes, but we have added a new section called "[Maintain the server](https://firefish.dev/firefish/firefish/-/blob/develop/docs/install.md#maintain-the-server)" in the installation guide. We suggest that you take a look at it. (and we welcome your docs contributions!)
|
||||
This is not related to the recent changes, but we have added a new section called "[Maintain the server](https://firefish.dev/firefish/firefish/-/blob/develop/docs/install.md#maintain-the-server)" in the installation guide. We suggest that you take a look at it (and we welcome your docs contributions)!
|
||||
|
||||
### For systemd/pm2 users
|
||||
|
||||
[Node.js will release a new security fix on July 8th](<https://nodejs.org/en/blog/vulnerability/july-2024-security-releases>). It is highly recommended that you upgrade your Node.js version once it's released.
|
||||
|
||||
### For Docker/Podman users
|
||||
|
||||
[Node.js will release a new security fix on July 8th](<https://nodejs.org/en/blog/vulnerability/july-2024-security-releases>). Once it's released and the [docker.io/node](<https://hub.docker.com/_/node>) image is updated, we'll rebuild the OCI image based on the new `docker.io/node` image and reupload it as [`registry.firefish.dev/firefish/firefish:latest`](<https://firefish.dev/firefish/firefish/container_registry/1>).
|
||||
|
||||
### For systemd/pm2 users
|
||||
|
||||
|
|
|
@ -1241,6 +1241,7 @@ showNoAltTextWarning: "Show a warning if you attempt to post files without a des
|
|||
showAddFileDescriptionAtFirstPost: "Automatically open a form to write a description
|
||||
when you attempt to post files without a description"
|
||||
addAlt4MeTag: "Automatically append #Alt4Me hashtag to your post if attached file has no description"
|
||||
turnOffCatLanguage: "Turn off cat language conversion"
|
||||
|
||||
_emojiModPerm:
|
||||
unauthorized: "None"
|
||||
|
|
5
packages/backend-rs/index.d.ts
vendored
5
packages/backend-rs/index.d.ts
vendored
|
@ -1306,6 +1306,8 @@ export interface Services {
|
|||
outbound: Array<Outbound>
|
||||
}
|
||||
|
||||
export declare function shouldNyaify(readerUserId: string): Promise<boolean>
|
||||
|
||||
/** Prints the server hardware information as the server info log. */
|
||||
export declare function showServerInfo(): void
|
||||
|
||||
|
@ -1429,6 +1431,7 @@ export interface User {
|
|||
emojiModPerm: UserEmojiModPerm
|
||||
isIndexable: boolean
|
||||
alsoKnownAs: Array<string> | null
|
||||
readCatLanguage: boolean
|
||||
}
|
||||
|
||||
export const USER_ACTIVE_THRESHOLD: number
|
||||
|
@ -1547,10 +1550,10 @@ export interface UserProfile {
|
|||
preventAiLearning: boolean
|
||||
isIndexable: boolean
|
||||
mutedPatterns: Array<string>
|
||||
mentions: Json
|
||||
mutedInstances: Array<string>
|
||||
mutedWords: Array<string>
|
||||
lang: string | null
|
||||
mentions: Json
|
||||
}
|
||||
|
||||
export enum UserProfileFfvisibility {
|
||||
|
|
|
@ -432,6 +432,7 @@ module.exports.removeOldAttestationChallenges = nativeBinding.removeOldAttestati
|
|||
module.exports.safeForSql = nativeBinding.safeForSql
|
||||
module.exports.SECOND = nativeBinding.SECOND
|
||||
module.exports.sendPushNotification = nativeBinding.sendPushNotification
|
||||
module.exports.shouldNyaify = nativeBinding.shouldNyaify
|
||||
module.exports.showServerInfo = nativeBinding.showServerInfo
|
||||
module.exports.sqlLikeEscape = nativeBinding.sqlLikeEscape
|
||||
module.exports.sqlRegexEscape = nativeBinding.sqlRegexEscape
|
||||
|
|
|
@ -9,6 +9,7 @@ pub enum Category {
|
|||
FetchUrl,
|
||||
Block,
|
||||
Follow,
|
||||
CatLang,
|
||||
#[cfg(test)]
|
||||
Test,
|
||||
}
|
||||
|
@ -33,6 +34,7 @@ fn categorize(category: Category, key: &str) -> String {
|
|||
Category::FetchUrl => "fetchUrl",
|
||||
Category::Block => "blocking",
|
||||
Category::Follow => "following",
|
||||
Category::CatLang => "catlang",
|
||||
#[cfg(test)]
|
||||
Category::Test => "usedOnlyForTesting",
|
||||
};
|
||||
|
|
|
@ -15,4 +15,5 @@ pub mod nyaify;
|
|||
pub mod password;
|
||||
pub mod reaction;
|
||||
pub mod remove_old_attestation_challenges;
|
||||
pub mod should_nyaify;
|
||||
pub mod system_info;
|
||||
|
|
45
packages/backend-rs/src/misc/should_nyaify.rs
Normal file
45
packages/backend-rs/src/misc/should_nyaify.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
//! Determine whether to enable the cat language conversion
|
||||
|
||||
use crate::{
|
||||
database::{cache, db_conn},
|
||||
model::entity::user,
|
||||
};
|
||||
use sea_orm::{DbErr, EntityTrait, QuerySelect, SelectColumns};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[doc = "database error"]
|
||||
#[error(transparent)]
|
||||
Db(#[from] DbErr),
|
||||
#[doc = "cache error"]
|
||||
#[error(transparent)]
|
||||
Cache(#[from] cache::Error),
|
||||
#[error("user {0} not found")]
|
||||
NotFound(String),
|
||||
}
|
||||
|
||||
#[macros::export]
|
||||
pub async fn should_nyaify(reader_user_id: &str) -> Result<bool, Error> {
|
||||
let cached_value = cache::get_one::<bool>(cache::Category::CatLang, reader_user_id).await?;
|
||||
if let Some(value) = cached_value {
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
let fetched_value = user::Entity::find_by_id(reader_user_id)
|
||||
.select_only()
|
||||
.select_column(user::Column::ReadCatLanguage)
|
||||
.into_tuple::<bool>()
|
||||
.one(db_conn().await?)
|
||||
.await?
|
||||
.ok_or_else(|| Error::NotFound(reader_user_id.to_owned()))?;
|
||||
|
||||
cache::set_one(
|
||||
cache::Category::CatLang,
|
||||
reader_user_id,
|
||||
&fetched_value,
|
||||
10 * 60,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(fetched_value)
|
||||
}
|
|
@ -77,6 +77,8 @@ pub struct Model {
|
|||
pub is_indexable: bool,
|
||||
#[sea_orm(column_name = "alsoKnownAs")]
|
||||
pub also_known_as: Option<Vec<String>>,
|
||||
#[sea_orm(column_name = "readCatLanguage")]
|
||||
pub read_cat_language: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
@ -68,13 +68,13 @@ pub struct Model {
|
|||
pub is_indexable: bool,
|
||||
#[sea_orm(column_name = "mutedPatterns")]
|
||||
pub muted_patterns: Vec<String>,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub mentions: Json,
|
||||
#[sea_orm(column_name = "mutedInstances")]
|
||||
pub muted_instances: Vec<String>,
|
||||
#[sea_orm(column_name = "mutedWords")]
|
||||
pub muted_words: Vec<String>,
|
||||
pub lang: Option<String>,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub mentions: Json,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
@ -28,18 +28,18 @@
|
|||
"@koa/router": "12.0.1",
|
||||
"@ladjs/koa-views": "9.0.0",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@redocly/openapi-core": "1.17.0",
|
||||
"@redocly/openapi-core": "1.17.1",
|
||||
"@sinonjs/fake-timers": "11.2.2",
|
||||
"adm-zip": "0.5.14",
|
||||
"ajv": "8.16.0",
|
||||
"archiver": "7.0.1",
|
||||
"async-lock": "1.4.0",
|
||||
"async-mutex": "0.5.0",
|
||||
"aws-sdk": "2.1653.0",
|
||||
"aws-sdk": "2.1654.0",
|
||||
"axios": "1.7.2",
|
||||
"backend-rs": "workspace:*",
|
||||
"blurhash": "2.0.5",
|
||||
"bull": "4.15.0",
|
||||
"bull": "4.15.1",
|
||||
"cacheable-lookup": "git+https://github.com/TheEssem/cacheable-lookup.git#dd2fb616366a3c68dcf321a57a67295967b204bf",
|
||||
"cbor-x": "1.5.9",
|
||||
"chalk": "5.3.0",
|
||||
|
@ -174,6 +174,6 @@
|
|||
"type-fest": "4.21.0",
|
||||
"typescript": "5.5.3",
|
||||
"webpack": "5.92.1",
|
||||
"ws": "8.17.1"
|
||||
"ws": "8.18.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,10 +25,11 @@ export async function masterMain() {
|
|||
// initialize app
|
||||
try {
|
||||
greet();
|
||||
showEnvironment();
|
||||
showServerInfo();
|
||||
showEnvironment();
|
||||
showNodejsVersion();
|
||||
await connectDb();
|
||||
await updateMetaCache();
|
||||
} catch (e) {
|
||||
bootLogger.error(
|
||||
`Fatal error occurred during initialization:\n${inspect(e)}`,
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class TurnOffCatLanguage1720107645050 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ADD COLUMN "readCatLanguage" boolean NOT NULL DEFAULT true`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`COMMENT ON COLUMN "user"."readCatLanguage" IS 'Whether to enable the cat language conversion.'`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "readCatLanguage"`);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import { noteVisibilities } from "@/types.js";
|
||||
|
||||
export type Post = {
|
||||
text: string | undefined;
|
||||
cw: string | null;
|
||||
|
@ -12,7 +14,9 @@ export function parse(acct: any): Post {
|
|||
cw: acct.cw,
|
||||
localOnly: acct.localOnly,
|
||||
createdAt: new Date(acct.createdAt),
|
||||
visibility: `hidden${acct.visibility || ""}`,
|
||||
visibility: noteVisibilities.includes(acct.visibility)
|
||||
? acct.visibility
|
||||
: "specified",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -159,6 +159,12 @@ export class User {
|
|||
})
|
||||
public speakAsCat: boolean;
|
||||
|
||||
@Column("boolean", {
|
||||
default: true,
|
||||
comment: "Whether to enable the cat language conversion.",
|
||||
})
|
||||
public readCatLanguage: boolean;
|
||||
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
comment: "Whether the User is the admin.",
|
||||
|
|
|
@ -14,7 +14,12 @@ import {
|
|||
Notes,
|
||||
} from "../index.js";
|
||||
import type { Packed } from "@/misc/schema.js";
|
||||
import { countReactions, decodeReaction, nyaify } from "backend-rs";
|
||||
import {
|
||||
countReactions,
|
||||
decodeReaction,
|
||||
nyaify,
|
||||
shouldNyaify,
|
||||
} from "backend-rs";
|
||||
import { awaitAll } from "@/prelude/await-all.js";
|
||||
import type { NoteReaction } from "@/models/entities/note-reaction.js";
|
||||
import {
|
||||
|
@ -311,7 +316,13 @@ export const NoteRepository = db.getRepository(Note).extend({
|
|||
mentionedRemoteUsers: this.mentionedRemoteUsers(note),
|
||||
});
|
||||
|
||||
if (packed.user.isCat && packed.user.speakAsCat && packed.text) {
|
||||
if (
|
||||
packed.user.isCat &&
|
||||
packed.user.speakAsCat &&
|
||||
packed.text != null &&
|
||||
meId != null &&
|
||||
(await shouldNyaify(meId))
|
||||
) {
|
||||
const tokens = packed.text ? mfm.parse(packed.text) : [];
|
||||
function nyaifyNode(node: mfm.MfmNode) {
|
||||
if (node.type === "quote") return;
|
||||
|
|
|
@ -464,7 +464,8 @@ export const UserRepository = db.getRepository(User).extend({
|
|||
isLocked: user.isLocked,
|
||||
isIndexable: user.isIndexable,
|
||||
isCat: user.isCat || falsy,
|
||||
speakAsCat: user.speakAsCat || falsy,
|
||||
speakAsCat: user.speakAsCat,
|
||||
readCatLanguage: user.readCatLanguage,
|
||||
instance: user.host
|
||||
? userInstanceCache
|
||||
.fetch(
|
||||
|
|
|
@ -92,22 +92,29 @@ export async function importCkPost(
|
|||
logger.info("Post updated");
|
||||
}
|
||||
if (note == null) {
|
||||
note = await create(user, {
|
||||
createdAt: createdAt,
|
||||
files: files.length === 0 ? undefined : files,
|
||||
poll: undefined,
|
||||
text: text || undefined,
|
||||
reply: post.replyId ? job.data.parent : null,
|
||||
renote: post.renoteId ? job.data.parent : null,
|
||||
cw: cw,
|
||||
localOnly,
|
||||
visibility: visibility,
|
||||
visibleUsers: [],
|
||||
channel: null,
|
||||
apMentions: new Array(0),
|
||||
apHashtags: undefined,
|
||||
apEmojis: undefined,
|
||||
});
|
||||
note = await create(
|
||||
user,
|
||||
{
|
||||
createdAt: createdAt,
|
||||
scheduledAt: undefined,
|
||||
files: files.length === 0 ? undefined : files,
|
||||
poll: undefined,
|
||||
text: text || undefined,
|
||||
reply: post.replyId ? job.data.parent : null,
|
||||
renote: post.renoteId ? job.data.parent : null,
|
||||
cw: cw,
|
||||
localOnly,
|
||||
visibility: visibility,
|
||||
visibleUsers: [],
|
||||
channel: null,
|
||||
apMentions: new Array(0),
|
||||
apHashtags: undefined,
|
||||
apEmojis: undefined,
|
||||
},
|
||||
false,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
logger.debug("New post has been created");
|
||||
} else {
|
||||
logger.info("This post already exists");
|
||||
|
|
|
@ -10,6 +10,11 @@ import type { DriveFile } from "@/models/entities/drive-file.js";
|
|||
import { Notes, NoteEdits } from "@/models/index.js";
|
||||
import type { Note } from "@/models/entities/note.js";
|
||||
import { genId } from "backend-rs";
|
||||
import promiseLimit from "promise-limit";
|
||||
import { unique, concat } from "@/prelude/array.js";
|
||||
import type { CacheableUser } from "@/models/entities/user.js";
|
||||
import { resolvePerson } from "@/remote/activitypub/models/person.js";
|
||||
import { isPublic } from "@/remote/activitypub/audience.js";
|
||||
|
||||
const logger = queueLogger.createSubLogger("import-masto-post");
|
||||
|
||||
|
@ -118,24 +123,57 @@ export async function importMastoPost(
|
|||
logger.info("Post updated");
|
||||
}
|
||||
if (note == null) {
|
||||
note = await create(user, {
|
||||
createdAt: isRenote
|
||||
? new Date(post.published)
|
||||
: new Date(post.object.published),
|
||||
files: files.length === 0 ? undefined : files,
|
||||
poll: undefined,
|
||||
text: text || undefined,
|
||||
reply,
|
||||
renote,
|
||||
cw: !isRenote && post.object.sensitive ? post.object.summary : undefined,
|
||||
localOnly: false,
|
||||
visibility: "hiddenpublic",
|
||||
visibleUsers: [],
|
||||
channel: null,
|
||||
apMentions: new Array(0),
|
||||
apHashtags: undefined,
|
||||
apEmojis: undefined,
|
||||
});
|
||||
let visibility = "specified";
|
||||
let visibleUsers: CacheableUser[] = [];
|
||||
if ((post.to as string[]).some(isPublic)) {
|
||||
visibility = "public";
|
||||
} else if ((post.cc as string[]).some(isPublic)) {
|
||||
visibility = "home";
|
||||
} else if ((post.cc as string[]).some((cc) => cc.endsWith("/followers"))) {
|
||||
visibility = "followers";
|
||||
} else {
|
||||
try {
|
||||
const visibleUsersList = unique(concat([post.to, post.cc]));
|
||||
|
||||
const limit = promiseLimit<CacheableUser | null>(2);
|
||||
visibleUsers = (
|
||||
await Promise.all(
|
||||
visibleUsersList.map((id) =>
|
||||
limit(() => resolvePerson(id).catch(() => null)),
|
||||
),
|
||||
)
|
||||
).filter((x): x is CacheableUser => x != null);
|
||||
} catch {
|
||||
// nothing need to do.
|
||||
}
|
||||
}
|
||||
|
||||
note = await create(
|
||||
user,
|
||||
{
|
||||
createdAt: isRenote
|
||||
? new Date(post.published)
|
||||
: new Date(post.object.published),
|
||||
scheduledAt: undefined,
|
||||
files: files.length === 0 ? undefined : files,
|
||||
poll: undefined,
|
||||
text: text || undefined,
|
||||
reply,
|
||||
renote,
|
||||
cw:
|
||||
!isRenote && post.object.sensitive ? post.object.summary : undefined,
|
||||
localOnly: false,
|
||||
visibility,
|
||||
visibleUsers,
|
||||
channel: null,
|
||||
apMentions: new Array(0),
|
||||
apHashtags: undefined,
|
||||
apEmojis: undefined,
|
||||
},
|
||||
false,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
logger.debug("New post has been created");
|
||||
} else {
|
||||
logger.info("This post already exists");
|
||||
|
|
|
@ -90,7 +90,7 @@ function groupingAudience(ids: string[], actor: CacheableRemoteUser) {
|
|||
return groups;
|
||||
}
|
||||
|
||||
function isPublic(id: string) {
|
||||
export function isPublic(id: string) {
|
||||
return [
|
||||
"https://www.w3.org/ns/activitystreams#Public",
|
||||
"as:Public",
|
||||
|
@ -98,6 +98,6 @@ function isPublic(id: string) {
|
|||
].includes(id);
|
||||
}
|
||||
|
||||
function isFollowers(id: string, actor: CacheableRemoteUser) {
|
||||
export function isFollowers(id: string, actor: CacheableRemoteUser) {
|
||||
return id === (actor.followersUri || `${actor.uri}/followers`);
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ export const paramDef = {
|
|||
isBot: { type: "boolean" },
|
||||
isCat: { type: "boolean" },
|
||||
speakAsCat: { type: "boolean", nullable: true },
|
||||
readCatLanguage: { type: "boolean", nullable: true },
|
||||
isIndexable: { type: "boolean" },
|
||||
injectFeaturedNote: { type: "boolean" },
|
||||
receiveAnnouncementEmail: { type: "boolean" },
|
||||
|
@ -220,6 +221,8 @@ export default define(meta, paramDef, async (ps, _user, token) => {
|
|||
profileUpdates.isIndexable = ps.isIndexable;
|
||||
}
|
||||
if (typeof ps.speakAsCat === "boolean") updates.speakAsCat = ps.speakAsCat;
|
||||
if (typeof ps.readCatLanguage === "boolean")
|
||||
updates.readCatLanguage = ps.readCatLanguage;
|
||||
if (typeof ps.injectFeaturedNote === "boolean")
|
||||
profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
|
||||
if (typeof ps.receiveAnnouncementEmail === "boolean")
|
||||
|
|
|
@ -162,11 +162,12 @@ export default async (
|
|||
data: NoteLike,
|
||||
silent = false,
|
||||
waitToPublish?: (note: Note) => Promise<void>,
|
||||
dontFederateInitially = false,
|
||||
) =>
|
||||
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: FIXME
|
||||
new Promise<Note>(async (res, rej) => {
|
||||
const dontFederateInitially =
|
||||
data.visibility?.startsWith("hidden") === true;
|
||||
dontFederateInitially =
|
||||
dontFederateInitially || data.visibility?.startsWith("hidden");
|
||||
|
||||
// Whether this is a scheduled "draft" post (yet to be published)
|
||||
const isDraft = data.scheduledAt != null;
|
||||
|
@ -204,8 +205,6 @@ export default async (
|
|||
if (data.channel != null) data.visibility = "public";
|
||||
if (data.channel != null) data.visibleUsers = [];
|
||||
if (data.channel != null) data.localOnly = true;
|
||||
if (data.visibility.startsWith("hidden") && data.visibility !== "hidden")
|
||||
data.visibility = data.visibility.slice(6);
|
||||
|
||||
// enforce silent clients on server
|
||||
if (
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
<MkButton inline primary @click="search"
|
||||
>{{ i18n.ts.search }}
|
||||
</MkButton>
|
||||
<MkButton inline @click="lookup">{{ i18n.ts.lookup }}</MkButton>
|
||||
<MkButton inline primary @click="lookup">{{ i18n.ts.lookup }}</MkButton>
|
||||
<MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -61,9 +61,11 @@ function checkForSplash() {
|
|||
if (splash) {
|
||||
splash.style.opacity = "0";
|
||||
splash.style.pointerEvents = "none";
|
||||
splash.addEventListener("transitionend", () => {
|
||||
|
||||
// remove splash screen
|
||||
window.setTimeout(() => {
|
||||
splash.remove();
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,18 +5,76 @@ import { set, get } from "idb-keyval";
|
|||
|
||||
// TODO: 他のタブと永続化されたstateを同期
|
||||
|
||||
// TODO: get("instance") requires top-level await
|
||||
let instance: entities.DetailedInstanceMetadata;
|
||||
// TODO: fallback to defaults more nicely (with #10947)
|
||||
// default values
|
||||
let instanceMeta: entities.DetailedInstanceMetadata = {
|
||||
maintainerName: "",
|
||||
maintainerEmail: "",
|
||||
version: "",
|
||||
name: null,
|
||||
uri: "",
|
||||
tosUrl: null,
|
||||
description: null,
|
||||
disableRegistration: true,
|
||||
disableLocalTimeline: false,
|
||||
disableGlobalTimeline: false,
|
||||
disableRecommendedTimeline: true,
|
||||
enableGuestTimeline: false,
|
||||
driveCapacityPerLocalUserMb: 1000,
|
||||
driveCapacityPerRemoteUserMb: 0,
|
||||
antennaLimit: 5,
|
||||
enableHcaptcha: false,
|
||||
hcaptchaSiteKey: null,
|
||||
enableRecaptcha: false,
|
||||
recaptchaSiteKey: null,
|
||||
swPublickey: null,
|
||||
maxNoteTextLength: 3000,
|
||||
maxCaptionTextLength: 1500,
|
||||
enableEmail: false,
|
||||
enableServiceWorker: false,
|
||||
markLocalFilesNsfwByDefault: false,
|
||||
emojis: [],
|
||||
ads: [],
|
||||
langs: [],
|
||||
moreUrls: [],
|
||||
repositoryUrl: "https://firefish.dev/firefish/firefish",
|
||||
feedbackUrl: "https://firefish.dev/firefish/firefish/-/issues",
|
||||
defaultDarkTheme: null,
|
||||
defaultLightTheme: null,
|
||||
defaultReaction: "⭐",
|
||||
cacheRemoteFiles: false,
|
||||
proxyAccountName: null,
|
||||
emailRequiredForSignup: false,
|
||||
mascotImageUrl: "",
|
||||
bannerUrl: "",
|
||||
backgroundImageUrl: "",
|
||||
errorImageUrl: "",
|
||||
iconUrl: null,
|
||||
requireSetup: false,
|
||||
translatorAvailable: false,
|
||||
features: {
|
||||
registration: false,
|
||||
localTimeLine: true,
|
||||
recommendedTimeLine: false,
|
||||
globalTimeLine: true,
|
||||
searchFilters: true,
|
||||
hcaptcha: false,
|
||||
recaptcha: false,
|
||||
objectStorage: false,
|
||||
serviceWorker: false,
|
||||
},
|
||||
};
|
||||
|
||||
// get("instanceMeta") requires top-level await
|
||||
export function getInstanceInfo(): entities.DetailedInstanceMetadata {
|
||||
return instance;
|
||||
return instanceMeta;
|
||||
}
|
||||
|
||||
export async function initializeInstanceCache(): Promise<void> {
|
||||
// Is the data stored in IndexDB?
|
||||
const fromIdb = await get<string>("instance");
|
||||
const fromIdb = await get<string>("instanceMeta");
|
||||
if (fromIdb != null) {
|
||||
instance = JSON.parse(fromIdb);
|
||||
instanceMeta = JSON.parse(fromIdb);
|
||||
}
|
||||
// Call API
|
||||
updateInstanceCache();
|
||||
|
@ -27,29 +85,25 @@ export async function updateInstanceCache(): Promise<void> {
|
|||
detail: true,
|
||||
});
|
||||
|
||||
// TODO: set default values
|
||||
instance = {} as entities.DetailedInstanceMetadata;
|
||||
|
||||
for (const [k, v] of Object.entries(meta)) {
|
||||
instance[k] = v;
|
||||
instanceMeta[k] = v;
|
||||
}
|
||||
|
||||
set("instance", JSON.stringify(instance));
|
||||
set("instanceMeta", JSON.stringify(instanceMeta));
|
||||
}
|
||||
|
||||
export const emojiCategories = computed(() => {
|
||||
if (instance.emojis == null) return [];
|
||||
if (instanceMeta.emojis == null) return [];
|
||||
const categories = new Set();
|
||||
for (const emoji of instance.emojis) {
|
||||
for (const emoji of instanceMeta.emojis) {
|
||||
categories.add(emoji.category);
|
||||
}
|
||||
return Array.from(categories);
|
||||
});
|
||||
|
||||
export const emojiTags = computed(() => {
|
||||
if (instance.emojis == null) return [];
|
||||
if (instanceMeta.emojis == null) return [];
|
||||
const tags = new Set();
|
||||
for (const emoji of instance.emojis) {
|
||||
for (const emoji of instanceMeta.emojis) {
|
||||
for (const tag of emoji.aliases) {
|
||||
tags.add(tag);
|
||||
}
|
||||
|
|
|
@ -200,6 +200,11 @@
|
|||
i18n.ts.expandOnNoteClickDesc
|
||||
}}</template>
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="turnOffCatLanguage" @update:modelValue="save()" class="_formBlock"
|
||||
>{{ i18n.ts.turnOffCatLanguage }}<template #caption>{{
|
||||
i18n.ts.reflectMayTakeTime
|
||||
}}</template>
|
||||
</FormSwitch>
|
||||
<FormSwitch v-model="advancedMfm" class="_formBlock">
|
||||
{{ i18n.ts._mfm.advanced
|
||||
}}<template #caption>{{
|
||||
|
@ -423,6 +428,13 @@ const serverLang = ref(me?.lang);
|
|||
const translateLang = ref(localStorage.getItem("translateLang"));
|
||||
const fontSize = ref(localStorage.getItem("fontSize"));
|
||||
const useSystemFont = ref(localStorage.getItem("useSystemFont") !== "f");
|
||||
const turnOffCatLanguage = ref(!me?.readCatLanguage);
|
||||
|
||||
function save() {
|
||||
os.api("i/update", {
|
||||
readCatLanguage: !turnOffCatLanguage.value,
|
||||
});
|
||||
}
|
||||
|
||||
async function reloadAsk() {
|
||||
const { canceled } = await os.confirm({
|
||||
|
|
|
@ -110,6 +110,10 @@ provideMetadataReceiver((info) => {
|
|||
});
|
||||
|
||||
const root = computed(() => mainRouter.currentRoute.value?.name === "index");
|
||||
const showMenu = ref(false);
|
||||
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
|
||||
const narrow = ref(window.innerWidth < 1280);
|
||||
const meta = ref();
|
||||
|
||||
os.api("meta", { detail: true }).then((res) => {
|
||||
meta.value = res;
|
||||
|
|
|
@ -598,6 +598,8 @@ export type Endpoints = {
|
|||
preventAiLearning?: boolean;
|
||||
isBot?: boolean;
|
||||
isCat?: boolean;
|
||||
speakAsCat?: boolean;
|
||||
readCatLanguage?: boolean;
|
||||
injectFeaturedNote?: boolean;
|
||||
receiveAnnouncementEmail?: boolean;
|
||||
alwaysMarkNsfw?: boolean;
|
||||
|
|
|
@ -30,6 +30,7 @@ export type UserLite = {
|
|||
isIndexable: boolean;
|
||||
isCat?: boolean;
|
||||
speakAsCat?: boolean;
|
||||
readCatLanguage?: boolean;
|
||||
driveCapacityOverrideMb: number | null;
|
||||
};
|
||||
|
||||
|
@ -395,7 +396,7 @@ export type DetailedInstanceMetadata = LiteInstanceMetadata & {
|
|||
miauth?: boolean;
|
||||
};
|
||||
langs: string[];
|
||||
moreUrls: object;
|
||||
moreUrls: { name: string; url: string }[];
|
||||
repositoryUrl: string;
|
||||
feedbackUrl: string;
|
||||
defaultDarkTheme: string | null;
|
||||
|
|
|
@ -76,6 +76,11 @@ export const packedUserLiteSchema = {
|
|||
nullable: false,
|
||||
optional: true,
|
||||
},
|
||||
readCatLanguage: {
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
optional: true,
|
||||
},
|
||||
emojis: {
|
||||
type: "array",
|
||||
nullable: false,
|
||||
|
|
|
@ -67,8 +67,8 @@ importers:
|
|||
specifier: 1.7.0
|
||||
version: 1.7.0
|
||||
'@redocly/openapi-core':
|
||||
specifier: 1.17.0
|
||||
version: 1.17.0
|
||||
specifier: 1.17.1
|
||||
version: 1.17.1
|
||||
'@sinonjs/fake-timers':
|
||||
specifier: 11.2.2
|
||||
version: 11.2.2
|
||||
|
@ -88,8 +88,8 @@ importers:
|
|||
specifier: 0.5.0
|
||||
version: 0.5.0
|
||||
aws-sdk:
|
||||
specifier: 2.1653.0
|
||||
version: 2.1653.0
|
||||
specifier: 2.1654.0
|
||||
version: 2.1654.0
|
||||
axios:
|
||||
specifier: 1.7.2
|
||||
version: 1.7.2
|
||||
|
@ -100,8 +100,8 @@ importers:
|
|||
specifier: 2.0.5
|
||||
version: 2.0.5
|
||||
bull:
|
||||
specifier: 4.15.0
|
||||
version: 4.15.0
|
||||
specifier: 4.15.1
|
||||
version: 4.15.1
|
||||
cacheable-lookup:
|
||||
specifier: git+https://github.com/TheEssem/cacheable-lookup.git#dd2fb616366a3c68dcf321a57a67295967b204bf
|
||||
version: https://codeload.github.com/TheEssem/cacheable-lookup/tar.gz/dd2fb616366a3c68dcf321a57a67295967b204bf
|
||||
|
@ -500,8 +500,8 @@ importers:
|
|||
specifier: 5.92.1
|
||||
version: 5.92.1
|
||||
ws:
|
||||
specifier: 8.17.1
|
||||
version: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
specifier: 8.18.0
|
||||
version: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
|
||||
packages/backend-rs:
|
||||
devDependencies:
|
||||
|
@ -2099,8 +2099,8 @@ packages:
|
|||
'@redocly/config@0.6.2':
|
||||
resolution: {integrity: sha512-c3K5u64eMnr2ootPcpEI0ioIRLE8QP8ptvLxG9MwAmb2sU8HMRfVwXDU3AZiMVY2w4Ts0mDc+Xv4HTIk8DRqFw==}
|
||||
|
||||
'@redocly/openapi-core@1.17.0':
|
||||
resolution: {integrity: sha512-XoNIuksnOGAzAcfpyJkHrMxwurXaQfglnovNE7/pTx4OEjik3OT91+tKAyRCkklVCdMtAA3YokGMZzdhjViUWA==}
|
||||
'@redocly/openapi-core@1.17.1':
|
||||
resolution: {integrity: sha512-PQxDLLNk5cBatJBBxvfk49HFw/nVozw1XZ6Dw/GX0Tviq+WxeEjEuLAKfnLVvb5L0wgs4TNmVG4Y+JyofSPu1A==}
|
||||
engines: {node: '>=14.19.0', npm: '>=7.0.0'}
|
||||
|
||||
'@rollup/plugin-alias@5.1.0':
|
||||
|
@ -2839,8 +2839,8 @@ packages:
|
|||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
aws-sdk@2.1653.0:
|
||||
resolution: {integrity: sha512-9f42kuLpMcL1EPZOsLM8u6wlnOMtFwED1b24SN0fBbi/N7N1xTLZ7vbEMt/haz06Lc3Vr3VMDyv0atfMmkboBw==}
|
||||
aws-sdk@2.1654.0:
|
||||
resolution: {integrity: sha512-b5ryvXipBJod9Uh1GUfQNgi5tIIiluxJbyqr/hZ/mr5U8WxrrfjVq3nGnx5JjevFKYRqXIywhumsVyanfACzFA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
axios@0.24.0:
|
||||
|
@ -2981,8 +2981,8 @@ packages:
|
|||
resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==}
|
||||
engines: {node: '>=6.14.2'}
|
||||
|
||||
bull@4.15.0:
|
||||
resolution: {integrity: sha512-nOEAfUXwUXtFbRPQP3bWCwpQ/NAerAu2Nym/ucv5C1E+Qh2x6RGdKKsYIfZam4mYncayTynTUN/HLhRgGi2N8w==}
|
||||
bull@4.15.1:
|
||||
resolution: {integrity: sha512-knVKiZdrXbRkB+fWqNryDz85b3JfsT3dBrZexkztwvTH/AFmpHvsC933VB3JX18aJCz47E+xdO57xbDvxljoAg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
busboy@1.6.0:
|
||||
|
@ -6856,8 +6856,8 @@ packages:
|
|||
resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||
|
||||
ws@8.17.1:
|
||||
resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
|
||||
ws@8.18.0:
|
||||
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
|
@ -8229,7 +8229,7 @@ snapshots:
|
|||
|
||||
'@redocly/config@0.6.2': {}
|
||||
|
||||
'@redocly/openapi-core@1.17.0':
|
||||
'@redocly/openapi-core@1.17.1':
|
||||
dependencies:
|
||||
'@redocly/ajv': 8.11.0
|
||||
'@redocly/config': 0.6.2
|
||||
|
@ -9028,7 +9028,7 @@ snapshots:
|
|||
dependencies:
|
||||
possible-typed-array-names: 1.0.0
|
||||
|
||||
aws-sdk@2.1653.0:
|
||||
aws-sdk@2.1654.0:
|
||||
dependencies:
|
||||
buffer: 4.9.2
|
||||
events: 1.1.1
|
||||
|
@ -9223,7 +9223,7 @@ snapshots:
|
|||
dependencies:
|
||||
node-gyp-build: 4.8.1
|
||||
|
||||
bull@4.15.0:
|
||||
bull@4.15.1:
|
||||
dependencies:
|
||||
cron-parser: 4.9.0
|
||||
get-port: 5.1.1
|
||||
|
@ -11130,7 +11130,7 @@ snapshots:
|
|||
whatwg-encoding: 3.1.1
|
||||
whatwg-mimetype: 4.0.0
|
||||
whatwg-url: 14.0.0
|
||||
ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
|
||||
xml-name-validator: 5.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
|
@ -13323,7 +13323,7 @@ snapshots:
|
|||
imurmurhash: 0.1.4
|
||||
signal-exit: 3.0.7
|
||||
|
||||
ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10):
|
||||
ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10):
|
||||
optionalDependencies:
|
||||
bufferutil: 4.0.8
|
||||
utf-8-validate: 5.0.10
|
||||
|
|
Loading…
Reference in a new issue