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  }