From 1f216b732e81add069596a90c8056253b9cf6e1f Mon Sep 17 00:00:00 2001
From: Gary Zhao
Date: Fri, 29 Mar 2024 10:52:43 -0400
Subject: [PATCH] [EASI-4023] New Discussion Reply Notification (#1024)
* [EASI-3944] Notifications - On Discussion Replied Notification (#990)
* wip: draft of sending notifications on discussion reply
* wip: implementing PR feedback
* wip: implement various PR feedback
* wip: corrected actor ID to be reply creator ID and added discussion creator ID to meta
* fix: corrected notification recipient from actor to discussion creator
* feat: testing preview for discussion reply notification
* chore: updated postman collection
* fix: corrected activity meta base struct type
* chore: added comment to helper function
* chore: deleted unused generated resolver
* fix: revereted incorrectly changed user ID assignment
* removed irrelevant comment
* chore: added more assertions to unit test
* Add new reply type guard
* add more conditional renders based on type guard
* Add the new discussion reply gql fragment
* implementing new discussion reply into the UI
* Add a few more queries to the getNotification query
* Notification content to show on discussions
* add model name to util
* move notification settings to its own test
* add e2e test
---------
Co-authored-by: Tom Brooks <100007843+OddTomBrooks@users.noreply.github.com>
---
MINT.postman_collection.json | 11 +-
cypress/e2e/notification.spec.js | 99 ++-
pkg/graph/generated/generated.go | 779 +++++++++++++++++-
pkg/graph/resolvers/activity.resolvers.go | 23 +
pkg/graph/resolvers/plan_discussion.go | 48 +-
pkg/graph/schema/types/activity.graphql | 13 +-
pkg/models/new_discussion_replied_meta.go | 69 ++
pkg/notifications/activity.go | 8 +
.../new_discussion_replied_meta.go | 28 +
.../new_discussion_replied_meta_test.go | 47 ++
pkg/notifications/notifications_test.go | 16 +
.../Notifications/GetNotifications.ts | 11 +
src/gql/gen/graphql.ts | 28 +-
src/gql/gen/types/GetNotifications.ts | 18 +-
.../_components/IndividualNotification.tsx | 24 +-
.../Notifications/Home/_components/_utils.tsx | 31 +
16 files changed, 1199 insertions(+), 54 deletions(-)
create mode 100644 pkg/models/new_discussion_replied_meta.go
create mode 100644 pkg/notifications/new_discussion_replied_meta.go
create mode 100644 pkg/notifications/new_discussion_replied_meta_test.go
diff --git a/MINT.postman_collection.json b/MINT.postman_collection.json
index 9b7a54f489..45d25235bc 100644
--- a/MINT.postman_collection.json
+++ b/MINT.postman_collection.json
@@ -1,9 +1,9 @@
{
"info": {
- "_postman_id": "32329f70-986e-430d-8a0b-4509ca71dab1",
+ "_postman_id": "e2043604-4cd9-443c-9267-db489feb92d8",
"name": "MINT",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
- "_exporter_id": "20320964"
+ "_exporter_id": "20435042"
},
"item": [
{
@@ -2089,7 +2089,8 @@
"// pm.collectionVariables.set(\"operationalSolutionSubtaskID\"+i, subtaskID);",
"// }"
],
- "type": "text/javascript"
+ "type": "text/javascript",
+ "packages": {}
}
}
],
@@ -2099,7 +2100,7 @@
"body": {
"mode": "graphql",
"graphql": {
- "query": "query currentUser {\n currentUser {\n notifications {\n numUnreadNotifications\n notifications {\n __typename\n id\n isRead\n inAppSent\n emailSent\n activity {\n activityType\n entityID\n actorID\n metaData {\n __typename\n ... on TaggedInPlanDiscussionActivityMeta{\n version\n type\n modelPlanID\n modelPlan{\n modelName\n } \n discussionID\n content\n }\n ... on TaggedInDiscussionReplyActivityMeta {\n version\n type\n modelPlanID\n modelPlan{\n modelName\n } \n discussionID\n replyID\n content\n }\n ... on DailyDigestCompleteActivityMeta{\n version\n type\n modelPlanIDs\n date\n analyzedAudits{\n id\n modelPlanID\n modelName\n date\n changes{\n modelPlan{\n oldName\n statusChanges\n }\n documents{\n count\n }\n crTdls{\n activity\n }\n planSections{\n updated\n readyForReview\n readyForClearance\n }\n modelLeads{\n added{\n id\n commonName\n # userAccount{\n # id\n # email\n # }\n }\n }\n planDiscussions{\n activity\n }\n }\n\n }\n\n } \n }\n createdByUserAccount {\n commonName\n }\n }\n createdByUserAccount {\n commonName\n }\n }\n }\n }\n}\n",
+ "query": "query currentUser {\n currentUser {\n notifications {\n numUnreadNotifications\n notifications {\n __typename\n id\n isRead\n inAppSent\n emailSent\n activity {\n activityType\n entityID\n actorID\n metaData {\n __typename\n ... on TaggedInPlanDiscussionActivityMeta{\n version\n type\n modelPlanID\n modelPlan{\n modelName\n } \n discussionID\n content\n }\n ... on TaggedInDiscussionReplyActivityMeta {\n version\n type\n modelPlanID\n modelPlan{\n modelName\n } \n discussionID\n replyID\n content\n }\n ... on NewDiscussionRepliedActivityMeta {\n version\n type\n discussionID\n replyID\n content\n } \n ... on DailyDigestCompleteActivityMeta{\n version\n type\n modelPlanIDs\n date\n analyzedAudits{\n id\n modelPlanID\n modelName\n date\n changes{\n modelPlan{\n oldName\n statusChanges\n }\n documents{\n count\n }\n crTdls{\n activity\n }\n planSections{\n updated\n readyForReview\n readyForClearance\n }\n modelLeads{\n added{\n id\n commonName\n # userAccount{\n # id\n # email\n # }\n }\n }\n planDiscussions{\n activity\n }\n }\n\n }\n\n } \n }\n createdByUserAccount {\n commonName\n }\n }\n createdByUserAccount {\n commonName\n }\n }\n }\n }\n}\n",
"variables": ""
}
},
@@ -2424,4 +2425,4 @@
"value": ""
}
]
-}
\ No newline at end of file
+}
diff --git a/cypress/e2e/notification.spec.js b/cypress/e2e/notification.spec.js
index 8c5ec7212f..c86f7957f4 100644
--- a/cypress/e2e/notification.spec.js
+++ b/cypress/e2e/notification.spec.js
@@ -48,7 +48,8 @@ describe('Notification Center', () => {
cy.contains('button', 'Save discussion').click();
- cy.visit('/notifications');
+ cy.get('[data-testid="close-discussions"]').click();
+ cy.get('[data-testid="navmenu__notification"]').first().click();
// Actual Notification Test
cy.get('[data-testid="navmenu__notification"]')
@@ -67,7 +68,9 @@ describe('Notification Center', () => {
.find('button', 'View Discussion')
.click();
- cy.visit('/notifications');
+ // Navigate to Notification page (faster than cy.visit)
+ cy.get('[data-testid="close-discussions"]').click();
+ cy.get('[data-testid="navmenu__notification"]').first().click();
// Check to see first entry should no longer have red dot
cy.get('[data-testid="individual-notification"]')
@@ -83,6 +86,30 @@ describe('Notification Center', () => {
'exist'
);
cy.get('[data-testid="notification-red-dot"]').should('have.length', 0);
+ });
+
+ it('navigates to see Daily Digest notification', () => {
+ cy.localLogin({ name: 'MINT', role: 'MINT_ASSESSMENT_NONPROD' });
+ cy.visit('/notifications');
+
+ cy.get('[data-testid="individual-notification"]')
+ .first()
+ .find('[data-testid="notification-red-dot"]')
+ .should('exist');
+ cy.contains('button', 'View digest').click();
+
+ cy.get('[data-testid="notification--daily-digest"').should('exist');
+
+ cy.contains('h3', 'Empty Plan').siblings('a').click();
+
+ cy.location().should(loc => {
+ expect(loc.pathname).to.match(/models\/.{36}\/read-only\/model-basics/);
+ });
+ });
+
+ it('navigates to see Notification Settings', () => {
+ cy.localLogin({ name: 'MINT', role: 'MINT_ASSESSMENT_NONPROD' });
+ cy.visit('/notifications');
// Notification Settings Test
cy.contains('a', 'Notification settings').click();
@@ -107,22 +134,66 @@ describe('Notification Center', () => {
);
});
- it('navigates to see Daily Digest notification', () => {
- cy.localLogin({ name: 'MINT', role: 'MINT_ASSESSMENT_NONPROD' });
- cy.visit('/notifications');
+ it.only('testing New Discussion Reply Notification', () => {
+ cy.localLogin({ name: 'JTTC', role: 'MINT_ASSESSMENT_NONPROD' });
+ cy.clickPlanTableByName('Empty Plan');
+
+ // Create a discussion to start things off
+ cy.contains('button', 'Start a discussion').click();
+
+ cy.contains('h1', 'Start a discussion');
+
+ cy.contains('button', 'Save discussion').should('be.disabled');
+
+ cy.get('#user-role').should('not.be.disabled');
+
+ cy.get('#user-role').select('None of the above');
+
+ cy.get('#user-role-description')
+ .type('Designer')
+ .should('have.value', 'Designer');
+
+ cy.get('#mention-editor').type('@ana');
+ cy.get('#JTTC').contains('Anabelle Jerde (JTTC)').click();
+ cy.get('#mention-editor').type('First Notification');
+ cy.get('#mention-editor').should(
+ 'have.text',
+ '@Anabelle Jerde (JTTC) First Notification'
+ );
+
+ cy.contains('button', 'Save discussion').click();
+
+ // New Discussion Reply test
+ cy.contains('button', 'Reply').click();
+
+ cy.contains('label', 'Type your reply');
+
+ cy.get('#mention-editor').type(
+ 'Triggering new discussion reply notification'
+ );
+
+ cy.contains('button', 'Save reply').click();
+
+ cy.get('[data-testid="close-discussions"]').click();
+ cy.get('[data-testid="navmenu__notification"]').first().click();
+
+ cy.get('[data-testid="navmenu__notifications--yesNotification"').should(
+ 'exist'
+ );
+
+ cy.get('[data-testid="individual-notification"]').should('have.length', 2);
cy.get('[data-testid="individual-notification"]')
.first()
- .find('[data-testid="notification-red-dot"]')
- .should('exist');
- cy.contains('button', 'View digest').click();
-
- cy.get('[data-testid="notification--daily-digest"').should('exist');
+ .find('button', 'View Discussion')
+ .click();
- cy.contains('h3', 'Empty Plan').siblings('a').click();
+ cy.get('[data-testid="close-discussions"]').click();
+ cy.get('[data-testid="navmenu__notification"]').first().click();
- cy.location().should(loc => {
- expect(loc.pathname).to.match(/models\/.{36}\/read-only\/model-basics/);
- });
+ cy.get('[data-testid="individual-notification"]')
+ .first()
+ .find('[data-testid="notification-red-dot"]')
+ .should('not.exist');
});
});
diff --git a/pkg/graph/generated/generated.go b/pkg/graph/generated/generated.go
index 85b034703b..ae0942ac42 100644
--- a/pkg/graph/generated/generated.go
+++ b/pkg/graph/generated/generated.go
@@ -51,6 +51,7 @@ type ResolverRoot interface {
ExistingModelLinks() ExistingModelLinksResolver
ModelPlan() ModelPlanResolver
Mutation() MutationResolver
+ NewDiscussionRepliedActivityMeta() NewDiscussionRepliedActivityMetaResolver
OperationalNeed() OperationalNeedResolver
OperationalSolution() OperationalSolutionResolver
PlanBasics() PlanBasicsResolver
@@ -332,6 +333,18 @@ type ComplexityRoot struct {
AgreedDts func(childComplexity int) int
}
+ NewDiscussionRepliedActivityMeta struct {
+ Content func(childComplexity int) int
+ Discussion func(childComplexity int) int
+ DiscussionID func(childComplexity int) int
+ ModelPlan func(childComplexity int) int
+ ModelPlanID func(childComplexity int) int
+ Reply func(childComplexity int) int
+ ReplyID func(childComplexity int) int
+ Type func(childComplexity int) int
+ Version func(childComplexity int) int
+ }
+
OperationalNeed struct {
CreatedBy func(childComplexity int) int
CreatedByUserAccount func(childComplexity int) int
@@ -1251,6 +1264,13 @@ type MutationResolver interface {
MarkAllNotificationsAsRead(ctx context.Context) ([]*models.UserNotification, error)
UpdateUserNotificationPreferences(ctx context.Context, changes map[string]interface{}) (*models.UserNotificationPreferences, error)
}
+type NewDiscussionRepliedActivityMetaResolver interface {
+ ModelPlan(ctx context.Context, obj *models.NewDiscussionRepliedActivityMeta) (*models.ModelPlan, error)
+
+ Discussion(ctx context.Context, obj *models.NewDiscussionRepliedActivityMeta) (*models.PlanDiscussion, error)
+
+ Reply(ctx context.Context, obj *models.NewDiscussionRepliedActivityMeta) (*models.DiscussionReply, error)
+}
type OperationalNeedResolver interface {
Solutions(ctx context.Context, obj *models.OperationalNeed, includeNotNeeded bool) ([]*models.OperationalSolution, error)
}
@@ -2990,6 +3010,69 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.NDAInfo.AgreedDts(childComplexity), true
+ case "NewDiscussionRepliedActivityMeta.content":
+ if e.complexity.NewDiscussionRepliedActivityMeta.Content == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.Content(childComplexity), true
+
+ case "NewDiscussionRepliedActivityMeta.discussion":
+ if e.complexity.NewDiscussionRepliedActivityMeta.Discussion == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.Discussion(childComplexity), true
+
+ case "NewDiscussionRepliedActivityMeta.discussionID":
+ if e.complexity.NewDiscussionRepliedActivityMeta.DiscussionID == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.DiscussionID(childComplexity), true
+
+ case "NewDiscussionRepliedActivityMeta.modelPlan":
+ if e.complexity.NewDiscussionRepliedActivityMeta.ModelPlan == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.ModelPlan(childComplexity), true
+
+ case "NewDiscussionRepliedActivityMeta.modelPlanID":
+ if e.complexity.NewDiscussionRepliedActivityMeta.ModelPlanID == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.ModelPlanID(childComplexity), true
+
+ case "NewDiscussionRepliedActivityMeta.reply":
+ if e.complexity.NewDiscussionRepliedActivityMeta.Reply == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.Reply(childComplexity), true
+
+ case "NewDiscussionRepliedActivityMeta.replyID":
+ if e.complexity.NewDiscussionRepliedActivityMeta.ReplyID == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.ReplyID(childComplexity), true
+
+ case "NewDiscussionRepliedActivityMeta.type":
+ if e.complexity.NewDiscussionRepliedActivityMeta.Type == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.Type(childComplexity), true
+
+ case "NewDiscussionRepliedActivityMeta.version":
+ if e.complexity.NewDiscussionRepliedActivityMeta.Version == nil {
+ break
+ }
+
+ return e.complexity.NewDiscussionRepliedActivityMeta.Version(childComplexity), true
+
case "OperationalNeed.createdBy":
if e.complexity.OperationalNeed.CreatedBy == nil {
break
@@ -8331,7 +8414,7 @@ enum ActivityType {
"""
ActivityMetaData is a type that represents all the data that can be captured in an Activity
"""
-union ActivityMetaData = TaggedInPlanDiscussionActivityMeta | TaggedInDiscussionReplyActivityMeta | DailyDigestCompleteActivityMeta
+union ActivityMetaData = TaggedInPlanDiscussionActivityMeta | TaggedInDiscussionReplyActivityMeta | DailyDigestCompleteActivityMeta | NewDiscussionRepliedActivityMeta
type TaggedInPlanDiscussionActivityMeta {
version: Int!
@@ -8355,6 +8438,17 @@ type TaggedInDiscussionReplyActivityMeta {
content: String!
}
+type NewDiscussionRepliedActivityMeta {
+ version: Int!
+ type: ActivityType!
+ modelPlanID: UUID!
+ modelPlan: ModelPlan!
+ discussionID: UUID!
+ discussion: PlanDiscussion!
+ replyID: UUID!
+ reply: DiscussionReply!
+ content: String!
+}
type DailyDigestCompleteActivityMeta {
version: Int!
type: ActivityType!
@@ -25444,6 +25538,512 @@ func (ec *executionContext) fieldContext_NDAInfo_agreedDts(ctx context.Context,
return fc, nil
}
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_version(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_version(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Version, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(int)
+ fc.Result = res
+ return ec.marshalNInt2int(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type Int does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_type(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_type(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Type, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(models.ActivityType)
+ fc.Result = res
+ return ec.marshalNActivityType2githubᚗcomᚋcmsgovᚋmintᚑappᚋpkgᚋmodelsᚐActivityType(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type ActivityType does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_modelPlanID(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_modelPlanID(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.ModelPlanID, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(uuid.UUID)
+ fc.Result = res
+ return ec.marshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_modelPlanID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type UUID does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_modelPlan(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_modelPlan(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.NewDiscussionRepliedActivityMeta().ModelPlan(rctx, obj)
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*models.ModelPlan)
+ fc.Result = res
+ return ec.marshalNModelPlan2ᚖgithubᚗcomᚋcmsgovᚋmintᚑappᚋpkgᚋmodelsᚐModelPlan(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_modelPlan(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "id":
+ return ec.fieldContext_ModelPlan_id(ctx, field)
+ case "modelName":
+ return ec.fieldContext_ModelPlan_modelName(ctx, field)
+ case "abbreviation":
+ return ec.fieldContext_ModelPlan_abbreviation(ctx, field)
+ case "archived":
+ return ec.fieldContext_ModelPlan_archived(ctx, field)
+ case "createdBy":
+ return ec.fieldContext_ModelPlan_createdBy(ctx, field)
+ case "createdByUserAccount":
+ return ec.fieldContext_ModelPlan_createdByUserAccount(ctx, field)
+ case "createdDts":
+ return ec.fieldContext_ModelPlan_createdDts(ctx, field)
+ case "modifiedBy":
+ return ec.fieldContext_ModelPlan_modifiedBy(ctx, field)
+ case "modifiedByUserAccount":
+ return ec.fieldContext_ModelPlan_modifiedByUserAccount(ctx, field)
+ case "modifiedDts":
+ return ec.fieldContext_ModelPlan_modifiedDts(ctx, field)
+ case "basics":
+ return ec.fieldContext_ModelPlan_basics(ctx, field)
+ case "generalCharacteristics":
+ return ec.fieldContext_ModelPlan_generalCharacteristics(ctx, field)
+ case "participantsAndProviders":
+ return ec.fieldContext_ModelPlan_participantsAndProviders(ctx, field)
+ case "beneficiaries":
+ return ec.fieldContext_ModelPlan_beneficiaries(ctx, field)
+ case "opsEvalAndLearning":
+ return ec.fieldContext_ModelPlan_opsEvalAndLearning(ctx, field)
+ case "collaborators":
+ return ec.fieldContext_ModelPlan_collaborators(ctx, field)
+ case "documents":
+ return ec.fieldContext_ModelPlan_documents(ctx, field)
+ case "discussions":
+ return ec.fieldContext_ModelPlan_discussions(ctx, field)
+ case "payments":
+ return ec.fieldContext_ModelPlan_payments(ctx, field)
+ case "status":
+ return ec.fieldContext_ModelPlan_status(ctx, field)
+ case "isFavorite":
+ return ec.fieldContext_ModelPlan_isFavorite(ctx, field)
+ case "isCollaborator":
+ return ec.fieldContext_ModelPlan_isCollaborator(ctx, field)
+ case "crs":
+ return ec.fieldContext_ModelPlan_crs(ctx, field)
+ case "tdls":
+ return ec.fieldContext_ModelPlan_tdls(ctx, field)
+ case "prepareForClearance":
+ return ec.fieldContext_ModelPlan_prepareForClearance(ctx, field)
+ case "nameHistory":
+ return ec.fieldContext_ModelPlan_nameHistory(ctx, field)
+ case "operationalNeeds":
+ return ec.fieldContext_ModelPlan_operationalNeeds(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type ModelPlan", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_discussionID(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_discussionID(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.DiscussionID, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(uuid.UUID)
+ fc.Result = res
+ return ec.marshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_discussionID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type UUID does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_discussion(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_discussion(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.NewDiscussionRepliedActivityMeta().Discussion(rctx, obj)
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*models.PlanDiscussion)
+ fc.Result = res
+ return ec.marshalNPlanDiscussion2ᚖgithubᚗcomᚋcmsgovᚋmintᚑappᚋpkgᚋmodelsᚐPlanDiscussion(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_discussion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "id":
+ return ec.fieldContext_PlanDiscussion_id(ctx, field)
+ case "modelPlanID":
+ return ec.fieldContext_PlanDiscussion_modelPlanID(ctx, field)
+ case "content":
+ return ec.fieldContext_PlanDiscussion_content(ctx, field)
+ case "userRole":
+ return ec.fieldContext_PlanDiscussion_userRole(ctx, field)
+ case "userRoleDescription":
+ return ec.fieldContext_PlanDiscussion_userRoleDescription(ctx, field)
+ case "replies":
+ return ec.fieldContext_PlanDiscussion_replies(ctx, field)
+ case "isAssessment":
+ return ec.fieldContext_PlanDiscussion_isAssessment(ctx, field)
+ case "createdBy":
+ return ec.fieldContext_PlanDiscussion_createdBy(ctx, field)
+ case "createdByUserAccount":
+ return ec.fieldContext_PlanDiscussion_createdByUserAccount(ctx, field)
+ case "createdDts":
+ return ec.fieldContext_PlanDiscussion_createdDts(ctx, field)
+ case "modifiedBy":
+ return ec.fieldContext_PlanDiscussion_modifiedBy(ctx, field)
+ case "modifiedByUserAccount":
+ return ec.fieldContext_PlanDiscussion_modifiedByUserAccount(ctx, field)
+ case "modifiedDts":
+ return ec.fieldContext_PlanDiscussion_modifiedDts(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type PlanDiscussion", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_replyID(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_replyID(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.ReplyID, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(uuid.UUID)
+ fc.Result = res
+ return ec.marshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_replyID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type UUID does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_reply(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_reply(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.NewDiscussionRepliedActivityMeta().Reply(rctx, obj)
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*models.DiscussionReply)
+ fc.Result = res
+ return ec.marshalNDiscussionReply2ᚖgithubᚗcomᚋcmsgovᚋmintᚑappᚋpkgᚋmodelsᚐDiscussionReply(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_reply(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "id":
+ return ec.fieldContext_DiscussionReply_id(ctx, field)
+ case "discussionID":
+ return ec.fieldContext_DiscussionReply_discussionID(ctx, field)
+ case "content":
+ return ec.fieldContext_DiscussionReply_content(ctx, field)
+ case "userRole":
+ return ec.fieldContext_DiscussionReply_userRole(ctx, field)
+ case "userRoleDescription":
+ return ec.fieldContext_DiscussionReply_userRoleDescription(ctx, field)
+ case "isAssessment":
+ return ec.fieldContext_DiscussionReply_isAssessment(ctx, field)
+ case "createdBy":
+ return ec.fieldContext_DiscussionReply_createdBy(ctx, field)
+ case "createdByUserAccount":
+ return ec.fieldContext_DiscussionReply_createdByUserAccount(ctx, field)
+ case "createdDts":
+ return ec.fieldContext_DiscussionReply_createdDts(ctx, field)
+ case "modifiedBy":
+ return ec.fieldContext_DiscussionReply_modifiedBy(ctx, field)
+ case "modifiedByUserAccount":
+ return ec.fieldContext_DiscussionReply_modifiedByUserAccount(ctx, field)
+ case "modifiedDts":
+ return ec.fieldContext_DiscussionReply_modifiedDts(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type DiscussionReply", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta_content(ctx context.Context, field graphql.CollectedField, obj *models.NewDiscussionRepliedActivityMeta) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_NewDiscussionRepliedActivityMeta_content(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.Content, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(string)
+ fc.Result = res
+ return ec.marshalNString2string(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_NewDiscussionRepliedActivityMeta_content(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "NewDiscussionRepliedActivityMeta",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type String does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _OperationalNeed_id(ctx context.Context, field graphql.CollectedField, obj *models.OperationalNeed) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_OperationalNeed_id(ctx, field)
if err != nil {
@@ -62278,6 +62878,11 @@ func (ec *executionContext) _ActivityMetaData(ctx context.Context, sel ast.Selec
return graphql.Null
}
return ec._DailyDigestCompleteActivityMeta(ctx, sel, obj)
+ case *models.NewDiscussionRepliedActivityMeta:
+ if obj == nil {
+ return graphql.Null
+ }
+ return ec._NewDiscussionRepliedActivityMeta(ctx, sel, obj)
default:
panic(fmt.Errorf("unexpected type %T", obj))
}
@@ -65173,6 +65778,178 @@ func (ec *executionContext) _NDAInfo(ctx context.Context, sel ast.SelectionSet,
return out
}
+var newDiscussionRepliedActivityMetaImplementors = []string{"NewDiscussionRepliedActivityMeta", "ActivityMetaData"}
+
+func (ec *executionContext) _NewDiscussionRepliedActivityMeta(ctx context.Context, sel ast.SelectionSet, obj *models.NewDiscussionRepliedActivityMeta) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, newDiscussionRepliedActivityMetaImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ deferred := make(map[string]*graphql.FieldSet)
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("NewDiscussionRepliedActivityMeta")
+ case "version":
+ out.Values[i] = ec._NewDiscussionRepliedActivityMeta_version(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ atomic.AddUint32(&out.Invalids, 1)
+ }
+ case "type":
+ out.Values[i] = ec._NewDiscussionRepliedActivityMeta_type(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ atomic.AddUint32(&out.Invalids, 1)
+ }
+ case "modelPlanID":
+ out.Values[i] = ec._NewDiscussionRepliedActivityMeta_modelPlanID(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ atomic.AddUint32(&out.Invalids, 1)
+ }
+ case "modelPlan":
+ field := field
+
+ innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._NewDiscussionRepliedActivityMeta_modelPlan(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&fs.Invalids, 1)
+ }
+ return res
+ }
+
+ if field.Deferrable != nil {
+ dfs, ok := deferred[field.Deferrable.Label]
+ di := 0
+ if ok {
+ dfs.AddField(field)
+ di = len(dfs.Values) - 1
+ } else {
+ dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
+ deferred[field.Deferrable.Label] = dfs
+ }
+ dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
+ return innerFunc(ctx, dfs)
+ })
+
+ // don't run the out.Concurrently() call below
+ out.Values[i] = graphql.Null
+ continue
+ }
+
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ case "discussionID":
+ out.Values[i] = ec._NewDiscussionRepliedActivityMeta_discussionID(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ atomic.AddUint32(&out.Invalids, 1)
+ }
+ case "discussion":
+ field := field
+
+ innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._NewDiscussionRepliedActivityMeta_discussion(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&fs.Invalids, 1)
+ }
+ return res
+ }
+
+ if field.Deferrable != nil {
+ dfs, ok := deferred[field.Deferrable.Label]
+ di := 0
+ if ok {
+ dfs.AddField(field)
+ di = len(dfs.Values) - 1
+ } else {
+ dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
+ deferred[field.Deferrable.Label] = dfs
+ }
+ dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
+ return innerFunc(ctx, dfs)
+ })
+
+ // don't run the out.Concurrently() call below
+ out.Values[i] = graphql.Null
+ continue
+ }
+
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ case "replyID":
+ out.Values[i] = ec._NewDiscussionRepliedActivityMeta_replyID(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ atomic.AddUint32(&out.Invalids, 1)
+ }
+ case "reply":
+ field := field
+
+ innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._NewDiscussionRepliedActivityMeta_reply(ctx, field, obj)
+ if res == graphql.Null {
+ atomic.AddUint32(&fs.Invalids, 1)
+ }
+ return res
+ }
+
+ if field.Deferrable != nil {
+ dfs, ok := deferred[field.Deferrable.Label]
+ di := 0
+ if ok {
+ dfs.AddField(field)
+ di = len(dfs.Values) - 1
+ } else {
+ dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
+ deferred[field.Deferrable.Label] = dfs
+ }
+ dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
+ return innerFunc(ctx, dfs)
+ })
+
+ // don't run the out.Concurrently() call below
+ out.Values[i] = graphql.Null
+ continue
+ }
+
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
+ case "content":
+ out.Values[i] = ec._NewDiscussionRepliedActivityMeta_content(ctx, field, obj)
+ if out.Values[i] == graphql.Null {
+ atomic.AddUint32(&out.Invalids, 1)
+ }
+ default:
+ panic("unknown field " + strconv.Quote(field.Name))
+ }
+ }
+ out.Dispatch(ctx)
+ if out.Invalids > 0 {
+ return graphql.Null
+ }
+
+ atomic.AddInt32(&ec.deferred, int32(len(deferred)))
+
+ for label, dfs := range deferred {
+ ec.processDeferredGroup(graphql.DeferredGroup{
+ Label: label,
+ Path: graphql.GetPath(ctx),
+ FieldSet: dfs,
+ Context: ctx,
+ })
+ }
+
+ return out
+}
+
var operationalNeedImplementors = []string{"OperationalNeed"}
func (ec *executionContext) _OperationalNeed(ctx context.Context, sel ast.SelectionSet, obj *models.OperationalNeed) graphql.Marshaler {
diff --git a/pkg/graph/resolvers/activity.resolvers.go b/pkg/graph/resolvers/activity.resolvers.go
index cbb3431a88..e104abfcc1 100644
--- a/pkg/graph/resolvers/activity.resolvers.go
+++ b/pkg/graph/resolvers/activity.resolvers.go
@@ -24,6 +24,23 @@ func (r *dailyDigestCompleteActivityMetaResolver) AnalyzedAudits(ctx context.Con
return loaders.AnalyzedAuditGetByModelPlanIDsAndDate(ctx, obj.ModelPlanIDs, obj.Date)
}
+// ModelPlan is the resolver for the modelPlan field.
+func (r *newDiscussionRepliedActivityMetaResolver) ModelPlan(ctx context.Context, obj *models.NewDiscussionRepliedActivityMeta) (*models.ModelPlan, error) {
+ return ModelPlanGetByIDLOADER(ctx, obj.ModelPlanID)
+}
+
+// Discussion is the resolver for the discussion field.
+func (r *newDiscussionRepliedActivityMetaResolver) Discussion(ctx context.Context, obj *models.NewDiscussionRepliedActivityMeta) (*models.PlanDiscussion, error) {
+ logger := appcontext.ZLogger(ctx)
+ return PlanDiscussionGetByID(ctx, r.store, logger, obj.DiscussionID)
+}
+
+// Reply is the resolver for the reply field.
+func (r *newDiscussionRepliedActivityMetaResolver) Reply(ctx context.Context, obj *models.NewDiscussionRepliedActivityMeta) (*models.DiscussionReply, error) {
+ logger := appcontext.ZLogger(ctx)
+ return DiscussionReplyGetByID(ctx, r.store, logger, obj.ReplyID)
+}
+
// ModelPlan is the resolver for the modelPlan field.
func (r *taggedInDiscussionReplyActivityMetaResolver) ModelPlan(ctx context.Context, obj *models.TaggedInDiscussionReplyActivityMeta) (*models.ModelPlan, error) {
return ModelPlanGetByIDLOADER(ctx, obj.ModelPlanID)
@@ -60,6 +77,11 @@ func (r *Resolver) DailyDigestCompleteActivityMeta() generated.DailyDigestComple
return &dailyDigestCompleteActivityMetaResolver{r}
}
+// NewDiscussionRepliedActivityMeta returns generated.NewDiscussionRepliedActivityMetaResolver implementation.
+func (r *Resolver) NewDiscussionRepliedActivityMeta() generated.NewDiscussionRepliedActivityMetaResolver {
+ return &newDiscussionRepliedActivityMetaResolver{r}
+}
+
// TaggedInDiscussionReplyActivityMeta returns generated.TaggedInDiscussionReplyActivityMetaResolver implementation.
func (r *Resolver) TaggedInDiscussionReplyActivityMeta() generated.TaggedInDiscussionReplyActivityMetaResolver {
return &taggedInDiscussionReplyActivityMetaResolver{r}
@@ -72,5 +94,6 @@ func (r *Resolver) TaggedInPlanDiscussionActivityMeta() generated.TaggedInPlanDi
type activityResolver struct{ *Resolver }
type dailyDigestCompleteActivityMetaResolver struct{ *Resolver }
+type newDiscussionRepliedActivityMetaResolver struct{ *Resolver }
type taggedInDiscussionReplyActivityMetaResolver struct{ *Resolver }
type taggedInPlanDiscussionActivityMetaResolver struct{ *Resolver }
diff --git a/pkg/graph/resolvers/plan_discussion.go b/pkg/graph/resolvers/plan_discussion.go
index 6a00a03fa9..f940f73000 100644
--- a/pkg/graph/resolvers/plan_discussion.go
+++ b/pkg/graph/resolvers/plan_discussion.go
@@ -454,13 +454,24 @@ func CreateDiscussionReply(
if err != nil {
return reply, err
}
+
// Create Activity and notifications in the DB
_, notificationErr := notifications.ActivityTaggedInDiscussionReplyCreate(ctx, tx, principal.Account().ID, discussion.ModelPlanID, discussion.ID, reply.ID, reply.Content, loaders.UserNotificationPreferencesGetByUserID)
if notificationErr != nil {
return nil, fmt.Errorf("unable to generate notifications, %w", notificationErr)
}
- go func() {
+ discussionCreatorPref, err := UserNotificationPreferencesGetByUserID(ctx, discussion.CreatedBy)
+ if err != nil {
+ return nil, fmt.Errorf("unable to get user notification preference, Notification not created %w", err)
+ }
+
+ _, err = notifications.ActivityNewDiscussionRepliedCreate(ctx, tx, reply.CreatedBy, discussion.ModelPlanID, discussion.ID, discussion.CreatedBy, reply.ID, reply.Content, discussionCreatorPref)
+ if notificationErr != nil {
+ return nil, fmt.Errorf("unable to generate notifications, %w", notificationErr)
+ }
+
+ go func() {
replyUser := principal.Account()
commonName := replyUser.CommonName
@@ -470,22 +481,24 @@ func CreateDiscussionReply(
zap.Error(err))
}
- errReplyEmail := sendDiscussionReplyEmails(
- ctx,
- store,
- logger,
- emailService,
- emailTemplateService,
- addressBook,
- discussion,
- reply,
- modelPlan,
- replyUser,
- )
- if errReplyEmail != nil {
- logger.Error("error sending tagged in plan discussion reply emails to tagged users and teams",
- zap.String("discussionID", discussion.ID.String()),
- zap.Error(errReplyEmail))
+ if discussionCreatorPref.NewDiscussionReply.SendEmail() {
+ errReplyEmail := sendDiscussionReplyEmails(
+ ctx,
+ store,
+ logger,
+ emailService,
+ emailTemplateService,
+ addressBook,
+ discussion,
+ reply,
+ modelPlan,
+ replyUser,
+ )
+ if errReplyEmail != nil {
+ logger.Error("error sending tagged in plan discussion reply emails to tagged users and teams",
+ zap.String("discussionID", discussion.ID.String()),
+ zap.Error(errReplyEmail))
+ }
}
err = sendPlanDiscussionTagEmails(
@@ -511,6 +524,7 @@ func CreateDiscussionReply(
}
}
}()
+
return reply, err
})
if err != nil {
diff --git a/pkg/graph/schema/types/activity.graphql b/pkg/graph/schema/types/activity.graphql
index bfe8596fa1..742119e779 100644
--- a/pkg/graph/schema/types/activity.graphql
+++ b/pkg/graph/schema/types/activity.graphql
@@ -13,7 +13,7 @@ enum ActivityType {
"""
ActivityMetaData is a type that represents all the data that can be captured in an Activity
"""
-union ActivityMetaData = TaggedInPlanDiscussionActivityMeta | TaggedInDiscussionReplyActivityMeta | DailyDigestCompleteActivityMeta
+union ActivityMetaData = TaggedInPlanDiscussionActivityMeta | TaggedInDiscussionReplyActivityMeta | DailyDigestCompleteActivityMeta | NewDiscussionRepliedActivityMeta
type TaggedInPlanDiscussionActivityMeta {
version: Int!
@@ -37,6 +37,17 @@ type TaggedInDiscussionReplyActivityMeta {
content: String!
}
+type NewDiscussionRepliedActivityMeta {
+ version: Int!
+ type: ActivityType!
+ modelPlanID: UUID!
+ modelPlan: ModelPlan!
+ discussionID: UUID!
+ discussion: PlanDiscussion!
+ replyID: UUID!
+ reply: DiscussionReply!
+ content: String!
+}
type DailyDigestCompleteActivityMeta {
version: Int!
type: ActivityType!
diff --git a/pkg/models/new_discussion_replied_meta.go b/pkg/models/new_discussion_replied_meta.go
new file mode 100644
index 0000000000..69e7d10878
--- /dev/null
+++ b/pkg/models/new_discussion_replied_meta.go
@@ -0,0 +1,69 @@
+package models
+
+import (
+ "database/sql/driver"
+ "encoding/json"
+ "errors"
+
+ "github.com/google/uuid"
+)
+
+// NewDiscussionRepliedActivityMeta represents the notification data that is relevant to a new Discussion Reply
+type NewDiscussionRepliedActivityMeta struct {
+ ActivityMetaBaseStruct
+ discussionRelation
+ modelPlanRelation
+ ReplyID uuid.UUID `json:"replyID"`
+ Content string `json:"content"`
+}
+
+// newNewDiscussionRepliedActivityMeta creates a New NewDiscussionRepliedActivityMeta
+func newNewDiscussionRepliedActivityMeta(modelPlanID uuid.UUID, discussionID uuid.UUID, replyID uuid.UUID, content string) *NewDiscussionRepliedActivityMeta {
+ version := 0 //iterate this if this type ever updates
+ return &NewDiscussionRepliedActivityMeta{
+ ActivityMetaBaseStruct: NewActivityMetaBaseStruct(ActivityNewDiscussionReply, version),
+ discussionRelation: NewDiscussionRelation(discussionID),
+ modelPlanRelation: NewModelPlanRelation(modelPlanID),
+ ReplyID: replyID,
+ Content: content,
+ }
+
+}
+
+// NewNewDiscussionRepliedActivity creates a New New Discussion Replied type of Activity
+func NewNewDiscussionRepliedActivity(actorID uuid.UUID, modelPlanID uuid.UUID, discussionID uuid.UUID, replyID uuid.UUID, content string) *Activity {
+ return &Activity{
+ baseStruct: NewBaseStruct(actorID),
+ ActorID: actorID,
+ EntityID: discussionID,
+ ActivityType: ActivityNewDiscussionReply,
+ MetaData: newNewDiscussionRepliedActivityMeta(modelPlanID, discussionID, replyID, content),
+ }
+}
+
+// Future Enhancement: --> Refactor these all to have a generic scan / value
+
+// Value allows us to satisfy the valuer interface so we can write to the database
+// We need to do a specific implementation instead of relying on the implementation of the embedded struct, as that will only serialize the common data
+func (d NewDiscussionRepliedActivityMeta) Value() (driver.Value, error) {
+
+ j, err := json.Marshal(d)
+ return j, err
+}
+
+// Scan implements the scanner interface so we can translate the JSONb from the db to an object in GO
+func (d *NewDiscussionRepliedActivityMeta) Scan(src interface{}) error {
+ if src == nil {
+ return nil
+ }
+ source, ok := src.([]byte)
+ if !ok {
+ return errors.New("type assertion .([]byte) failed")
+ }
+ err := json.Unmarshal(source, d)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/pkg/notifications/activity.go b/pkg/notifications/activity.go
index e697e3ba0e..f987cb4c9c 100644
--- a/pkg/notifications/activity.go
+++ b/pkg/notifications/activity.go
@@ -102,6 +102,14 @@ func parseRawActivityMetaData(activityType models.ActivityType, rawMetaDataJSON
}
return &meta, nil
+ case models.ActivityNewDiscussionReply:
+ // Deserialize the raw JSON into NewDiscussionReplyActivityMeta
+ meta := models.NewDiscussionRepliedActivityMeta{}
+ if err := json.Unmarshal(rawData, &meta); err != nil {
+ return nil, err
+ }
+ return &meta, nil
+
// Add cases for other ActivityTypes as needed
default:
diff --git a/pkg/notifications/new_discussion_replied_meta.go b/pkg/notifications/new_discussion_replied_meta.go
new file mode 100644
index 0000000000..5d7cc6917c
--- /dev/null
+++ b/pkg/notifications/new_discussion_replied_meta.go
@@ -0,0 +1,28 @@
+package notifications
+
+import (
+ "context"
+
+ "github.com/google/uuid"
+
+ "github.com/cmsgov/mint-app/pkg/models"
+ "github.com/cmsgov/mint-app/pkg/sqlutils"
+)
+
+// ActivityNewDiscussionRepliedCreate creates an activity for when a Discussion is replied to.
+func ActivityNewDiscussionRepliedCreate(ctx context.Context, np sqlutils.NamedPreparer, actorID uuid.UUID, modelPlanID uuid.UUID, discussionID uuid.UUID, discussionCreatorID uuid.UUID, replyID uuid.UUID, discussionReplyContent models.TaggedHTML, userPreferences *models.UserNotificationPreferences) (*models.Activity, error) {
+
+ activity := models.NewNewDiscussionRepliedActivity(actorID, modelPlanID, discussionID, replyID, discussionReplyContent.RawContent.String())
+
+ retActivity, actErr := activityCreate(ctx, np, activity)
+ if actErr != nil {
+ return nil, actErr
+ }
+
+ _, err := userNotificationCreate(ctx, np, retActivity, discussionCreatorID, userPreferences.NewDiscussionReply)
+ if err != nil {
+ return nil, err
+ }
+
+ return retActivity, nil
+}
diff --git a/pkg/notifications/new_discussion_replied_meta_test.go b/pkg/notifications/new_discussion_replied_meta_test.go
new file mode 100644
index 0000000000..80234515cf
--- /dev/null
+++ b/pkg/notifications/new_discussion_replied_meta_test.go
@@ -0,0 +1,47 @@
+package notifications
+
+import (
+ "github.com/google/uuid"
+
+ "github.com/cmsgov/mint-app/pkg/models"
+)
+
+func (suite *NotificationsSuite) TestActivityNewDiscussionRepliedCreate() {
+
+ html := `Hey there! Are you available for a quick sync on this issue? Thanks!
`
+ taggedContent, err := models.NewTaggedContentFromString(html)
+ suite.NoError(err)
+ input := models.TaggedHTML(taggedContent)
+
+ modelPlanID := uuid.New() // We are just choosing a valid UUID to set for the entityID
+ discussionID := uuid.New()
+ replyID := uuid.New()
+ actorID := suite.testConfigs.Principal.Account().ID
+ userPrefs := models.NewUserNotificationPreferences(actorID)
+
+ discussionCreator, err := suite.testConfigs.GetTestPrincipal(suite.testConfigs.Store, "FRED")
+ suite.NoError(err)
+
+ testActivity, err := ActivityNewDiscussionRepliedCreate(suite.testConfigs.Context, suite.testConfigs.Store, actorID, modelPlanID, discussionID, discussionCreator.Account().ID, replyID, input, userPrefs)
+ suite.NoError(err)
+
+ // Assert about notification object creation
+ suite.NotNil(testActivity)
+ suite.EqualValues(models.ActivityNewDiscussionReply, testActivity.ActivityType)
+
+ // Assert about notification delivery
+ actorNots, err := UserNotificationCollectionGetByUser(suite.testConfigs.Context, suite.testConfigs.Store, suite.testConfigs.Principal)
+ suite.NoError(err)
+ suite.EqualValues(0, actorNots.NumUnreadNotifications())
+
+ recipientNots, err := UserNotificationCollectionGetByUser(suite.testConfigs.Context, suite.testConfigs.Store, discussionCreator)
+ suite.NoError(err)
+ suite.EqualValues(1, recipientNots.NumUnreadNotifications())
+
+ // Assert about meta data values
+ meta := suite.deserializeActivityMetadata(testActivity)
+ suite.EqualValues(input.RawContent.String(), meta.(*models.NewDiscussionRepliedActivityMeta).Content)
+ suite.EqualValues(discussionID, meta.(*models.NewDiscussionRepliedActivityMeta).DiscussionID)
+ suite.EqualValues(modelPlanID, meta.(*models.NewDiscussionRepliedActivityMeta).ModelPlanID)
+ suite.EqualValues(replyID, meta.(*models.NewDiscussionRepliedActivityMeta).ReplyID)
+}
diff --git a/pkg/notifications/notifications_test.go b/pkg/notifications/notifications_test.go
index fafc0e77c5..c03687dbcf 100644
--- a/pkg/notifications/notifications_test.go
+++ b/pkg/notifications/notifications_test.go
@@ -3,6 +3,8 @@ package notifications
import (
"testing"
+ "github.com/cmsgov/mint-app/pkg/models"
+
"github.com/stretchr/testify/suite"
"github.com/cmsgov/mint-app/pkg/testconfig"
@@ -27,3 +29,17 @@ func TestNotificationsSuite(t *testing.T) {
suite.Run(t, rs)
}
+
+// deserializeActivityMetadata is a helper function to deserialize the metadata of an activity
+// It asserts that the metadata is not deserialized, and that the raw metadata is deserialized
+// This method is used to test the deserialization of the metadata of an activity in notification unit tests
+func (suite *NotificationsSuite) deserializeActivityMetadata(testActivity *models.Activity) models.ActivityMetaData {
+ suite.Nil(testActivity.MetaData) // Assert meta data is not deserialized here
+ suite.NotNil(testActivity.MetaDataRaw) // Assert meta data can be deserialized
+
+ meta, err := parseRawActivityMetaData(testActivity.ActivityType, testActivity.MetaDataRaw)
+ suite.NoError(err)
+ suite.NotNil(meta)
+
+ return meta
+}
diff --git a/src/gql/apolloGQL/Notifications/GetNotifications.ts b/src/gql/apolloGQL/Notifications/GetNotifications.ts
index f0900e5684..ef7f68c866 100644
--- a/src/gql/apolloGQL/Notifications/GetNotifications.ts
+++ b/src/gql/apolloGQL/Notifications/GetNotifications.ts
@@ -21,6 +21,17 @@ export default gql(/* GraphQL */ `
}
metaData {
__typename
+ ... on NewDiscussionRepliedActivityMeta {
+ version
+ type
+ discussionID
+ replyID
+ modelPlanID
+ modelPlan {
+ modelName
+ }
+ content
+ }
... on TaggedInPlanDiscussionActivityMeta {
version
type
diff --git a/src/gql/gen/graphql.ts b/src/gql/gen/graphql.ts
index b466779dac..9b25202927 100644
--- a/src/gql/gen/graphql.ts
+++ b/src/gql/gen/graphql.ts
@@ -55,7 +55,7 @@ export type Activity = {
};
/** ActivityMetaData is a type that represents all the data that can be captured in an Activity */
-export type ActivityMetaData = DailyDigestCompleteActivityMeta | TaggedInDiscussionReplyActivityMeta | TaggedInPlanDiscussionActivityMeta;
+export type ActivityMetaData = DailyDigestCompleteActivityMeta | NewDiscussionRepliedActivityMeta | TaggedInDiscussionReplyActivityMeta | TaggedInPlanDiscussionActivityMeta;
/** ActivityType represents the possible activities that happen in application that might result in a notification */
export enum ActivityType {
@@ -986,6 +986,19 @@ export type NdaInfo = {
agreedDts?: Maybe;
};
+export type NewDiscussionRepliedActivityMeta = {
+ __typename: 'NewDiscussionRepliedActivityMeta';
+ content: Scalars['String']['output'];
+ discussion: PlanDiscussion;
+ discussionID: Scalars['UUID']['output'];
+ modelPlan: ModelPlan;
+ modelPlanID: Scalars['UUID']['output'];
+ reply: DiscussionReply;
+ replyID: Scalars['UUID']['output'];
+ type: ActivityType;
+ version: Scalars['Int']['output'];
+};
+
export enum NonClaimsBasedPayType {
ADVANCED_PAYMENT = 'ADVANCED_PAYMENT',
BUNDLED_EPISODE_OF_CARE = 'BUNDLED_EPISODE_OF_CARE',
@@ -3362,7 +3375,7 @@ export type GetNotificationSettingsQuery = { __typename: 'Query', currentUser: {
export type GetNotificationsQueryVariables = Exact<{ [key: string]: never; }>;
-export type GetNotificationsQuery = { __typename: 'Query', currentUser: { __typename: 'CurrentUser', notifications: { __typename: 'UserNotifications', numUnreadNotifications: number, notifications: Array<{ __typename: 'UserNotification', id: UUID, isRead: boolean, inAppSent: boolean, emailSent: boolean, createdDts: Time, activity: { __typename: 'Activity', activityType: ActivityType, entityID: UUID, actorID: UUID, actorUserAccount: { __typename: 'UserAccount', commonName: string }, metaData: { __typename: 'DailyDigestCompleteActivityMeta', version: number, type: ActivityType, modelPlanIDs: Array, date: Time, analyzedAudits: Array<{ __typename: 'AnalyzedAudit', id: UUID, modelPlanID: UUID, modelName: string, date: Time, changes: { __typename: 'AnalyzedAuditChange', modelPlan?: { __typename: 'AnalyzedModelPlan', oldName?: string | null, statusChanges?: Array | null } | null, documents?: { __typename: 'AnalyzedDocuments', count?: number | null } | null, crTdls?: { __typename: 'AnalyzedCrTdls', activity?: boolean | null } | null, planSections?: { __typename: 'AnalyzedPlanSections', updated: Array, readyForReview: Array, readyForClearance: Array } | null, modelLeads?: { __typename: 'AnalyzedModelLeads', added: Array<{ __typename: 'AnalyzedModelLeadInfo', id: UUID, commonName: string }> } | null, planDiscussions?: { __typename: 'AnalyzedPlanDiscussions', activity?: boolean | null } | null } }> } | { __typename: 'TaggedInDiscussionReplyActivityMeta', version: number, type: ActivityType, modelPlanID: UUID, discussionID: UUID, replyID: UUID, content: string, modelPlan: { __typename: 'ModelPlan', modelName: string } } | { __typename: 'TaggedInPlanDiscussionActivityMeta', version: number, type: ActivityType, modelPlanID: UUID, discussionID: UUID, content: string, modelPlan: { __typename: 'ModelPlan', modelName: string } } } }> } } };
+export type GetNotificationsQuery = { __typename: 'Query', currentUser: { __typename: 'CurrentUser', notifications: { __typename: 'UserNotifications', numUnreadNotifications: number, notifications: Array<{ __typename: 'UserNotification', id: UUID, isRead: boolean, inAppSent: boolean, emailSent: boolean, createdDts: Time, activity: { __typename: 'Activity', activityType: ActivityType, entityID: UUID, actorID: UUID, actorUserAccount: { __typename: 'UserAccount', commonName: string }, metaData: { __typename: 'DailyDigestCompleteActivityMeta', version: number, type: ActivityType, modelPlanIDs: Array, date: Time, analyzedAudits: Array<{ __typename: 'AnalyzedAudit', id: UUID, modelPlanID: UUID, modelName: string, date: Time, changes: { __typename: 'AnalyzedAuditChange', modelPlan?: { __typename: 'AnalyzedModelPlan', oldName?: string | null, statusChanges?: Array | null } | null, documents?: { __typename: 'AnalyzedDocuments', count?: number | null } | null, crTdls?: { __typename: 'AnalyzedCrTdls', activity?: boolean | null } | null, planSections?: { __typename: 'AnalyzedPlanSections', updated: Array, readyForReview: Array, readyForClearance: Array } | null, modelLeads?: { __typename: 'AnalyzedModelLeads', added: Array<{ __typename: 'AnalyzedModelLeadInfo', id: UUID, commonName: string }> } | null, planDiscussions?: { __typename: 'AnalyzedPlanDiscussions', activity?: boolean | null } | null } }> } | { __typename: 'NewDiscussionRepliedActivityMeta', version: number, type: ActivityType, discussionID: UUID, replyID: UUID, modelPlanID: UUID, content: string, modelPlan: { __typename: 'ModelPlan', modelName: string } } | { __typename: 'TaggedInDiscussionReplyActivityMeta', version: number, type: ActivityType, modelPlanID: UUID, discussionID: UUID, replyID: UUID, content: string, modelPlan: { __typename: 'ModelPlan', modelName: string } } | { __typename: 'TaggedInPlanDiscussionActivityMeta', version: number, type: ActivityType, modelPlanID: UUID, discussionID: UUID, content: string, modelPlan: { __typename: 'ModelPlan', modelName: string } } } }> } } };
export type GetPollNotificationsQueryVariables = Exact<{ [key: string]: never; }>;
@@ -7624,6 +7637,17 @@ export const GetNotificationsDocument = gql`
}
metaData {
__typename
+ ... on NewDiscussionRepliedActivityMeta {
+ version
+ type
+ discussionID
+ replyID
+ modelPlanID
+ modelPlan {
+ modelName
+ }
+ content
+ }
... on TaggedInPlanDiscussionActivityMeta {
version
type
diff --git a/src/gql/gen/types/GetNotifications.ts b/src/gql/gen/types/GetNotifications.ts
index ba62daeb67..3038a96357 100644
--- a/src/gql/gen/types/GetNotifications.ts
+++ b/src/gql/gen/types/GetNotifications.ts
@@ -14,6 +14,22 @@ export interface GetNotifications_currentUser_notifications_notifications_activi
commonName: string;
}
+export interface GetNotifications_currentUser_notifications_notifications_activity_metaData_NewDiscussionRepliedActivityMeta_modelPlan {
+ __typename: "ModelPlan";
+ modelName: string;
+}
+
+export interface GetNotifications_currentUser_notifications_notifications_activity_metaData_NewDiscussionRepliedActivityMeta {
+ __typename: "NewDiscussionRepliedActivityMeta";
+ version: number;
+ type: ActivityType;
+ discussionID: UUID;
+ replyID: UUID;
+ modelPlanID: UUID;
+ modelPlan: GetNotifications_currentUser_notifications_notifications_activity_metaData_NewDiscussionRepliedActivityMeta_modelPlan;
+ content: string;
+}
+
export interface GetNotifications_currentUser_notifications_notifications_activity_metaData_TaggedInPlanDiscussionActivityMeta_modelPlan {
__typename: "ModelPlan";
modelName: string;
@@ -115,7 +131,7 @@ export interface GetNotifications_currentUser_notifications_notifications_activi
analyzedAudits: GetNotifications_currentUser_notifications_notifications_activity_metaData_DailyDigestCompleteActivityMeta_analyzedAudits[];
}
-export type GetNotifications_currentUser_notifications_notifications_activity_metaData = GetNotifications_currentUser_notifications_notifications_activity_metaData_TaggedInPlanDiscussionActivityMeta | GetNotifications_currentUser_notifications_notifications_activity_metaData_TaggedInDiscussionReplyActivityMeta | GetNotifications_currentUser_notifications_notifications_activity_metaData_DailyDigestCompleteActivityMeta;
+export type GetNotifications_currentUser_notifications_notifications_activity_metaData = GetNotifications_currentUser_notifications_notifications_activity_metaData_NewDiscussionRepliedActivityMeta | GetNotifications_currentUser_notifications_notifications_activity_metaData_TaggedInPlanDiscussionActivityMeta | GetNotifications_currentUser_notifications_notifications_activity_metaData_TaggedInDiscussionReplyActivityMeta | GetNotifications_currentUser_notifications_notifications_activity_metaData_DailyDigestCompleteActivityMeta;
export interface GetNotifications_currentUser_notifications_notifications_activity {
__typename: "Activity";
diff --git a/src/views/Notifications/Home/_components/IndividualNotification.tsx b/src/views/Notifications/Home/_components/IndividualNotification.tsx
index a881b43969..21e1d343bc 100644
--- a/src/views/Notifications/Home/_components/IndividualNotification.tsx
+++ b/src/views/Notifications/Home/_components/IndividualNotification.tsx
@@ -7,7 +7,6 @@ import { GetNotifications_currentUser_notifications_notifications_activity as No
import { arrayOfColors } from 'components/shared/IconInitial';
import MentionTextArea from 'components/shared/MentionTextArea';
-import useCheckResponsiveScreen from 'hooks/useCheckMobile';
import { getTimeElapsed } from 'utils/date';
import { getUserInitials } from 'utils/modelPlan';
@@ -15,6 +14,7 @@ import {
ActivityCTA,
activityText,
isDailyDigest,
+ isNewDiscussionReply,
isTaggedInDiscussion,
isTaggedInDiscussionReply
} from './_utils';
@@ -43,7 +43,6 @@ const IndividualNotification = ({
const [isExpanded, setIsExpanded] = useState(false);
const history = useHistory();
- const isMobile = useCheckResponsiveScreen('mobile');
const [markAsRead] = useMarkNotificationAsReadMutation();
@@ -123,16 +122,14 @@ const IndividualNotification = ({
{name}
{activityText(metaData)}
- {!isMobile &&
- (isTaggedInDiscussion(metaData) ||
- isTaggedInDiscussionReply(metaData)) && (
-
- )}
+ {!isDailyDigest(metaData) && (
+
+ )}