From 97485ec430f7fd3c2fcceac72d3f93198484e94c Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 23 Oct 2024 18:35:28 -0400 Subject: [PATCH 1/4] temp commit --- src/services/NotifService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/NotifService.ts b/src/services/NotifService.ts index 184d7cf..e664a5a 100644 --- a/src/services/NotifService.ts +++ b/src/services/NotifService.ts @@ -24,6 +24,8 @@ export class NotifService { // * @param {Object} expoServer the server object to connect with // */ +// Comment to push main + public sendNotifChunks = async (notifs : ExpoPushMessage[], expoServer : Expo) => { let chunks = expoServer.chunkPushNotifications(notifs); let tickets = []; From 17a8c12e23fc73f864fcbef8b30abd1961cf3938 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 30 Oct 2024 18:00:43 -0400 Subject: [PATCH 2/4] Added request and discount notifications --- src/api/controllers/NotifController.ts | 12 ++- src/repositories/UserRepository.ts | 8 ++ src/services/NotifService.ts | 119 ++++++++++++++++++++++++- src/types/ApiRequests.ts | 13 +++ 4 files changed, 150 insertions(+), 2 deletions(-) diff --git a/src/api/controllers/NotifController.ts b/src/api/controllers/NotifController.ts index 1b3dcfc..4a751d1 100644 --- a/src/api/controllers/NotifController.ts +++ b/src/api/controllers/NotifController.ts @@ -1,5 +1,5 @@ import { Body, CurrentUser, Delete, Get, JsonController, Params, Post } from 'routing-controllers'; -import { ExpoPushMessage, PushTicket, FindTokensRequest } from 'src/types'; +import { ExpoPushMessage, PushTicket, FindTokensRequest, DiscountNotificationRequest, RequestMatchNotificationRequest } from 'src/types'; import { NotifService } from '../../services/NotifService'; @JsonController('notif/') @@ -14,5 +14,15 @@ export class NotifController { async sendNotif(@Body() findTokensRequest: FindTokensRequest) { return this.notifService.sendNotifs(findTokensRequest); } + + @Post('discount') + async sendDiscountNotif(@Body() discountRequest: DiscountNotificationRequest) { + return this.notifService.sendDiscountNotification(discountRequest); + } + + @Post('request-match') + async sendRequestMatchNotif(@Body() matchRequest: RequestMatchNotificationRequest) { + return this.notifService.sendRequestMatchNotification(matchRequest); + } } diff --git a/src/repositories/UserRepository.ts b/src/repositories/UserRepository.ts index 1c818be..eae2dea 100644 --- a/src/repositories/UserRepository.ts +++ b/src/repositories/UserRepository.ts @@ -68,6 +68,14 @@ export class UserRepository extends AbstractRepository { .getOne(); } + public async getUsersWhoSavedPost(postId: Uuid): Promise { + return await this.repository + .createQueryBuilder("user") + .leftJoin("user.saved", "saved_posts") + .where("saved_posts.id = :postId", { postId }) + .getMany(); +} + public async getUserByEmail(email: string): Promise { return await this.repository .createQueryBuilder("user") diff --git a/src/services/NotifService.ts b/src/services/NotifService.ts index e664a5a..6de19ae 100644 --- a/src/services/NotifService.ts +++ b/src/services/NotifService.ts @@ -1,6 +1,6 @@ import { NotFoundError} from 'routing-controllers'; import { Service } from 'typedi'; -import { ExpoPushMessage, PushTicket, FindTokensRequest, NotifSent } from '../types'; +import { ExpoPushMessage, PushTicket, FindTokensRequest, NotifSent, DiscountNotificationRequest, RequestMatchNotificationRequest } from '../types'; import { Expo } from 'expo-server-sdk'; import { UserRepository } from 'src/repositories/UserRepository'; import Repositories, { TransactionsManager } from '../repositories'; @@ -83,4 +83,121 @@ export class NotifService { console.log(err) } }) } + + public async sendDiscountNotification(request: DiscountNotificationRequest) { + return this.transactions.readWrite(async (transactionalEntityManager) => { + const postRepository = Repositories.post(transactionalEntityManager); + const userRepository = Repositories.user(transactionalEntityManager); + + // Get the post + const post = await postRepository.getPostById(request.listingId); + if (!post) { + throw new NotFoundError("Post not found!"); + } + + // Get all users who saved this post + const usersWithSavedPost = await userRepository.getUsersWhoSavedPost(post.id); + + for (const user of usersWithSavedPost) { + const userSessionRepository = Repositories.session(transactionalEntityManager); + const allDeviceTokens = []; + const allsessions = await userSessionRepository.getSessionsByUserId(user.id); + + for (var sess of allsessions) { + if (sess.deviceToken) { + allDeviceTokens.push(sess.deviceToken); + } + } + + if (allDeviceTokens.length > 0) { + const discountPercentage = Math.round( + ((request.oldPrice - request.newPrice) / request.oldPrice) * 100 + ); + + let notif: ExpoPushMessage = { + to: allDeviceTokens, + sound: 'default', + title: 'Price Drop Alert! 🎉', + body: `A post you saved is now ${discountPercentage}% off!`, + data: { + postId: request.listingId, + oldPrice: request.oldPrice, + newPrice: request.newPrice + } as unknown as JSON + }; + + try { + let notifs: ExpoPushMessage[] = []; + notif.to.forEach(token => { + notifs.push({ + to: [token], + sound: 'default', + title: notif.title, + body: notif.body, + data: notif.data + }); + }); + await this.sendNotifChunks(notifs, expoServer); + } catch (err) { + console.log(err); + } + } + } + }); + } + + public async sendRequestMatchNotification(request: RequestMatchNotificationRequest) { + return this.transactions.readWrite(async (transactionalEntityManager) => { + const postRepository = Repositories.post(transactionalEntityManager); + const requestRepository = Repositories.request(transactionalEntityManager); + + const post = await postRepository.getPostById(request.listingId); + const userRequest = await requestRepository.getRequestById(request.requestId); + + if (!post || !userRequest) { + throw new NotFoundError("Post or Request not found!"); + } + + const userSessionRepository = Repositories.session(transactionalEntityManager); + const allDeviceTokens = []; + const allsessions = await userSessionRepository.getSessionsByUserId(request.userId); + + for (var sess of allsessions) { + if (sess.deviceToken) { + allDeviceTokens.push(sess.deviceToken); + } + } + + if (allDeviceTokens.length > 0) { + let notif: ExpoPushMessage = { + to: allDeviceTokens, + sound: 'default', + title: 'Request Match Found! 🎯', + body: `We found a post that matches your request for ${userRequest.title}`, + data: { + postId: request.listingId, + requestId: request.requestId, + postTitle: post.title, + price: post.original_price + } as unknown as JSON + }; + + try { + let notifs: ExpoPushMessage[] = []; + notif.to.forEach(token => { + notifs.push({ + to: [token], + sound: 'default', + title: notif.title, + body: notif.body, + data: notif.data + }); + }); + await this.sendNotifChunks(notifs, expoServer); + } catch (err) { + console.log(err); + } + } + }); + } } \ No newline at end of file diff --git a/src/types/ApiRequests.ts b/src/types/ApiRequests.ts index e2cbd46..bd68b54 100644 --- a/src/types/ApiRequests.ts +++ b/src/types/ApiRequests.ts @@ -143,6 +143,19 @@ export interface FindTokensRequest { data: JSON; } +export interface DiscountNotificationRequest { + listingId: Uuid; + oldPrice: number; + newPrice: number; + sellerId: Uuid; +} + +export interface RequestMatchNotificationRequest { + requestId: Uuid; + listingId: Uuid; + userId: Uuid; +} + // REPORTS export interface ReportPostRequest { reported: Uuid; From 46476928b7b35bd8a948ab0674d8cc71904d8982 Mon Sep 17 00:00:00 2001 From: Ashley Herrera Date: Wed, 30 Oct 2024 18:11:32 -0400 Subject: [PATCH 3/4] Added bookmark listing notifications --- src/services/PostService.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/services/PostService.ts b/src/services/PostService.ts index 5578ae3..052a970 100644 --- a/src/services/PostService.ts +++ b/src/services/PostService.ts @@ -12,12 +12,14 @@ import { CreatePostRequest, FilterPostsRequest, FilterPostsByPriceRequest, GetSe import { uploadImage } from '../utils/Requests'; import { getLoadedModel } from '../utils/SentenceEncoder'; import { PostRepository } from 'src/repositories/PostRepository'; +import { FindTokensRequest } from '../types'; +import { NotifService } from './NotifService'; require('@tensorflow-models/universal-sentence-encoder') @Service() export class PostService { private transactions: TransactionsManager; - + constructor(@InjectManager() entityManager: EntityManager) { this.transactions = new TransactionsManager(entityManager); } @@ -232,6 +234,17 @@ export class PostService { if (!post) throw new NotFoundError('Post not found!'); if (post.user.isActive == false) throw new NotFoundError('User is not active!'); const userRepository = Repositories.user(transactionalEntityManager); + const postOwner = await userRepository.getUserById(post.user.id); + if (!postOwner) throw new NotFoundError('Post owner not found!'); + const bookmarkNotifRequest: FindTokensRequest = + { + email: postOwner.email, + title: "Bookmark Listing Notification", + body: user.username + " bookmarked your listing!", + data: {} as JSON + } + const notifService = new NotifService(transactionalEntityManager); + await notifService.sendNotifs(bookmarkNotifRequest); return await userRepository.savePost(user, post); }); } From 751abc7229e9644911ba246fbd975d679bb088d5 Mon Sep 17 00:00:00 2001 From: Ashley Herrera Date: Wed, 30 Oct 2024 18:11:32 -0400 Subject: [PATCH 4/4] Added bookmark listing notifications --- src/services/PostService.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/services/PostService.ts b/src/services/PostService.ts index 5578ae3..052a970 100644 --- a/src/services/PostService.ts +++ b/src/services/PostService.ts @@ -12,12 +12,14 @@ import { CreatePostRequest, FilterPostsRequest, FilterPostsByPriceRequest, GetSe import { uploadImage } from '../utils/Requests'; import { getLoadedModel } from '../utils/SentenceEncoder'; import { PostRepository } from 'src/repositories/PostRepository'; +import { FindTokensRequest } from '../types'; +import { NotifService } from './NotifService'; require('@tensorflow-models/universal-sentence-encoder') @Service() export class PostService { private transactions: TransactionsManager; - + constructor(@InjectManager() entityManager: EntityManager) { this.transactions = new TransactionsManager(entityManager); } @@ -232,6 +234,17 @@ export class PostService { if (!post) throw new NotFoundError('Post not found!'); if (post.user.isActive == false) throw new NotFoundError('User is not active!'); const userRepository = Repositories.user(transactionalEntityManager); + const postOwner = await userRepository.getUserById(post.user.id); + if (!postOwner) throw new NotFoundError('Post owner not found!'); + const bookmarkNotifRequest: FindTokensRequest = + { + email: postOwner.email, + title: "Bookmark Listing Notification", + body: user.username + " bookmarked your listing!", + data: {} as JSON + } + const notifService = new NotifService(transactionalEntityManager); + await notifService.sendNotifs(bookmarkNotifRequest); return await userRepository.savePost(user, post); }); }