Skip to content

Commit

Permalink
Merge branch 'develop' into sw-set
Browse files Browse the repository at this point in the history
  • Loading branch information
tamaina committed Feb 21, 2024
2 parents 61c016f + b36e6b1 commit 142c37f
Show file tree
Hide file tree
Showing 21 changed files with 365 additions and 149 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@
-
-->
## 202x.x.x (unreleased)

### General

### Client
- Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
- Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
- Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
- Fix: チャートのラベルが消えている問題を修正
- Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正

### Server
- Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
- Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正

## 2024.2.0

Expand Down Expand Up @@ -83,6 +97,7 @@
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
- Fix: MkCodeEditorで行がずれていってしまう問題の修正
- Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正

### Server
- Enhance: 連合先のレートリミットを超過した際にリトライするようになりました
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class RepositoryUrlFromSyuiloToMisskeyDev1708266695091 {
name = 'RepositoryUrlFromSyuiloToMisskeyDev1708266695091'

async up(queryRunner) {
await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://github.com/misskey-dev/misskey' WHERE "repositoryUrl" = 'https://github.com/syuilo/misskey'`);
}

async down(queryRunner) {
// no valid down migration
}
}
4 changes: 2 additions & 2 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"@fastify/multipart": "8.1.0",
"@fastify/static": "6.12.0",
"@fastify/view": "8.2.0",
"@misskey-dev/sharp-read-bmp": "^1.1.1",
"@misskey-dev/sharp-read-bmp": "^1.2.0",
"@misskey-dev/summaly": "^5.0.3",
"@nestjs/common": "10.2.10",
"@nestjs/core": "10.2.10",
Expand Down Expand Up @@ -164,7 +164,7 @@
"rxjs": "7.8.1",
"sanitize-html": "2.11.0",
"secure-json-parse": "2.7.0",
"sharp": "0.32.6",
"sharp": "0.33.2",
"slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
Expand Down
9 changes: 5 additions & 4 deletions packages/backend/src/core/FileInfoService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import isSvg from 'is-svg';
import probeImageSize from 'probe-image-size';
import { type predictionType } from 'nsfwjs';
import sharp from 'sharp';
import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
import { encode } from 'blurhash';
import { createTempDir } from '@/misc/create-temp.js';
import { AiService } from '@/core/AiService.js';
Expand Down Expand Up @@ -122,7 +123,7 @@ export class FileInfoService {
'image/avif',
'image/svg+xml',
].includes(type.mime)) {
blurhash = await this.getBlurhash(path).catch(e => {
blurhash = await this.getBlurhash(path, type.mime).catch(e => {
warnings.push(`getBlurhash failed: ${e}`);
return undefined;
});
Expand Down Expand Up @@ -407,9 +408,9 @@ export class FileInfoService {
* Calculate average color of image
*/
@bindThis
private getBlurhash(path: string): Promise<string> {
return new Promise((resolve, reject) => {
sharp(path)
private getBlurhash(path: string, type: string): Promise<string> {
return new Promise(async (resolve, reject) => {
(await sharpBmp(path, type))
.raw()
.ensureAlpha()
.resize(64, 64, { fit: 'inside' })
Expand Down
5 changes: 2 additions & 3 deletions packages/backend/src/core/NoteCreateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { UtilityService } from '@/core/UtilityService.js';
import { UserBlockingService } from '@/core/UserBlockingService.js';
import { isReply } from '@/misc/is-reply.js';
import { trackPromise } from '@/misc/promise-tracker.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';

type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';

Expand Down Expand Up @@ -151,8 +152,6 @@ type Option = {
export class NoteCreateService implements OnApplicationShutdown {
#shutdownController = new AbortController();

public static ContainsProhibitedWordsError = class extends Error {};

constructor(
@Inject(DI.config)
private config: Config,
Expand Down Expand Up @@ -264,7 +263,7 @@ export class NoteCreateService implements OnApplicationShutdown {
}

if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) {
throw new NoteCreateService.ContainsProhibitedWordsError();
throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
}

const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host);
Expand Down
49 changes: 25 additions & 24 deletions packages/backend/src/core/ReactionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,35 +322,36 @@ export class ReactionService {
//#endregion
}

/**
* 文字列タイプのレガシーな形式のリアクションを現在の形式に変換しつつ、
* データベース上には存在する「0個のリアクションがついている」という情報を削除する。
*/
@bindThis
public convertLegacyReactions(reactions: Record<string, number>) {
const _reactions = {} as Record<string, number>;
public convertLegacyReactions(reactions: MiNote['reactions']): MiNote['reactions'] {
return Object.entries(reactions)
.filter(([, count]) => {
// `ReactionService.prototype.delete`ではリアクション削除時に、
// `MiNote['reactions']`のエントリの値をデクリメントしているが、
// デクリメントしているだけなのでエントリ自体は0を値として持つ形で残り続ける。
// そのため、この処理がなければ、「0個のリアクションがついている」ということになってしまう。
return count > 0;
})
.map(([reaction, count]) => {
// unchecked indexed access
const convertedReaction = legacies[reaction] as string | undefined;

for (const reaction of Object.keys(reactions)) {
if (reactions[reaction] <= 0) continue;
const key = this.decodeReaction(convertedReaction ?? reaction).reaction;

if (Object.keys(legacies).includes(reaction)) {
if (_reactions[legacies[reaction]]) {
_reactions[legacies[reaction]] += reactions[reaction];
} else {
_reactions[legacies[reaction]] = reactions[reaction];
}
} else {
if (_reactions[reaction]) {
_reactions[reaction] += reactions[reaction];
} else {
_reactions[reaction] = reactions[reaction];
}
}
}

const _reactions2 = {} as Record<string, number>;
return [key, count] as const;
})
.reduce<MiNote['reactions']>((acc, [key, count]) => {
// unchecked indexed access
const prevCount = acc[key] as number | undefined;

for (const reaction of Object.keys(_reactions)) {
_reactions2[this.decodeReaction(reaction).reaction] = _reactions[reaction];
}
acc[key] = (prevCount ?? 0) + count;

return _reactions2;
return acc;
}, {});
}

@bindThis
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/models/Meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ export class MiMeta {
})
public turnstileSecretKey: string | null;

// chaptcha系を追加した際にはnodeinfoのレスポンスに追加するのを忘れないようにすること

@Column('enum', {
enum: ['none', 'all', 'local', 'remote'],
default: 'none',
Expand Down
10 changes: 9 additions & 1 deletion packages/backend/src/queue/processors/InboxProcessorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js';
import { ApInboxService } from '@/core/activitypub/ApInboxService.js';
import { bindThis } from '@/decorators.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type { InboxJobData } from '../types.js';

Expand Down Expand Up @@ -180,7 +181,14 @@ export class InboxProcessorService {
});

// アクティビティを処理
await this.apInboxService.performActivity(authUser.user, activity);
try {
await this.apInboxService.performActivity(authUser.user, activity);
} catch (e) {
if (e instanceof IdentifiableError) {
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') return 'blocked notes with prohibited words';
}
throw e;
}
return 'ok';
}
}
2 changes: 2 additions & 0 deletions packages/backend/src/server/NodeinfoServerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ export class NodeinfoServerService {
emailRequiredForSignup: meta.emailRequiredForSignup,
enableHcaptcha: meta.enableHcaptcha,
enableRecaptcha: meta.enableRecaptcha,
enableMcaptcha: meta.enableMcaptcha,
enableTurnstile: meta.enableTurnstile,
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
enableEmail: meta.enableEmail,
enableServiceWorker: meta.enableServiceWorker,
Expand Down
5 changes: 3 additions & 2 deletions packages/backend/src/server/api/endpoints/notes/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { DI } from '@/di-symbols.js';
import { isPureRenote } from '@/misc/is-pure-renote.js';
import { MetaService } from '@/core/MetaService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { ApiError } from '../../error.js';

export const meta = {
Expand Down Expand Up @@ -376,8 +377,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
};
} catch (e) {
// TODO: 他のErrorもここでキャッチしてエラーメッセージを当てるようにしたい
if (e instanceof NoteCreateService.ContainsProhibitedWordsError) {
throw new ApiError(meta.errors.containsProhibitedWords);
if (e instanceof IdentifiableError) {
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') throw new ApiError(meta.errors.containsProhibitedWords);
}

throw e;
Expand Down
41 changes: 41 additions & 0 deletions packages/backend/test/unit/ReactionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,45 @@ describe('ReactionService', () => {
assert.strictEqual(await reactionService.normalize('unknown'), '❤');
});
});

describe('convertLegacyReactions', () => {
test('空の入力に対しては何もしない', () => {
const input = {};
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
});

test('Unicode絵文字リアクションを変換してしまわない', () => {
const input = { '👍': 1, '🍮': 2 };
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
});

test('カスタム絵文字リアクションを変換してしまわない', () => {
const input = { ':like@.:': 1, ':pudding@example.tld:': 2 };
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
});

test('文字列によるレガシーなリアクションを変換する', () => {
const input = { 'like': 1, 'pudding': 2 };
const output = { '👍': 1, '🍮': 2 };
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
});

test('host部分が省略されたレガシーなカスタム絵文字リアクションを変換する', () => {
const input = { ':custom_emoji:': 1 };
const output = { ':custom_emoji@.:': 1 };
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
});

test('「0個のリアクション」情報を削除する', () => {
const input = { 'angry': 0 };
const output = {};
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
});

test('host部分の有無によりデコードすると同じ表記になるカスタム絵文字リアクションの個数情報を正しく足し合わせる', () => {
const input = { ':custom_emoji:': 1, ':custom_emoji@.:': 2 };
const output = { ':custom_emoji@.:': 3 };
assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
});
});
});
2 changes: 1 addition & 1 deletion packages/frontend/src/components/MkChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ const render = () => {
},
external: externalTooltipHandler,
callbacks: {
label: (item) => chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString(),
label: (item) => `${item.dataset.label}: ${chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString()}`,
},
},
zoom: props.detailed ? {
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/MkCode.core.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async function fetchLanguage(to: string): Promise<void> {
return bundle.id === language || bundle.aliases?.includes(language);
});
if (bundles.length > 0) {
console.log(`Loading language: ${language}`);
if (_DEV_) console.log(`Loading language: ${language}`);
await highlighter.loadLanguage(bundles[0].import);
codeLang.value = language;
} else {
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/src/components/MkPostFormAttaches.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,11 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent): void {
icon: 'ti ti-crop',
action: () : void => { crop(file); },
}] : [], {
type: 'divider',
}, {
text: i18n.ts.attachCancel,
icon: 'ti ti-circle-x',
action: () => { detachMedia(file.id); },
}, {
type: 'divider',
}, {
text: i18n.ts.deleteFile,
icon: 'ti ti-trash',
Expand Down
1 change: 0 additions & 1 deletion packages/frontend/src/directives/user-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export class UserPreview {
this.el.removeEventListener('mouseover', this.onMouseover);
this.el.removeEventListener('mouseleave', this.onMouseleave);
this.el.removeEventListener('click', this.onClick);
window.clearInterval(this.checkTimer);
}
}

Expand Down
2 changes: 0 additions & 2 deletions packages/frontend/src/pages/admin/modlog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ const pagination = {
})),
};

console.log(Misskey);

const headerActions = computed(() => []);

const headerTabs = computed(() => []);
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/pages/reversi/game.board.vue
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ if (game.value.isStarted && !game.value.isEnded) {
crc32: crc32.toString(),
}).then((res) => {
if (res.desynced) {
console.log('resynced');
if (_DEV_) console.log('resynced');
restoreGame(res.game!);
}
});
Expand Down
4 changes: 3 additions & 1 deletion packages/frontend/src/scripts/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@ export class Autocomplete {
return;
}

const afterLastMfmParam = text.split(/\$\[[a-zA-Z]+/).pop();

const isMention = mentionIndex !== -1;
const isHashtag = hashtagIndex !== -1;
const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.');
const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam?.includes(' ');
const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');

Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/scripts/sound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
*/
export function playMisskeySfx(operationType: OperationType) {
const sound = defaultStore.state[`sound_${operationType}`];
if (sound.type == null || !canPlay) return;
if (sound.type == null || !canPlay || !navigator.userActivation.hasBeenActive) return;

canPlay = false;
playMisskeySfxFile(sound).finally(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/ui/deck/channel-column.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only

<template v-if="column.channelId">
<div style="padding: 8px; text-align: center;">
<MkButton primary gradate rounded inline @click="post"><i class="ti ti-pencil"></i></MkButton>
<MkButton primary gradate rounded inline small @click="post"><i class="ti ti-pencil"></i></MkButton>
</div>
<MkTimeline ref="timeline" src="channel" :channel="column.channelId"/>
</template>
Expand Down
Loading

0 comments on commit 142c37f

Please sign in to comment.