Skip to content

Commit

Permalink
Merge remote-branch 'misskey/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
noridev committed Oct 21, 2023
2 parents c38aff3 + 2697826 commit 8a1ed93
Show file tree
Hide file tree
Showing 44 changed files with 1,036 additions and 48 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@

## 2023.x.x (unreleased)

### General
-

## Client
- Feat: プラグイン・テーマを外部サイトから直接インストールできるようになりました
- 外部サイトでの実装が必要です。詳細は Misskey Hub をご覧ください
https://misskey-hub.net/docs/advanced/publish-on-your-website.html

### Server
-

## 2023.10.2

### General
- Feat: アンテナでローカルの投稿のみ収集できるようになりました
- Feat: サーバーサイレンス機能が追加されました
Expand All @@ -22,6 +35,7 @@
- Enhance: フォロー/フォロー解除したときに過去分のHTLにも含まれる投稿が反映されるように
- Enhance: ローカリゼーションの更新
- Enhance: 依存関係の更新
- Enhance: すでにフォローしたすべての人の返信をTLに追加できるように

### Client
- Enhance: TLの返信表示オプションを記憶するように
Expand Down
8 changes: 8 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1221,13 +1221,18 @@ export interface Locale {
"fileAttachedOnly": string;
"showRepliesToOthersInTimeline": string;
"hideRepliesToOthersInTimeline": string;
"showRepliesToOthersInTimelineAll": string;
"hideRepliesToOthersInTimelineAll": string;
"confirmShowRepliesAll": string;
"confirmHideRepliesAll": string;
"externalServices": string;
"impressum": string;
"impressumUrl": string;
"impressumDescription": string;
"privacyPolicy": string;
"privacyPolicyUrl": string;
"tosAndPrivacyPolicy": string;
"avatarDecorations": string;
"showUnreadNotificationCount": string;
"showCatOnly": string;
"additionalPermissionsForFlash": string;
Expand Down Expand Up @@ -2592,6 +2597,9 @@ export interface Locale {
"createAd": string;
"deleteAd": string;
"updateAd": string;
"createAvatarDecoration": string;
"updateAvatarDecoration": string;
"deleteAvatarDecoration": string;
};
"_fileViewer": {
"title": string;
Expand Down
8 changes: 8 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1218,13 +1218,18 @@ mutualFollow: "相互フォロー"
fileAttachedOnly: "ファイル付きのみ"
showRepliesToOthersInTimeline: "TLに他の人への返信を含める"
hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない"
showRepliesToOthersInTimelineAll: "TLに現在フォロー中の人全員の返信を含めるようにする"
hideRepliesToOthersInTimelineAll: "TLに現在フォロー中の人全員の返信を含めないようにする"
confirmShowRepliesAll: "この操作は元の戻せません。本当にTLに現在フォロー中の人全員の返信を含めるようにしますか"
confirmHideRepliesAll: "この操作は元の戻せません。本当にTLに現在フォロー中の人全員の返信を含めないようにしますか"
externalServices: "外部サービス"
impressum: "運営者情報"
impressumUrl: "運営者情報URL"
impressumDescription: "ドイツなどの一部の国と地域では表示が義務付けられています(Impressum)。"
privacyPolicy: "プライバシーポリシー"
privacyPolicyUrl: "プライバシーポリシーURL"
tosAndPrivacyPolicy: "利用規約・プライバシーポリシー"
avatarDecorations: "アイコンデコレーション"
showUnreadNotificationCount: "未読の通知の数を表示する"
showCatOnly: "キャット付きのみ"
additionalPermissionsForFlash: "Playへの追加許可"
Expand Down Expand Up @@ -2502,6 +2507,9 @@ _moderationLogTypes:
createAd: "広告を作成"
deleteAd: "広告を削除"
updateAd: "広告を更新"
createAvatarDecoration: "アイコンデコレーションを作成"
updateAvatarDecoration: "アイコンデコレーションを更新"
deleteAvatarDecoration: "アイコンデコレーションを削除"

_fileViewer:
title: "ファイルの詳細"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "cherrypick",
"version": "4.4.1",
"basedMisskeyVersion": "2023.10.2",
"basedMisskeyVersion": "2023.11.0-beta.1",
"codename": "nasubi",
"repository": {
"type": "git",
Expand Down
18 changes: 18 additions & 0 deletions packages/backend/migration/1697847397844-avatar-decoration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class AvatarDecoration1697847397844 {
name = 'AvatarDecoration1697847397844'

async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "avatar_decoration" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "url" character varying(1024) NOT NULL, "name" character varying(256) NOT NULL, "description" character varying(2048) NOT NULL, "roleIdsThatCanBeUsedThisDecoration" character varying(128) array NOT NULL DEFAULT '{}', CONSTRAINT "PK_b6de9296f6097078e1dc53f7603" PRIMARY KEY ("id"))`);
await queryRunner.query(`ALTER TABLE "user" ADD "avatarDecorations" character varying(512) array NOT NULL DEFAULT '{}'`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarDecorations"`);
await queryRunner.query(`DROP TABLE "avatar_decoration"`);
}
}
129 changes: 129 additions & 0 deletions packages/backend/src/core/AvatarDecorationService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import * as Redis from 'ioredis';
import type { AvatarDecorationsRepository, MiAvatarDecoration, MiUser } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { MemorySingleCache } from '@/misc/cache.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';

@Injectable()
export class AvatarDecorationService implements OnApplicationShutdown {
public cache: MemorySingleCache<MiAvatarDecoration[]>;

constructor(
@Inject(DI.redisForSub)
private redisForSub: Redis.Redis,

@Inject(DI.avatarDecorationsRepository)
private avatarDecorationsRepository: AvatarDecorationsRepository,

private idService: IdService,
private moderationLogService: ModerationLogService,
private globalEventService: GlobalEventService,
) {
this.cache = new MemorySingleCache<MiAvatarDecoration[]>(1000 * 60 * 30);

this.redisForSub.on('message', this.onMessage);
}

@bindThis
private async onMessage(_: string, data: string): Promise<void> {
const obj = JSON.parse(data);

if (obj.channel === 'internal') {
const { type, body } = obj.message as GlobalEvents['internal']['payload'];
switch (type) {
case 'avatarDecorationCreated':
case 'avatarDecorationUpdated':
case 'avatarDecorationDeleted': {
this.cache.delete();
break;
}
default:
break;
}
}
}

@bindThis
public async create(options: Partial<MiAvatarDecoration>, moderator?: MiUser): Promise<MiAvatarDecoration> {
const created = await this.avatarDecorationsRepository.insert({
id: this.idService.gen(),
...options,
}).then(x => this.avatarDecorationsRepository.findOneByOrFail(x.identifiers[0]));

this.globalEventService.publishInternalEvent('avatarDecorationCreated', created);

if (moderator) {
this.moderationLogService.log(moderator, 'createAvatarDecoration', {
avatarDecorationId: created.id,
avatarDecoration: created,
});
}

return created;
}

@bindThis
public async update(id: MiAvatarDecoration['id'], params: Partial<MiAvatarDecoration>, moderator?: MiUser): Promise<void> {
const avatarDecoration = await this.avatarDecorationsRepository.findOneByOrFail({ id });

const date = new Date();
await this.avatarDecorationsRepository.update(avatarDecoration.id, {
updatedAt: date,
...params,
});

const updated = await this.avatarDecorationsRepository.findOneByOrFail({ id: avatarDecoration.id });
this.globalEventService.publishInternalEvent('avatarDecorationUpdated', updated);

if (moderator) {
this.moderationLogService.log(moderator, 'updateAvatarDecoration', {
avatarDecorationId: avatarDecoration.id,
before: avatarDecoration,
after: updated,
});
}
}

@bindThis
public async delete(id: MiAvatarDecoration['id'], moderator?: MiUser): Promise<void> {
const avatarDecoration = await this.avatarDecorationsRepository.findOneByOrFail({ id });

await this.avatarDecorationsRepository.delete({ id: avatarDecoration.id });
this.globalEventService.publishInternalEvent('avatarDecorationDeleted', avatarDecoration);

if (moderator) {
this.moderationLogService.log(moderator, 'deleteAvatarDecoration', {
avatarDecorationId: avatarDecoration.id,
avatarDecoration: avatarDecoration,
});
}
}

@bindThis
public async getAll(noCache = false): Promise<MiAvatarDecoration[]> {
if (noCache) {
this.cache.delete();
}
return this.cache.fetch(() => this.avatarDecorationsRepository.find());
}

@bindThis
public dispose(): void {
this.redisForSub.off('message', this.onMessage);
}

@bindThis
public onApplicationShutdown(signal?: string | undefined): void {
this.dispose();
}
}
6 changes: 6 additions & 0 deletions packages/backend/src/core/CoreModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AnnouncementService } from './AnnouncementService.js';
import { AntennaService } from './AntennaService.js';
import { AppLockService } from './AppLockService.js';
import { AchievementService } from './AchievementService.js';
import { AvatarDecorationService } from './AvatarDecorationService.js';
import { CaptchaService } from './CaptchaService.js';
import { CreateSystemUserService } from './CreateSystemUserService.js';
import { CustomEmojiService } from './CustomEmojiService.js';
Expand Down Expand Up @@ -145,6 +146,7 @@ const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExis
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
const $AvatarDecorationService: Provider = { provide: 'AvatarDecorationService', useExisting: AvatarDecorationService };
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
const $CreateSystemUserService: Provider = { provide: 'CreateSystemUserService', useExisting: CreateSystemUserService };
const $CustomEmojiService: Provider = { provide: 'CustomEmojiService', useExisting: CustomEmojiService };
Expand Down Expand Up @@ -283,6 +285,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
AntennaService,
AppLockService,
AchievementService,
AvatarDecorationService,
CaptchaService,
CreateSystemUserService,
CustomEmojiService,
Expand Down Expand Up @@ -414,6 +417,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
$AntennaService,
$AppLockService,
$AchievementService,
$AvatarDecorationService,
$CaptchaService,
$CreateSystemUserService,
$CustomEmojiService,
Expand Down Expand Up @@ -546,6 +550,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
AntennaService,
AppLockService,
AchievementService,
AvatarDecorationService,
CaptchaService,
CreateSystemUserService,
CustomEmojiService,
Expand Down Expand Up @@ -676,6 +681,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
$AntennaService,
$AppLockService,
$AchievementService,
$AvatarDecorationService,
$CaptchaService,
$CreateSystemUserService,
$CustomEmojiService,
Expand Down
5 changes: 4 additions & 1 deletion packages/backend/src/core/GlobalEventService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { MiSignin } from '@/models/Signin.js';
import type { MiPage } from '@/models/Page.js';
import type { MiWebhook } from '@/models/Webhook.js';
import type { MiMeta } from '@/models/Meta.js';
import { MiRole, MiRoleAssignment } from '@/models/_.js';
import { MiAvatarDecoration, MiRole, MiRoleAssignment } from '@/models/_.js';
import type { Packed } from '@/misc/json-schema.js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
Expand Down Expand Up @@ -219,6 +219,9 @@ export interface InternalEventTypes {
antennaCreated: MiAntenna;
antennaDeleted: MiAntenna;
antennaUpdated: MiAntenna;
avatarDecorationCreated: MiAvatarDecoration;
avatarDecorationDeleted: MiAvatarDecoration;
avatarDecorationUpdated: MiAvatarDecoration;
metaUpdated: MiMeta;
followChannel: { userId: MiUser['id']; channelId: MiChannel['id']; };
unfollowChannel: { userId: MiUser['id']; channelId: MiChannel['id']; };
Expand Down
6 changes: 6 additions & 0 deletions packages/backend/src/core/RoleService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,12 @@ export class RoleService implements OnApplicationShutdown {
}
}

@bindThis
public async getRoles() {
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
return roles;
}

@bindThis
public async getUserAssigns(userId: MiUser['id']) {
const now = Date.now();
Expand Down
13 changes: 9 additions & 4 deletions packages/backend/src/core/entities/UserEntityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import { RoleService } from '@/core/RoleService.js';
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { IdService } from '@/core/IdService.js';
import type { AnnouncementService } from '@/core/AnnouncementService.js';
import type { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
import type { OnModuleInit } from '@nestjs/common';
import type { AnnouncementService } from '../AnnouncementService.js';
import type { CustomEmojiService } from '../CustomEmojiService.js';
import type { NoteEntityService } from './NoteEntityService.js';
import type { DriveFileEntityService } from './DriveFileEntityService.js';
import type { PageEntityService } from './PageEntityService.js';
Expand Down Expand Up @@ -63,6 +64,7 @@ export class UserEntityService implements OnModuleInit {
private roleService: RoleService;
private federatedInstanceService: FederatedInstanceService;
private idService: IdService;
private avatarDecorationService: AvatarDecorationService;

constructor(
private moduleRef: ModuleRef,
Expand Down Expand Up @@ -133,6 +135,7 @@ export class UserEntityService implements OnModuleInit {
this.roleService = this.moduleRef.get('RoleService');
this.federatedInstanceService = this.moduleRef.get('FederatedInstanceService');
this.idService = this.moduleRef.get('IdService');
this.avatarDecorationService = this.moduleRef.get('AvatarDecorationService');
}

//#region Validators
Expand Down Expand Up @@ -384,15 +387,17 @@ export class UserEntityService implements OnModuleInit {

const notificationsInfo = isMe && opts.detail ? await this.getNotificationsInfo(user.id) : null;

const falsy = opts.detail ? false : undefined;

const packed = {
id: user.id,
name: user.name,
username: user.username,
host: user.host,
avatarUrl: user.avatarUrl ?? this.getIdenticonUrl(user),
avatarBlurhash: user.avatarBlurhash,
avatarDecorations: user.avatarDecorations.length > 0 ? this.avatarDecorationService.getAll().then(decorations => decorations.filter(decoration => user.avatarDecorations.includes(decoration.id)).map(decoration => ({
id: decoration.id,
url: decoration.url,
}))) : [],
isBot: user.isBot,
isCat: user.isCat,
instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? {
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/di-symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const DI = {
announcementsRepository: Symbol('announcementsRepository'),
announcementReadsRepository: Symbol('announcementReadsRepository'),
appsRepository: Symbol('appsRepository'),
avatarDecorationsRepository: Symbol('avatarDecorationsRepository'),
noteFavoritesRepository: Symbol('noteFavoritesRepository'),
noteThreadMutingsRepository: Symbol('noteThreadMutingsRepository'),
noteReactionsRepository: Symbol('noteReactionsRepository'),
Expand Down
Loading

0 comments on commit 8a1ed93

Please sign in to comment.