diff --git a/pkg/controller/inbox/inbox.go b/pkg/controller/inbox/inbox.go index 75a895a..a7eaea0 100644 --- a/pkg/controller/inbox/inbox.go +++ b/pkg/controller/inbox/inbox.go @@ -24,15 +24,18 @@ func Inbox() http.HandlerFunc { return } - // Default is inbox, what about sentbox? - var showSent = r.FormValue("box") == "sent" + // What view are we looking at? Threads (default), Inbox, or Outbox? + var box = r.FormValue("box") + if box != "inbox" && box != "sent" { + box = "threads" + } // Are we reading a specific message? var ( - viewThread []*models.Message - threadPager *models.Pagination - composeToUsername string - msgId int + viewThread []*models.Message + threadPager *models.Pagination + composeToUser *models.User + msgId int ) if uri := ReadURLRegexp.FindStringSubmatch(r.URL.Path); uri != nil { msgId, _ = strconv.Atoi(uri[1]) @@ -58,7 +61,7 @@ func Inbox() http.HandlerFunc { if err != nil { session.FlashError(w, r, "Couldn't get sender of that message: %s", err) } - composeToUsername = sender.Username + composeToUser = sender // Get the full chat thread (paginated). threadPager = &models.Pagination{ @@ -86,6 +89,7 @@ func Inbox() http.HandlerFunc { } // Get the inbox list of messages. + var messages []*models.Message pager := &models.Pagination{ Page: 1, PerPage: config.PageSizeInboxList, @@ -95,9 +99,20 @@ func Inbox() http.HandlerFunc { // On the main inbox view, ?page= params page thru the message list, not a thread. pager.ParsePage(r) } - messages, err := models.GetMessages(currentUser, showSent, pager) - if err != nil { - session.FlashError(w, r, "Couldn't get your messages from DB: %s", err) + + // Viewing the threads, or a specific inbox/sent box? + if box == "threads" { + if result, err := models.GetMessageThreads(currentUser, pager); err != nil { + session.FlashError(w, r, "Couldn't get your messages from DB: %s", err) + } else { + messages = result + } + } else { + if result, err := models.GetMessages(currentUser, box == "sent", pager); err != nil { + session.FlashError(w, r, "Couldn't get your messages from DB: %s", err) + } else { + messages = result + } } // How many unreads? @@ -111,10 +126,8 @@ func Inbox() http.HandlerFunc { for _, m := range messages { userIDs = append(userIDs, m.SourceUserID, m.TargetUserID) } - if viewThread != nil { - for _, m := range viewThread { - userIDs = append(userIDs, m.SourceUserID, m.TargetUserID) - } + for _, m := range viewThread { + userIDs = append(userIDs, m.SourceUserID, m.TargetUserID) } userMap, err := models.MapUsers(currentUser, userIDs) if err != nil { @@ -126,10 +139,10 @@ func Inbox() http.HandlerFunc { "UserMap": userMap, "Unread": unread, "Pager": pager, - "IsSentBox": showSent, + "Box": box, "ViewThread": viewThread, // nil on inbox page "ThreadPager": threadPager, - "ReplyTo": composeToUsername, + "ReplyTo": composeToUser, "MessageID": msgId, } if err := tmpl.Execute(w, r, vars); err != nil { diff --git a/pkg/models/message.go b/pkg/models/message.go index 13d6372..c409ea6 100644 --- a/pkg/models/message.go +++ b/pkg/models/message.go @@ -23,7 +23,7 @@ func GetMessage(id uint64) (*Message, error) { return m, result.Error } -// GetMessages for a user. +// GetMessages for a user, e-mail style for the inbox or sent box view. func GetMessages(user *User, sent bool, pager *Pagination) ([]*Message, error) { var ( m = []*Message{} @@ -70,6 +70,73 @@ func GetMessages(user *User, sent bool, pager *Pagination) ([]*Message, error) { return m, result.Error } +// GetMessageThreads for a user: combined inbox/sent view grouped by username. +func GetMessageThreads(user *User, pager *Pagination) ([]*Message, error) { + var ( + m = []*Message{} + blockedUserIDs = BlockedUserIDs(user) + where = []string{} + placeholders = []interface{}{} + ) + + where = append(where, "target_user_id = ?") + placeholders = append(placeholders, user.ID) + + if len(blockedUserIDs) > 0 { + where = append(where, "source_user_id NOT IN ?") + placeholders = append(placeholders, blockedUserIDs) + } + + // Don't show messages from banned or disabled accounts. + where = append(where, ` + NOT EXISTS ( + SELECT 1 + FROM users + WHERE users.id IN (messages.target_user_id, messages.source_user_id) + AND users.status <> 'active' + ) + `) + + type newest struct { + ID uint64 + SourceUserID uint64 + TargetUserID uint64 + } + var scan []newest + + // Get the newest message IDs grouped by username for everyone we are chatting with. + query := DB.Model(&Message{}).Select( + "max(id) AS id", + "source_user_id", + "target_user_id", + ).Where( + strings.Join(where, " AND "), + placeholders..., + ).Group( + "source_user_id, target_user_id", + ).Order("id desc").Scan(&scan) + if query.Error != nil { + return nil, query.Error + } + + pager.Total = int64(len(scan)) + + // Get the details from these message IDs. + var messageIDs = []uint64{} + for _, row := range scan { + messageIDs = append(messageIDs, row.ID) + } + query = DB.Where( + "id IN ?", + messageIDs, + ).Order(pager.Sort) + + query.Model(&Message{}).Count(&pager.Total) + result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&m) + + return m, result.Error +} + // GetMessageThread returns paginated message history between two people. func GetMessageThread(sourceUserID, targetUserID uint64, pager *Pagination) ([]*Message, error) { var m = []*Message{} diff --git a/web/templates/inbox/inbox.html b/web/templates/inbox/inbox.html index 4165acb..a1d5390 100644 --- a/web/templates/inbox/inbox.html +++ b/web/templates/inbox/inbox.html @@ -8,7 +8,14 @@ Messages -

{{if .IsSentBox}}Sent{{else}}Inbox{{end}}

+

+ {{if eq .Box "threads"}} + Threads + {{else if eq .Box "sent"}} + Sent + {{else}} + Inbox + {{end}}

@@ -23,16 +30,29 @@
{{if .ViewThread}}
+
+
+
+ To: +
+
+ {{template "avatar-24x24" .ReplyTo}} +
+ +
+
{{InputCSRF}} - +