diff --git a/cmd/web.go b/cmd/web.go index 7ba7a7ded4..bbf386d43f 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -167,6 +167,8 @@ func runWeb(ctx *cli.Context) error { bindIgnErr := binding.BindIgnErr + m.Use(user.GetNotificationCount) + // FIXME: not all routes need go through same middlewares. // Especially some AJAX requests, we can reduce middleware number to improve performance. // Routers. @@ -577,6 +579,8 @@ func runWeb(ctx *cli.Context) error { }) // ***** END: Repository ***** + m.Get("/notifications", reqSignIn, user.Notifications) + m.Group("/api", func() { apiv1.RegisterRoutes(m) }, ignSignIn) diff --git a/models/notification.go b/models/notification.go index 69f96bb5e6..e2460e8369 100644 --- a/models/notification.go +++ b/models/notification.go @@ -182,14 +182,20 @@ func getIssueNotification(e Engine, userID, issueID int64) (*Notification, error } // NotificationsForUser returns notifications for a given user and status -func NotificationsForUser(user *User, status NotificationStatus) ([]*Notification, error) { - return notificationsForUser(x, user, status) +func NotificationsForUser(user *User, status NotificationStatus, page, perPage int) ([]*Notification, error) { + return notificationsForUser(x, user, status, page, perPage) } -func notificationsForUser(e Engine, user *User, status NotificationStatus) (notifications []*Notification, err error) { - err = e. +func notificationsForUser(e Engine, user *User, status NotificationStatus, page, perPage int) (notifications []*Notification, err error) { + sess := e. Where("user_id = ?", user.ID). And("status = ?", status). - OrderBy("updated_unix DESC"). + OrderBy("updated_unix DESC") + + if page > 0 && perPage > 0 { + sess.Limit(perPage, (page-1)*perPage) + } + + err = sess. Find(¬ifications) return } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6a8edad79e..bcb9df8717 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -13,6 +13,7 @@ version = Version page = Page template = Template language = Language +notifications = Notifications create_new = Create... user_profile_and_more = User profile and more signed_in_as = Signed in as @@ -1232,3 +1233,10 @@ default_message = Drop files here or click to upload. invalid_input_type = You can't upload files of this type. file_too_big = File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB). 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. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 5d9ae7a206..4893a7644d 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -13,6 +13,7 @@ version=Versão page=Página template=Template language=Idioma +notifications = Notificações create_new=Criar... user_profile_and_more=Perfil do usuário e configurações signed_in_as=Logado como @@ -1197,3 +1198,10 @@ default_message=Arraste e solte arquivos aqui, ou clique para selecioná-los. 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). 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. diff --git a/public/css/index.css b/public/css/index.css index 2d86e812e3..f5581c3eaf 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -2704,6 +2704,24 @@ footer .ui.language .menu { .user.followers .follow .ui.button { 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: #21ba45; +} +.user.notification .octicon-issue-closed { + color: #d01919; +} +.user.notification .octicon-git-merge { + color: #a333c8; +} .dashboard { padding-top: 15px; padding-bottom: 80px; diff --git a/public/less/_user.less b/public/less/_user.less index 3e37011cfb..b446351bd4 100644 --- a/public/less/_user.less +++ b/public/less/_user.less @@ -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: #21ba45; + } + .octicon-issue-closed { + color: #d01919; + } + .octicon-git-merge { + color: #a333c8; + } + } } diff --git a/routers/user/notification.go b/routers/user/notification.go new file mode 100644 index 0000000000..7e556ae2ea --- /dev/null +++ b/routers/user/notification.go @@ -0,0 +1,81 @@ +package user + +import ( + "fmt" + "strings" + + "github.com/Unknwon/paginater" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" +) + +const ( + tplNotification base.TplName = "user/notification/notification" +) + +// GetNotificationCount is the middleware that sets the notification count in the context +func GetNotificationCount(c *context.Context) { + if strings.HasPrefix(c.Req.URL.Path, "/api") { + return + } + + if !c.IsSigned { + return + } + + count, err := models.GetNotificationUnreadCount(c.User) + if err != nil { + c.Handle(500, "GetNotificationCount", err) + return + } + + c.Data["NotificationUnreadCount"] = count +} + +// Notifications is the notifications page +func Notifications(c *context.Context) { + var ( + keyword = c.Query("q") + status models.NotificationStatus + page = c.QueryInt("page") + perPage = c.QueryInt("perPage") + ) + if page < 1 { + page = 1 + } + if perPage < 1 { + perPage = 20 + } + + switch keyword { + case "read": + status = models.NotificationStatusRead + default: + status = models.NotificationStatusUnread + } + + notifications, err := models.NotificationsForUser(c.User, status, page, perPage) + if err != nil { + c.Handle(500, "ErrNotificationsForUser", err) + return + } + + total, err := models.GetNotificationCount(c.User, status) + if err != nil { + c.Handle(500, "ErrGetNotificationCount", err) + return + } + + title := "Notifications" + if count := len(notifications); count > 0 { + title = fmt.Sprintf("(%d) %s", count, title) + } + c.Data["Title"] = title + c.Data["Keyword"] = keyword + c.Data["Status"] = status + c.Data["Notifications"] = notifications + c.Data["Page"] = paginater.New(int(total), perPage, page, 5) + c.HTML(200, tplNotification) +} diff --git a/routers/user/setting.go b/routers/user/setting.go index e078f8c17a..a465b0cd8c 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -29,7 +29,6 @@ const ( tplSettingsSocial base.TplName = "user/settings/social" tplSettingsApplications base.TplName = "user/settings/applications" tplSettingsDelete base.TplName = "user/settings/delete" - tplNotification base.TplName = "user/notification" tplSecurity base.TplName = "user/security" ) diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 847503022e..a0b5220fd2 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -82,6 +82,18 @@ {{if .IsSigned}}