1:1
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/internal/service"
|
||||
@@ -56,7 +58,7 @@ func (h *NotificationHandler) List(c *gin.Context) {
|
||||
|
||||
payload := make([]gin.H, 0, len(result.Notifications))
|
||||
for i := range result.Notifications {
|
||||
payload = append(payload, serializeNotification(&result.Notifications[i]))
|
||||
payload = append(payload, h.serializeNotification(c.Request.Context(), &result.Notifications[i]))
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": gin.H{
|
||||
@@ -91,7 +93,7 @@ func (h *NotificationHandler) Get(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, serializeNotification(notification))
|
||||
c.JSON(http.StatusOK, h.serializeNotification(c.Request.Context(), notification))
|
||||
}
|
||||
|
||||
// Update marks a notification as read.
|
||||
@@ -112,7 +114,7 @@ func (h *NotificationHandler) Update(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, serializeNotification(notification))
|
||||
c.JSON(http.StatusOK, h.serializeNotification(c.Request.Context(), notification))
|
||||
}
|
||||
|
||||
// MarkAllRead marks all unread notifications as read for the current user in account.
|
||||
@@ -185,7 +187,7 @@ func (h *NotificationHandler) Snooze(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, serializeNotification(notification))
|
||||
c.JSON(http.StatusOK, h.serializeNotification(c.Request.Context(), notification))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -201,7 +203,7 @@ func (h *NotificationHandler) Snooze(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, serializeNotification(notification))
|
||||
c.JSON(http.StatusOK, h.serializeNotification(c.Request.Context(), notification))
|
||||
}
|
||||
|
||||
// Unread marks a notification as unread.
|
||||
@@ -223,7 +225,7 @@ func (h *NotificationHandler) Unread(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, serializeNotification(notification))
|
||||
c.JSON(http.StatusOK, h.serializeNotification(c.Request.Context(), notification))
|
||||
}
|
||||
|
||||
// Destroy deletes a single notification.
|
||||
@@ -288,7 +290,19 @@ func notificationIncludes(c *gin.Context, value string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *NotificationHandler) serializeNotification(ctx context.Context, notification *model.Notification) gin.H {
|
||||
var db *gorm.DB
|
||||
if h != nil && h.notificationService != nil {
|
||||
db = h.notificationService.DB()
|
||||
}
|
||||
return serializeNotificationWithDB(ctx, db, notification)
|
||||
}
|
||||
|
||||
func serializeNotification(notification *model.Notification) gin.H {
|
||||
return serializeNotificationWithDB(context.Background(), nil, notification)
|
||||
}
|
||||
|
||||
func serializeNotificationWithDB(ctx context.Context, db *gorm.DB, notification *model.Notification) gin.H {
|
||||
meta := gin.H{}
|
||||
if len(notification.AdditionalAttributes) > 0 {
|
||||
_ = json.Unmarshal(notification.AdditionalAttributes, &meta)
|
||||
@@ -300,10 +314,10 @@ func serializeNotification(notification *model.Notification) gin.H {
|
||||
"push_message_body": notificationBody(notification),
|
||||
"primary_actor_type": notification.PrimaryActorType,
|
||||
"primary_actor_id": notification.PrimaryActorID,
|
||||
"primary_actor": notificationActor(notification.PrimaryActorType, notification.PrimaryActorID),
|
||||
"primary_actor": notificationActor(ctx, db, notification.PrimaryActorType, notification.PrimaryActorID, notification.AccountID),
|
||||
"read_at": notification.ReadAt,
|
||||
"secondary_actor": notificationActor(notification.SecondaryActorType, notification.SecondaryActorID),
|
||||
"user": gin.H{"id": notification.UserID},
|
||||
"secondary_actor": notificationActor(ctx, db, notification.SecondaryActorType, notification.SecondaryActorID, notification.AccountID),
|
||||
"user": notificationUser(ctx, db, notification.UserID, notification.AccountID),
|
||||
"created_at": notification.CreatedAt.Unix(),
|
||||
"last_activity_at": notification.UpdatedAt.Unix(),
|
||||
"snoozed_until": notification.SnoozedUntil,
|
||||
@@ -312,13 +326,138 @@ func serializeNotification(notification *model.Notification) gin.H {
|
||||
}
|
||||
}
|
||||
|
||||
func notificationActor(actorType string, actorID uint) interface{} {
|
||||
func notificationActor(ctx context.Context, db *gorm.DB, actorType string, actorID uint, accountID *uint) interface{} {
|
||||
if actorType == "" || actorID == 0 {
|
||||
return nil
|
||||
}
|
||||
if db == nil {
|
||||
return gin.H{"id": actorID, "type": actorType, "meta": gin.H{}}
|
||||
}
|
||||
switch strings.ToLower(actorType) {
|
||||
case "conversation":
|
||||
var conversation model.Conversation
|
||||
query := db.WithContext(ctx).Preload("Contact").Preload("Inbox").First(&conversation, actorID)
|
||||
if query.Error == nil && notificationAccountMatches(accountID, conversation.AccountID) {
|
||||
return serializeConversationForNotification(ctx, db, &conversation)
|
||||
}
|
||||
case "contact":
|
||||
var contact model.Contact
|
||||
query := db.WithContext(ctx).First(&contact, actorID)
|
||||
if query.Error == nil && notificationAccountMatches(accountID, contact.AccountID) {
|
||||
return serializeContactForNotification(&contact)
|
||||
}
|
||||
case "user":
|
||||
var user model.User
|
||||
query := db.WithContext(ctx).First(&user, actorID)
|
||||
if query.Error == nil && notificationAccountMatches(accountID, user.AccountID) {
|
||||
return serializeUserForNotification(&user)
|
||||
}
|
||||
case "agentbot", "agent_bot":
|
||||
var bot model.AgentBot
|
||||
query := db.WithContext(ctx).First(&bot, actorID)
|
||||
if query.Error == nil && (bot.AccountID == nil || accountID == nil || *bot.AccountID == *accountID) {
|
||||
return serializeAgentBotSender(&bot)
|
||||
}
|
||||
}
|
||||
return gin.H{"id": actorID, "type": actorType, "meta": gin.H{}}
|
||||
}
|
||||
|
||||
func notificationUser(ctx context.Context, db *gorm.DB, userID uint, accountID *uint) interface{} {
|
||||
if db == nil || userID == 0 {
|
||||
return gin.H{"id": userID}
|
||||
}
|
||||
var user model.User
|
||||
query := db.WithContext(ctx).First(&user, userID)
|
||||
if query.Error == nil && notificationAccountMatches(accountID, user.AccountID) {
|
||||
return serializeUserForNotification(&user)
|
||||
}
|
||||
return gin.H{"id": userID}
|
||||
}
|
||||
|
||||
func notificationAccountMatches(accountID *uint, recordAccountID uint) bool {
|
||||
return accountID == nil || *accountID == recordAccountID
|
||||
}
|
||||
|
||||
func serializeConversationForNotification(ctx context.Context, db *gorm.DB, conversation *model.Conversation) map[string]any {
|
||||
payload := map[string]any{
|
||||
"additional_attributes": jsonObject(conversation.AdditionalAttributes),
|
||||
"can_reply": conversationCanReply(ctx, db, conversation),
|
||||
"channel": conversation.ChannelType,
|
||||
"contact_inbox": nil,
|
||||
"id": conversationDisplayID(conversation),
|
||||
"inbox_id": conversation.InboxID,
|
||||
"labels": labelList(conversation.Labels),
|
||||
"meta": serializeConversationMeta(ctx, db, conversation),
|
||||
"status": conversation.Status,
|
||||
"custom_attributes": jsonObject(conversation.CustomAttributes),
|
||||
"snoozed_until": conversation.SnoozedUntil,
|
||||
"unread_count": unreadCount(ctx, db, conversation),
|
||||
"first_reply_created_at": int64Value(conversation.FirstReplyCreatedAt),
|
||||
"priority": conversation.Priority,
|
||||
"waiting_since": int64Value(conversation.WaitingSince),
|
||||
"agent_last_seen_at": int64Value(conversation.AgentLastSeenAt),
|
||||
"contact_last_seen_at": int64Value(conversation.ContactLastSeenAt),
|
||||
"last_activity_at": int64Value(conversation.LastActivityAt),
|
||||
"timestamp": int64Value(conversation.LastActivityAt),
|
||||
"created_at": conversation.CreatedAt.Unix(),
|
||||
"updated_at": float64(conversation.UpdatedAt.UnixNano()) / float64(time.Second),
|
||||
}
|
||||
payload["contact_inbox"] = serializeConversationContactInboxForNotification(ctx, db, conversation)
|
||||
if message := latestChatMessageForNotification(ctx, db, conversation); message != nil {
|
||||
payload["messages"] = []any{serializeMessage(ctx, db, message, conversation)}
|
||||
} else {
|
||||
payload["messages"] = []any{}
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
func serializeConversationContactInboxForNotification(ctx context.Context, db *gorm.DB, conversation *model.Conversation) map[string]any {
|
||||
if db == nil || conversation.ContactInboxID == nil || *conversation.ContactInboxID == 0 {
|
||||
return nil
|
||||
}
|
||||
var contactInbox model.ContactInbox
|
||||
if err := db.WithContext(ctx).First(&contactInbox, *conversation.ContactInboxID).Error; err != nil {
|
||||
return nil
|
||||
}
|
||||
return serializeContactInboxShell(ctx, db, &contactInbox)
|
||||
}
|
||||
|
||||
func latestChatMessageForNotification(ctx context.Context, db *gorm.DB, conversation *model.Conversation) *model.Message {
|
||||
if db == nil || conversation == nil {
|
||||
return nil
|
||||
}
|
||||
var message model.Message
|
||||
query := db.WithContext(ctx).
|
||||
Where("conversation_id = ? AND account_id = ? AND message_type != ?", conversation.ID, conversation.AccountID, "activity").
|
||||
Order("created_at DESC, id DESC").
|
||||
First(&message)
|
||||
if query.Error != nil {
|
||||
return nil
|
||||
}
|
||||
return &message
|
||||
}
|
||||
|
||||
func serializeContactForNotification(contact *model.Contact) map[string]any {
|
||||
payload := serializeContact(contact)
|
||||
payload["type"] = "contact"
|
||||
delete(payload, "availability_status")
|
||||
delete(payload, "last_activity_at")
|
||||
delete(payload, "created_at")
|
||||
return payload
|
||||
}
|
||||
|
||||
func serializeUserForNotification(user *model.User) map[string]any {
|
||||
return map[string]any{
|
||||
"id": user.ID,
|
||||
"name": user.Name,
|
||||
"available_name": nonEmpty(user.DisplayName, user.Name),
|
||||
"avatar_url": user.AvatarURL,
|
||||
"type": "user",
|
||||
"availability_status": availabilityStatus(user.Available),
|
||||
"thumbnail": user.AvatarURL,
|
||||
}
|
||||
}
|
||||
|
||||
func notificationTitle(notification *model.Notification) string {
|
||||
if title := stringFromNotificationAttrs(notification.AdditionalAttributes, "push_message_title", "title"); title != "" {
|
||||
return title
|
||||
|
||||
Reference in New Issue
Block a user