wip
This commit is contained in:
parent
8bae53fb3f
commit
00b68de761
|
@ -1096,6 +1096,11 @@ doYouAgree: "同意しますか?"
|
|||
beSureToReadThisAsItIsImportant: "重要ですので必ずお読みください。"
|
||||
iHaveReadXCarefullyAndAgree: "「{x}」の内容をよく読み、同意します。"
|
||||
|
||||
_announcement:
|
||||
forExistingUsers: "既存ユーザーのみ"
|
||||
forExistingUsersDescription: "有効にすると、このお知らせ作成時点で存在するユーザーにのみお知らせが表示されます。無効にすると、このお知らせ作成後にアカウントを作成したユーザーにもお知らせが表示されます。"
|
||||
deactivate: "お知らせを終了"
|
||||
|
||||
_initialAccountSetting:
|
||||
accountCreated: "アカウントの作成が完了しました!"
|
||||
letsStartAccountSetup: "アカウントの初期設定を行いましょう。"
|
||||
|
|
|
@ -46,7 +46,10 @@ export class AnnouncementService {
|
|||
qb.orWhere('announcement.userId = :userId', { userId: user.id });
|
||||
qb.orWhere('announcement.userId IS NULL');
|
||||
}))
|
||||
.andWhere('announcement.createdAt > :createdAt', { createdAt: user.createdAt })
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('announcement.forExistingUsers = false');
|
||||
qb.orWhere('announcement.createdAt > :createdAt', { createdAt: user.createdAt });
|
||||
}))
|
||||
.andWhere(`announcement.id NOT IN (${ readsQuery.getQuery() })`);
|
||||
|
||||
q.setParameters(readsQuery.getParameters());
|
||||
|
|
|
@ -54,6 +54,12 @@ export class Announcement {
|
|||
})
|
||||
public isActive: boolean;
|
||||
|
||||
@Index()
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
})
|
||||
public forExistingUsers: boolean;
|
||||
|
||||
@Index()
|
||||
@Column({
|
||||
...id(),
|
||||
|
|
|
@ -58,6 +58,7 @@ export const paramDef = {
|
|||
text: { type: 'string', minLength: 1 },
|
||||
imageUrl: { type: 'string', nullable: true, minLength: 1 },
|
||||
display: { type: 'string', enum: ['normal', 'banner', 'dialog'], default: 'normal' },
|
||||
forExistingUsers: { type: 'boolean', default: false },
|
||||
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||
},
|
||||
required: ['title', 'text', 'imageUrl'],
|
||||
|
@ -81,6 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||
text: ps.text,
|
||||
imageUrl: ps.imageUrl,
|
||||
display: ps.display,
|
||||
forExistingUsers: ps.forExistingUsers,
|
||||
userId: ps.userId,
|
||||
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ export const paramDef = {
|
|||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@ -84,6 +85,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId);
|
||||
if (ps.userId) {
|
||||
query.andWhere('announcement.userId = :userId', { userId: ps.userId });
|
||||
} else {
|
||||
query.andWhere('announcement.userId IS NULL');
|
||||
}
|
||||
|
||||
const announcements = await query.limit(ps.limit).getMany();
|
||||
|
||||
|
@ -102,6 +108,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||
title: announcement.title,
|
||||
text: announcement.text,
|
||||
imageUrl: announcement.imageUrl,
|
||||
display: announcement.display,
|
||||
isActive: announcement.isActive,
|
||||
forExistingUsers: announcement.forExistingUsers,
|
||||
reads: reads.get(announcement)!,
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -31,8 +31,11 @@ export const paramDef = {
|
|||
title: { type: 'string', minLength: 1 },
|
||||
text: { type: 'string', minLength: 1 },
|
||||
imageUrl: { type: 'string', nullable: true, minLength: 0 },
|
||||
display: { type: 'string', enum: ['normal', 'banner', 'dialog'] },
|
||||
forExistingUsers: { type: 'boolean' },
|
||||
isActive: { type: 'boolean' },
|
||||
},
|
||||
required: ['id', 'title', 'text', 'imageUrl'],
|
||||
required: ['id', 'title', 'text', 'imageUrl', 'display', 'forExistingUsers', 'isActive'],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -53,6 +56,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||
text: ps.text,
|
||||
/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */
|
||||
imageUrl: ps.imageUrl || null,
|
||||
display: ps.display,
|
||||
forExistingUsers: ps.forExistingUsers,
|
||||
isActive: ps.isActive,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,9 +19,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkInput v-model="announcement.imageUrl">
|
||||
<template #label>{{ i18n.ts.imageUrl }}</template>
|
||||
</MkInput>
|
||||
<MkSwitch v-model="announcement.forExistingUsers">
|
||||
{{ i18n.ts._announcement.forExistingUsers }}
|
||||
<template #caption>{{ i18n.ts._announcement.forExistingUsersDescription }}</template>
|
||||
</MkSwitch>
|
||||
<p v-if="announcement.reads">{{ i18n.t('nUsersRead', { n: announcement.reads }) }}</p>
|
||||
<div class="buttons _buttons">
|
||||
<MkButton class="button" inline primary @click="save(announcement)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
|
||||
<MkButton class="button" inline @click="deactivate(announcement)"><i class="ti ti-trash"></i> {{ i18n.ts._announcement.deactivate }}</MkButton>
|
||||
<MkButton class="button" inline danger @click="remove(announcement)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -67,32 +72,20 @@ function remove(announcement) {
|
|||
});
|
||||
}
|
||||
|
||||
function save(announcement) {
|
||||
async function deactivate(announcement) {
|
||||
await os.apiWithDialog('admin/announcements/update', {
|
||||
...announcement,
|
||||
isActive: false,
|
||||
});
|
||||
refresh();
|
||||
}
|
||||
|
||||
async function save(announcement) {
|
||||
if (announcement.id == null) {
|
||||
os.api('admin/announcements/create', announcement).then(() => {
|
||||
os.alert({
|
||||
type: 'success',
|
||||
text: i18n.ts.saved,
|
||||
});
|
||||
refresh();
|
||||
}).catch(err => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: err,
|
||||
});
|
||||
});
|
||||
await os.apiWithDialog('admin/announcements/create', announcement);
|
||||
refresh();
|
||||
} else {
|
||||
os.api('admin/announcements/update', announcement).then(() => {
|
||||
os.alert({
|
||||
type: 'success',
|
||||
text: i18n.ts.saved,
|
||||
});
|
||||
}).catch(err => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: err,
|
||||
});
|
||||
});
|
||||
os.apiWithDialog('admin/announcements/update', announcement);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue