Add importCompleted notification. Send importCompleted when antenna/customEmoji/muting/userList is imported
The only userImportableEntities that don't notify are blocking and following because they fork off a batch of single Closes #891
This commit is contained in:
parent
69f3c8a58e
commit
a00a3c6841
14 changed files with 104 additions and 7 deletions
8
locales/index.d.ts
vendored
8
locales/index.d.ts
vendored
|
|
@ -10378,6 +10378,10 @@ export interface Locale extends ILocale {
|
|||
* Scheduled Note was posted
|
||||
*/
|
||||
"scheduledNotePosted": string;
|
||||
/**
|
||||
* Import of {x} has been completed
|
||||
*/
|
||||
"importOfXCompleted": ParameterizedString<"x">;
|
||||
};
|
||||
"_deck": {
|
||||
/**
|
||||
|
|
@ -13346,6 +13350,10 @@ export interface Locale extends ILocale {
|
|||
* Don't delete files used as avatars&c
|
||||
*/
|
||||
"keepFilesInUse": string;
|
||||
/**
|
||||
* this option requires more complicated database queries, you may need to increase the value of db.extra.statement_timeout in the configuration file
|
||||
*/
|
||||
"keepFilesInUseDescription": string;
|
||||
};
|
||||
}
|
||||
declare const locales: {
|
||||
|
|
|
|||
|
|
@ -187,6 +187,10 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
exportedEntity: notification.exportedEntity,
|
||||
fileId: notification.fileId,
|
||||
} : {}),
|
||||
...(notification.type === 'importCompleted' ? {
|
||||
importedEntity: notification.importedEntity,
|
||||
fileId: notification.fileId,
|
||||
} : {}),
|
||||
...(notification.type === 'scheduledNoteFailed' ? {
|
||||
reason: notification.reason,
|
||||
} : {}),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { userExportableEntities } from '@/types.js';
|
||||
import { userExportableEntities, userImportableEntities } from '@/types.js';
|
||||
import { MiUser } from './User.js';
|
||||
import { MiNote } from './Note.js';
|
||||
import { MiAccessToken } from './AccessToken.js';
|
||||
|
|
@ -92,6 +92,12 @@ export type MiNotification = {
|
|||
createdAt: string;
|
||||
exportedEntity: typeof userExportableEntities[number];
|
||||
fileId: MiDriveFile['id'];
|
||||
} | {
|
||||
type: 'importCompleted';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
importedEntity: typeof userImportableEntities[number];
|
||||
fileId?: MiDriveFile['id'];
|
||||
} | {
|
||||
type: 'login';
|
||||
id: string;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { notificationTypes, userExportableEntities } from '@/types.js';
|
||||
import { notificationTypes, userExportableEntities, userImportableEntities } from '@/types.js';
|
||||
|
||||
const baseSchema = {
|
||||
type: 'object',
|
||||
|
|
@ -334,6 +334,26 @@ export const packedNotificationSchema = {
|
|||
format: 'id',
|
||||
},
|
||||
},
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
...baseSchema.properties,
|
||||
type: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
enum: ['importCompleted'],
|
||||
},
|
||||
importedEntity: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
enum: userImportableEntities,
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
optional: false, nullable: false,
|
||||
format: 'id',
|
||||
},
|
||||
},
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import type { AntennasRepository, UsersRepository } from '@/models/_.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import { NotificationService } from '@/core/NotificationService.js';
|
||||
import { DBAntennaImportJobData } from '../types.js';
|
||||
import type * as Bull from 'bullmq';
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ export class ImportAntennasProcessorService {
|
|||
private queueLoggerService: QueueLoggerService,
|
||||
private idService: IdService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private notificationService: NotificationService,
|
||||
) {
|
||||
this.logger = this.queueLoggerService.logger.createSubLogger('import-antennas');
|
||||
}
|
||||
|
|
@ -106,6 +108,10 @@ export class ImportAntennasProcessorService {
|
|||
this.logger.debug('Antenna created: ' + result.id);
|
||||
this.globalEventService.publishInternalEvent('antennaCreated', result);
|
||||
}
|
||||
|
||||
this.notificationService.createNotification(job.data.user.id, 'importCompleted', {
|
||||
importedEntity: 'antenna',
|
||||
});
|
||||
} catch (err: any) {
|
||||
this.logger.error('Error importing antennas:', err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { bindThis } from '@/decorators.js';
|
|||
import type { Config } from '@/config.js';
|
||||
import { renderInlineError } from '@/misc/render-inline-error.js';
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import { NotificationService } from '@/core/NotificationService.js';
|
||||
import type * as Bull from 'bullmq';
|
||||
import type { DbUserImportJobData } from '../types.js';
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ export class ImportCustomEmojisProcessorService {
|
|||
private driveService: DriveService,
|
||||
private downloadService: DownloadService,
|
||||
private queueLoggerService: QueueLoggerService,
|
||||
private notificationService: NotificationService,
|
||||
) {
|
||||
this.logger = this.queueLoggerService.logger.createSubLogger('import-custom-emojis');
|
||||
}
|
||||
|
|
@ -127,7 +129,12 @@ export class ImportCustomEmojisProcessorService {
|
|||
|
||||
cleanup();
|
||||
|
||||
this.logger.debug('Imported');
|
||||
this.notificationService.createNotification(job.data.user.id, 'importCompleted', {
|
||||
importedEntity: 'customEmoji',
|
||||
fileId: file.id,
|
||||
});
|
||||
|
||||
this.logger.debug('Imported', file.name);
|
||||
} catch (e) {
|
||||
this.logger.error('Error importing custom emojis:', e as Error);
|
||||
cleanup();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { UtilityService } from '@/core/UtilityService.js';
|
|||
import { bindThis } from '@/decorators.js';
|
||||
import { renderInlineError } from '@/misc/render-inline-error.js';
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import { NotificationService } from '@/core/NotificationService.js';
|
||||
import type * as Bull from 'bullmq';
|
||||
import type { DbUserImportJobData } from '../types.js';
|
||||
|
||||
|
|
@ -35,6 +36,7 @@ export class ImportMutingProcessorService {
|
|||
private remoteUserResolveService: RemoteUserResolveService,
|
||||
private downloadService: DownloadService,
|
||||
private queueLoggerService: QueueLoggerService,
|
||||
private notificationService: NotificationService,
|
||||
) {
|
||||
this.logger = this.queueLoggerService.logger.createSubLogger('import-muting');
|
||||
}
|
||||
|
|
@ -99,6 +101,11 @@ export class ImportMutingProcessorService {
|
|||
}
|
||||
}
|
||||
|
||||
this.notificationService.createNotification(job.data.user.id, 'importCompleted', {
|
||||
importedEntity: 'muting',
|
||||
fileId: file.id,
|
||||
});
|
||||
|
||||
this.logger.debug('Imported');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { UtilityService } from '@/core/UtilityService.js';
|
|||
import { bindThis } from '@/decorators.js';
|
||||
import { renderInlineError } from '@/misc/render-inline-error.js';
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import { NotificationService } from '@/core/NotificationService.js';
|
||||
import type * as Bull from 'bullmq';
|
||||
import type { DbUserImportJobData } from '../types.js';
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ export class ImportUserListsProcessorService {
|
|||
private remoteUserResolveService: RemoteUserResolveService,
|
||||
private downloadService: DownloadService,
|
||||
private queueLoggerService: QueueLoggerService,
|
||||
private notificationService: NotificationService,
|
||||
) {
|
||||
this.logger = this.queueLoggerService.logger.createSubLogger('import-user-lists');
|
||||
}
|
||||
|
|
@ -109,6 +111,11 @@ export class ImportUserListsProcessorService {
|
|||
}
|
||||
}
|
||||
|
||||
this.notificationService.createNotification(job.data.user.id, 'importCompleted', {
|
||||
importedEntity: 'userList',
|
||||
fileId: file.id,
|
||||
});
|
||||
|
||||
this.logger.debug('Imported');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ export const notificationTypes = [
|
|||
'chatRoomInvitationReceived',
|
||||
'achievementEarned',
|
||||
'exportCompleted',
|
||||
'importCompleted',
|
||||
'login',
|
||||
'createToken',
|
||||
'scheduledNoteFailed',
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ export const notificationTypes = [
|
|||
'chatRoomInvitationReceived',
|
||||
'achievementEarned',
|
||||
'exportCompleted',
|
||||
'importCompleted',
|
||||
'login',
|
||||
'createToken',
|
||||
'test',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div :class="$style.root">
|
||||
<div :class="$style.head">
|
||||
<MkAvatar v-if="['pollEnded', 'note', 'edited', 'scheduledNotePosted'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'login', 'createToken', 'scheduledNoteFailed'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'importCompleted', 'login', 'createToken', 'scheduledNoteFailed'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
||||
<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div>
|
||||
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div>
|
||||
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
||||
|
|
@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
[$style.t_pollEnded]: notification.type === 'pollEnded',
|
||||
[$style.t_achievementEarned]: notification.type === 'achievementEarned',
|
||||
[$style.t_exportCompleted]: notification.type === 'exportCompleted',
|
||||
[$style.t_importCompleted]: notification.type === 'importCompleted',
|
||||
[$style.t_login]: notification.type === 'login',
|
||||
[$style.t_createToken]: notification.type === 'createToken',
|
||||
[$style.t_chatRoomInvitationReceived]: notification.type === 'chatRoomInvitationReceived',
|
||||
|
|
@ -44,6 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<i v-else-if="notification.type === 'pollEnded'" class="ti ti-chart-arrows"></i>
|
||||
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
|
||||
<i v-else-if="notification.type === 'exportCompleted'" class="ti ti-archive"></i>
|
||||
<i v-else-if="notification.type === 'importCompleted'" class="ti ti-archive"></i>
|
||||
<i v-else-if="notification.type === 'login'" class="ti ti-login-2"></i>
|
||||
<i v-else-if="notification.type === 'createToken'" class="ti ti-key"></i>
|
||||
<i v-else-if="notification.type === 'chatRoomInvitationReceived'" class="ti ti-messages"></i>
|
||||
|
|
@ -75,6 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span v-else-if="notification.type === 'createToken'">{{ i18n.ts._notification.createToken }}</span>
|
||||
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
|
||||
<span v-else-if="notification.type === 'exportCompleted'">{{ i18n.tsx._notification.exportOfXCompleted({ x: exportEntityName[notification.exportedEntity] }) }}</span>
|
||||
<span v-else-if="notification.type === 'importCompleted'">{{ i18n.tsx._notification.importOfXCompleted({ x: importEntityName[notification.importedEntity] }) }}</span>
|
||||
<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
|
||||
<span v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'">{{ i18n.tsx._notification.likedBySomeUsers({ n: getActualReactedUsersCount(notification) }) }}</span>
|
||||
<span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.tsx._notification.reactedBySomeUsers({ n: getActualReactedUsersCount(notification) }) }}</span>
|
||||
|
|
@ -122,7 +125,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkA v-else-if="notification.type === 'achievementEarned'" :class="$style.text" to="/my/achievements">
|
||||
{{ i18n.ts._achievements._types['_' + notification.achievement].title }}
|
||||
</MkA>
|
||||
<MkA v-else-if="notification.type === 'exportCompleted'" :class="$style.text" :to="`/my/drive/file/${notification.fileId}`">
|
||||
<MkA v-else-if="notification.type === 'exportCompleted' || (notification.type === 'importCompleted' && notification.fileId)" :class="$style.text" :to="`/my/drive/file/${notification.fileId}`">
|
||||
{{ i18n.ts.showFile }}
|
||||
</MkA>
|
||||
<MkA v-else-if="notification.type === 'createToken'" :class="$style.text" to="/settings/apps">
|
||||
|
|
@ -219,6 +222,7 @@ const props = withDefaults(defineProps<{
|
|||
});
|
||||
|
||||
type ExportCompletedNotification = Misskey.entities.Notification & { type: 'exportCompleted' };
|
||||
type ImportCompletedNotification = Misskey.entities.Notification & { type: 'importCompleted' };
|
||||
|
||||
const exportEntityName = {
|
||||
antenna: i18n.ts.antennas,
|
||||
|
|
@ -232,6 +236,15 @@ const exportEntityName = {
|
|||
userList: i18n.ts.lists,
|
||||
} as const satisfies Record<ExportCompletedNotification['exportedEntity'], string>;
|
||||
|
||||
const importEntityName = {
|
||||
antenna: i18n.ts.antennas,
|
||||
blocking: i18n.ts.blockedUsers,
|
||||
customEmoji: i18n.ts.customEmojis,
|
||||
following: i18n.ts.following,
|
||||
muting: i18n.ts.mutedUsers,
|
||||
userList: i18n.ts.lists,
|
||||
} as const satisfies Record<ImportCompletedNotification['importedEntity'], string>;
|
||||
|
||||
const followRequestDone = ref(true);
|
||||
const userDetailed: Ref<Misskey.entities.UserDetailed | null> = ref(null);
|
||||
|
||||
|
|
@ -398,7 +411,7 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
.t_exportCompleted {
|
||||
.t_exportCompleted, .t_importCompleted {
|
||||
background: var(--eventOther);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
|
|||
|
||||
const $i = ensureSignin();
|
||||
|
||||
const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted'] satisfies (typeof notificationTypes[number])[] as string[];
|
||||
const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted', 'importCompleted'] satisfies (typeof notificationTypes[number])[] as string[];
|
||||
|
||||
const onlyOnOrOffNotificationTypes = ['app', 'achievementEarned', 'login', 'createToken', 'scheduledNoteFailed', 'scheduledNotePosted'] satisfies (typeof notificationTypes[number])[] as string[];
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,22 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif
|
|||
}];
|
||||
}
|
||||
|
||||
case 'importCompleted': {
|
||||
const entityName = {
|
||||
antenna: i18n.ts.antennas,
|
||||
blocking: i18n.ts.blockedUsers,
|
||||
customEmoji: i18n.ts.customEmojis,
|
||||
following: i18n.ts.following,
|
||||
muting: i18n.ts.mutedUsers,
|
||||
userList: i18n.ts.lists,
|
||||
} as const satisfies Record<typeof data.body.importedEntity, string>;
|
||||
|
||||
return [i18n.tsx._notification.importOfXCompleted({ x: entityName[data.body.importedEntity] }), {
|
||||
badge: iconUrl('circle-check'),
|
||||
data,
|
||||
}];
|
||||
}
|
||||
|
||||
case 'pollEnded':
|
||||
return [i18n.ts._notification.pollEnded, {
|
||||
body: data.body.note.text ?? '',
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@ _notification:
|
|||
edited: "Note got edited"
|
||||
scheduledNoteFailed: "Posting scheduled note failed"
|
||||
scheduledNotePosted: "Scheduled Note was posted"
|
||||
importOfXCompleted: "Import of {x} has been completed"
|
||||
_types:
|
||||
renote: "Boosts"
|
||||
edited: "Edits"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue