back interdiff of round #1 and #0

appview: introduce email notifications for @ mentions on issue/pr comments #393

closed
opened by boltless.me targeting master from boltless.me/core: feat/mentions

Stacked on top of #392

Yes, I know we have stacked PRs, but I want to explicitly separate two sets of commits and review both on different places

This is MVC implementation of email notification.

Still lot of parts are missing, but this is a PR with most basic features.

ERROR
appview/middleware/middleware.go

Failed to calculate interdiff for this file.

ERROR
appview/repo/artifact.go

Failed to calculate interdiff for this file.

ERROR
appview/repo/index.go

Failed to calculate interdiff for this file.

ERROR
appview/reporesolver/resolver.go

Failed to calculate interdiff for this file.

ERROR
appview/db/repos.go

Failed to calculate interdiff for this file.

ERROR
appview/pages/markup/markdown.go

Failed to calculate interdiff for this file.

ERROR
appview/pages/markup/markdown_at_extension.go

Failed to calculate interdiff for this file.

REVERTED
appview/issues/issues.go
···
createdAt := time.Now().Format(time.RFC3339)
commentIdInt64 := int64(comment.CommentId)
+
issueAt, err := db.GetIssueAt(rp.db, f.RepoAt(), issueIdInt)
-
issue, err := db.GetIssue(rp.db, f.RepoAt(), issueIdInt)
if err != nil {
+
log.Println("failed to get issue at", err)
-
log.Println("failed to get issue", err)
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
return
}
···
Record: &lexutil.LexiconTypeDecoder{
Val: &tangled.RepoIssueComment{
Repo: &atUri,
+
Issue: issueAt,
-
Issue: issue.IssueAt,
CommentId: &commentIdInt64,
Owner: &comment.OwnerDid,
Body: body,
···
mentions := markup.FindUserMentions(comment.Body)
+
rp.notifier.NewIssueComment(r.Context(), comment, mentions)
-
rp.notifier.NewIssueComment(r.Context(), &f.Repo, issue, comment, mentions)
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), comment.Issue, comment.CommentId))
return
REVERTED
appview/email/notifier.go
···
var _ notify.Notifier = &EmailNotifier{}
+
func (n *EmailNotifier) buildIssueEmail(ctx context.Context, repo *db.Repo, issue *db.Issue, comment *db.Comment, did string) (Email, error) {
+
// TODO: check email preferences
+
email, err := db.GetPrimaryEmail(n.db, did)
+
if err != nil {
+
return Email{}, fmt.Errorf("db.GetPrimaryEmail: %w", err)
+
}
+
commentOwner, err := n.idResolver.ResolveIdent(ctx, comment.OwnerDid)
+
if err != nil || commentOwner.Handle.IsInvalidHandle() {
+
return Email{}, fmt.Errorf("resolve comment owner did: %w", err)
+
}
-
// TODO: yeah this is just bad design. should be moved under idResolver ore include repoResolver at first place
-
func (n *EmailNotifier) repoOwnerSlashName(ctx context.Context, repo *db.Repo) (string, error) {
repoOwnerID, err := n.idResolver.ResolveIdent(ctx, repo.Did)
if err != nil || repoOwnerID.Handle.IsInvalidHandle() {
+
return Email{}, fmt.Errorf("resolve repo owner did: %w", err)
-
return "", fmt.Errorf("resolve comment owner did: %w", err)
}
repoOwnerHandle := repoOwnerID.Handle
var repoOwnerSlashName string
···
} else {
repoOwnerSlashName = repo.DidSlashRepo()
}
-
return repoOwnerSlashName, nil
-
}
-
-
func (n *EmailNotifier) buildIssueEmail(ctx context.Context, repo *db.Repo, issue *db.Issue, comment *db.Comment, did string) (Email, error) {
-
// TODO: check email preferences
-
email, err := db.GetPrimaryEmail(n.db, did)
-
if err != nil {
-
return Email{}, fmt.Errorf("db.GetPrimaryEmail: %w", err)
-
}
-
commentOwner, err := n.idResolver.ResolveIdent(ctx, comment.OwnerDid)
-
if err != nil || commentOwner.Handle.IsInvalidHandle() {
-
return Email{}, fmt.Errorf("resolve comment owner did: %w", err)
-
}
// TODO: make this configurable
baseUrl := "https://tangled.sh"
-
repoOwnerSlashName, err := n.repoOwnerSlashName(ctx, repo)
-
if err != nil {
-
return Email{}, nil
-
}
url := fmt.Sprintf("%s/%s/issues/%d#comment-%d", baseUrl, repoOwnerSlashName, comment.Issue, comment.CommentId)
return Email{
APIKey: n.Config.Resend.ApiKey,
···
}, nil
}
-
func (n *EmailNotifier) buildPullEmail(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment, did string) (Email, error) {
-
// TODO: check email preferences
-
email, err := db.GetPrimaryEmail(n.db, did)
-
if err != nil {
-
return Email{}, fmt.Errorf("db.GetPrimaryEmail: %w", err)
-
}
-
commentOwner, err := n.idResolver.ResolveIdent(ctx, comment.OwnerDid)
-
if err != nil || commentOwner.Handle.IsInvalidHandle() {
-
return Email{}, fmt.Errorf("resolve comment owner did: %w", err)
-
}
-
repoOwnerSlashName, err := n.repoOwnerSlashName(ctx, repo)
-
if err != nil {
-
return Email{}, nil
-
}
-
// TODO: make this configurable
-
baseUrl := "https://tangled.sh"
-
url := fmt.Sprintf("%s/%s/pulls/%d#comment-%d", baseUrl, repoOwnerSlashName, comment.PullId, comment.ID)
-
return Email{
-
APIKey: n.Config.Resend.ApiKey,
-
From: n.Config.Resend.SentFrom,
-
To: email.Address,
-
Subject: fmt.Sprintf("[%s] %s (pr#%d)", repoOwnerSlashName, pull.Title, pull.PullId),
-
Html: fmt.Sprintf(`<p><b>@%s</b> mentioned you:</p><a href="%s">View it on tangled.sh</a>.`, commentOwner.Handle.String(), url),
-
}, nil
-
}
-
func (n *EmailNotifier) NewIssueComment(ctx context.Context, repo *db.Repo, issue *db.Issue, comment *db.Comment, mentions []string) {
resolvedIds := n.idResolver.ResolveIdents(ctx, mentions)
handleDidMap := make(map[string]string)
···
}
}
+
// func (n *EmailNotifier) NewPullComment(ctx context.Context, comment *db.PullComment, []string) {
+
// n.usersMentioned(ctx, mentions)
+
// }
-
func (n *EmailNotifier) NewPullComment(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment, mentions []string) {
-
resolvedIds := n.idResolver.ResolveIdents(ctx, mentions)
-
handleDidMap := make(map[string]string)
-
for _, identity := range resolvedIds {
-
if !identity.Handle.IsInvalidHandle() {
-
handleDidMap[identity.Handle.String()] = identity.DID.String()
-
}
-
}
-
for _, handle := range mentions {
-
id, err := n.idResolver.ResolveIdent(ctx, handle)
-
email, err := n.buildPullEmail(ctx, repo, pull, comment, id.DID.String())
-
if err != nil {
-
log.Println("failed to create issue-email:", err)
-
}
-
SendEmail(email)
-
}
-
}
REVERTED
appview/notify/merged_notifier.go
···
notifier.NewPull(ctx, pull)
}
}
+
func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {
-
func (m *mergedNotifier) NewPullComment(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment, mentions []string) {
for _, notifier := range m.notifiers {
+
notifier.NewPullComment(ctx, comment)
-
notifier.NewPullComment(ctx, repo, pull, comment, mentions)
}
}
REVERTED
appview/notify/notifier.go
···
DeleteFollow(ctx context.Context, follow *db.Follow)
NewPull(ctx context.Context, pull *db.Pull)
+
NewPullComment(ctx context.Context, comment *db.PullComment)
-
NewPullComment(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment, mentions []string)
UpdateProfile(ctx context.Context, profile *db.Profile)
}
···
func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *db.Follow) {}
func (m *BaseNotifier) NewPull(ctx context.Context, pull *db.Pull) {}
+
func (m *BaseNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {}
-
func (m *BaseNotifier) NewPullComment(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment, mentions []string) {}
func (m *BaseNotifier) UpdateProfile(ctx context.Context, profile *db.Profile) {}
REVERTED
appview/posthog/notifier.go
···
}
}
+
func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {
-
func (n *posthogNotifier) NewPullComment(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment, mentions []string) {
err := n.client.Enqueue(posthog.Capture{
DistinctId: comment.OwnerDid,
Event: "new_pull_comment",
ERROR
appview/pulls/pulls.go

Failed to calculate interdiff for this file.