Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10427,6 +10427,10 @@ export interface Locale extends ILocale {
* Edits
*/
"edited": string;
/**
* Accepted emoji suggestions
*/
"emojiSuggestionAccepted": string;
/**
* Posting scheduled note failed
*/
Expand Down Expand Up @@ -10482,6 +10486,14 @@ export interface Locale extends ILocale {
* Shared access login
*/
"sharedAccessLogin": string;
/**
* Emoji suggestion accepted
*/
"emojiSuggestionAccepted": string;
/**
* :{name}: was added to this server.
*/
"emojiSuggestionAcceptedDescription": ParameterizedString<"name">;
};
"_deck": {
/**
Expand Down Expand Up @@ -12088,6 +12100,10 @@ export interface Locale extends ILocale {
* There are no pending emoji suggestions.
*/
"emojiSuggestionNoPending": string;
/**
* Emoji suggestions are waiting for review.
*/
"emojiSuggestionsPendingReview": string;
/**
* Proposed by
*/
Expand Down
37 changes: 35 additions & 2 deletions packages/backend/src/core/EmojiSuggestionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { DriveService } from '@/core/DriveService.js';
import { NotificationService } from '@/core/NotificationService.js';
import { RoleService } from '@/core/RoleService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { LoggerService } from '@/core/LoggerService.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
Expand Down Expand Up @@ -65,6 +68,9 @@ export class EmojiSuggestionService {

private readonly customEmojiService: CustomEmojiService,
private readonly driveService: DriveService,
private readonly notificationService: NotificationService,
private readonly roleService: RoleService,
private readonly globalEventService: GlobalEventService,
private readonly idService: IdService,
loggerService: LoggerService,
) {
Expand Down Expand Up @@ -122,6 +128,8 @@ export class EmojiSuggestionService {
throw error;
}

await this.publishQueueChanged();

return { ok: true, value: suggestion };
}

Expand All @@ -136,6 +144,14 @@ export class EmojiSuggestionService {
});
if (suggestion == null) return { ok: false, reason: 'noSuchSuggestion' };

const acceptedResult = async (emoji: MiEmoji): Promise<EmojiSuggestionResult<MiEmoji>> => {
this.notificationService.createNotification(suggestion.userId, 'emojiSuggestionAccepted', {
emojiName: suggestion.name,
});
await this.publishQueueChanged();
Comment thread
PrivateGER marked this conversation as resolved.
return { ok: true, value: emoji };
};

// Consume the suggestion before doing any work. This makes acceptance,
// cancellation, rejection, and another acceptance mutually exclusive.
const claimed = await this.emojiSuggestionsRepository.delete({
Expand Down Expand Up @@ -195,7 +211,7 @@ export class EmojiSuggestionService {
roleIdsThatCanBeUsedThisEmojiAsReaction: [],
}, { moderator });

return { ok: true, value: emoji };
return await acceptedResult(emoji);
} catch (error) {
if (emojiFile != null) {
// createEmoji inserts before publishing and moderation logging. If a
Expand All @@ -205,7 +221,7 @@ export class EmojiSuggestionService {
host: IsNull(),
originalUrl: emojiFile.url,
});
if (insertedEmoji != null) return { ok: true, value: insertedEmoji };
if (insertedEmoji != null) return await acceptedResult(insertedEmoji);

try {
await this.driveService.deleteFile(emojiFile, false, moderator);
Expand All @@ -231,12 +247,29 @@ export class EmojiSuggestionService {
id: suggestionId,
userId: user.id,
});
if (result.affected === 1) await this.publishQueueChanged();
return result.affected === 1;
}

@bindThis
public async reject(suggestionId: string): Promise<boolean> {
const result = await this.emojiSuggestionsRepository.delete(suggestionId);
if (result.affected === 1) await this.publishQueueChanged();
return result.affected === 1;
}

private async publishQueueChanged(): Promise<void> {
try {
const reviewerIds = await this.roleService.getModeratorIds({
includeAdmins: true,
includeRoot: true,
excludeExpire: true,
});
await Promise.all(reviewerIds.map(reviewerId =>
this.globalEventService.publishAdminStream(reviewerId, 'emojiSuggestionQueueChanged', {}),
));
} catch (error) {
this.logger.error(`Failed to publish emoji suggestion queue update: ${renderInlineError(error)}`);
}
}
}
1 change: 1 addition & 0 deletions packages/backend/src/core/GlobalEventService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export interface AdminEventTypes {
reporterId: MiUser['id'],
comment: string;
};
emojiSuggestionQueueChanged: EmptyObject;
}

export interface ChatEventTypes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ export class NotificationEntityService implements OnModuleInit {
...(notification.type === 'achievementEarned' ? {
achievement: notification.achievement,
} : {}),
...(notification.type === 'emojiSuggestionAccepted' ? {
emojiName: notification.emojiName,
} : {}),
...(notification.type === 'exportCompleted' ? {
exportedEntity: notification.exportedEntity,
fileId: notification.fileId,
Expand Down
5 changes: 5 additions & 0 deletions packages/backend/src/models/Notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ export type MiNotification = {
id: string;
createdAt: string;
achievement: string;
} | {
type: 'emojiSuggestionAccepted';
id: string;
createdAt: string;
emojiName: string;
} | {
type: 'exportCompleted';
id: string;
Expand Down
14 changes: 14 additions & 0 deletions packages/backend/src/models/json-schema/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,20 @@ export const packedNotificationSchema = {
ref: 'AchievementName',
},
},
}, {
type: 'object',
properties: {
...baseSchema.properties,
type: {
type: 'string',
optional: false, nullable: false,
enum: ['emojiSuggestionAccepted'],
},
emojiName: {
type: 'string',
optional: false, nullable: false,
},
},
}, {
type: 'object',
properties: {
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/models/json-schema/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,7 @@ export const packedMeDetailedOnlySchema = {
roleAssigned: { optional: true, ...notificationRecieveConfig },
chatRoomInvitationReceived: { optional: true, ...notificationRecieveConfig },
achievementEarned: { optional: true, ...notificationRecieveConfig },
emojiSuggestionAccepted: { optional: true, ...notificationRecieveConfig },
app: { optional: true, ...notificationRecieveConfig },
test: { optional: true, ...notificationRecieveConfig },
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export const meta = {
roleAssigned: { optional: true, ...notificationRecieveConfig },
chatRoomInvitationReceived: { optional: true, ...notificationRecieveConfig },
achievementEarned: { optional: true, ...notificationRecieveConfig },
emojiSuggestionAccepted: { optional: true, ...notificationRecieveConfig },
app: { optional: true, ...notificationRecieveConfig },
test: { optional: true, ...notificationRecieveConfig },
},
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/server/api/endpoints/i/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ export const paramDef = {
roleAssigned: notificationRecieveConfig,
chatRoomInvitationReceived: notificationRecieveConfig,
achievementEarned: notificationRecieveConfig,
emojiSuggestionAccepted: notificationRecieveConfig,
app: notificationRecieveConfig,
test: notificationRecieveConfig,
},
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* chatRoomInvitationReceived - チャットルームに招待された
* achievementEarned - 実績を獲得
* exportCompleted - エクスポートが完了
* emojiSuggestionAccepted - 絵文字の提案が承認された
* login - ログイン
* createToken - トークン作成
* app - アプリ通知
Expand All @@ -38,6 +39,7 @@ export const notificationTypes = [
'roleAssigned',
'chatRoomInvitationReceived',
'achievementEarned',
'emojiSuggestionAccepted',
'exportCompleted',
'importCompleted',
'login',
Expand Down
Loading
Loading