status-im/status-go

View on GitHub
services/local-notifications/core.go

Summary

Maintainability
A
0 mins
Test Coverage
A
90%
package localnotifications

import (
    "database/sql"
    "encoding/json"
    "sync"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/event"
    "github.com/ethereum/go-ethereum/log"
    "github.com/ethereum/go-ethereum/p2p"
    "github.com/ethereum/go-ethereum/rpc"
    gocommon "github.com/status-im/status-go/common"
    "github.com/status-im/status-go/multiaccounts/accounts"
    "github.com/status-im/status-go/services/wallet/transfer"
    "github.com/status-im/status-go/signal"
)

type PushCategory string

type NotificationType string

type NotificationBody interface {
    json.Marshaler
}

type Notification struct {
    ID                  common.Hash
    Platform            float32
    Body                NotificationBody
    BodyType            NotificationType
    Title               string
    Message             string
    Category            PushCategory
    Deeplink            string
    Image               string
    IsScheduled         bool
    ScheduledTime       string
    IsConversation      bool
    IsGroupConversation bool
    ConversationID      string
    Timestamp           uint64
    Author              NotificationAuthor
    Deleted             bool
}

type NotificationAuthor struct {
    ID   string `json:"id"`
    Icon string `json:"icon"`
    Name string `json:"name"`
}

// notificationAlias is an interim struct used for json un/marshalling
type notificationAlias struct {
    ID                  common.Hash        `json:"id"`
    Platform            float32            `json:"platform,omitempty"`
    Body                json.RawMessage    `json:"body"`
    BodyType            NotificationType   `json:"bodyType"`
    Title               string             `json:"title,omitempty"`
    Message             string             `json:"message,omitempty"`
    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"`
    IsConversation      bool               `json:"isConversation,omitempty"`
    IsGroupConversation bool               `json:"isGroupConversation,omitempty"`
    ConversationID      string             `json:"conversationId,omitempty"`
    Timestamp           uint64             `json:"timestamp,omitempty"`
    Author              NotificationAuthor `json:"notificationAuthor,omitempty"`
    Deleted             bool               `json:"deleted,omitempty"`
}

// MessageEvent - structure used to pass messages from chat to bus
type MessageEvent struct{}

// CustomEvent - structure used to pass custom user set messages to bus
type CustomEvent struct{}

type transmitter struct {
    publisher *event.Feed

    wg   sync.WaitGroup
    quit chan struct{}
}

// Service keeps the state of message bus
type Service struct {
    started           bool
    WatchingEnabled   bool
    chainID           uint64
    transmitter       *transmitter
    walletTransmitter *transmitter
    db                *Database
    walletDB          *transfer.Database
    accountsDB        *accounts.Database
}

func NewService(appDB *sql.DB, walletDB *transfer.Database, chainID uint64) (*Service, error) {
    db := NewDB(appDB, chainID)
    accountsDB, err := accounts.NewDB(appDB)
    if err != nil {
        return nil, err
    }
    trans := &transmitter{}
    walletTrans := &transmitter{}

    return &Service{
        db:                db,
        chainID:           chainID,
        walletDB:          walletDB,
        accountsDB:        accountsDB,
        transmitter:       trans,
        walletTransmitter: walletTrans,
    }, nil
}

func (n *Notification) MarshalJSON() ([]byte, error) {

    var body json.RawMessage
    if n.Body != nil {
        encodedBody, err := n.Body.MarshalJSON()
        if err != nil {
            return nil, err
        }
        body = encodedBody
    }

    alias := notificationAlias{
        ID:                  n.ID,
        Platform:            n.Platform,
        Body:                body,
        BodyType:            n.BodyType,
        Category:            n.Category,
        Title:               n.Title,
        Message:             n.Message,
        Deeplink:            n.Deeplink,
        Image:               n.Image,
        IsScheduled:         n.IsScheduled,
        ScheduledTime:       n.ScheduledTime,
        IsConversation:      n.IsConversation,
        IsGroupConversation: n.IsGroupConversation,
        ConversationID:      n.ConversationID,
        Timestamp:           n.Timestamp,
        Author:              n.Author,
        Deleted:             n.Deleted,
    }

    return json.Marshal(alias)
}

func PushMessages(ns []*Notification) {
    for _, n := range ns {
        pushMessage(n)
    }
}

func pushMessage(notification *Notification) {
    log.Debug("Pushing a new push notification")
    signal.SendLocalNotifications(notification)
}

// Start Worker which processes all incoming messages
func (s *Service) Start() error {
    s.started = true

    s.transmitter.quit = make(chan struct{})
    s.transmitter.publisher = &event.Feed{}

    events := make(chan TransactionEvent, 10)
    sub := s.transmitter.publisher.Subscribe(events)

    s.transmitter.wg.Add(1)
    go func() {
        defer gocommon.LogOnPanic()
        defer s.transmitter.wg.Done()
        for {
            select {
            case <-s.transmitter.quit:
                sub.Unsubscribe()
                return
            case err := <-sub.Err():
                if err != nil {
                    log.Error("Local notifications transmitter failed with", "error", err)
                }
                return
            case event := <-events:
                s.transactionsHandler(event)
            }
        }
    }()

    log.Info("Successful start")

    return nil
}

// Stop worker
func (s *Service) Stop() error {
    s.started = false

    if s.transmitter.quit != nil {
        close(s.transmitter.quit)
        s.transmitter.wg.Wait()
        s.transmitter.quit = nil
    }

    if s.walletTransmitter.quit != nil {
        close(s.walletTransmitter.quit)
        s.walletTransmitter.wg.Wait()
        s.walletTransmitter.quit = nil
    }

    return nil
}

// APIs returns list of available RPC APIs.
func (s *Service) APIs() []rpc.API {
    return []rpc.API{
        {
            Namespace: "localnotifications",
            Version:   "0.1.0",
            Service:   NewAPI(s),
        },
    }
}

// Protocols returns list of p2p protocols.
func (s *Service) Protocols() []p2p.Protocol {
    return nil
}

func (s *Service) IsStarted() bool {
    return s.started
}