From c1ecbf3f4082e1972f4699f775881cb5f5e8ff92 Mon Sep 17 00:00:00 2001 From: Vin Bui <75594943+vinnie4k@users.noreply.github.com> Date: Sun, 26 Nov 2023 18:56:44 -0500 Subject: [PATCH] Merge `main` to `release` (#129) * Add Jest testing (#63) * Initial boilerplate setup * Add ArticleFactory, TestingDBConnection, FactoryUtils, placeholder article test file * Add test cases on getAllArticles * Create PublicationFactory, add tests for getAllArticles, getArticlesByPublicationID(s), getArticlesByPublicationSlug(s) * Add hooks * Update README * Add pr changes * Add pr changes * Add tests for incrementShoutouts, searchArticle, getArticlesAfterDate * Add publication tests and refactor factory files * Remove test db open message * Update src/tests/article.test.ts Co-authored-by: Archit Mehta <4architmehta@gmail.com> * Update src/tests/data/ArticleFactory.ts Co-authored-by: Archit Mehta <4architmehta@gmail.com> * Refactor code * Refactor byDate Co-authored-by: Kidus Zegeye Co-authored-by: Archit Mehta <4architmehta@gmail.com> * Add Docstrings to Test Factory Functions (#64) * Initial boilerplate setup * Add ArticleFactory, TestingDBConnection, FactoryUtils, placeholder article test file * Add test cases on getAllArticles * Create PublicationFactory, add tests for getAllArticles, getArticlesByPublicationID(s), getArticlesByPublicationSlug(s) * Add hooks * Update README * Add pr changes * Add pr changes * Add tests for incrementShoutouts, searchArticle, getArticlesAfterDate * Add publication tests and refactor factory files * Remove test db open message * Update src/tests/article.test.ts Co-authored-by: Archit Mehta <4architmehta@gmail.com> * Update src/tests/data/ArticleFactory.ts Co-authored-by: Archit Mehta <4architmehta@gmail.com> * Refactor code * Refactor byDate * Add docstrings Co-authored-by: Kidus Zegeye Co-authored-by: Archit Mehta <4architmehta@gmail.com> * Update README.md * Implement community board models (#65) * Implemented community board models - implemented Flyer and Organization models for community board feature - modified User model - updated typescript version to 4.0.5 to resolve es lint issue * Revert package.json changes - reverted package.json changes that are addressed in a different pr * Add magazines to weekly debrief (#66) * Implemented community board models - implemented Flyer and Organization models for community board feature - modified User model - updated typescript version to 4.0.5 to resolve es lint issue * Implemented weekly debrief magazines - added magazines to weekly debrief feature - updated user and weekly debrief models to allow tracking of read magazines - updated logic in user and weekly debrief controllers to enable reading of magazines * Revert package.json changes - reverted package.json changes back to the original file - added additional rules to eslintrc to suppress es lint warnings * Create jest testing for magazines - added jest test cases for magazines * Implement jest testing for User - implemented unit testing for UserRepo - indirectly test Weekly Debrief with User unit tests * Update gitignore - updated gitignore file to include secrets folder * Implement reshuffle articles (#68) * Add Docstrings to Test Factory Functions (#64) * Initial boilerplate setup * Add ArticleFactory, TestingDBConnection, FactoryUtils, placeholder article test file * Add test cases on getAllArticles * Create PublicationFactory, add tests for getAllArticles, getArticlesByPublicationID(s), getArticlesByPublicationSlug(s) * Add hooks * Update README * Add pr changes * Add pr changes * Add tests for incrementShoutouts, searchArticle, getArticlesAfterDate * Add publication tests and refactor factory files * Remove test db open message * Update src/tests/article.test.ts Co-authored-by: Archit Mehta <4architmehta@gmail.com> * Update src/tests/data/ArticleFactory.ts Co-authored-by: Archit Mehta <4architmehta@gmail.com> * Refactor code * Refactor byDate * Add docstrings Co-authored-by: Kidus Zegeye Co-authored-by: Archit Mehta <4architmehta@gmail.com> Implement shuffle article function try to implement reshuffling with aggregate * Implement reshuffling of articles * Revert package.json, update eslint and update .gitignore to match main branch --------- Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com> * Implement magazine search (#69) * Implement magazine search - implemented searching for magazines * update magazinerepo * Add unit test for magazine search - created unit test for MagazineRepo.searchMagazine * Convert result of shuffling resolver to ArticleModel (#71) * Add 3 new publications (Cornell Daily Sun, Collective X, and Cornell Healthcare Review) to publications.js (#70) * Resolve merge conflicts in publications.json (keep release version) (#76) * Kidus/filter articles (#48) * Add covid filter to all article query functions * Add filtering capability * Fix getArticleById filter * Create filter migration script * Finish migration script * Add toggle for filtering enforcement * Implement PR changes Co-authored-by: Kidus Zegeye * Fix BRSN's url * Migrate bookmark resolver and update User Repo & Entity * Resolve remaining merge conflicts * Implement chronological sorting * Update prod deployment script * Remove unused MagazineURL index from Magazine DB * Add no rules to publications.json * Remove trailing comma --------- Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com> Co-authored-by: Kidus Zegeye Co-authored-by: Archit404Error <4architmehta@gmail.com> * Revert "Resolve merge conflicts in publications.json (keep release version) (#76)" (#77) This reverts commit eccf00c0fe5ebbbe81bf9b1f643640d991cb1c29. * fix merge conflicts (#78) * Kidus/filter articles (#48) * Add covid filter to all article query functions * Add filtering capability * Fix getArticleById filter * Create filter migration script * Finish migration script * Add toggle for filtering enforcement * Implement PR changes Co-authored-by: Kidus Zegeye * Fix BRSN's url * Migrate bookmark resolver and update User Repo & Entity * Resolve remaining merge conflicts * Implement chronological sorting * Update prod deployment script * Remove unused MagazineURL index from Magazine DB * Add no rules to publications.json * Remove trailing comma --------- Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com> Co-authored-by: Kidus Zegeye Co-authored-by: Archit404Error <4architmehta@gmail.com> * Implement community board [1/7] (#81) * Begin implementing FlyerRepo - implemented basic FlyerRepo functions - updated Flyer model to support uploading in app instead of through a google form * Implement flyers for community board - alphabetized imports and fields - finished implementing flyers for community board * Address pr review comments - addressed pr review comments - removed redundant filtering in FlyerRepo - added checks for null return values in FlyerRepo - alphabetized imports and exports * Add imageURL field to Magazine entity. (#73) * Add no rules to volume-backend's publications.json (#82) * Implement community board (flyer tests) [2/7] (#84) * Begin implementing FlyerRepo - implemented basic FlyerRepo functions - updated Flyer model to support uploading in app instead of through a google form * Implement flyers for community board - alphabetized imports and fields - finished implementing flyers for community board * Implement jest testing for flyers - created jest unit testing for FlyerRepo - created FlyerFactory to help with jest testing * Remove isFiltered field from Flyer entity - removed redundant isFiltered field from Flyer entity (already checked in microservice) - updated FlyerRepo and jest testing to reflect changes * Alphabetize imports and exports - alphabetized imports and exports in FlyerRepo, flyer.test, and FlyerFactory * Revert merge conflict changes - reverted merge conflict changes (accept incoming when actually wanted to keep current) * Address review comments - alphabetize imports - add unit test cases for FlyerRepo including searching for 0 flyers and maximum limit in response - added unit test case for FlyerRepo.getTrendingFlyers * Address review comments (Shungo) - added spaces between test cases * Implement community board organizations (#85) - resolve merge conflicts in Flyer.ts and FlyerFactory.ts - update Organization model - implement logic in OrganizationRepo - implement queries and mutations in OrganizationResolver * Implement community board (organization tests) [4/7] (#86) * Implement community board organizations - resolve merge conflicts in Flyer.ts and FlyerFactory.ts - update Organization model - implement logic in OrganizationRepo - implement queries and mutations in OrganizationResolver * Implement jest testing for organizations - implemented jest testing for community board organizations - created organizations.json with 3 onboarded organizations - added FlyerResolver and OrganizationResolver to app.ts * Implement community board (user queries/mutations) [5/7] (#87) * Kidus/filter articles (#48) * Add covid filter to all article query functions * Add filtering capability * Fix getArticleById filter * Create filter migration script * Finish migration script * Add toggle for filtering enforcement * Implement PR changes Co-authored-by: Kidus Zegeye * Fix BRSN's url * Migrate bookmark resolver and update User Repo & Entity * Resolve remaining merge conflicts * Implement chronological sorting * Update prod deployment script * Remove unused MagazineURL index from Magazine DB * Add no rules to publications.json * Remove trailing comma * Begin implementing FlyerRepo - implemented basic FlyerRepo functions - updated Flyer model to support uploading in app instead of through a google form * Implement basic community board features - finished implementing basic organization and flyer repos/resolvers - created jest testing for organization and flyer repos * Remove unnecessary OrganizationRepo function - removed content types route from organization repo and organization resolver * Implement user routes for community board - implemented user routes for community board to allow following orgs and reading flyers - added jest unit test cases for new UserRepo functions * Implement mutations for following and unfollowing organizations - added resolvers to UserResolver for following and unfollowing organizations - updated documentation in UserRepo - added import of FlyerRepo in app.ts * Address pr review comments - fix spelling * Address pr review comments - made style consistent in UserRepo by checking for if(user) in appendReadFlyer, appendReadArticle, and appendReadMagazine --------- Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com> Co-authored-by: Kidus Zegeye Co-authored-by: Archit404Error <4architmehta@gmail.com> * Onboard organizations (#90) - add organizations from volume-microservice onto organizations.json * Implement community board (categories and org lists) [6/7] (#89) * Implement community board organizations - resolve merge conflicts in Flyer.ts and FlyerFactory.ts - update Organization model - implement logic in OrganizationRepo - implement queries and mutations in OrganizationResolver * Implement jest testing for organizations - implemented jest testing for community board organizations - created organizations.json with 3 onboarded organizations - added FlyerResolver and OrganizationResolver to app.ts * initial commit * Implement organizations as list for flyer model - updated flyer model to allow a list of organizations and organizationSlugs to be associated with each flyer - updated flyer and organization queries accordingly - added unit jest test cases and update old ones accordingly * Address pr review comments - removed redundant resolvers - updated getOrganizationByCategory test * Address pr review comments (kate) - updated docs in OrganizationResolver * Implement cboard start and end dates (#93) * Implement end dates - implemented start and end dates - changed shoutouts to times clicked - updated testing and documentation to reflect changes * Address pr review comments - removed async from getFlyersBeforeDate and getFlyersAfterDate - updated documentation in FactoryUtils * add incrementTimesClicked to resolver * update organization shoutouts query (#94) * Implement end dates - implemented start and end dates - changed shoutouts to times clicked - updated testing and documentation to reflect changes * Address pr review comments - removed async from getFlyersBeforeDate and getFlyersAfterDate - updated documentation in FactoryUtils * add incrementTimesClicked to resolver * Update organization clicks - changed shoutouts query to clicks in OrganizationResolver * Implement trending flyers logic (#95) - updated trending flyers logic - updated FlyerRepo - updated documentation in FlyerResolver * Fixed notification issue (#97) * Flyer Notifications + Org/Flyer Model Changes (#99) * Fixed notification issue * Added flyer notification * Added flyer notification * Updated Organization and Flyer models * Updated test cases * Inline notification calls * Improve Search Algorithm (#101) * Update README.md start instructions * Update search function for articles * Remove console log * Update articles search to use text search instead of regex * Update search for Flyers and Magazines * Update documentation, remove unused Fuse package * Fix README.md start docs * Filter out past flyers during flyers search * Fix formatting, improve concision * Remove start.md * Changed indexing method to being declared on the models * Update README.md Co-authored-by: Archit Mehta <4architmehta@gmail.com> --------- Co-authored-by: Archit Mehta <4architmehta@gmail.com> * getFlyersByCategorySlug Query (#100) * Fixed notification issue * Added flyer notification * Added flyer notification * Updated Organization and Flyer models * Updated test cases * Added getFlyersByCategorySlug query * Update query description * Inline notification calls * Update flyer.test.ts * Update flyer.test.ts * Merge `vin/create-flyer` to `main` (#104) * Fixed notification issue * Added flyer notification * Added flyer notification * Updated Organization and Flyer models * Updated test cases * Added getFlyersByCategorySlug query * Update query description * Inline notification calls * Add createFlyer mutation * `deleteFlyer` mutation (#103) * Add deleteFlyer mutation * Address PR comments * Update utils.ts * Update FlyerRepo.ts * Trendiness Update and Migration Script for New Flyers Model (#106) * Add migration script to update data based on new Flyers schema * Update Flyer trendiness * Fix comments * Clean up syntax for get trending flyers * Remove unnecessary createSpecificsByIndex function * Zach/trendiness and migration hot fix (#107) * Add migration script to update data based on new Flyers schema * Update Flyer trendiness * Fix comments * Clean up syntax for get trending flyers * Remove unnecessary createSpecificsByIndex function * Fix TypeScript compilation error * Micro PR for Flyers categorySlug migration (#108) * Add migration script to update data based on new Flyers schema * Update Flyer trendiness * Fix comments * Clean up syntax for get trending flyers * Remove unnecessary createSpecificsByIndex function * Fix TypeScript compilation error * Update migration script to account for category slug * Fix formatting for flyers test * Create `editFlyer` mutation (#105) * Add `editFlyer` * Address PR comments * `checkAccessCode` query for Organization authentication (#109) * Added Organization authentication * Updated Authorization * Implemented getAllFlyerCategories and tests (#110) * Implemented getAllFlyerCategories and tests * update jest * update jest * implemented suggestions from zach, vin, and archit * got rid of ESLint comment * fixed names for tests * Added loops to test cases * Fixed test case to create flyers with the same category * Changed 'push' to 'concat' --------- Co-authored-by: Cindy * Changing specifications for getFlyersBeforeDate and getFlyersAfterDate (#112) * Cindy/flyer categories (#113) * Implemented getAllFlyerCategories and tests * update jest * update jest * implemented suggestions from zach, vin, and archit * got rid of ESLint comment * fixed names for tests * Added loops to test cases * Fixed test case to create flyers with the same category * Changed 'push' to 'concat' * Updated `getAllFlyerCategories` --------- Co-authored-by: Cindy Co-authored-by: Vin Bui * Increased HTTP request size (#114) * Added limit to JSON request body * Update package-lock.json * Add error message (#116) * Added limit to JSON request body * Update package-lock.json * Added error message * Update FlyerMiddleware.ts (#119) * Update publications.json (#122) Add WoMENA as a publication * Change Create Flyer route to use form data for image upload (#121) * Add post express route using form data * Remove .idea * revert readme * try catch for large file uploads * Remove uncertain comments * Refactor upload image util to use form data * Cover the case where the Flyer ID that was sent is invalid * Update file key, add documentation * Fixed issue with edit flyer (#124) * Fixed issue with edit flyer * Update app.ts * Adding routes and migration script for bookmarking (#126) * Adding routes and migration script for bookmarking * Refactoring WeeklyDebrief * Fixing unbookmarking functions * Adding (un)bookmark articles tests * More article bookmark tests * Adding more tests for flyers and magazines * Enabling notifications for adding, editing, and deleting a flyer (#118) * Implemented getAllFlyerCategories and tests * update jest * update jest * implemented suggestions from zach, vin, and archit * got rid of ESLint comment * fixed names for tests * Added loops to test cases * Fixed test case to create flyers with the same category * Changed 'push' to 'concat' * Enabled notifications for adding, editing, and deleting a flyer - Created a function in NotificationRepo.ts called notifyFlyersForOrganization that takes in a flyerID and a title of the notification - the title of the notification should reflect the action performed (add, edit, delete) - This function is called in FlyerResolver.ts * Added more specific body text for updating flyers, removed notifyNewFlyers * Addressed Vin's Comments - used enums for action commparison instead of strings - used template strings instead of concat - added documentation for the various branches of the if statement in the notification for editing a flyer * Addressing Vin's Comments - changed nested if statements to a switch block - used proper capitlization for Enums * add num flyers for organizations * Changed edit and delete flyer notifications to only be sent to users who have flyers bookmarked - in UserRepo added getUsersBookmarkedFlyers to get a list of all the users that have the desired flyer bookmarked - in NotificationRepo, factored out a function titled notifyFlyersForBookmarks - in FlyerResolver, for edit/delete flyers notifyFlyersForBookmarks will be called instead of notifyFlyersForOrganizations * fixed specs for added functions * addressing vin's comments --------- Co-authored-by: Cindy Co-authored-by: Vin Bui * Update app.ts (#128) --------- Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com> Co-authored-by: Kidus Zegeye Co-authored-by: Archit Mehta <4architmehta@gmail.com> Co-authored-by: Shungo Najima Co-authored-by: Isaac Han Co-authored-by: Sasha Loayza <104698418+SashaLoayza@users.noreply.github.com> Co-authored-by: Zachary Seidner <58796478+zachseidner1@users.noreply.github.com> Co-authored-by: cindy-x-liang <67083541+cindy-x-liang@users.noreply.github.com> Co-authored-by: Cindy Co-authored-by: Aayush <68517064+Aayush-Agnihotri@users.noreply.github.com> Co-authored-by: Jennifer Gu <57200368+jjennifergu@users.noreply.github.com> --- src/app.ts | 34 ++++++++++- src/repos/FlyerRepo.ts | 8 ++- src/repos/NotificationRepo.ts | 88 ++++++++++++++++++++++++++- src/repos/UserRepo.ts | 10 +++ src/resolvers/FlyerResolver.ts | 39 +++++++++++- src/resolvers/OrganizationResolver.ts | 7 +++ 6 files changed, 178 insertions(+), 8 deletions(-) diff --git a/src/app.ts b/src/app.ts index a96429a..283d575 100644 --- a/src/app.ts +++ b/src/app.ts @@ -21,7 +21,7 @@ import OrganizationResolver from './resolvers/OrganizationResolver'; import PublicationResolver from './resolvers/PublicationResolver'; import UserResolver from './resolvers/UserResolver'; import utils from './utils'; -import FlyerRepo from './repos/FlyerRepo'; +import FlyerRepo, { Actions } from './repos/FlyerRepo'; const main = async () => { const schema = await buildSchema({ @@ -146,7 +146,12 @@ const main = async () => { startDate, title, }); - await FlyerModel.create(newFlyer); + const flyer = await FlyerModel.create(newFlyer); + NotificationRepo.notifyFlyersForOrganizations( + flyer.id, + ' just added a new flyer!', + Actions.Add, + ); return res.status(201).json(newFlyer); }); @@ -174,7 +179,7 @@ const main = async () => { // Get the file from form-data and await the upload service const imageURL = req.file ? await utils.uploadImage(req.file) : undefined; - await FlyerRepo.editFlyer( + const flyer = await FlyerRepo.editFlyer( flyerID, categorySlug, endDate, @@ -185,6 +190,29 @@ const main = async () => { title, ); + let editedResponse = ''; + if ((endDate && location) || (location && startDate)) { + // if endDate and location or startDate and location are nonempty, notification body would return date and location changed + editedResponse = 'changed its date and location'; + } else if (endDate && startDate) { + // if both endDate and startDate values changed, the notifcation body would just return that the event changed its date + editedResponse = 'changed its date'; + } else if (endDate) { + // if only the endDate changed for the event, then the notification body would print out specifically what the date was changed to + const date = new Date(endDate); + // the format for toDateString is Day of the Week Month Date Year ex: Tue Sep 05 2023 + editedResponse = `changed its end date to ${date.toDateString()}`; + } else if (location) { + // if only the location changed for the event, then the notification body would print out specifically what the location was changed to + editedResponse = `changed its location to ${location}`; + } else if (startDate) { + // if only the startDate changed for the event, then the notification body would print out specifically what the date was changed to + const date = new Date(startDate); + // the format for toDateString is Day of the Week Month Date Year ex: Tue Sep 05 2023 + editedResponse = `changed its start date to ${date.toDateString()}`; + } + + NotificationRepo.notifyFlyersForBookmarks(flyer.id, editedResponse, Actions.Edit); return res.status(201).json(await FlyerModel.findById(flyerID)); }); diff --git a/src/repos/FlyerRepo.ts b/src/repos/FlyerRepo.ts index 162f359..2bbd17a 100644 --- a/src/repos/FlyerRepo.ts +++ b/src/repos/FlyerRepo.ts @@ -7,7 +7,11 @@ import { OrganizationModel } from '../entities/Organization'; import utils from '../utils'; const { IS_FILTER_ACTIVE } = process.env; - +export enum Actions { + Add, + Edit, + Delete, +} function isFlyerFiltered(flyer: Flyer) { if (IS_FILTER_ACTIVE === 'true') { const filter = new Filter({ list: FILTERED_WORDS }); @@ -251,6 +255,7 @@ const createFlyer = async ( startDate, title, }); + return FlyerModel.create(newFlyer); }; @@ -303,6 +308,7 @@ const editFlyer = async ( // Update flyer fields (if not nul) if (categorySlug) flyer.categorySlug = categorySlug; if (endDate) flyer.endDate = new Date(endDate); + if (flyerURL) flyer.flyerURL = flyerURL; if (imageURL) flyer.imageURL = imageURL; if (location) flyer.location = location; diff --git a/src/repos/NotificationRepo.ts b/src/repos/NotificationRepo.ts index aae4a01..3a91c21 100644 --- a/src/repos/NotificationRepo.ts +++ b/src/repos/NotificationRepo.ts @@ -1,7 +1,7 @@ import * as admin from 'firebase-admin'; import { User } from '../entities/User'; import ArticleRepo from './ArticleRepo'; -import FlyerRepo from './FlyerRepo'; +import FlyerRepo, { Actions } from './FlyerRepo'; import MagazineRepo from './MagazineRepo'; import OrganizationRepo from './OrganizationRepo'; import PublicationRepo from './PublicationRepo'; @@ -137,6 +137,90 @@ const notifyNewFlyers = async (flyerIDs: string[]): Promise => { }); }; +/** + * Send notifications for edited flyers or deleted flyers for those bookmarked by the user. + * + * @param flyerID - ID of the flyer being added/changed/deleted + * @param bodyText - a string containing the body of the notification + * @param action - a string indicating the action being peformed, a for add, e for edit, d for delete + */ +const notifyFlyersForBookmarks = async ( + flyerID: string, + bodyText: string, + action: Actions, +): Promise => { + const flyer = await FlyerRepo.getFlyerByID(flyerID); + const organization = await OrganizationRepo.getOrganizationBySlug(flyer.organizationSlug); + const followers = await UserRepo.getUsersBookmarkedFlyer(flyerID); + + let notifTitle = ''; + let notifBody = ''; + + followers.forEach(async (follower) => { + switch (action) { + case Actions.Edit: + notifTitle = `New Update from ${organization.name}!`; + notifBody = `${flyer.title} has ${bodyText}`; + break; + case Actions.Delete: + notifTitle = `${organization.name} Event Deleted`; + // the format for toDateString is Day of the Week Month Date Year ex: Tue Sep 05 2023 + notifBody = `${flyer.title} on ${flyer.startDate.toDateString()} has been removed`; + break; + default: + notifTitle = ''; + notifBody = ''; + } + + const uniqueData = { + flyerID: flyer.id, + flyerURL: flyer.flyerURL, + }; + + await sendNotif(follower, notifTitle, notifBody, uniqueData, 'flyer_notif'); + }); +}; + +/** + * Send notifications for new flyers by organizations followed by the user. + * + * @param flyerID - ID of the flyer being added/changed/deleted + * @param bodyText - a string containing the body of the notification + * @param action - a string indicating the action being peformed, a for add, e for edit, d for delete + */ +const notifyFlyersForOrganizations = async ( + flyerID: string, + bodyText: string, + action: Actions, +): Promise => { + const flyer = await FlyerRepo.getFlyerByID(flyerID); // eslint-disable-line + const organization = await OrganizationRepo.getOrganizationBySlug(flyer.organizationSlug); + const followers = await UserRepo.getUsersFollowingOrganization(flyer.organizationSlug); + + let notifTitle = ''; + let notifBody = ''; + + followers.forEach(async (follower) => { + switch (action) { + case Actions.Add: + notifTitle = `New ${organization.name} Event!`; + // the format for toDateString is Day of the Week Month Date Year ex: Tue Sep 05 2023 + notifBody = `${flyer.title} on ${flyer.startDate.toDateString()} at ${flyer.location}`; + break; + default: + notifTitle = ''; + notifBody = ''; + } + + const uniqueData = { + flyerID: flyer.id, + flyerURL: flyer.flyerURL, + }; + + await sendNotif(follower, notifTitle, notifBody, uniqueData, 'flyer_notif'); + }); +}; + /** * Send notifications for weekly debrief release. */ @@ -153,5 +237,7 @@ export default { notifyNewArticles, notifyNewFlyers, notifyNewMagazines, + notifyFlyersForBookmarks, + notifyFlyersForOrganizations, notifyWeeklyDebrief, }; diff --git a/src/repos/UserRepo.ts b/src/repos/UserRepo.ts index a12fe4e..1f52f3e 100644 --- a/src/repos/UserRepo.ts +++ b/src/repos/UserRepo.ts @@ -121,6 +121,15 @@ const getUsersFollowingOrganization = async (orgSlug: string): Promise = return matchedUsers; }; +/** + * Return all users who have a flyer bookmarked + */ +const getUsersBookmarkedFlyer = async (flyerID: string): Promise => { + const matchedUsers = await UserModel.find({ + bookmarkedFlyers: { $elemMatch: { _id: flyerID } }, + }); + return matchedUsers; +}; /** * Add article to a user's readArticles */ @@ -314,6 +323,7 @@ export default { getUserByUUID, getUsersFollowingPublication, getUsersFollowingOrganization, + getUsersBookmarkedFlyer, bookmarkArticle, bookmarkMagazine, bookmarkFlyer, diff --git a/src/resolvers/FlyerResolver.ts b/src/resolvers/FlyerResolver.ts index ba0c45d..ee848f2 100644 --- a/src/resolvers/FlyerResolver.ts +++ b/src/resolvers/FlyerResolver.ts @@ -11,8 +11,9 @@ import { import { Context } from 'vm'; import { Flyer } from '../entities/Flyer'; import { DEFAULT_LIMIT, DEFAULT_OFFSET } from '../common/constants'; -import FlyerRepo from '../repos/FlyerRepo'; +import FlyerRepo, { Actions } from '../repos/FlyerRepo'; import FlyerMiddleware from '../middlewares/FlyerMiddleware'; +import NotificationRepo from '../repos/NotificationRepo'; @Resolver((_of) => Flyer) class FlyerResolver { @@ -192,7 +193,7 @@ class FlyerResolver { @Arg('title') title: string, @Ctx() ctx: Context, ) { - return FlyerRepo.createFlyer( + const flyer = await FlyerRepo.createFlyer( categorySlug, endDate, flyerURL, @@ -202,12 +203,20 @@ class FlyerResolver { startDate, title, ); + NotificationRepo.notifyFlyersForOrganizations( + flyer.id, + ' just added a new flyer!', + Actions.Add, + ); + return flyer; } @Mutation((_returns) => Flyer, { description: `Delete a flyer with the id .`, }) async deleteFlyer(@Arg('id') id: string) { + const flyer = await FlyerRepo.getFlyerByID(id); + NotificationRepo.notifyFlyersForBookmarks(flyer.id, ' just deleted a flyer', Actions.Delete); return FlyerRepo.deleteFlyer(id); } @@ -228,7 +237,7 @@ class FlyerResolver { @Arg('title', { nullable: true }) title: string, @Ctx() ctx: Context, ) { - return FlyerRepo.editFlyer( + const flyer = await FlyerRepo.editFlyer( id, categorySlug, endDate, @@ -238,6 +247,30 @@ class FlyerResolver { startDate, title, ); + let editedResponse = ''; + if ((endDate && location) || (location && startDate)) { + // if endDate and location or startDate and location are nonempty, notification body would return date and location changed + editedResponse = 'changed its date and location'; + } else if (endDate && startDate) { + // if both endDate and startDate values changed, the notifcation body would just return that the event changed its date + editedResponse = 'changed its date'; + } else if (endDate) { + // if only the endDate changed for the event, then the notification body would print out specifically what the date was changed to + const date = new Date(endDate); + // the format for toDateString is Day of the Week Month Date Year ex: Tue Sep 05 2023 + editedResponse = `changed its end date to ${date.toDateString()}`; + } else if (location) { + // if only the location changed for the event, then the notification body would print out specifically what the location was changed to + editedResponse = `changed its location to ${location}`; + } else if (startDate) { + // if only the startDate changed for the event, then the notification body would print out specifically what the date was changed to + const date = new Date(startDate); + // the format for toDateString is Day of the Week Month Date Year ex: Tue Sep 05 2023 + editedResponse = `changed its start date to ${date.toDateString()}`; + } + + NotificationRepo.notifyFlyersForBookmarks(flyer.id, editedResponse, Actions.Edit); + return flyer; } } diff --git a/src/resolvers/OrganizationResolver.ts b/src/resolvers/OrganizationResolver.ts index 17a11b1..4bae6be 100644 --- a/src/resolvers/OrganizationResolver.ts +++ b/src/resolvers/OrganizationResolver.ts @@ -57,6 +57,13 @@ class OrganizationResolver { async clicks(@Root() organization: Organization): Promise { return OrganizationRepo.getClicks(organization); } + + @FieldResolver((_returns) => Number, { + description: "Returns the total times clicked of an ", + }) + async numFlyers(@Root() organization: Organization): Promise { + return OrganizationRepo.getNumFlyers(organization); + } } export default OrganizationResolver;