github.com/status-im/status-go@v1.1.0/services/local-notifications/core.go (about) 1 package localnotifications 2 3 import ( 4 "database/sql" 5 "encoding/json" 6 "sync" 7 8 "github.com/ethereum/go-ethereum/common" 9 "github.com/ethereum/go-ethereum/event" 10 "github.com/ethereum/go-ethereum/log" 11 "github.com/ethereum/go-ethereum/p2p" 12 "github.com/ethereum/go-ethereum/rpc" 13 "github.com/status-im/status-go/multiaccounts/accounts" 14 "github.com/status-im/status-go/services/wallet/transfer" 15 "github.com/status-im/status-go/signal" 16 ) 17 18 type PushCategory string 19 20 type NotificationType string 21 22 type NotificationBody interface { 23 json.Marshaler 24 } 25 26 type Notification struct { 27 ID common.Hash 28 Platform float32 29 Body NotificationBody 30 BodyType NotificationType 31 Title string 32 Message string 33 Category PushCategory 34 Deeplink string 35 Image string 36 IsScheduled bool 37 ScheduledTime string 38 IsConversation bool 39 IsGroupConversation bool 40 ConversationID string 41 Timestamp uint64 42 Author NotificationAuthor 43 Deleted bool 44 } 45 46 type NotificationAuthor struct { 47 ID string `json:"id"` 48 Icon string `json:"icon"` 49 Name string `json:"name"` 50 } 51 52 // notificationAlias is an interim struct used for json un/marshalling 53 type notificationAlias struct { 54 ID common.Hash `json:"id"` 55 Platform float32 `json:"platform,omitempty"` 56 Body json.RawMessage `json:"body"` 57 BodyType NotificationType `json:"bodyType"` 58 Title string `json:"title,omitempty"` 59 Message string `json:"message,omitempty"` 60 Category PushCategory `json:"category,omitempty"` 61 Deeplink string `json:"deepLink,omitempty"` 62 Image string `json:"imageUrl,omitempty"` 63 IsScheduled bool `json:"isScheduled,omitempty"` 64 ScheduledTime string `json:"scheduleTime,omitempty"` 65 IsConversation bool `json:"isConversation,omitempty"` 66 IsGroupConversation bool `json:"isGroupConversation,omitempty"` 67 ConversationID string `json:"conversationId,omitempty"` 68 Timestamp uint64 `json:"timestamp,omitempty"` 69 Author NotificationAuthor `json:"notificationAuthor,omitempty"` 70 Deleted bool `json:"deleted,omitempty"` 71 } 72 73 // MessageEvent - structure used to pass messages from chat to bus 74 type MessageEvent struct{} 75 76 // CustomEvent - structure used to pass custom user set messages to bus 77 type CustomEvent struct{} 78 79 type transmitter struct { 80 publisher *event.Feed 81 82 wg sync.WaitGroup 83 quit chan struct{} 84 } 85 86 // Service keeps the state of message bus 87 type Service struct { 88 started bool 89 WatchingEnabled bool 90 chainID uint64 91 transmitter *transmitter 92 walletTransmitter *transmitter 93 db *Database 94 walletDB *transfer.Database 95 accountsDB *accounts.Database 96 } 97 98 func NewService(appDB *sql.DB, walletDB *transfer.Database, chainID uint64) (*Service, error) { 99 db := NewDB(appDB, chainID) 100 accountsDB, err := accounts.NewDB(appDB) 101 if err != nil { 102 return nil, err 103 } 104 trans := &transmitter{} 105 walletTrans := &transmitter{} 106 107 return &Service{ 108 db: db, 109 chainID: chainID, 110 walletDB: walletDB, 111 accountsDB: accountsDB, 112 transmitter: trans, 113 walletTransmitter: walletTrans, 114 }, nil 115 } 116 117 func (n *Notification) MarshalJSON() ([]byte, error) { 118 119 var body json.RawMessage 120 if n.Body != nil { 121 encodedBody, err := n.Body.MarshalJSON() 122 if err != nil { 123 return nil, err 124 } 125 body = encodedBody 126 } 127 128 alias := notificationAlias{ 129 ID: n.ID, 130 Platform: n.Platform, 131 Body: body, 132 BodyType: n.BodyType, 133 Category: n.Category, 134 Title: n.Title, 135 Message: n.Message, 136 Deeplink: n.Deeplink, 137 Image: n.Image, 138 IsScheduled: n.IsScheduled, 139 ScheduledTime: n.ScheduledTime, 140 IsConversation: n.IsConversation, 141 IsGroupConversation: n.IsGroupConversation, 142 ConversationID: n.ConversationID, 143 Timestamp: n.Timestamp, 144 Author: n.Author, 145 Deleted: n.Deleted, 146 } 147 148 return json.Marshal(alias) 149 } 150 151 func PushMessages(ns []*Notification) { 152 for _, n := range ns { 153 pushMessage(n) 154 } 155 } 156 157 func pushMessage(notification *Notification) { 158 log.Debug("Pushing a new push notification") 159 signal.SendLocalNotifications(notification) 160 } 161 162 // Start Worker which processes all incoming messages 163 func (s *Service) Start() error { 164 s.started = true 165 166 s.transmitter.quit = make(chan struct{}) 167 s.transmitter.publisher = &event.Feed{} 168 169 events := make(chan TransactionEvent, 10) 170 sub := s.transmitter.publisher.Subscribe(events) 171 172 s.transmitter.wg.Add(1) 173 go func() { 174 defer s.transmitter.wg.Done() 175 for { 176 select { 177 case <-s.transmitter.quit: 178 sub.Unsubscribe() 179 return 180 case err := <-sub.Err(): 181 if err != nil { 182 log.Error("Local notifications transmitter failed with", "error", err) 183 } 184 return 185 case event := <-events: 186 s.transactionsHandler(event) 187 } 188 } 189 }() 190 191 log.Info("Successful start") 192 193 return nil 194 } 195 196 // Stop worker 197 func (s *Service) Stop() error { 198 s.started = false 199 200 if s.transmitter.quit != nil { 201 close(s.transmitter.quit) 202 s.transmitter.wg.Wait() 203 s.transmitter.quit = nil 204 } 205 206 if s.walletTransmitter.quit != nil { 207 close(s.walletTransmitter.quit) 208 s.walletTransmitter.wg.Wait() 209 s.walletTransmitter.quit = nil 210 } 211 212 return nil 213 } 214 215 // APIs returns list of available RPC APIs. 216 func (s *Service) APIs() []rpc.API { 217 return []rpc.API{ 218 { 219 Namespace: "localnotifications", 220 Version: "0.1.0", 221 Service: NewAPI(s), 222 }, 223 } 224 } 225 226 // Protocols returns list of p2p protocols. 227 func (s *Service) Protocols() []p2p.Protocol { 228 return nil 229 } 230 231 func (s *Service) IsStarted() bool { 232 return s.started 233 }