package models import ( "context" "fmt" "time" "github.com/bluesky-social/indigo/atproto/syntax" "tangled.org/core/idresolver" ) 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" NotificationTypeIssueAssigned NotificationType = "issue_assigned" NotificationTypeIssueUnassigned NotificationType = "issue_unassigned" NotificationTypePullAssigned NotificationType = "pull_assigned" NotificationTypePullUnassigned NotificationType = "pull_unassigned" ) var SocialNotificationTypes = []NotificationType{ NotificationTypeRepoStarred, NotificationTypeFollowed, } var WorkNotificationTypes = []NotificationType{ NotificationTypeIssueCreated, NotificationTypeIssueCommented, NotificationTypeIssueClosed, NotificationTypeIssueReopen, NotificationTypePullCreated, NotificationTypePullCommented, NotificationTypePullMerged, NotificationTypePullClosed, NotificationTypePullReopen, NotificationTypeUserMentioned, NotificationTypeIssueAssigned, NotificationTypeIssueUnassigned, NotificationTypePullAssigned, NotificationTypePullUnassigned, } type Notification struct { ID int64 RecipientDid string ActorDid string Type NotificationType EntityType string EntityId string Read bool Created time.Time // foreign key references RepoId *int64 IssueId *int64 PullId *int64 } // lucide icon that represents this notification func (n *Notification) Icon() string { switch n.Type { case NotificationTypeRepoStarred: return "star" case NotificationTypeIssueCreated: return "circle-dot" case NotificationTypeIssueCommented: return "message-square" case NotificationTypeIssueClosed: return "ban" case NotificationTypeIssueReopen: return "circle-dot" case NotificationTypePullCreated: return "git-pull-request-create" case NotificationTypePullCommented: return "message-square" case NotificationTypePullMerged: return "git-merge" case NotificationTypePullClosed: return "git-pull-request-closed" case NotificationTypePullReopen: return "git-pull-request-create" case NotificationTypeFollowed: return "user-plus" case NotificationTypeUserMentioned: return "at-sign" case NotificationTypeIssueAssigned, NotificationTypePullAssigned: return "user-round-arrow-forward" case NotificationTypeIssueUnassigned, NotificationTypePullUnassigned: return "user-round-minus" default: return "" } } type NotificationWithEntity struct { *Notification Repo *Repo Issue *Issue Pull *Pull } // URL returns the navigable path for the notification, resolving DIDs to // handles via the provided resolver. func (n *NotificationWithEntity) URL(res *idresolver.Resolver) string { resolve := func(did string) string { if id, err := res.ResolveIdent(context.Background(), did); err == nil && !id.Handle.IsInvalidHandle() { return id.Handle.String() } return did } switch n.Type { case NotificationTypeFollowed: return "/" + resolve(n.ActorDid) } if n.Repo == nil { return "" } repoHandle := resolve(n.Repo.Did) slug := n.Repo.Slug() switch { case n.Issue != nil: return fmt.Sprintf("/%s/%s/issues/%d", repoHandle, slug, n.Issue.IssueId) case n.Pull != nil: return fmt.Sprintf("/%s/%s/pulls/%d", repoHandle, slug, n.Pull.PullId) default: // repo_starred and other repo-level types return fmt.Sprintf("/%s/%s", repoHandle, slug) } } type NotificationPreferences struct { ID int64 UserDid syntax.DID RepoStarred bool IssueCreated bool IssueCommented bool PullCreated bool PullCommented bool Followed bool UserMentioned bool PullMerged bool IssueClosed bool EmailNotifications bool } func (prefs *NotificationPreferences) ShouldNotify(t NotificationType) bool { switch t { case NotificationTypeRepoStarred: return prefs.RepoStarred case NotificationTypeIssueCreated: return prefs.IssueCreated case NotificationTypeIssueCommented: return prefs.IssueCommented case NotificationTypeIssueClosed: return prefs.IssueClosed case NotificationTypeIssueReopen: return prefs.IssueCreated // same pref for now case NotificationTypePullCreated: return prefs.PullCreated case NotificationTypePullCommented: return prefs.PullCommented case NotificationTypePullMerged: return prefs.PullMerged case NotificationTypePullClosed: return prefs.PullMerged // same pref for now case NotificationTypePullReopen: return prefs.PullCreated // same pref for now case NotificationTypeFollowed: return prefs.Followed case NotificationTypeUserMentioned, NotificationTypeIssueAssigned, NotificationTypeIssueUnassigned, NotificationTypePullAssigned, NotificationTypePullUnassigned: return prefs.UserMentioned default: return false } } func DefaultNotificationPreferences(user syntax.DID) *NotificationPreferences { return &NotificationPreferences{ UserDid: user, RepoStarred: true, IssueCreated: true, IssueCommented: true, PullCreated: true, PullCommented: true, Followed: true, UserMentioned: true, PullMerged: true, IssueClosed: true, EmailNotifications: false, } }