Gitfed
bastien-mrq/gitfed / cmd / gitfed-web / handlers_notifications.go
package main

import (
	"bytes"
	"html/template"
	"net/http"
	"strconv"

	"git.neuromancer.ovh/bastien-mrq/gitfed/internal/i18n"
	"git.neuromancer.ovh/bastien-mrq/gitfed/internal/store"
)

var notificationsTpl = newTpl("notifications", `
{{.Flash}}
<div class="gf-page-head"><h1>{{t .Lang "notif.title"}}</h1></div>
<p class="muted" style="margin-top:-0.6rem;">{{t .Lang "notif.note"}}</p>

<div class="gf-card gf-repo-list">
{{range .Notifications}}
  <div class="gf-repo-row">
    <div class="gf-repo-icon">{{icon .IconName}}</div>
    <div class="gf-repo-main">
      <div class="name">{{.Repo}}</div>
      {{if eq .Kind "mr_opened"}}
      <div class="meta">{{t $.Lang "notif.mr_opened" .Actor .MRNumber .Title}}</div>
      {{else if eq .Kind "mr_comment"}}
      <div class="meta">{{t $.Lang "notif.mr_comment" .Actor .MRNumber .Title}}</div>
      {{else}}
      <div class="meta">{{t $.Lang "notif.granted_by"}} {{.Actor}} · {{.FromDomain}} · {{roleLabel $.Lang .Role}}</div>
      {{end}}
    </div>
    {{if .MRURL}}<a href="{{.MRURL}}" class="gf-btn">{{t $.Lang "notif.view"}}</a>{{end}}
    {{if eq (print .Status) "pending"}}
    <form class="inline" method="post" action="/notifications/accept">
      <input type="hidden" name="id" value="{{.ID}}">
      <button type="submit" class="gf-btn primary">{{t $.Lang "notif.accept"}}</button>
    </form>
    <form class="inline" method="post" action="/notifications/dismiss">
      <input type="hidden" name="id" value="{{.ID}}">
      <button type="submit" class="linklike" title="{{t $.Lang "notif.dismiss"}}" aria-label="{{t $.Lang "notif.dismiss"}}">{{icon "close"}}</button>
    </form>
    {{else if eq (print .Status) "accepted"}}
    <span class="badge trusted">{{t $.Lang "notif.status_accepted"}}</span>
    {{else}}
    <span class="badge plain">{{t $.Lang "notif.status_dismissed"}}</span>
    {{end}}
  </div>
{{else}}
  <div class="gf-repo-row"><span class="muted">{{t .Lang "notif.empty"}}</span></div>
{{end}}
</div>
`)

// notificationView adds display-only fields to a store.Notification: a
// link to the merge request it's about (mr_opened/mr_comment only) and
// which sprite icon fits its kind — kept out of the store type since
// they're purely a rendering concern.
type notificationView struct {
	store.Notification
}

func (n notificationView) IconName() string {
	if n.EffectiveKind() != store.NotificationGrant {
		return "branch"
	}
	return "bell"
}

func (n notificationView) MRURL() string {
	if n.EffectiveKind() == store.NotificationGrant {
		return ""
	}
	// Repo names can contain "/" (owner/repo-style namespacing) and the
	// {repo...} route wildcard expects that literally, so this isn't
	// PathEscape'd — same convention as every repo link built directly in
	// a template (e.g. repoTpl's "/r/{{.Repo.Name}}").
	return "/repo-mr/" + n.Repo + "?number=" + strconv.Itoa(n.MRNumber)
}

func (s *server) handleNotifications(w http.ResponseWriter, r *http.Request) {
	sess, _ := s.currentSession(r)
	lang := s.lang(r)

	notifs, err := s.ops.ListNotifications(sess.Principal)
	if err != nil {
		s.serverError(w, r, err)
		return
	}
	views := make([]notificationView, len(notifs))
	for i, n := range notifs {
		views[i] = notificationView{n}
	}

	var buf bytes.Buffer
	_ = notificationsTpl.Execute(&buf, struct {
		Notifications []notificationView
		Lang          string
		Flash         template.HTML
	}{views, string(lang), flash(r)})
	s.render(w, r, i18n.T(lang, "notif.title"), "", template.HTML(buf.String()))
}

func (s *server) handleAcceptNotification(w http.ResponseWriter, r *http.Request) {
	sess, _ := s.currentSession(r)
	if err := s.ops.AcceptNotification(sess.Principal, r.FormValue("id")); err != nil {
		s.serverError(w, r, err)
		return
	}
	lang := s.lang(r)
	redirectWithMsg(w, r, "/notifications", i18n.T(lang, "notif.msg_accepted"), false)
}

func (s *server) handleDismissNotification(w http.ResponseWriter, r *http.Request) {
	sess, _ := s.currentSession(r)
	if err := s.ops.DismissNotification(sess.Principal, r.FormValue("id")); err != nil {
		s.serverError(w, r, err)
		return
	}
	http.Redirect(w, r, "/notifications", http.StatusSeeOther)
}