From 84db2fb4727314f27ebf10be1d14df3003626169 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 26 Jan 2021 12:49:37 +0100 Subject: [PATCH] Revert "Revert "Expand Local Notifications to support multiple Notification types (#2100)"" This reverts commit 5887337b881561fa1418239d587d1ce2116c6ba8. --- cmd/statusd/main.go | 5 +- protocol/local_notifications.go | 9 ++ protocol/message_handler.go | 10 ++ protocol/messenger_response.go | 5 +- services/ext/service.go | 2 + services/local-notifications/core.go | 142 +++++++++++++++++++++- services/local-notifications/core_test.go | 8 +- 7 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 protocol/local_notifications.go diff --git a/cmd/statusd/main.go b/cmd/statusd/main.go index 31d001716..fe74ec8cf 100644 --- a/cmd/statusd/main.go +++ b/cmd/statusd/main.go @@ -31,6 +31,7 @@ import ( "github.com/status-im/status-go/params" "github.com/status-im/status-go/profiling" "github.com/status-im/status-go/protocol" + localnotifications "github.com/status-im/status-go/services/local-notifications" ) const ( @@ -399,11 +400,13 @@ func retrieveMessagesLoop(messenger *protocol.Messenger, tick time.Duration, can for { select { case <-ticker.C: - _, err := messenger.RetrieveAll() + mr, err := messenger.RetrieveAll() if err != nil { logger.Error("failed to retrieve raw messages", "err", err) continue } + + localnotifications.SendMessageNotifications(mr.Notifications) case <-cancel: return } diff --git a/protocol/local_notifications.go b/protocol/local_notifications.go new file mode 100644 index 000000000..fdb859191 --- /dev/null +++ b/protocol/local_notifications.go @@ -0,0 +1,9 @@ +package protocol + +import "github.com/status-im/status-go/protocol/common" + +type MessageNotificationBody struct { + Message *common.Message + Contact *Contact + Chat *Chat +} diff --git a/protocol/message_handler.go b/protocol/message_handler.go index eb5d27be3..cb1ee4d9d 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -483,6 +483,16 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error { // Add to response state.Response.Messages = append(state.Response.Messages, receivedMessage) + // Create notification body to be eventually passed to `localnotifications.SendMessageNotifications()` + state.Response.Notifications = append( + state.Response.Notifications, + MessageNotificationBody{ + Message: receivedMessage, + Contact: contact, + Chat: chat, + }, + ) + return nil } diff --git a/protocol/messenger_response.go b/protocol/messenger_response.go index 88af276d9..8114365dc 100644 --- a/protocol/messenger_response.go +++ b/protocol/messenger_response.go @@ -23,10 +23,13 @@ type MessengerResponse struct { Mailservers []mailservers.Mailserver `json:"mailservers,omitempty"` MailserverTopics []mailservers.MailserverTopic `json:"mailserverTopics,omitempty"` MailserverRanges []mailservers.ChatRequestRange `json:"mailserverRanges,omitempty"` + + // Notifications a list of MessageNotificationBody derived from received messages that are useful to notify the user about + Notifications []MessageNotificationBody `json:"notifications"` } func (m *MessengerResponse) IsEmpty() bool { - return len(m.Chats)+len(m.Messages)+len(m.Contacts)+len(m.Installations)+len(m.Invitations)+len(m.EmojiReactions)+len(m.Communities)+len(m.CommunityChanges)+len(m.Filters)+len(m.RemovedFilters)+len(m.RemovedChats)+len(m.MailserverTopics)+len(m.Mailservers)+len(m.MailserverRanges) == 0 + return len(m.Chats)+len(m.Messages)+len(m.Contacts)+len(m.Installations)+len(m.Invitations)+len(m.EmojiReactions)+len(m.Communities)+len(m.CommunityChanges)+len(m.Filters)+len(m.RemovedFilters)+len(m.RemovedChats)+len(m.Notifications)+len(m.MailserverTopics)+len(m.Mailservers)+len(m.MailserverRanges) == 0 } // Merge takes another response and appends the new Chats & new Messages and replaces diff --git a/services/ext/service.go b/services/ext/service.go index e188d02a7..c3b377d56 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -31,6 +31,7 @@ import ( "github.com/status-im/status-go/protocol/pushnotificationserver" "github.com/status-im/status-go/protocol/transport" "github.com/status-im/status-go/services/ext/mailservers" + localnotifications "github.com/status-im/status-go/services/local-notifications" mailserversDB "github.com/status-im/status-go/services/mailservers" "github.com/status-im/status-go/services/wallet" "github.com/status-im/status-go/signal" @@ -197,6 +198,7 @@ func (s *Service) retrieveMessagesLoop(tick time.Duration, cancel <-chan struct{ } if !response.IsEmpty() { PublisherSignalHandler{}.NewMessages(response) + localnotifications.SendMessageNotifications(response.Notifications) } case <-cancel: return diff --git a/services/local-notifications/core.go b/services/local-notifications/core.go index 5ec3fe157..5390a3218 100644 --- a/services/local-notifications/core.go +++ b/services/local-notifications/core.go @@ -2,6 +2,8 @@ package localnotifications import ( "database/sql" + "encoding/json" + "fmt" "math/big" "sync" @@ -13,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/multiaccounts/accounts" + "github.com/status-im/status-go/protocol" "github.com/status-im/status-go/services/wallet" "github.com/status-im/status-go/signal" ) @@ -21,12 +24,24 @@ type PushCategory string type transactionState string -const walletDeeplinkPrefix = "status-im://wallet/" +type NotificationType string const ( + walletDeeplinkPrefix = "status-im://wallet/" + failed transactionState = "failed" inbound transactionState = "inbound" outbound transactionState = "outbound" + + CategoryTransaction PushCategory = "transaction" + CategoryMessage PushCategory = "newMessage" + + TypeTransaction NotificationType = "transaction" + TypeMessage NotificationType = "message" +) + +var ( + marshalTypeMismatchErr = "notification type mismatch, expected '%s', Body could not be marshalled into this type" ) type notificationBody struct { @@ -42,9 +57,23 @@ type notificationBody struct { } type Notification struct { + ID common.Hash `json:"id"` + Platform float32 `json:"platform,omitempty"` + Body interface{} + BodyType NotificationType `json:"bodyType"` + Category PushCategory `json:"category,omitempty"` + Deeplink string `json:"deepLink,omitempty"` + Image string `json:"imageUrl,omitempty"` + IsScheduled bool `json:"isScheduled,omitempty"` + ScheduledTime string `json:"scheduleTime,omitempty"` +} + +// notificationAlias is an interim struct used for json un/marshalling +type notificationAlias struct { ID common.Hash `json:"id"` Platform float32 `json:"platform,omitempty"` - Body notificationBody `json:"body"` + Body json.RawMessage `json:"body"` + BodyType NotificationType `json:"bodyType"` Category PushCategory `json:"category,omitempty"` Deeplink string `json:"deepLink,omitempty"` Image string `json:"imageUrl,omitempty"` @@ -102,6 +131,94 @@ func NewService(appDB *sql.DB, network uint64) *Service { } } +func (n *Notification) MarshalJSON() ([]byte, error) { + var body json.RawMessage + var err error + + switch n.BodyType { + case TypeTransaction: + if nb, ok := n.Body.(*notificationBody); ok { + body, err = json.Marshal(nb) + if err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf(marshalTypeMismatchErr, n.BodyType) + } + + case TypeMessage: + if nmb, ok := n.Body.(*protocol.MessageNotificationBody); ok { + body, err = json.Marshal(nmb) + if err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf(marshalTypeMismatchErr, n.BodyType) + } + + default: + return nil, fmt.Errorf("unknown NotificationType '%s'", n.BodyType) + } + + alias := notificationAlias{ + n.ID, + n.Platform, + body, + n.BodyType, + n.Category, + n.Deeplink, + n.Image, + n.IsScheduled, + n.ScheduledTime, + } + + return json.Marshal(alias) +} + +func (n *Notification) UnmarshalJSON(data []byte) error { + var alias notificationAlias + err := json.Unmarshal(data, &alias) + if err != nil { + return err + } + + n.BodyType = alias.BodyType + n.Category = alias.Category + n.Platform = alias.Platform + n.ID = alias.ID + n.Image = alias.Image + n.Deeplink = alias.Deeplink + n.IsScheduled = alias.IsScheduled + n.ScheduledTime = alias.ScheduledTime + + switch n.BodyType { + case TypeTransaction: + return n.unmarshalAndAttachBody(alias.Body, ¬ificationBody{}) + + case TypeMessage: + return n.unmarshalAndAttachBody(alias.Body, &protocol.MessageNotificationBody{}) + + default: + return fmt.Errorf("unknown NotificationType '%s'", n.BodyType) + } +} + +func (n *Notification) unmarshalAndAttachBody(body json.RawMessage, bodyStruct interface{}) error { + err := json.Unmarshal(body, &bodyStruct) + if err != nil { + return err + } + + n.Body = bodyStruct + return nil +} + +func pushMessages(ns []*Notification) { + for _, n := range ns { + pushMessage(n) + } +} + func pushMessage(notification *Notification) { log.Info("Pushing a new push notification", "info", notification) signal.SendLocalNotifications(notification) @@ -154,10 +271,11 @@ func (s *Service) buildTransactionNotification(rawTransfer wallet.Transfer) *Not } return &Notification{ + BodyType: TypeTransaction, ID: transfer.ID, - Body: body, + Body: &body, Deeplink: deeplink, - Category: "transaction", + Category: CategoryTransaction, } } @@ -355,3 +473,19 @@ func (s *Service) Protocols() []p2p.Protocol { func (s *Service) IsStarted() bool { return s.started } + +func SendMessageNotifications(mnb []protocol.MessageNotificationBody) { + var ns []*Notification + for _, n := range mnb { + ns = append(ns, &Notification{ + Body: n, + BodyType: TypeMessage, + Category: CategoryMessage, + Deeplink: "", // TODO find what if any Deeplink should be used here + Image: "", // TODO do we want to attach any image data contained on the MessageBody{}? + }) + } + + // sends notifications messages to the OS level application + pushMessages(ns) +} diff --git a/services/local-notifications/core_test.go b/services/local-notifications/core_test.go index ff962396d..0eb158644 100644 --- a/services/local-notifications/core_test.go +++ b/services/local-notifications/core_test.go @@ -120,7 +120,7 @@ func TestTransactionNotification(t *testing.T) { require.NoError(t, utils.Eventually(func() error { if signalEvent == nil { - return fmt.Errorf("Signal was not handled") + return fmt.Errorf("signal was not handled") } notification := struct { Type string @@ -130,10 +130,10 @@ func TestTransactionNotification(t *testing.T) { require.NoError(t, json.Unmarshal(signalEvent, ¬ification)) if notification.Type != "local-notifications" { - return fmt.Errorf("Wrong signal was sent") + return fmt.Errorf("wrong signal was sent") } - if notification.Event.Body.To != header.Address { - return fmt.Errorf("Transaction to address is wrong") + if notification.Event.Body.(*notificationBody).To != header.Address { + return fmt.Errorf("transaction to address is wrong") } return nil }, 2*time.Second, 100*time.Millisecond))