Compare commits
10 Commits
main
...
notificati
Author | SHA1 | Date |
---|---|---|
Andrey Nering | 7da3f899a6 | |
Andrey Nering | a9844aeb8d | |
Andrey Nering | fedf445f6a | |
Andrey Nering | f692adea69 | |
Andrey Nering | 679d91afdf | |
Andrey Nering | 8a3c856b1c | |
Andrey Nering | c3810c6d43 | |
Andrey Nering | 75c2752ee6 | |
Andrey Nering | b19ec55338 | |
Andrey Nering | 0f1b484e9a |
|
@ -562,6 +562,8 @@ func runWeb(ctx *cli.Context) error {
|
||||||
})
|
})
|
||||||
// ***** END: Repository *****
|
// ***** END: Repository *****
|
||||||
|
|
||||||
|
m.Get("/notifications", reqSignIn, user.Notifications)
|
||||||
|
|
||||||
m.Group("/api", func() {
|
m.Group("/api", func() {
|
||||||
apiv1.RegisterRoutes(m)
|
apiv1.RegisterRoutes(m)
|
||||||
}, ignSignIn)
|
}, ignSignIn)
|
||||||
|
|
|
@ -13,6 +13,7 @@ version = Version
|
||||||
page = Page
|
page = Page
|
||||||
template = Template
|
template = Template
|
||||||
language = Language
|
language = Language
|
||||||
|
notifications = Notifications
|
||||||
create_new = Create...
|
create_new = Create...
|
||||||
user_profile_and_more = User profile and more
|
user_profile_and_more = User profile and more
|
||||||
signed_in_as = Signed in as
|
signed_in_as = Signed in as
|
||||||
|
@ -1207,3 +1208,10 @@ default_message = Drop files here or click to upload.
|
||||||
invalid_input_type = You can't upload files of this type.
|
invalid_input_type = You can't upload files of this type.
|
||||||
file_too_big = File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB).
|
file_too_big = File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB).
|
||||||
remove_file = Remove file
|
remove_file = Remove file
|
||||||
|
|
||||||
|
[notification]
|
||||||
|
notifications = Notifications
|
||||||
|
unread = Unread
|
||||||
|
read = Read
|
||||||
|
no_unread = You have no unread notifications.
|
||||||
|
no_read = You have no read notifications.
|
||||||
|
|
|
@ -13,6 +13,7 @@ version=Versão
|
||||||
page=Página
|
page=Página
|
||||||
template=Template
|
template=Template
|
||||||
language=Idioma
|
language=Idioma
|
||||||
|
notifications = Notificações
|
||||||
create_new=Criar...
|
create_new=Criar...
|
||||||
user_profile_and_more=Perfil do usuário e configurações
|
user_profile_and_more=Perfil do usuário e configurações
|
||||||
signed_in_as=Logado como
|
signed_in_as=Logado como
|
||||||
|
@ -1198,3 +1199,9 @@ invalid_input_type=Você não pode enviar arquivos deste tipo.
|
||||||
file_too_big=O tamanho do arquivo ({{filesize}} MB) excede o limite máximo ({{maxFilesize}} MB).
|
file_too_big=O tamanho do arquivo ({{filesize}} MB) excede o limite máximo ({{maxFilesize}} MB).
|
||||||
remove_file=Remover
|
remove_file=Remover
|
||||||
|
|
||||||
|
[notification]
|
||||||
|
notifications = Notificações
|
||||||
|
unread = Não lidas
|
||||||
|
read = Lidas
|
||||||
|
no_unread = Você não possui notificações não lidas.
|
||||||
|
no_read = Você não possui notificações lidas.
|
||||||
|
|
|
@ -68,15 +68,40 @@ var (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tables = append(tables,
|
tables = append(tables,
|
||||||
new(User), new(PublicKey), new(AccessToken),
|
new(User),
|
||||||
new(Repository), new(DeployKey), new(Collaboration), new(Access), new(Upload),
|
new(PublicKey),
|
||||||
new(Watch), new(Star), new(Follow), new(Action),
|
new(AccessToken),
|
||||||
new(Issue), new(PullRequest), new(Comment), new(Attachment), new(IssueUser),
|
new(Repository),
|
||||||
new(Label), new(IssueLabel), new(Milestone),
|
new(DeployKey),
|
||||||
new(Mirror), new(Release), new(LoginSource), new(Webhook),
|
new(Collaboration),
|
||||||
new(UpdateTask), new(HookTask),
|
new(Access),
|
||||||
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
new(Upload),
|
||||||
new(Notice), new(EmailAddress))
|
new(Watch),
|
||||||
|
new(Star),
|
||||||
|
new(Follow),
|
||||||
|
new(Action),
|
||||||
|
new(Issue),
|
||||||
|
new(PullRequest),
|
||||||
|
new(Comment),
|
||||||
|
new(Attachment),
|
||||||
|
new(IssueUser),
|
||||||
|
new(Label),
|
||||||
|
new(IssueLabel),
|
||||||
|
new(Milestone),
|
||||||
|
new(Mirror),
|
||||||
|
new(Release),
|
||||||
|
new(LoginSource),
|
||||||
|
new(Webhook),
|
||||||
|
new(UpdateTask),
|
||||||
|
new(HookTask),
|
||||||
|
new(Team),
|
||||||
|
new(OrgUser),
|
||||||
|
new(TeamUser),
|
||||||
|
new(TeamRepo),
|
||||||
|
new(Notice),
|
||||||
|
new(EmailAddress),
|
||||||
|
new(Notification),
|
||||||
|
)
|
||||||
|
|
||||||
gonicNames := []string{"SSL", "UID"}
|
gonicNames := []string{"SSL", "UID"}
|
||||||
for _, name := range gonicNames {
|
for _, name := range gonicNames {
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// NotificationStatus is the status of the notification (read or unread)
|
||||||
|
NotificationStatus uint8
|
||||||
|
// NotificationSource is the source of the notification (issue, PR, commit, etc)
|
||||||
|
NotificationSource uint8
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NotificationStatusUnread represents an unread notification
|
||||||
|
NotificationStatusUnread NotificationStatus = iota + 1
|
||||||
|
// NotificationStatusRead represents a read notification
|
||||||
|
NotificationStatusRead
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NotificationSourceIssue is a notification of an issue
|
||||||
|
NotificationSourceIssue NotificationSource = iota + 1
|
||||||
|
// NotificationSourcePullRequest is a notification of a pull request
|
||||||
|
NotificationSourcePullRequest
|
||||||
|
// NotificationSourceCommit is a notification of a commit
|
||||||
|
NotificationSourceCommit
|
||||||
|
)
|
||||||
|
|
||||||
|
// Notification represents a notification
|
||||||
|
type Notification struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||||
|
RepoID int64 `xorm:"INDEX NOT NULL"`
|
||||||
|
|
||||||
|
Status NotificationStatus `xorm:"SMALLINT INDEX NOT NULL"`
|
||||||
|
Source NotificationSource `xorm:"SMALLINT INDEX NOT NULL"`
|
||||||
|
|
||||||
|
IssueID int64 `xorm:"INDEX NOT NULL"`
|
||||||
|
PullID int64 `xorm:"INDEX"`
|
||||||
|
CommitID string `xorm:"INDEX"`
|
||||||
|
|
||||||
|
Issue *Issue `xorm:"-"`
|
||||||
|
PullRequest *PullRequest `xorm:"-"`
|
||||||
|
|
||||||
|
Created time.Time `xorm:"-"`
|
||||||
|
CreatedUnix int64 `xorm:"INDEX NOT NULL"`
|
||||||
|
Updated time.Time `xorm:"-"`
|
||||||
|
UpdatedUnix int64 `xorm:"INDEX NOT NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeInsert runs while inserting a record
|
||||||
|
func (n *Notification) BeforeInsert() {
|
||||||
|
var (
|
||||||
|
now = time.Now()
|
||||||
|
nowUnix = now.Unix()
|
||||||
|
)
|
||||||
|
n.Created = now
|
||||||
|
n.CreatedUnix = nowUnix
|
||||||
|
n.Updated = now
|
||||||
|
n.UpdatedUnix = nowUnix
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate runs while updateing a record
|
||||||
|
func (n *Notification) BeforeUpdate() {
|
||||||
|
var (
|
||||||
|
now = time.Now()
|
||||||
|
nowUnix = now.Unix()
|
||||||
|
)
|
||||||
|
n.Updated = now
|
||||||
|
n.UpdatedUnix = nowUnix
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrUpdateIssueNotifications creates an issue notification
|
||||||
|
// for each watcher, or updates it if already exists
|
||||||
|
func CreateOrUpdateIssueNotifications(issue *Issue) error {
|
||||||
|
watches, err := getWatchers(x, issue.RepoID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sess.Close()
|
||||||
|
|
||||||
|
for _, watch := range watches {
|
||||||
|
exists, err := issueNotificationExists(sess, watch.UserID, issue.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
err = updateIssueNotification(sess, watch.UserID, issue.ID)
|
||||||
|
} else {
|
||||||
|
err = createIssueNotification(sess, watch.UserID, issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func issueNotificationExists(e Engine, userID, issueID int64) (bool, error) {
|
||||||
|
count, err := e.
|
||||||
|
Where("user_id = ?", userID).
|
||||||
|
And("issue_id = ?", issueID).
|
||||||
|
Count(Notification{})
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIssueNotification(e Engine, userID int64, issue *Issue) error {
|
||||||
|
notification := &Notification{
|
||||||
|
UserID: userID,
|
||||||
|
RepoID: issue.RepoID,
|
||||||
|
Status: NotificationStatusUnread,
|
||||||
|
IssueID: issue.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.IsPull {
|
||||||
|
notification.Source = NotificationSourcePullRequest
|
||||||
|
} else {
|
||||||
|
notification.Source = NotificationSourceIssue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := e.Insert(notification)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateIssueNotification(e Engine, userID, issueID int64) error {
|
||||||
|
notification, err := getIssueNotification(e, userID, issueID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.Status = NotificationStatusUnread
|
||||||
|
|
||||||
|
_, err = e.Id(notification.ID).Update(notification)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIssueNotification(e Engine, userID, issueID int64) (*Notification, error) {
|
||||||
|
notification := new(Notification)
|
||||||
|
_, err := e.
|
||||||
|
Where("user_id = ?", userID).
|
||||||
|
And("issue_id = ?", issueID).
|
||||||
|
Get(notification)
|
||||||
|
return notification, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotificationsForUser returns notifications for a given user and status
|
||||||
|
func NotificationsForUser(user *User, status NotificationStatus) ([]*Notification, error) {
|
||||||
|
return notificationsForUser(x, user, status)
|
||||||
|
}
|
||||||
|
func notificationsForUser(e Engine, user *User, status NotificationStatus) (notifications []*Notification, err error) {
|
||||||
|
err = e.
|
||||||
|
Where("user_id = ?", user.ID).
|
||||||
|
And("status = ?", status).
|
||||||
|
OrderBy("updated_unix DESC").
|
||||||
|
Find(¬ifications)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepo returns the repo of the notification
|
||||||
|
func (n *Notification) GetRepo() (repo *Repository, err error) {
|
||||||
|
repo = new(Repository)
|
||||||
|
_, err = x.
|
||||||
|
Where("id = ?", n.RepoID).
|
||||||
|
Get(repo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssue returns the issue of the notification
|
||||||
|
func (n *Notification) GetIssue() (issue *Issue, err error) {
|
||||||
|
issue = new(Issue)
|
||||||
|
_, err = x.
|
||||||
|
Where("id = ?", n.IssueID).
|
||||||
|
Get(issue)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type notificationService struct {
|
||||||
|
issueQueue chan *models.Issue
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Service is the notification service
|
||||||
|
Service = ¬ificationService{
|
||||||
|
issueQueue: make(chan *models.Issue),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
go Service.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *notificationService) Run() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case issue := <-ns.issueQueue:
|
||||||
|
if err := models.CreateOrUpdateIssueNotifications(issue); err != nil {
|
||||||
|
log.Error(4, "Was unable to create issue notification: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *notificationService) NotifyIssue(issue *models.Issue) {
|
||||||
|
ns.issueQueue <- issue
|
||||||
|
}
|
|
@ -2691,6 +2691,24 @@ footer .ui.language .menu {
|
||||||
.user.followers .follow .ui.button {
|
.user.followers .follow .ui.button {
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
}
|
}
|
||||||
|
.user.notification .octicon {
|
||||||
|
float: left;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
.user.notification .content {
|
||||||
|
float: left;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
.user.notification .octicon-issue-opened,
|
||||||
|
.user.notification .octicon-git-pull-request {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.user.notification .octicon-issue-closed {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.user.notification .octicon-git-merge {
|
||||||
|
color: purple;
|
||||||
|
}
|
||||||
.dashboard {
|
.dashboard {
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
padding-bottom: 80px;
|
padding-bottom: 80px;
|
||||||
|
|
|
@ -74,4 +74,25 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.notification {
|
||||||
|
.octicon {
|
||||||
|
float: left;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
float: left;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.octicon-issue-opened, .octicon-git-pull-request {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.octicon-issue-closed {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.octicon-git-merge {
|
||||||
|
color: purple;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/markdown"
|
"code.gitea.io/gitea/modules/markdown"
|
||||||
|
"code.gitea.io/gitea/modules/notification"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -453,6 +454,8 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notification.Service.NotifyIssue(issue)
|
||||||
|
|
||||||
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
|
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
|
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + com.ToStr(issue.Index))
|
||||||
}
|
}
|
||||||
|
@ -898,6 +901,8 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notification.Service.NotifyIssue(issue)
|
||||||
|
|
||||||
log.Trace("Comment created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID)
|
log.Trace("Comment created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tplNotification base.TplName = "user/notification/notification"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Notifications is the notifications page
|
||||||
|
func Notifications(c *context.Context) {
|
||||||
|
var status models.NotificationStatus
|
||||||
|
switch c.Query("status") {
|
||||||
|
case "read":
|
||||||
|
status = models.NotificationStatusRead
|
||||||
|
default:
|
||||||
|
status = models.NotificationStatusUnread
|
||||||
|
}
|
||||||
|
|
||||||
|
notifications, err := models.NotificationsForUser(c.User, status)
|
||||||
|
if err != nil {
|
||||||
|
c.Handle(500, "ErrNotificationsForUser", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
title := "Notifications"
|
||||||
|
if count := len(notifications); count > 0 {
|
||||||
|
title = fmt.Sprintf("(%d) %s", count, title)
|
||||||
|
}
|
||||||
|
c.Data["Title"] = title
|
||||||
|
c.Data["Status"] = status
|
||||||
|
c.Data["Notifications"] = notifications
|
||||||
|
c.HTML(200, tplNotification)
|
||||||
|
}
|
|
@ -29,7 +29,6 @@ const (
|
||||||
tplSettingsSocial base.TplName = "user/settings/social"
|
tplSettingsSocial base.TplName = "user/settings/social"
|
||||||
tplSettingsApplications base.TplName = "user/settings/applications"
|
tplSettingsApplications base.TplName = "user/settings/applications"
|
||||||
tplSettingsDelete base.TplName = "user/settings/delete"
|
tplSettingsDelete base.TplName = "user/settings/delete"
|
||||||
tplNotification base.TplName = "user/notification"
|
|
||||||
tplSecurity base.TplName = "user/security"
|
tplSecurity base.TplName = "user/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,12 @@
|
||||||
|
|
||||||
{{if .IsSigned}}
|
{{if .IsSigned}}
|
||||||
<div class="right menu">
|
<div class="right menu">
|
||||||
|
<a href="/notifications" class="ui head link jump item poping up" data-content='{{.i18n.Tr "notifications"}}' data-variation="tiny inverted">
|
||||||
|
<span class="text">
|
||||||
|
<i class="octicon octicon-inbox"><span class="sr-only">{{.i18n.Tr "notifications"}}</span></i>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="ui dropdown head link jump item poping up" data-content="{{.i18n.Tr "create_new"}}" data-variation="tiny inverted">
|
<div class="ui dropdown head link jump item poping up" data-content="{{.i18n.Tr "create_new"}}" data-variation="tiny inverted">
|
||||||
<span class="text">
|
<span class="text">
|
||||||
<i class="octicon octicon-plus"><span class="sr-only">{{.i18n.Tr "create_new"}}</span></i>
|
<i class="octicon octicon-plus"><span class="sr-only">{{.i18n.Tr "create_new"}}</span></i>
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
{{template "base/head" .}}
|
||||||
|
|
||||||
|
<div class="user notification">
|
||||||
|
<div class="ui container">
|
||||||
|
<h1 class="ui header">{{.i18n.Tr "notification.notifications"}}</h1>
|
||||||
|
|
||||||
|
<div class="ui top attached tabular menu">
|
||||||
|
<a href="/notifications?status=unread">
|
||||||
|
<div class="{{if eq .Status 1}}active{{end}} item">
|
||||||
|
{{.i18n.Tr "notification.unread"}}
|
||||||
|
{{if eq .Status 1}}
|
||||||
|
<div class="ui label">{{len .Notifications}}</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="/notifications?status=read">
|
||||||
|
<div class="{{if eq .Status 2}}active{{end}} item">
|
||||||
|
{{.i18n.Tr "notification.read"}}
|
||||||
|
{{if eq .Status 2}}
|
||||||
|
<div class="ui label">{{len .Notifications}}</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="ui bottom attached active tab segment">
|
||||||
|
{{if eq (len .Notifications) 0}}
|
||||||
|
{{if eq .Status 1}}
|
||||||
|
{{.i18n.Tr "notification.no_unread"}}
|
||||||
|
{{else}}
|
||||||
|
{{.i18n.Tr "notification.no_read"}}
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
<div class="ui relaxed divided list">
|
||||||
|
{{range $notification := .Notifications}}
|
||||||
|
{{$issue := $notification.GetIssue}}
|
||||||
|
{{$repo := $notification.GetRepo}}
|
||||||
|
{{$repoOwner := $repo.MustOwner}}
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<a href="{{$.AppSubUrl}}/{{$repoOwner.Name}}/{{$repo.Name}}/issues/{{$issue.Index}}">
|
||||||
|
{{if and $issue.IsPull}}
|
||||||
|
{{if $issue.IsClosed}}
|
||||||
|
<i class="octicon octicon-git-merge"></i>
|
||||||
|
{{else}}
|
||||||
|
<i class="octicon octicon-git-pull-request"></i>
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
{{if $issue.IsClosed}}
|
||||||
|
<i class="octicon octicon-issue-closed"></i>
|
||||||
|
{{else}}
|
||||||
|
<i class="octicon octicon-issue-opened"></i>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div class="header">{{$repoOwner.Name}}/{{$repo.Name}}</div>
|
||||||
|
<div class="description">{{$issue.Title}}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "base/footer" .}}
|
Loading…
Reference in New Issue