enhance: 非通知なお知らせを作成できるように

This commit is contained in:
syuilo 2023-11-05 09:04:38 +09:00
parent bdbb3266ae
commit c2ddb649f8
11 changed files with 44 additions and 2 deletions

View file

@ -29,6 +29,7 @@
- ユーザーが誤ったメールアドレスを入力した場合に招待コードが失効してしまう問題が解消されます。 - ユーザーが誤ったメールアドレスを入力した場合に招待コードが失効してしまう問題が解消されます。
- Enhance: すでにフォローしたすべての人の返信をTLに追加できるように - Enhance: すでにフォローしたすべての人の返信をTLに追加できるように
- Enhance: 未読の通知数を表示できるように - Enhance: 未読の通知数を表示できるように
- Enhance: 通知されず、確認の必要もないお知らせ(silence)を作成可能になりました
- Enhance: ローカリゼーションの更新 - Enhance: ローカリゼーションの更新
- Enhance: 依存関係の更新 - Enhance: 依存関係の更新
- Change: CWを使用する場合、注釈を空にすることは許可されなくなりました - Change: CWを使用する場合、注釈を空にすることは許可されなくなりました

2
locales/index.d.ts vendored
View file

@ -1172,6 +1172,8 @@ export interface Locale {
"readConfirmText": string; "readConfirmText": string;
"shouldNotBeUsedToPresentPermanentInfo": string; "shouldNotBeUsedToPresentPermanentInfo": string;
"dialogAnnouncementUxWarn": string; "dialogAnnouncementUxWarn": string;
"silence": string;
"silenceDescription": string;
}; };
"_initialAccountSetting": { "_initialAccountSetting": {
"accountCreated": string; "accountCreated": string;

View file

@ -1170,6 +1170,8 @@ _announcement:
readConfirmText: "「{title}」の内容を読み、既読にします。" readConfirmText: "「{title}」の内容を読み、既読にします。"
shouldNotBeUsedToPresentPermanentInfo: "特に新規ユーザーのUXを損ねる可能性が高いため、ストック情報ではなくフロー情報の掲示にお知らせを使用することを推奨します。" shouldNotBeUsedToPresentPermanentInfo: "特に新規ユーザーのUXを損ねる可能性が高いため、ストック情報ではなくフロー情報の掲示にお知らせを使用することを推奨します。"
dialogAnnouncementUxWarn: "ダイアログ形式のお知らせが同時に2つ以上ある場合、UXに悪影響を及ぼす可能性が非常に高いため、使用は慎重に行うことを推奨します。" dialogAnnouncementUxWarn: "ダイアログ形式のお知らせが同時に2つ以上ある場合、UXに悪影響を及ぼす可能性が非常に高いため、使用は慎重に行うことを推奨します。"
silence: "非通知"
silenceDescription: "オンにすると、このお知らせは通知されず、既読にする必要もなくなります。"
_initialAccountSetting: _initialAccountSetting:
accountCreated: "アカウントの作成が完了しました!" accountCreated: "アカウントの作成が完了しました!"

View file

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class AnnouncementSilence1699141698112 {
name = 'AnnouncementSilence1699141698112'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "announcement" ADD "silence" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`CREATE INDEX "IDX_7b8d9225168e962f94ea517e00" ON "announcement" ("silence") `);
}
async down(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_7b8d9225168e962f94ea517e00"`);
await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "silence"`);
}
}

View file

@ -47,6 +47,7 @@ export class AnnouncementService {
const q = this.announcementsRepository.createQueryBuilder('announcement') const q = this.announcementsRepository.createQueryBuilder('announcement')
.where('announcement.isActive = true') .where('announcement.isActive = true')
.andWhere('announcement.silence = false')
.andWhere(new Brackets(qb => { .andWhere(new Brackets(qb => {
qb.orWhere('announcement.userId = :userId', { userId: user.id }); qb.orWhere('announcement.userId = :userId', { userId: user.id });
qb.orWhere('announcement.userId IS NULL'); qb.orWhere('announcement.userId IS NULL');
@ -73,6 +74,7 @@ export class AnnouncementService {
icon: values.icon, icon: values.icon,
display: values.display, display: values.display,
forExistingUsers: values.forExistingUsers, forExistingUsers: values.forExistingUsers,
silence: values.silence,
needConfirmationToRead: values.needConfirmationToRead, needConfirmationToRead: values.needConfirmationToRead,
userId: values.userId, userId: values.userId,
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0])); }).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
@ -124,6 +126,7 @@ export class AnnouncementService {
display: values.display, display: values.display,
icon: values.icon, icon: values.icon,
forExistingUsers: values.forExistingUsers, forExistingUsers: values.forExistingUsers,
silence: values.silence,
needConfirmationToRead: values.needConfirmationToRead, needConfirmationToRead: values.needConfirmationToRead,
isActive: values.isActive, isActive: values.isActive,
}); });
@ -210,6 +213,7 @@ export class AnnouncementService {
icon: announcement.icon, icon: announcement.icon,
display: announcement.display, display: announcement.display,
needConfirmationToRead: announcement.needConfirmationToRead, needConfirmationToRead: announcement.needConfirmationToRead,
silence: announcement.silence,
forYou: announcement.userId === me?.id, forYou: announcement.userId === me?.id,
isRead: reads.some(read => read.announcementId === announcement.id), isRead: reads.some(read => read.announcementId === announcement.id),
})); }));

View file

@ -66,6 +66,12 @@ export class MiAnnouncement {
}) })
public forExistingUsers: boolean; public forExistingUsers: boolean;
@Index()
@Column('boolean', {
default: false,
})
public silence: boolean;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),

View file

@ -58,6 +58,7 @@ export const paramDef = {
icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'], default: 'info' }, icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'], default: 'info' },
display: { type: 'string', enum: ['normal', 'banner', 'dialog'], default: 'normal' }, display: { type: 'string', enum: ['normal', 'banner', 'dialog'], default: 'normal' },
forExistingUsers: { type: 'boolean', default: false }, forExistingUsers: { type: 'boolean', default: false },
silence: { type: 'boolean', default: false },
needConfirmationToRead: { type: 'boolean', default: false }, needConfirmationToRead: { type: 'boolean', default: false },
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
}, },
@ -78,6 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
icon: ps.icon, icon: ps.icon,
display: ps.display, display: ps.display,
forExistingUsers: ps.forExistingUsers, forExistingUsers: ps.forExistingUsers,
silence: ps.silence,
needConfirmationToRead: ps.needConfirmationToRead, needConfirmationToRead: ps.needConfirmationToRead,
userId: ps.userId, userId: ps.userId,
}, me); }, me);

View file

@ -114,6 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
display: announcement.display, display: announcement.display,
isActive: announcement.isActive, isActive: announcement.isActive,
forExistingUsers: announcement.forExistingUsers, forExistingUsers: announcement.forExistingUsers,
silence: announcement.silence,
needConfirmationToRead: announcement.needConfirmationToRead, needConfirmationToRead: announcement.needConfirmationToRead,
userId: announcement.userId, userId: announcement.userId,
reads: reads.get(announcement)!, reads: reads.get(announcement)!,

View file

@ -35,6 +35,7 @@ export const paramDef = {
icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'] }, icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'] },
display: { type: 'string', enum: ['normal', 'banner', 'dialog'] }, display: { type: 'string', enum: ['normal', 'banner', 'dialog'] },
forExistingUsers: { type: 'boolean' }, forExistingUsers: { type: 'boolean' },
silence: { type: 'boolean' },
needConfirmationToRead: { type: 'boolean' }, needConfirmationToRead: { type: 'boolean' },
isActive: { type: 'boolean' }, isActive: { type: 'boolean' },
}, },
@ -63,6 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
display: ps.display, display: ps.display,
icon: ps.icon, icon: ps.icon,
forExistingUsers: ps.forExistingUsers, forExistingUsers: ps.forExistingUsers,
silence: ps.silence,
needConfirmationToRead: ps.needConfirmationToRead, needConfirmationToRead: ps.needConfirmationToRead,
isActive: ps.isActive, isActive: ps.isActive,
}, me); }, me);

View file

@ -48,6 +48,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="announcement.forExistingUsers" :helpText="i18n.ts._announcement.forExistingUsersDescription"> <MkSwitch v-model="announcement.forExistingUsers" :helpText="i18n.ts._announcement.forExistingUsersDescription">
{{ i18n.ts._announcement.forExistingUsers }} {{ i18n.ts._announcement.forExistingUsers }}
</MkSwitch> </MkSwitch>
<MkSwitch v-model="announcement.silence" :helpText="i18n.ts._announcement.silenceDescription">
{{ i18n.ts._announcement.silence }}
</MkSwitch>
<MkSwitch v-model="announcement.needConfirmationToRead" :helpText="i18n.ts._announcement.needConfirmationToReadDescription"> <MkSwitch v-model="announcement.needConfirmationToRead" :helpText="i18n.ts._announcement.needConfirmationToReadDescription">
{{ i18n.ts._announcement.needConfirmationToRead }} {{ i18n.ts._announcement.needConfirmationToRead }}
</MkSwitch> </MkSwitch>
@ -97,6 +100,7 @@ function add() {
icon: 'info', icon: 'info',
display: 'normal', display: 'normal',
forExistingUsers: false, forExistingUsers: false,
silence: false,
needConfirmationToRead: false, needConfirmationToRead: false,
}); });
} }

View file

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<section v-for="announcement in items" :key="announcement.id" class="_panel" :class="$style.announcement"> <section v-for="announcement in items" :key="announcement.id" class="_panel" :class="$style.announcement">
<div v-if="announcement.forYou" :class="$style.forYou"><i class="ti ti-pin"></i> {{ i18n.ts.forYou }}</div> <div v-if="announcement.forYou" :class="$style.forYou"><i class="ti ti-pin"></i> {{ i18n.ts.forYou }}</div>
<div :class="$style.header"> <div :class="$style.header">
<span v-if="$i && !announcement.isRead" style="margin-right: 0.5em;">🆕</span> <span v-if="$i && !announcement.silence && !announcement.isRead" style="margin-right: 0.5em;">🆕</span>
<span style="margin-right: 0.5em;"> <span style="margin-right: 0.5em;">
<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i> <i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
<i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i> <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTime :time="announcement.updatedAt ?? announcement.createdAt" mode="detail"/> <MkTime :time="announcement.updatedAt ?? announcement.createdAt" mode="detail"/>
</div> </div>
</div> </div>
<div v-if="tab !== 'past' && $i && !announcement.isRead" :class="$style.footer"> <div v-if="tab !== 'past' && $i && !announcement.silence && !announcement.isRead" :class="$style.footer">
<MkButton primary @click="read(announcement)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton> <MkButton primary @click="read(announcement)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton>
</div> </div>
</section> </section>