diff --git a/.github/ISSUE_TEMPLATE/02_feature-request.yml b/.github/ISSUE_TEMPLATE/02_feature-request.yml index 17926412f..8420475b3 100644 --- a/.github/ISSUE_TEMPLATE/02_feature-request.yml +++ b/.github/ISSUE_TEMPLATE/02_feature-request.yml @@ -9,3 +9,9 @@ body: description: Tell us what the suggestion is validations: required: true + - type: textarea + attributes: + label: Purpose + description: Describe the specific problem or need you think this feature will solve, and who it will help. + validations: + required: true \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ee98f4ccb..57415554e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,19 @@ --> +## 2023.x.x (unreleased) + +### General +- + +### Client +- Fix: アイコンデコレーションが複数の場所で見切れている問題を修正 +― Fix: 「フォロー中の人全員の返信を含める/含めないようにする」のボタンを押下した際の確認が機能していない問題を修正 + +### Server +- Fix: トークンのないプラグインをアンインストールするときにエラーが出ないように +- Fix: 投稿通知がオンでもダイレクト投稿はユーザーに通知されないようにされました + ## 2023.11.0 ### Note diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index acd11a9fa..86f220abd 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -521,11 +521,13 @@ export class NoteCreateService implements OnApplicationShutdown { followeeId: user.id, notify: 'normal', }).then(followings => { - for (const following of followings) { - // TODO: ワードミュート考慮 - this.notificationService.createNotification(following.followerId, 'note', { - noteId: note.id, - }, user.id); + if (note.visibility !== 'specified') { + for (const following of followings) { + // TODO: ワードミュート考慮 + this.notificationService.createNotification(following.followerId, 'note', { + noteId: note.id, + }, user.id); + } } }); } diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 8bba150ec..7d16a7a80 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -47,6 +47,7 @@ export class InstanceEntityService { faviconUrl: instance.faviconUrl, themeColor: instance.themeColor, infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, + latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null, }; } diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index 4ad84d02f..442e1076f 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -103,5 +103,10 @@ export const packedFederationInstanceSchema = { optional: false, nullable: true, format: 'date-time', }, + latestRequestReceivedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index e8bb28253..98d866f86 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -18,7 +18,7 @@ export const paramDef = { type: 'object', properties: { tokenId: { type: 'string', format: 'misskey:id' }, - token: { type: 'string' }, + token: { type: 'string', nullable: true }, }, anyOf: [ { required: ['tokenId'] }, diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 7a2a52a98..3e35d5415 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -175,6 +175,7 @@ export class ClientServerService { serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg', infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg', notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg', + instanceUrl: this.config.url, }; } diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 9b6c671ca..2cb3fd473 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -26,6 +26,7 @@ html meta(name='theme-color' content= themeColor || '#86b300') meta(name='theme-color-orig' content= themeColor || '#86b300') meta(property='og:site_name' content= instanceName || 'Misskey') + meta(property='instance_url' content= instanceUrl) meta(name='viewport' content='width=device-width, initial-scale=1') link(rel='icon' href= icon || '/favicon.ico') link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png') diff --git a/packages/frontend/package.json b/packages/frontend/package.json index de7492264..25b2fcb4c 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -29,7 +29,7 @@ "@vue/compiler-sfc": "3.3.7", "astring": "1.8.6", "autosize": "6.0.1", - "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.5", + "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6", "broadcast-channel": "6.0.0", "browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3", "buraha": "0.0.1", diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index b11d0db04..71236e4c5 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -8,7 +8,7 @@ import { common } from './common.js'; import { version, ui, lang, updateLocale } from '@/config.js'; import { i18n, updateI18n } from '@/i18n.js'; import { confirm, alert, post, popup, toast } from '@/os.js'; -import { useStream, isReloading } from '@/stream.js'; +import { useStream } from '@/stream.js'; import * as sound from '@/scripts/sound.js'; import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js'; import { defaultStore, ColdDeviceStorage } from '@/store.js'; @@ -39,7 +39,6 @@ export async function mainBoot() { let reloadDialogShowing = false; stream.on('_disconnected_', async () => { - if (isReloading) return; if (defaultStore.state.serverDisconnectedBehavior === 'reload') { location.reload(); } else if (defaultStore.state.serverDisconnectedBehavior === 'dialog') { @@ -58,7 +57,7 @@ export async function mainBoot() { }); for (const plugin of ColdDeviceStorage.get('plugins').filter(p => p.active)) { - import('../plugin').then(async ({ install }) => { + import('@/plugin.js').then(async ({ install }) => { // Workaround for https://bugs.webkit.org/show_bug.cgi?id=242740 await new Promise(r => setTimeout(r, 0)); install(plugin); diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 0ae3423a2..7e8b4c9b2 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -202,11 +202,17 @@ let note = $ref(deepClone(props.note)); // plugin if (noteViewInterruptors.length > 0) { onMounted(async () => { - let result:Misskey.entities.Note | null = deepClone(note); + let result: Misskey.entities.Note | null = deepClone(note); for (const interruptor of noteViewInterruptors) { - result = await interruptor.handler(result); - - if (result === null) return isDeleted.value = true; + try { + result = await interruptor.handler(result); + if (result === null) { + isDeleted.value = true; + return; + } + } catch (err) { + console.error(err); + } } note = result; }); @@ -228,8 +234,8 @@ const clipButton = shallowRef(); let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note); const isMyRenote = $i && ($i.id === note.userId); const showContent = ref(false); -const parsed = appearNote.text ? mfm.parse(appearNote.text) : null; -const urls = parsed ? extractUrlFromMfm(parsed) : null; +const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null); +const urls = $computed(() => parsed ? extractUrlFromMfm(parsed) : null); const isLong = shouldCollapsed(appearNote, urls ?? []); const collapsed = ref(appearNote.cw == null && isLong); const isDeleted = ref(false); @@ -298,7 +304,7 @@ function renote(viaKeyboard = false) { const { menu } = getRenoteMenu({ note: note, renoteButton, mock: props.mock }); os.popupMenu(menu, renoteButton.value, { viaKeyboard, - }).then(focus); + }); } function reply(viaKeyboard = false): void { diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 4ba7c7384..e339aa2b2 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -239,11 +239,17 @@ let note = $ref(deepClone(props.note)); // plugin if (noteViewInterruptors.length > 0) { onMounted(async () => { - let result:Misskey.entities.Note | null = deepClone(note); + let result: Misskey.entities.Note | null = deepClone(note); for (const interruptor of noteViewInterruptors) { - result = await interruptor.handler(result); - - if (result === null) return isDeleted.value = true; + try { + result = await interruptor.handler(result); + if (result === null) { + isDeleted.value = true; + return; + } + } catch (err) { + console.error(err); + } } note = result; }); @@ -344,7 +350,7 @@ function renote(viaKeyboard = false) { const { menu } = getRenoteMenu({ note: note, renoteButton }); os.popupMenu(menu, renoteButton.value, { viaKeyboard, - }).then(focus); + }); } function reply(viaKeyboard = false): void { diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 77e66f016..0c817bd64 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -96,6 +96,10 @@ onUnmounted(() => { onDeactivated(() => { if (connection) connection.dispose(); }); + +defineExpose({ + reload, +});