diff --git a/CALCKEY.md b/CALCKEY.md index 3b6afb177..892c79607 100644 --- a/CALCKEY.md +++ b/CALCKEY.md @@ -87,7 +87,9 @@ - AVIF support - Page drafts - Patron list +- Animations respect reduced motion - Obliteration of Ai-chan +- MissV: [fix Misskey Forkbomb](https://code.vtopia.live/Vtopia/MissV/commit/40b23c070bd4adbb3188c73546c6c625138fb3c1) - [Make showing ads optional](https://github.com/misskey-dev/misskey/pull/8996) - [Tapping avatar in mobile opens account modal](https://github.com/misskey-dev/misskey/pull/9056) - [OAuth bearer token authentication](https://github.com/misskey-dev/misskey/pull/9021) diff --git a/README.md b/README.md index 863354727..8e4b03be9 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ - Improved UI/UX (especially on mobile) - Improved notifications - Improved instance security + - Improved accessibility - Recommended Instances timeline - OCR image captioning - New and improved Groups @@ -48,13 +49,27 @@ This guide will work for both **starting from scratch** and **migrating from Mis ## ๐Ÿ“ฆ Dependencies -- At least ๐Ÿข [NodeJS](https://nodejs.org/en/) v18.12.1 (v19.1.0 recommended) - +- ๐Ÿข At least [NodeJS](https://nodejs.org/en/) v18.12.1 (v19.1.0 recommended) + - Install with [nvm](https://github.com/nvm-sh/nvm) - ๐Ÿ˜ At least [PostgreSQL](https://www.postgresql.org/) v12 - - ๐Ÿฑ At least [Redis](https://redis.io/) v6 (v7 recommended) -- ๐Ÿ›ฐ๏ธ (Optional, for non-Docker) [pm2](https://pm2.io/) +### ๐Ÿ˜— Optional dependencies + +- ๐Ÿ“— [FFmpeg](https://ffmpeg.org/) for video transcoding +- ๐Ÿ” [ElasticSearch](https://www.elastic.co/elasticsearch/) for full-text search + - OpenSearch/Sonic are not supported as of right now +- ๐Ÿฅก Management (choose one of the following) + - ๐Ÿ›ฐ๏ธ [pm2](https://pm2.io/) + - ๐Ÿณ [Docker](https://docker.com) + - ๐Ÿ“ Service manager (systemd, openrc, etc) + +### ๐Ÿ—๏ธ Build dependencies + +- ๐Ÿฆฌ C/C++ compiler & build tools + - `build-essential` on Debian/Ubuntu Linux + - `base-devel` on Arch Linux +- ๐Ÿ [Python 3](https://www.python.org/) ## ๐Ÿ‘€ Get folder ready @@ -71,10 +86,19 @@ cd calckey/ corepack enable ``` +## ๐Ÿ˜ Create database + +Assuming you set up PostgreSQL correctly, all you have to run is: + +```sh +psql postgres -c "create database calckey with encoding = 'UTF8';" +``` + ## ๐Ÿ’… Customize -- To add custom CSS for all users, edit `./custom/instance.css`. -- To add static assets (such as images for the splash screen), place them in the `./custom/` directory. They'll then be avaliable on `https://yourinstance.tld/static-assets/filename.ext`. +- To add custom CSS for all users, edit `./custom/assets/instance.css`. +- To add static assets (such as images for the splash screen), place them in the `./custom/assets/` directory. They'll then be avaliable on `https://yourinstance.tld/static-assets/filename.ext`. +- To add custom locales, place them in the `./custom/locales/` directory. If you name your custom locale the same as an existing locale, it will overwrite it. If you give it a unique name, it will be added to the list. Also make sure that the first part of the filename matches the locale you're basing it on. (Example: `en-FOO.yml`) - To update custom assets without rebuilding, just run `yarn run gulp`. ## ๐Ÿง‘โ€๐Ÿ”ฌ Configuring a new instance @@ -103,7 +127,7 @@ cp -r ../misskey/files . # if you don't use object storage ## ๐Ÿš€ Build and launch! -### ๐Ÿข NodeJS +### ๐Ÿข NodeJS + pm2 #### `git pull` and run these steps to update Calckey in the future! diff --git a/custom/instance.css b/custom/assets/instance.css similarity index 100% rename from custom/instance.css rename to custom/assets/instance.css diff --git a/custom/locales/.gitkeep b/custom/locales/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/gulpfile.js b/gulpfile.js index 86f860e56..89a6acb83 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,7 +16,7 @@ gulp.task('copy:backend:views', () => ); gulp.task('copy:backend:custom', () => - gulp.src('./custom/*').pipe(gulp.dest('./packages/backend/assets/')) + gulp.src('./custom/assets/*').pipe(gulp.dest('./packages/backend/assets/')) ); gulp.task('copy:client:fonts', () => diff --git a/locales/index.js b/locales/index.js index 92cd9b467..7399bb5a1 100644 --- a/locales/index.js +++ b/locales/index.js @@ -4,6 +4,8 @@ const fs = require('fs'); const yaml = require('js-yaml'); +let languages = [] +let languages_custom = [] const merge = (...args) => args.reduce((a, c) => ({ ...a, @@ -13,33 +15,20 @@ const merge = (...args) => args.reduce((a, c) => ({ .reduce((a, [k, v]) => (a[k] = merge(v, c[k]), a), {}) }), {}); -const languages = [ - 'ar-SA', - 'cs-CZ', - 'da-DK', - 'de-DE', - 'en-US', - 'es-ES', - 'fr-FR', - 'id-ID', - 'it-IT', - 'ja-JP', - 'ja-KS', - 'kab-KAB', - 'kn-IN', - 'ko-KR', - 'nl-NL', - 'no-NO', - 'pl-PL', - 'pt-PT', - 'ru-RU', - 'sk-SK', - 'ug-CN', - 'uk-UA', - 'vi-VN', - 'zh-CN', - 'zh-TW', -]; + +fs.readdirSync(__dirname).forEach((file) => { + if (file.includes('.yml')){ + file = file.slice(0, file.indexOf('.')) + languages.push(file); + } +}) + +fs.readdirSync(__dirname + '/../custom/locales').forEach((file) => { + if (file.includes('.yml')){ + file = file.slice(0, file.indexOf('.')) + languages_custom.push(file); + } +}) const primaries = { 'en': 'US', @@ -51,6 +40,8 @@ const primaries = { const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), ''); const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8'))) || {}, a), {}); +const locales_custom = languages_custom.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, 'utf-8'))) || {}, a), {}); +Object.assign(locales, locales_custom) module.exports = Object.entries(locales) .reduce((a, [k ,v]) => (a[k] = (() => { diff --git a/package.json b/package.json index 9ea51419a..2651c05f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "calckey", - "version": "12.119.0-calc.17.6", + "version": "12.119.0-calc.18-rc.7", "codename": "aqua", "repository": { "type": "git", diff --git a/packages/backend/src/models/repositories/notification.ts b/packages/backend/src/models/repositories/notification.ts index 42b47ab15..efa3e860b 100644 --- a/packages/backend/src/models/repositories/notification.ts +++ b/packages/backend/src/models/repositories/notification.ts @@ -1,14 +1,14 @@ import { In, Repository } from 'typeorm'; -import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js'; import { Notification } from '@/models/entities/notification.js'; import { awaitAll } from '@/prelude/await-all.js'; -import { Packed } from '@/misc/schema.js'; -import { Note } from '@/models/entities/note.js'; -import { NoteReaction } from '@/models/entities/note-reaction.js'; -import { User } from '@/models/entities/user.js'; +import type { Packed } from '@/misc/schema.js'; +import type { Note } from '@/models/entities/note.js'; +import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import type { User } from '@/models/entities/user.js'; import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; import { notificationTypes } from '@/types.js'; import { db } from '@/db/postgre.js'; +import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js'; export const NotificationRepository = db.getRepository(Notification).extend({ async pack( @@ -17,7 +17,7 @@ export const NotificationRepository = db.getRepository(Notification).extend({ _hintForEachNotes_?: { myReactions: Map; }; - } + }, ): Promise> { const notification = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); const token = notification.appAccessTokenId ? await AccessTokens.findOneByOrFail({ id: notification.appAccessTokenId }) : null; @@ -86,7 +86,7 @@ export const NotificationRepository = db.getRepository(Notification).extend({ async packMany( notifications: Notification[], - meId: User['id'] + meId: User['id'], ) { if (notifications.length === 0) return []; @@ -106,10 +106,15 @@ export const NotificationRepository = db.getRepository(Notification).extend({ await prefetchEmojis(aggregateNoteEmojis(notes)); - return await Promise.all(notifications.map(x => this.pack(x, { - _hintForEachNotes_: { - myReactions: myReactionsMap, - }, - }))); + const results = await Promise.all(notifications + .map(x => + this.pack(x, { + _hintForEachNotes_: { + myReactions: myReactionsMap, + }, + }).catch(e => null), + ), + ); + return results.filter(x => x != null); }, }); diff --git a/packages/backend/src/remote/activitypub/kernel/update/index.ts b/packages/backend/src/remote/activitypub/kernel/update/index.ts index 9e8a81bb3..022be0ad8 100644 --- a/packages/backend/src/remote/activitypub/kernel/update/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/update/index.ts @@ -26,7 +26,7 @@ export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise console.log(e)); + await updateQuestion(object, resolver).catch(e => console.log(e)); return `ok: Question updated`; } else { return `skip: Unknown type: ${getApType(object)}`; diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 6097e3b6e..5ef04588e 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -271,7 +271,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise logger.error(err)); + await updateFeatured(user!.id, resolver).catch(err => logger.error(err)); return user!; } @@ -384,7 +384,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), }); - await updateFeatured(exist.id).catch(err => logger.error(err)); + await updateFeatured(exist.id, resolver).catch(err => logger.error(err)); } /** @@ -462,14 +462,14 @@ export function analyzeAttachments(attachments: IObject | IObject[] | undefined) return { fields, services }; } -export async function updateFeatured(userId: User['id']) { +export async function updateFeatured(userId: User['id'], resolver?: Resolver) { const user = await Users.findOneByOrFail({ id: userId }); if (!Users.isRemoteUser(user)) return; if (!user.featured) return; logger.info(`Updating the featured: ${user.uri}`); - const resolver = new Resolver(); + if (resolver == null) resolver = new Resolver(); // Resolve to (Ordered)Collection Object const collection = await resolver.resolveCollection(user.featured); diff --git a/packages/backend/src/remote/activitypub/models/question.ts b/packages/backend/src/remote/activitypub/models/question.ts index f0321fdf2..94a50d4f7 100644 --- a/packages/backend/src/remote/activitypub/models/question.ts +++ b/packages/backend/src/remote/activitypub/models/question.ts @@ -40,7 +40,7 @@ export async function extractPollFromQuestion(source: string | IObject, resolver * @param uri URI of AP Question object * @returns true if updated */ -export async function updateQuestion(value: any) { +export async function updateQuestion(value: any, resolver?: Resolver) { const uri = typeof value === 'string' ? value : value.id; // URIใŒใ“ใฎใ‚ตใƒผใƒใƒผใ‚’ๆŒ‡ใ—ใฆใ„ใ‚‹ใชใ‚‰ใ‚นใ‚ญใƒƒใƒ— @@ -55,7 +55,7 @@ export async function updateQuestion(value: any) { //#endregion // resolve new Question object - const resolver = new Resolver(); + if (resolver == null) resolver = new Resolver(); const question = await resolver.resolve(value) as IQuestion; apLogger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts index 5c9d44292..94227e4db 100644 --- a/packages/backend/src/remote/activitypub/resolver.ts +++ b/packages/backend/src/remote/activitypub/resolver.ts @@ -19,9 +19,11 @@ import renderFollow from '@/remote/activitypub/renderer/follow.js'; export default class Resolver { private history: Set; private user?: ILocalUser; + private recursionLimit?: number; - constructor() { + constructor(recursionLimit = 100) { this.history = new Set(); + this.recursionLimit = recursionLimit; } public getHistory(): string[] { @@ -59,7 +61,9 @@ export default class Resolver { if (this.history.has(value)) { throw new Error('cannot resolve already resolved one'); } - + if (this.recursionLimit && this.history.size > this.recursionLimit) { + throw new Error('hit recursion limit'); + } this.history.add(value); const host = extractDbHost(value); diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css index cfdaac600..5072e0ad4 100644 --- a/packages/backend/src/server/web/style.css +++ b/packages/backend/src/server/web/style.css @@ -42,7 +42,7 @@ html { width: 28px; height: 28px; transform: translateY(110px); - display: none !important; + display: none; color: var(--accent); } #splashSpinner > .spinner { @@ -101,6 +101,16 @@ html { } } +@media(prefers-reduced-motion) { + #splashSpinner { + display: block; + } + + #splashIcon { + animation: none; + } +} + #splashText { position: absolute; top: 0; diff --git a/packages/client/src/components/MkInstanceTicker.vue b/packages/client/src/components/MkInstanceTicker.vue index ed9d30b8c..5c53b5fb3 100644 --- a/packages/client/src/components/MkInstanceTicker.vue +++ b/packages/client/src/components/MkInstanceTicker.vue @@ -64,6 +64,7 @@ const bg = { font-size: 0.9em; vertical-align: top; font-weight: bold; + text-overflow: clip; } } diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue index 597d20c5b..20d2bbee4 100644 --- a/packages/client/src/components/MkNote.vue +++ b/packages/client/src/components/MkNote.vue @@ -71,21 +71,21 @@
- - -
@@ -427,7 +427,7 @@ function readPromo() { display: flex; padding: 28px 32px 18px; cursor: pointer; - + > .avatar { flex-shrink: 0; display: block; diff --git a/packages/client/src/components/MkNoteDetailed.vue b/packages/client/src/components/MkNoteDetailed.vue index 233143ca6..54d7ec0ca 100644 --- a/packages/client/src/components/MkNoteDetailed.vue +++ b/packages/client/src/components/MkNoteDetailed.vue @@ -81,21 +81,21 @@ - - - @@ -117,7 +117,7 @@