diff --git a/appview/indexer/notifier.go b/appview/indexer/notifier.go --- a/appview/indexer/notifier.go +++ b/appview/indexer/notifier.go @@ -63,7 +63,7 @@ l.Debug("reindexing repo after issue deletion") ix.getAndReindexRepo(ctx, issue.RepoDid) } -func (ix *Indexer) NewIssueLabelOp(ctx context.Context, issue *models.Issue) { +func (ix *Indexer) NewIssueLabelOp(ctx context.Context, _ syntax.DID, issue *models.Issue, _ []models.LabelOp) { l := log.FromContext(ctx).With("notifier", "indexer", "issue", issue) l.Debug("reindexing issue after label change") err := ix.Issues.Index(ctx, *issue) @@ -72,7 +72,7 @@ l.Error("failed to index an issue", "err", err) } } -func (ix *Indexer) NewPullLabelOp(ctx context.Context, pull *models.Pull) { +func (ix *Indexer) NewPullLabelOp(ctx context.Context, _ syntax.DID, pull *models.Pull, _ []models.LabelOp) { l := log.FromContext(ctx).With("notifier", "indexer", "pull", pull) l.Debug("reindexing pull after label change") err := ix.Pulls.Index(ctx, pull) diff --git a/appview/labels/labels.go b/appview/labels/labels.go --- a/appview/labels/labels.go +++ b/appview/labels/labels.go @@ -253,13 +253,13 @@ subject := syntax.ATURI(subjectUri) if subject.Collection() == tangled.RepoIssueNSID { issues, err := db.GetIssues(l.db, orm.FilterEq("at_uri", subjectUri)) if err == nil && len(issues) == 1 { - l.notifier.NewIssueLabelOp(r.Context(), &issues[0]) + l.notifier.NewIssueLabelOp(r.Context(), syntax.DID(did), &issues[0], validLabelOps) } } if subject.Collection() == tangled.RepoPullNSID { pulls, err := db.GetPulls(l.db, orm.FilterEq("at_uri", subjectUri)) if err == nil && len(pulls) == 1 { - l.notifier.NewPullLabelOp(r.Context(), pulls[0]) + l.notifier.NewPullLabelOp(r.Context(), syntax.DID(did), pulls[0], validLabelOps) } } diff --git a/appview/models/notifications.go b/appview/models/notifications.go --- a/appview/models/notifications.go +++ b/appview/models/notifications.go @@ -9,18 +9,22 @@ type NotificationType string const ( - NotificationTypeRepoStarred NotificationType = "repo_starred" - NotificationTypeIssueCreated NotificationType = "issue_created" - NotificationTypeIssueCommented NotificationType = "issue_commented" - NotificationTypePullCreated NotificationType = "pull_created" - NotificationTypePullCommented NotificationType = "pull_commented" - NotificationTypeFollowed NotificationType = "followed" - NotificationTypePullMerged NotificationType = "pull_merged" - NotificationTypeIssueClosed NotificationType = "issue_closed" - NotificationTypeIssueReopen NotificationType = "issue_reopen" - NotificationTypePullClosed NotificationType = "pull_closed" - NotificationTypePullReopen NotificationType = "pull_reopen" - NotificationTypeUserMentioned NotificationType = "user_mentioned" + NotificationTypeRepoStarred NotificationType = "repo_starred" + NotificationTypeIssueCreated NotificationType = "issue_created" + NotificationTypeIssueCommented NotificationType = "issue_commented" + NotificationTypePullCreated NotificationType = "pull_created" + NotificationTypePullCommented NotificationType = "pull_commented" + NotificationTypeFollowed NotificationType = "followed" + NotificationTypePullMerged NotificationType = "pull_merged" + NotificationTypeIssueClosed NotificationType = "issue_closed" + NotificationTypeIssueReopen NotificationType = "issue_reopen" + NotificationTypePullClosed NotificationType = "pull_closed" + NotificationTypePullReopen NotificationType = "pull_reopen" + NotificationTypeUserMentioned NotificationType = "user_mentioned" + NotificationTypeIssueAssigned NotificationType = "issue_assigned" + NotificationTypeIssueUnassigned NotificationType = "issue_unassigned" + NotificationTypePullAssigned NotificationType = "pull_assigned" + NotificationTypePullUnassigned NotificationType = "pull_unassigned" ) var SocialNotificationTypes = []NotificationType{ @@ -39,6 +43,10 @@ NotificationTypePullMerged, NotificationTypePullClosed, NotificationTypePullReopen, NotificationTypeUserMentioned, + NotificationTypeIssueAssigned, + NotificationTypeIssueUnassigned, + NotificationTypePullAssigned, + NotificationTypePullUnassigned, } type Notification struct { @@ -84,6 +92,10 @@ case NotificationTypeFollowed: return "user-plus" case NotificationTypeUserMentioned: return "at-sign" + case NotificationTypeIssueAssigned, NotificationTypePullAssigned: + return "user-round-check" + case NotificationTypeIssueUnassigned, NotificationTypePullUnassigned: + return "user-round-x" default: return "" } @@ -135,7 +147,11 @@ case NotificationTypePullReopen: return prefs.PullCreated // same pref for now case NotificationTypeFollowed: return prefs.Followed - case NotificationTypeUserMentioned: + case NotificationTypeUserMentioned, + NotificationTypeIssueAssigned, + NotificationTypeIssueUnassigned, + NotificationTypePullAssigned, + NotificationTypePullUnassigned: return prefs.UserMentioned default: return false diff --git a/appview/notify/db/db.go b/appview/notify/db/db.go --- a/appview/notify/db/db.go +++ b/appview/notify/db/db.go @@ -16,7 +16,8 @@ "tangled.org/core/sets" ) const ( - maxMentions = 8 + maxMentions = 8 + assigneeLabelAt = "at://did:plc:wshs7t2adsemcrrd4snkeqli/sh.tangled.label.definition/assignee" ) type databaseNotifier struct { @@ -276,8 +277,62 @@ func (n *databaseNotifier) DeleteIssue(ctx context.Context, issue *models.Issue) { // no-op for now } -func (n *databaseNotifier) NewIssueLabelOp(ctx context.Context, issue *models.Issue) {} -func (n *databaseNotifier) NewPullLabelOp(ctx context.Context, pull *models.Pull) {} +func (n *databaseNotifier) NewIssueLabelOp(ctx context.Context, actor syntax.DID, issue *models.Issue, ops []models.LabelOp) { + entityType := "issue" + entityId := issue.AtUri().String() + repoId := &issue.Repo.Id + issueId := &issue.Id + var pullId *int64 + + assigned := sets.New[syntax.DID]() + unassigned := sets.New[syntax.DID]() + for _, op := range ops { + if op.OperandKey != assigneeLabelAt { + continue + } + assignee := syntax.DID(op.OperandValue) + switch op.Operation { + case models.LabelOperationAdd: + assigned.Insert(assignee) + case models.LabelOperationDel: + unassigned.Insert(assignee) + default: + continue + } + } + + n.notifyEvent(ctx, actor, assigned, models.NotificationTypeIssueAssigned, entityType, entityId, repoId, issueId, pullId) + n.notifyEvent(ctx, actor, unassigned, models.NotificationTypeIssueUnassigned, entityType, entityId, repoId, issueId, pullId) +} + +func (n *databaseNotifier) NewPullLabelOp(ctx context.Context, actor syntax.DID, pull *models.Pull, ops []models.LabelOp) { + entityType := "pull" + entityId := pull.AtUri().String() + repoId := &pull.Repo.Id + var issueId *int64 + p := int64(pull.ID) + pullId := &p + + assigned := sets.New[syntax.DID]() + unassigned := sets.New[syntax.DID]() + for _, op := range ops { + if op.OperandKey != assigneeLabelAt { + continue + } + assignee := syntax.DID(op.OperandValue) + switch op.Operation { + case models.LabelOperationAdd: + assigned.Insert(assignee) + case models.LabelOperationDel: + unassigned.Insert(assignee) + default: + continue + } + } + + n.notifyEvent(ctx, actor, assigned, models.NotificationTypePullAssigned, entityType, entityId, repoId, issueId, pullId) + n.notifyEvent(ctx, actor, unassigned, models.NotificationTypePullUnassigned, entityType, entityId, repoId, issueId, pullId) +} func (n *databaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) { actorDid := syntax.DID(follow.UserDid) diff --git a/appview/notify/logging/notifier.go b/appview/notify/logging/notifier.go --- a/appview/notify/logging/notifier.go +++ b/appview/notify/logging/notifier.go @@ -70,14 +70,14 @@ ctx = tlog.IntoContext(ctx, tlog.SubLogger(l.logger, "DeleteIssue")) l.inner.DeleteIssue(ctx, issue) } -func (l *loggingNotifier) NewIssueLabelOp(ctx context.Context, issue *models.Issue) { +func (l *loggingNotifier) NewIssueLabelOp(ctx context.Context, actor syntax.DID, issue *models.Issue, ops []models.LabelOp) { ctx = tlog.IntoContext(ctx, tlog.SubLogger(l.logger, "NewIssueLabelOp")) - l.inner.NewIssueLabelOp(ctx, issue) + l.inner.NewIssueLabelOp(ctx, actor, issue, ops) } -func (l *loggingNotifier) NewPullLabelOp(ctx context.Context, pull *models.Pull) { +func (l *loggingNotifier) NewPullLabelOp(ctx context.Context, actor syntax.DID, pull *models.Pull, ops []models.LabelOp) { ctx = tlog.IntoContext(ctx, tlog.SubLogger(l.logger, "NewPullLabelOp")) - l.inner.NewPullLabelOp(ctx, pull) + l.inner.NewPullLabelOp(ctx, actor, pull, ops) } func (l *loggingNotifier) NewFollow(ctx context.Context, follow *models.Follow) { diff --git a/appview/notify/merged_notifier.go b/appview/notify/merged_notifier.go --- a/appview/notify/merged_notifier.go +++ b/appview/notify/merged_notifier.go @@ -70,12 +70,12 @@ func (m *mergedNotifier) DeleteIssue(ctx context.Context, issue *models.Issue) { m.fanout(func(n Notifier) { n.DeleteIssue(ctx, issue) }) } -func (m *mergedNotifier) NewIssueLabelOp(ctx context.Context, issue *models.Issue) { - m.fanout(func(n Notifier) { n.NewIssueLabelOp(ctx, issue) }) +func (m *mergedNotifier) NewIssueLabelOp(ctx context.Context, actor syntax.DID, issue *models.Issue, ops []models.LabelOp) { + m.fanout(func(n Notifier) { n.NewIssueLabelOp(ctx, actor, issue, ops) }) } -func (m *mergedNotifier) NewPullLabelOp(ctx context.Context, pull *models.Pull) { - m.fanout(func(n Notifier) { n.NewPullLabelOp(ctx, pull) }) +func (m *mergedNotifier) NewPullLabelOp(ctx context.Context, actor syntax.DID, pull *models.Pull, ops []models.LabelOp) { + m.fanout(func(n Notifier) { n.NewPullLabelOp(ctx, actor, pull, ops) }) } func (m *mergedNotifier) NewFollow(ctx context.Context, follow *models.Follow) { diff --git a/appview/notify/notifier.go b/appview/notify/notifier.go --- a/appview/notify/notifier.go +++ b/appview/notify/notifier.go @@ -28,8 +28,8 @@ NewPull(ctx context.Context, pull *models.Pull) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) - NewIssueLabelOp(ctx context.Context, issue *models.Issue) - NewPullLabelOp(ctx context.Context, pull *models.Pull) + NewIssueLabelOp(ctx context.Context, actor syntax.DID, issue *models.Issue, ops []models.LabelOp) + NewPullLabelOp(ctx context.Context, actor syntax.DID, pull *models.Pull, ops []models.LabelOp) UpdateProfile(ctx context.Context, profile *models.Profile) @@ -63,8 +63,10 @@ func (m *BaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) {} func (m *BaseNotifier) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) {} func (m *BaseNotifier) DeleteIssue(ctx context.Context, issue *models.Issue) {} -func (m *BaseNotifier) NewIssueLabelOp(ctx context.Context, issue *models.Issue) {} -func (m *BaseNotifier) NewPullLabelOp(ctx context.Context, pull *models.Pull) {} +func (m *BaseNotifier) NewIssueLabelOp(ctx context.Context, actor syntax.DID, issue *models.Issue, ops []models.LabelOp) { +} +func (m *BaseNotifier) NewPullLabelOp(ctx context.Context, actor syntax.DID, pull *models.Pull, ops []models.LabelOp) { +} func (m *BaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {} func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {} diff --git a/appview/pages/templates/notifications/fragments/item.html b/appview/pages/templates/notifications/fragments/item.html --- a/appview/pages/templates/notifications/fragments/item.html +++ b/appview/pages/templates/notifications/fragments/item.html @@ -57,7 +57,9 @@ {{ if or (eq .Type "issue_created") (eq .Type "issue_reopen") }}text-green-600 dark:text-green-500 {{ else if or (eq .Type "pull_created") (eq .Type "pull_reopen") }}text-green-600 dark:text-green-500 {{ else if eq .Type "pull_merged" }}text-purple-600 dark:text-purple-500 {{ else if eq .Type "pull_closed" }}text-red-600 dark:text-red-500 - {{ else if eq .Type "user_mentioned" }}text-blue-600 dark:text-blue-500 + {{ else if eq .Type "user_mentioned" }}text-blue-500 dark:text-blue-400 + {{ else if eq .Type "repo_starred" }}text-yellow-500 dark:text-yellow-600 + {{ else if or (eq .Type "issue_assigned") (eq .Type "pull_assigned") }} text-blue-500 dark:text-blue-400 {{ else }}text-gray-500 dark:text-gray-400 {{ end }} {{ end }} @@ -113,6 +115,14 @@ mentioned you on a pull request in {{ $repo }} {{ else }} mentioned you in {{ $repo }} {{ end }} + {{ else if eq .Type "issue_assigned" }} + assigned you to an issue on {{ $repo }} + {{ else if eq .Type "issue_unassigned" }} + unassigned you from an issue on {{ $repo }} + {{ else if eq .Type "pull_assigned" }} + assigned you to a PR on {{ $repo }} + {{ else if eq .Type "pull_unassigned" }} + unassigned you from a PR on {{ $repo }} {{ end }} {{ end }} diff --git a/appview/pulls/labels.go b/appview/pulls/labels.go --- a/appview/pulls/labels.go +++ b/appview/pulls/labels.go @@ -173,7 +173,7 @@ } continue } - s.notifier.NewPullLabelOp(ctx, pull) + s.notifier.NewPullLabelOp(ctx, userDid, pull, valid) } }