diff --git a/client/src/app/services/actions.service.ts b/client/src/app/services/actions.service.ts index c4875c7..86ca468 100644 --- a/client/src/app/services/actions.service.ts +++ b/client/src/app/services/actions.service.ts @@ -19,6 +19,11 @@ export class ActionsService { ) {} doAction(action: INotificationAction, notification?: INotification) { + if (action.url) { + // we never send data here, it's inferred server side + this.http.post(`${environment.apiUrl}/${action.url}`, {}).subscribe(); + } + if (notification) { this.notificationService.clearActions(notification.id || '').subscribe(); this.store.dispatch(new ClearNotificationActions(notification.id || '')); @@ -27,11 +32,5 @@ export class ActionsService { if (action.action === 'navigate') { this.router.navigate([action.actionData.url]); } - - if (action.url) { - this.http - .post(`${environment.apiUrl}/${action.url}`, action.urlData || {}) - .subscribe(); - } } } diff --git a/client/src/app/services/gameplay.service.ts b/client/src/app/services/gameplay.service.ts index 085418f..7d1bc17 100644 --- a/client/src/app/services/gameplay.service.ts +++ b/client/src/app/services/gameplay.service.ts @@ -24,13 +24,6 @@ export class GameplayService { }); } - wave(targetUserId: string, isWaveBack = true) { - return this.http.post(`${environment.apiUrl}/gameplay/wave`, { - targetUserId, - isWaveBack, - }); - } - sellItem(instanceId: string) { return this.http.post(`${environment.apiUrl}/gameplay/sellitem`, { instanceId, diff --git a/server/src/modules/notification/notification.schema.ts b/server/src/modules/notification/notification.schema.ts index 6a7f678..c6ec93a 100644 --- a/server/src/modules/notification/notification.schema.ts +++ b/server/src/modules/notification/notification.schema.ts @@ -45,10 +45,20 @@ export class Notification implements INotification { liveAt = new Date(), expiresAfterHours = 24 * 7, ) { + this._id = new ObjectId(); + this.userId = userId; this.text = text; this.actions = actions; + if (this.actions) { + this.actions.forEach((action) => { + if (!action.url) return; + + action.url = `${action.url}/${this.id}`; + }); + } + this.read = false; this.liveAt = liveAt; diff --git a/server/src/modules/notification/notification.service.ts b/server/src/modules/notification/notification.service.ts index dcd7f72..3549cd0 100644 --- a/server/src/modules/notification/notification.service.ts +++ b/server/src/modules/notification/notification.service.ts @@ -16,6 +16,13 @@ export class NotificationService { return (await this.notifications.find({ userId })).reverse(); } + async getNotificationForUser( + userId: string, + notificationId: string, + ): Promise { + return this.notifications.findOne({ userId, id: notificationId }); + } + async getNotificationsForUserAfter( userId: string, after: Date, diff --git a/server/src/modules/player/gameplay.controller.ts b/server/src/modules/player/gameplay.controller.ts index 4b24d0a..9d7b33b 100644 --- a/server/src/modules/player/gameplay.controller.ts +++ b/server/src/modules/player/gameplay.controller.ts @@ -6,6 +6,7 @@ import { BadRequestException, Body, Controller, + Param, Post, UseGuards, } from '@nestjs/common'; @@ -55,15 +56,21 @@ export class GameplayController { @ApiOperation({ summary: 'Explore Event: Wave at another player' }) @Post('wave') @RollbarHandler() - async wave( + async wave(@User() user): Promise> { + return this.gameplayService.waveToPlayerFromExplore(user.userId); + } + + @UseGuards(JwtAuthGuard) + @ApiOperation({ summary: 'Explore Event: Wave at another player' }) + @Post('wave/:id') + @RollbarHandler() + async waveFromNotification( @User() user, - @Body('targetUserId') targetUserId: string, - @Body('isWaveBack') isWaveBack: boolean, + @Param('id') notificationId: string, ): Promise> { - return this.gameplayService.waveToPlayer( + return this.gameplayService.waveToPlayerFromNotification( user.userId, - targetUserId, - isWaveBack, + notificationId, ); } diff --git a/server/src/modules/player/gameplay.service.ts b/server/src/modules/player/gameplay.service.ts index 6d54b5d..cb8ae7f 100644 --- a/server/src/modules/player/gameplay.service.ts +++ b/server/src/modules/player/gameplay.service.ts @@ -3,6 +3,7 @@ import { IFullUser, IItem, ILocation, + INotificationAction, IPatchUser, TrackedStat, } from '@interfaces'; @@ -304,14 +305,44 @@ export class GameplayService { return { player: playerPatches }; } - async waveToPlayer( + async waveToPlayerFromExplore( userId: string, - targetUserId: string, - isWaveBack: boolean, ): Promise> { const player = await this.playerService.getPlayerForUser(userId); if (!player) throw new ForbiddenException('Player not found'); + if (!player.action) throw new ForbiddenException('Player has no action'); + + return this.waveToPlayer(userId, player, player.action); + } + + async waveToPlayerFromNotification( + userId: string, + notificationId: string, + ): Promise> { + const player = await this.playerService.getPlayerForUser(userId); + if (!player) throw new ForbiddenException('Player not found'); + + const notification = await this.notificationService.getNotificationForUser( + userId, + notificationId, + ); + if (!notification) throw new ForbiddenException('Notification not found'); + + const notificationAction = notification.actions?.[0]; + if (!notificationAction) + throw new ForbiddenException('Notification has no actions'); + + return this.waveToPlayer(userId, player, notificationAction); + } + + private async waveToPlayer( + userId: string, + player: Player, + action: INotificationAction, + ) { + const { targetUserId, isWaveBack } = action?.urlData ?? {}; + const otherPlayer = await this.playerService.getPlayerForUser(targetUserId); if (!otherPlayer) throw new ForbiddenException('Target player not found');