Local notifications service (#2026)
Also adds implementation for eth transactions notifications
This commit is contained in:
parent
3e446eed8c
commit
d04e54e54e
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/status-im/status-go/rpc"
|
||||
accountssvc "github.com/status-im/status-go/services/accounts"
|
||||
"github.com/status-im/status-go/services/browsers"
|
||||
localnotifications "github.com/status-im/status-go/services/local-notifications"
|
||||
"github.com/status-im/status-go/services/mailservers"
|
||||
"github.com/status-im/status-go/services/permissions"
|
||||
"github.com/status-im/status-go/services/personal"
|
||||
|
@ -96,6 +97,7 @@ func NewGethStatusBackend() *GethStatusBackend {
|
|||
transactor := transactions.NewTransactor()
|
||||
personalAPI := personal.NewAPI()
|
||||
rpcFilters := rpcfilters.New(statusNode)
|
||||
|
||||
return &GethStatusBackend{
|
||||
statusNode: statusNode,
|
||||
accountManager: accountManager,
|
||||
|
@ -339,10 +341,12 @@ func (b *GethStatusBackend) startNodeWithAccount(acc multiaccounts.Account, pass
|
|||
WatchAddresses: watchAddrs,
|
||||
MainAccount: walletAddr,
|
||||
}
|
||||
|
||||
err = b.StartNode(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.SelectAccount(login)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -351,6 +355,7 @@ func (b *GethStatusBackend) startNodeWithAccount(acc multiaccounts.Account, pass
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -514,6 +519,12 @@ func (b *GethStatusBackend) walletService(network uint64, accountsFeed *event.Fe
|
|||
}
|
||||
}
|
||||
|
||||
func (b *GethStatusBackend) localNotificationsService(network uint64) gethnode.ServiceConstructor {
|
||||
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
|
||||
return localnotifications.NewService(b.appDB, network), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -542,6 +553,7 @@ func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) {
|
|||
services = appendIf(config.PermissionsConfig.Enabled, services, b.permissionsService())
|
||||
services = appendIf(config.MailserversConfig.Enabled, services, b.mailserversService())
|
||||
services = appendIf(config.WalletConfig.Enabled, services, b.walletService(config.NetworkID, accountsFeed))
|
||||
services = appendIf(config.LocalNotificationsConfig.Enabled, services, b.localNotificationsService(config.NetworkID))
|
||||
|
||||
manager := b.accountManager.GetManager()
|
||||
if manager == nil {
|
||||
|
@ -889,17 +901,27 @@ func (b *GethStatusBackend) AppStateChange(state string) {
|
|||
}
|
||||
}
|
||||
} else if s == appStateBackground && !b.forceStopWallet {
|
||||
localNotifications, err := b.statusNode.LocalNotificationsService()
|
||||
if err != nil {
|
||||
b.log.Error("Retrieving of local notifications service failed on app state change", "error", err)
|
||||
}
|
||||
|
||||
wallet, err := b.statusNode.WalletService()
|
||||
if err != nil {
|
||||
b.log.Error("Retrieving of wallet service failed on app state change to background", "error", err)
|
||||
return
|
||||
}
|
||||
err = wallet.Stop()
|
||||
if err != nil {
|
||||
b.log.Error("Wallet service stop failed on app state change to background", "error", err)
|
||||
return
|
||||
|
||||
if !localNotifications.IsWatchingWallet() {
|
||||
err = wallet.Stop()
|
||||
|
||||
if err != nil {
|
||||
b.log.Error("Wallet service stop failed on app state change to background", "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: put node in low-power mode if the app is in background (or inactive)
|
||||
// and normal mode if the app is in foreground.
|
||||
}
|
||||
|
@ -948,6 +970,62 @@ func (b *GethStatusBackend) StartWallet() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *GethStatusBackend) StopLocalNotifications() error {
|
||||
localPN, err := b.statusNode.LocalNotificationsService()
|
||||
if err != nil {
|
||||
b.log.Error("Retrieving of LocalNotifications service failed on StopLocalNotifications", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if localPN.IsStarted() {
|
||||
err = localPN.Stop()
|
||||
if err != nil {
|
||||
b.log.Error("LocalNotifications service stop failed on StopLocalNotifications", "error", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *GethStatusBackend) StartLocalNotifications() error {
|
||||
localPN, err := b.statusNode.LocalNotificationsService()
|
||||
|
||||
if err != nil {
|
||||
b.log.Error("Retrieving of local notifications service failed on StartLocalNotifications", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
wallet, err := b.statusNode.WalletService()
|
||||
if err != nil {
|
||||
b.log.Error("Retrieving of wallet service failed on StartLocalNotifications", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !wallet.IsStarted() {
|
||||
b.log.Error("Can't start local notifications service without wallet service")
|
||||
return nil
|
||||
}
|
||||
|
||||
if !localPN.IsStarted() {
|
||||
err = localPN.Start(b.statusNode.Server())
|
||||
|
||||
if err != nil {
|
||||
b.log.Error("LocalNotifications service start failed on StartLocalNotifications", "error", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
err = localPN.SubscribeWallet(wallet.GetFeed())
|
||||
|
||||
if err != nil {
|
||||
b.log.Error("LocalNotifications service could not subscribe to wallet on StartLocalNotifications", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Logout clears whisper identities.
|
||||
func (b *GethStatusBackend) Logout() error {
|
||||
b.mu.Lock()
|
||||
|
@ -1118,6 +1196,7 @@ func (b *GethStatusBackend) startWallet() error {
|
|||
}
|
||||
|
||||
watchAddresses := b.accountManager.WatchAddresses()
|
||||
|
||||
mainAccountAddress, err := b.accountManager.MainAccountAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
// 0014_add_use_mailservers.up.sql (111B)
|
||||
// 0015_link_previews.down.sql (0)
|
||||
// 0015_link_previews.up.sql (203B)
|
||||
// 0016_local_notifications_preferences.down.sql (43B)
|
||||
// 0016_local_notifications_preferences.up.sql (213B)
|
||||
// doc.go (74B)
|
||||
|
||||
package migrations
|
||||
|
@ -47,7 +49,7 @@ import (
|
|||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %q: %w", name, err)
|
||||
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
@ -55,7 +57,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
|
|||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %q: %w", name, err)
|
||||
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
|
@ -111,7 +113,7 @@ func _0001_appDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0001_app.down.sql", size: 356, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0001_app.down.sql", size: 356, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb5, 0x25, 0xa0, 0xf8, 0x7d, 0x2d, 0xd, 0xcf, 0x18, 0xe4, 0x73, 0xc3, 0x95, 0xf5, 0x24, 0x20, 0xa9, 0xe6, 0x9e, 0x1d, 0x93, 0xe5, 0xc5, 0xad, 0x93, 0x8f, 0x5e, 0x40, 0xb5, 0x30, 0xaa, 0x25}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -131,7 +133,7 @@ func _0001_appUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0001_app.up.sql", size: 2967, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0001_app.up.sql", size: 2967, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf7, 0x3a, 0xa7, 0xf2, 0x8f, 0xfa, 0x82, 0x7c, 0xc5, 0x49, 0xac, 0xac, 0xf, 0xc, 0x77, 0xe2, 0xba, 0xe8, 0x4d, 0xe, 0x6f, 0x5d, 0x2c, 0x2c, 0x18, 0x80, 0xc2, 0x1d, 0xe, 0x25, 0xe, 0x18}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -151,7 +153,7 @@ func _0002_tokensDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0002_tokens.down.sql", size: 19, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0002_tokens.down.sql", size: 19, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0x31, 0x2, 0xcc, 0x2f, 0x38, 0x90, 0xf7, 0x58, 0x37, 0x47, 0xf4, 0x18, 0xf7, 0x72, 0x74, 0x67, 0x14, 0x7e, 0xf3, 0xb1, 0xd6, 0x5f, 0xb0, 0xd5, 0xe7, 0x91, 0xf4, 0x26, 0x77, 0x8e, 0x68}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -171,7 +173,7 @@ func _0002_tokensUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0002_tokens.up.sql", size: 248, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0002_tokens.up.sql", size: 248, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xcc, 0xd6, 0xde, 0xd3, 0x7b, 0xee, 0x92, 0x11, 0x38, 0xa4, 0xeb, 0x84, 0xca, 0xcb, 0x37, 0x75, 0x5, 0x77, 0x7f, 0x14, 0x39, 0xee, 0xa1, 0x8b, 0xd4, 0x5c, 0x6e, 0x55, 0x6, 0x50, 0x16, 0xd4}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -191,7 +193,7 @@ func _0003_settingsDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0003_settings.down.sql", size: 118, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0003_settings.down.sql", size: 118, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe5, 0xa6, 0xf5, 0xc0, 0x60, 0x64, 0x77, 0xe2, 0xe7, 0x3c, 0x9b, 0xb1, 0x52, 0xa9, 0x95, 0x16, 0xf8, 0x60, 0x2f, 0xa5, 0xeb, 0x46, 0xb9, 0xb9, 0x8f, 0x4c, 0xf4, 0xfd, 0xbb, 0xe7, 0xe5, 0xe5}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -211,7 +213,7 @@ func _0003_settingsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0003_settings.up.sql", size: 1311, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0003_settings.up.sql", size: 1311, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xea, 0x35, 0x0, 0xeb, 0xe2, 0x33, 0x68, 0xb9, 0xf4, 0xf6, 0x8e, 0x9e, 0x10, 0xe9, 0x58, 0x68, 0x28, 0xb, 0xcd, 0xec, 0x74, 0x71, 0xa7, 0x9a, 0x5a, 0x77, 0x59, 0xb1, 0x13, 0x1c, 0xa1, 0x5b}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -231,7 +233,7 @@ func _0004_pending_stickersDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0004_pending_stickers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0004_pending_stickers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -251,7 +253,7 @@ func _0004_pending_stickersUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0004_pending_stickers.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1602064144, 0)}
|
||||
info := bindataFileInfo{name: "0004_pending_stickers.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3c, 0xed, 0x25, 0xdf, 0x75, 0x2, 0x6c, 0xf0, 0xa2, 0xa8, 0x37, 0x62, 0x65, 0xad, 0xfd, 0x98, 0xa0, 0x9d, 0x63, 0x94, 0xdf, 0x6b, 0x46, 0xe0, 0x68, 0xec, 0x9c, 0x7f, 0x77, 0xdd, 0xb3, 0x6}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -271,7 +273,7 @@ func _0005_waku_modeDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0005_waku_mode.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0005_waku_mode.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -291,7 +293,7 @@ func _0005_waku_modeUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0005_waku_mode.up.sql", size: 146, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0005_waku_mode.up.sql", size: 146, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa6, 0x91, 0xc, 0xd7, 0x89, 0x61, 0x2e, 0x4c, 0x5a, 0xb6, 0x67, 0xd1, 0xc1, 0x42, 0x24, 0x38, 0xd6, 0x1b, 0x75, 0x41, 0x9c, 0x23, 0xb0, 0xca, 0x5c, 0xf1, 0x5c, 0xd0, 0x13, 0x92, 0x3e, 0xe1}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -311,7 +313,7 @@ func _0006_appearanceUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0006_appearance.up.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0006_appearance.up.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xae, 0x6, 0x25, 0x6c, 0xe4, 0x9d, 0xa7, 0x72, 0xe8, 0xbc, 0xe4, 0x1f, 0x1e, 0x2d, 0x7c, 0xb7, 0xf6, 0xa3, 0xec, 0x3b, 0x4e, 0x93, 0x2e, 0xa4, 0xec, 0x6f, 0xe5, 0x95, 0x94, 0xe8, 0x4, 0xfb}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -331,7 +333,7 @@ func _0007_enable_waku_defaultUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0007_enable_waku_default.up.sql", size: 38, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0007_enable_waku_default.up.sql", size: 38, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd4, 0x42, 0xb6, 0xe5, 0x48, 0x41, 0xeb, 0xc0, 0x7e, 0x3b, 0xe6, 0x8e, 0x96, 0x33, 0x20, 0x92, 0x24, 0x5a, 0x60, 0xfa, 0xa0, 0x3, 0x5e, 0x76, 0x4b, 0x89, 0xaa, 0x37, 0x66, 0xbc, 0x26, 0x11}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -351,7 +353,7 @@ func _0008_add_push_notificationsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 349, mode: os.FileMode(0644), modTime: time.Unix(1602064187, 0)}
|
||||
info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 349, mode: os.FileMode(0644), modTime: time.Unix(1598257438, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5a, 0x0, 0xbf, 0xd0, 0xdd, 0xcd, 0x73, 0xe0, 0x7c, 0x56, 0xef, 0xdc, 0x57, 0x61, 0x94, 0x64, 0x70, 0xb9, 0xfa, 0xa1, 0x2a, 0x36, 0xc, 0x2f, 0xf8, 0x95, 0xa, 0x57, 0x3e, 0x7a, 0xd7, 0x12}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -371,7 +373,7 @@ func _0009_enable_sending_push_notificationsDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0009_enable_sending_push_notifications.down.sql", size: 49, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0009_enable_sending_push_notifications.down.sql", size: 49, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe9, 0xae, 0x1b, 0x41, 0xcb, 0x9c, 0x2c, 0x93, 0xc6, 0x2a, 0x77, 0x3, 0xb9, 0x51, 0xe0, 0x68, 0x68, 0x0, 0xf7, 0x5b, 0xb3, 0x1e, 0x94, 0x44, 0xba, 0x9c, 0xd0, 0x3b, 0x80, 0x21, 0x6f, 0xb5}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -391,7 +393,7 @@ func _0009_enable_sending_push_notificationsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0009_enable_sending_push_notifications.up.sql", size: 49, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0009_enable_sending_push_notifications.up.sql", size: 49, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1b, 0x80, 0xe4, 0x9c, 0xc8, 0xb8, 0xd5, 0xef, 0xce, 0x74, 0x9b, 0x7b, 0xdd, 0xa, 0x99, 0x1e, 0xef, 0x7f, 0xb8, 0x99, 0x84, 0x4, 0x0, 0x6b, 0x1d, 0x2c, 0xa, 0xf8, 0x2c, 0x4f, 0xb5, 0x44}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -411,7 +413,7 @@ func _0010_add_block_mentionsDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0010_add_block_mentions.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0010_add_block_mentions.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6d, 0x9e, 0x27, 0x1e, 0xba, 0x9f, 0xca, 0xae, 0x98, 0x2e, 0x6e, 0xe3, 0xdd, 0xac, 0x73, 0x34, 0x4e, 0x69, 0x92, 0xb5, 0xf6, 0x9, 0xab, 0x50, 0x35, 0xd, 0xee, 0xeb, 0x3e, 0xcc, 0x7e, 0xce}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -431,7 +433,7 @@ func _0010_add_block_mentionsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0010_add_block_mentions.up.sql", size: 89, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0010_add_block_mentions.up.sql", size: 89, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd7, 0x23, 0x85, 0xa2, 0xb5, 0xb6, 0xb4, 0x3f, 0xdc, 0x4e, 0xff, 0xe2, 0x6b, 0x66, 0x68, 0x5e, 0xb2, 0xb4, 0x14, 0xb2, 0x1b, 0x4d, 0xb1, 0xce, 0xf7, 0x6, 0x58, 0xa7, 0xaf, 0x93, 0x3f, 0x25}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -451,7 +453,7 @@ func _0011_allow_webview_permission_requestsDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0011_allow_webview_permission_requests.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0011_allow_webview_permission_requests.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -471,7 +473,7 @@ func _0011_allow_webview_permission_requestsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0011_allow_webview_permission_requests.up.sql", size: 88, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0011_allow_webview_permission_requests.up.sql", size: 88, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x43, 0x5f, 0x22, 0x4c, 0x98, 0x1d, 0xc6, 0xf4, 0x89, 0xaf, 0xf4, 0x44, 0xba, 0xf8, 0x28, 0xa7, 0xb5, 0xb9, 0xf0, 0xf2, 0xcb, 0x5, 0x59, 0x7a, 0xc, 0xdf, 0xd3, 0x38, 0xa4, 0xb8, 0x98, 0xc2}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -491,7 +493,7 @@ func _0012_pending_transactionsDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0012_pending_transactions.down.sql", size: 33, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0012_pending_transactions.down.sql", size: 33, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x7e, 0x41, 0xfe, 0x5c, 0xd8, 0xc3, 0x29, 0xfd, 0x31, 0x78, 0x99, 0x7a, 0xeb, 0x17, 0x62, 0x88, 0x41, 0xb3, 0xe7, 0xb5, 0x5, 0x0, 0x90, 0xa1, 0x7, 0x1a, 0x23, 0x88, 0x81, 0xba, 0x56, 0x9d}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -511,7 +513,7 @@ func _0012_pending_transactionsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0012_pending_transactions.up.sql", size: 321, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0012_pending_transactions.up.sql", size: 321, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd, 0x17, 0xff, 0xd7, 0xa7, 0x49, 0x1e, 0x7b, 0x34, 0x63, 0x7c, 0x53, 0xaa, 0x6b, 0x2d, 0xc8, 0xe0, 0x82, 0x21, 0x90, 0x3a, 0x94, 0xf1, 0xa6, 0xe4, 0x70, 0xe5, 0x85, 0x1a, 0x48, 0x25, 0xb}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -531,7 +533,7 @@ func _0013_favouritesDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0013_favourites.down.sql", size: 23, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0013_favourites.down.sql", size: 23, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x32, 0xf8, 0x55, 0x13, 0x4f, 0x4a, 0x19, 0x83, 0x9c, 0xda, 0x34, 0xb8, 0x3, 0x54, 0x82, 0x1e, 0x99, 0x36, 0x6b, 0x42, 0x3, 0xf6, 0x43, 0xde, 0xe6, 0x32, 0xb6, 0xdf, 0xe2, 0x59, 0x8c, 0x84}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -551,7 +553,7 @@ func _0013_favouritesUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0013_favourites.up.sql", size: 132, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "0013_favourites.up.sql", size: 132, mode: os.FileMode(0644), modTime: time.Unix(1600853026, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xbe, 0x1, 0x27, 0x38, 0x76, 0xf5, 0xcb, 0x61, 0xda, 0x5b, 0xce, 0xd9, 0x8b, 0x18, 0x77, 0x61, 0x84, 0xe7, 0x22, 0xe2, 0x13, 0x99, 0xab, 0x32, 0xbc, 0xbe, 0xed, 0x1f, 0x2f, 0xb0, 0xe4, 0x8d}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -571,7 +573,7 @@ func _0014_add_use_mailserversDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0014_add_use_mailservers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1603194874, 0)}
|
||||
info := bindataFileInfo{name: "0014_add_use_mailservers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1603868977, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -591,7 +593,7 @@ func _0014_add_use_mailserversUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0014_add_use_mailservers.up.sql", size: 111, mode: os.FileMode(0644), modTime: time.Unix(1603194874, 0)}
|
||||
info := bindataFileInfo{name: "0014_add_use_mailservers.up.sql", size: 111, mode: os.FileMode(0644), modTime: time.Unix(1603868977, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc9, 0xba, 0x65, 0xbf, 0x1b, 0xc9, 0x6d, 0x45, 0xf2, 0xf5, 0x30, 0x7c, 0xc1, 0xde, 0xb8, 0xe3, 0x3f, 0xa9, 0x2f, 0x9f, 0xea, 0x1, 0x29, 0x29, 0x65, 0xe7, 0x38, 0xab, 0xa4, 0x62, 0xf, 0xd0}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -611,7 +613,7 @@ func _0015_link_previewsDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0015_link_previews.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1603816327, 0)}
|
||||
info := bindataFileInfo{name: "0015_link_previews.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1603869530, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -631,11 +633,51 @@ func _0015_link_previewsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0015_link_previews.up.sql", size: 203, mode: os.FileMode(0644), modTime: time.Unix(1603816369, 0)}
|
||||
info := bindataFileInfo{name: "0015_link_previews.up.sql", size: 203, mode: os.FileMode(0644), modTime: time.Unix(1603869530, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb1, 0xf7, 0x38, 0x25, 0xa6, 0xfc, 0x6b, 0x9, 0xe4, 0xd9, 0xbf, 0x58, 0x7b, 0x80, 0xd8, 0x48, 0x63, 0xde, 0xa5, 0x5e, 0x30, 0xa3, 0xeb, 0x68, 0x8e, 0x6a, 0x9f, 0xfd, 0xf4, 0x46, 0x41, 0x34}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __0016_local_notifications_preferencesDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\xc8\xc9\x4f\x4e\xcc\x89\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x2b\x8e\x2f\x28\x4a\x4d\x4b\x2d\x4a\xcd\x4b\x4e\x2d\xb6\x06\x04\x00\x00\xff\xff\xf0\xdb\xee\xaa\x2b\x00\x00\x00")
|
||||
|
||||
func _0016_local_notifications_preferencesDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__0016_local_notifications_preferencesDownSql,
|
||||
"0016_local_notifications_preferences.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _0016_local_notifications_preferencesDownSql() (*asset, error) {
|
||||
bytes, err := _0016_local_notifications_preferencesDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0016_local_notifications_preferences.down.sql", size: 43, mode: os.FileMode(0644), modTime: time.Unix(1603869534, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe0, 0x50, 0xc7, 0xdd, 0x53, 0x9c, 0x5d, 0x1e, 0xb5, 0x71, 0x25, 0x50, 0x58, 0xcf, 0x6d, 0xbe, 0x5a, 0x8, 0x12, 0xc9, 0x13, 0xd, 0x9a, 0x3d, 0x4b, 0x7a, 0x2f, 0x1b, 0xe5, 0x23, 0x52, 0x78}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __0016_local_notifications_preferencesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x8d\xc1\x6a\x84\x30\x14\x45\xf7\x7e\xc5\x5d\x2a\xf8\x07\x5d\x45\x4d\x51\x9a\x9a\x12\x62\xc5\x95\xa4\xf1\x09\x81\x10\x4b\x22\x7e\xff\x30\x3a\xc3\x30\xdb\x7b\x2e\xe7\xd4\x8a\x33\xcd\xa1\x59\x25\x38\xfc\x66\x8d\x9f\xc3\xb6\xbb\xd5\x59\xb3\xbb\x2d\xa4\xf9\x3f\xd2\x4a\x91\x82\xa5\x84\x3c\x03\x80\x44\xf1\x70\x96\xf0\xcb\x54\xdd\x32\x85\x5e\x6a\xf4\x83\x10\xe5\x49\xe9\xa0\xb0\x3f\xd9\x35\xb9\x85\xc2\x5d\x49\xf1\x7d\xa7\x60\xfe\x3c\x2d\xa8\xa4\x14\x9c\xf5\x68\xf8\x27\x1b\x84\xc6\x6a\x7c\xa2\xeb\xf2\xa3\xba\x6f\xa6\x26\x7c\xf1\x29\x7f\x74\xcb\xb3\x50\xbe\xa4\x45\x56\x60\xec\x74\x2b\x07\x0d\x25\xc7\xae\xf9\xb8\x05\x00\x00\xff\xff\xd8\x7b\x68\x58\xd5\x00\x00\x00")
|
||||
|
||||
func _0016_local_notifications_preferencesUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__0016_local_notifications_preferencesUpSql,
|
||||
"0016_local_notifications_preferences.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _0016_local_notifications_preferencesUpSql() (*asset, error) {
|
||||
bytes, err := _0016_local_notifications_preferencesUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0016_local_notifications_preferences.up.sql", size: 213, mode: os.FileMode(0644), modTime: time.Unix(1603869534, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x8, 0xde, 0x2, 0xbe, 0xf2, 0x84, 0x7, 0x3e, 0x44, 0xe5, 0xc6, 0xc9, 0xb, 0x70, 0x9f, 0x68, 0x2c, 0x6, 0x99, 0xe, 0xdd, 0xbb, 0x20, 0x0, 0x6a, 0x48, 0x15, 0xee, 0xb, 0x76, 0x9f, 0xd8}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
|
@ -651,7 +693,7 @@ func docGo() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1601293035, 0)}
|
||||
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1593853962, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x7c, 0x28, 0xcd, 0x47, 0xf2, 0xfa, 0x7c, 0x51, 0x2d, 0xd8, 0x38, 0xb, 0xb0, 0x34, 0x9d, 0x4c, 0x62, 0xa, 0x9e, 0x28, 0xc3, 0x31, 0x23, 0xd9, 0xbb, 0x89, 0x9f, 0xa0, 0x89, 0x1f, 0xe8}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -747,38 +789,66 @@ func AssetNames() []string {
|
|||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"0001_app.down.sql": _0001_appDownSql,
|
||||
"0001_app.up.sql": _0001_appUpSql,
|
||||
"0002_tokens.down.sql": _0002_tokensDownSql,
|
||||
"0002_tokens.up.sql": _0002_tokensUpSql,
|
||||
"0003_settings.down.sql": _0003_settingsDownSql,
|
||||
"0003_settings.up.sql": _0003_settingsUpSql,
|
||||
"0004_pending_stickers.down.sql": _0004_pending_stickersDownSql,
|
||||
"0004_pending_stickers.up.sql": _0004_pending_stickersUpSql,
|
||||
"0005_waku_mode.down.sql": _0005_waku_modeDownSql,
|
||||
"0005_waku_mode.up.sql": _0005_waku_modeUpSql,
|
||||
"0006_appearance.up.sql": _0006_appearanceUpSql,
|
||||
"0007_enable_waku_default.up.sql": _0007_enable_waku_defaultUpSql,
|
||||
"0008_add_push_notifications.up.sql": _0008_add_push_notificationsUpSql,
|
||||
"0009_enable_sending_push_notifications.down.sql": _0009_enable_sending_push_notificationsDownSql,
|
||||
"0009_enable_sending_push_notifications.up.sql": _0009_enable_sending_push_notificationsUpSql,
|
||||
"0010_add_block_mentions.down.sql": _0010_add_block_mentionsDownSql,
|
||||
"0010_add_block_mentions.up.sql": _0010_add_block_mentionsUpSql,
|
||||
"0011_allow_webview_permission_requests.down.sql": _0011_allow_webview_permission_requestsDownSql,
|
||||
"0011_allow_webview_permission_requests.up.sql": _0011_allow_webview_permission_requestsUpSql,
|
||||
"0012_pending_transactions.down.sql": _0012_pending_transactionsDownSql,
|
||||
"0012_pending_transactions.up.sql": _0012_pending_transactionsUpSql,
|
||||
"0013_favourites.down.sql": _0013_favouritesDownSql,
|
||||
"0013_favourites.up.sql": _0013_favouritesUpSql,
|
||||
"0014_add_use_mailservers.down.sql": _0014_add_use_mailserversDownSql,
|
||||
"0014_add_use_mailservers.up.sql": _0014_add_use_mailserversUpSql,
|
||||
"0015_link_previews.down.sql": _0015_link_previewsDownSql,
|
||||
"0015_link_previews.up.sql": _0015_link_previewsUpSql,
|
||||
"doc.go": docGo,
|
||||
}
|
||||
"0001_app.down.sql": _0001_appDownSql,
|
||||
|
||||
// AssetDebug is true if the assets were built with the debug flag enabled.
|
||||
const AssetDebug = false
|
||||
"0001_app.up.sql": _0001_appUpSql,
|
||||
|
||||
"0002_tokens.down.sql": _0002_tokensDownSql,
|
||||
|
||||
"0002_tokens.up.sql": _0002_tokensUpSql,
|
||||
|
||||
"0003_settings.down.sql": _0003_settingsDownSql,
|
||||
|
||||
"0003_settings.up.sql": _0003_settingsUpSql,
|
||||
|
||||
"0004_pending_stickers.down.sql": _0004_pending_stickersDownSql,
|
||||
|
||||
"0004_pending_stickers.up.sql": _0004_pending_stickersUpSql,
|
||||
|
||||
"0005_waku_mode.down.sql": _0005_waku_modeDownSql,
|
||||
|
||||
"0005_waku_mode.up.sql": _0005_waku_modeUpSql,
|
||||
|
||||
"0006_appearance.up.sql": _0006_appearanceUpSql,
|
||||
|
||||
"0007_enable_waku_default.up.sql": _0007_enable_waku_defaultUpSql,
|
||||
|
||||
"0008_add_push_notifications.up.sql": _0008_add_push_notificationsUpSql,
|
||||
|
||||
"0009_enable_sending_push_notifications.down.sql": _0009_enable_sending_push_notificationsDownSql,
|
||||
|
||||
"0009_enable_sending_push_notifications.up.sql": _0009_enable_sending_push_notificationsUpSql,
|
||||
|
||||
"0010_add_block_mentions.down.sql": _0010_add_block_mentionsDownSql,
|
||||
|
||||
"0010_add_block_mentions.up.sql": _0010_add_block_mentionsUpSql,
|
||||
|
||||
"0011_allow_webview_permission_requests.down.sql": _0011_allow_webview_permission_requestsDownSql,
|
||||
|
||||
"0011_allow_webview_permission_requests.up.sql": _0011_allow_webview_permission_requestsUpSql,
|
||||
|
||||
"0012_pending_transactions.down.sql": _0012_pending_transactionsDownSql,
|
||||
|
||||
"0012_pending_transactions.up.sql": _0012_pending_transactionsUpSql,
|
||||
|
||||
"0013_favourites.down.sql": _0013_favouritesDownSql,
|
||||
|
||||
"0013_favourites.up.sql": _0013_favouritesUpSql,
|
||||
|
||||
"0014_add_use_mailservers.down.sql": _0014_add_use_mailserversDownSql,
|
||||
|
||||
"0014_add_use_mailservers.up.sql": _0014_add_use_mailserversUpSql,
|
||||
|
||||
"0015_link_previews.down.sql": _0015_link_previewsDownSql,
|
||||
|
||||
"0015_link_previews.up.sql": _0015_link_previewsUpSql,
|
||||
|
||||
"0016_local_notifications_preferences.down.sql": _0016_local_notifications_preferencesDownSql,
|
||||
|
||||
"0016_local_notifications_preferences.up.sql": _0016_local_notifications_preferencesUpSql,
|
||||
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
|
@ -848,7 +918,9 @@ var _bintree = &bintree{nil, map[string]*bintree{
|
|||
"0014_add_use_mailservers.up.sql": &bintree{_0014_add_use_mailserversUpSql, map[string]*bintree{}},
|
||||
"0015_link_previews.down.sql": &bintree{_0015_link_previewsDownSql, map[string]*bintree{}},
|
||||
"0015_link_previews.up.sql": &bintree{_0015_link_previewsUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
"0016_local_notifications_preferences.down.sql": &bintree{_0016_local_notifications_preferencesDownSql, map[string]*bintree{}},
|
||||
"0016_local_notifications_preferences.up.sql": &bintree{_0016_local_notifications_preferencesUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE local_notifications_preferences;
|
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE local_notifications_preferences (
|
||||
service VARCHAR,
|
||||
event VARCHAR,
|
||||
identifier VARCHAR,
|
||||
enabled BOOLEAN DEFAULT false,
|
||||
PRIMARY KEY(service,event,identifier)
|
||||
) WITHOUT ROWID;
|
|
@ -636,6 +636,18 @@ func StartWallet() string {
|
|||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
// StartLocalNotifications
|
||||
func StartLocalNotifications() string {
|
||||
err := statusBackend.StartLocalNotifications()
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
// StopLocalNotifications
|
||||
func StopLocalNotifications() string {
|
||||
err := statusBackend.StopLocalNotifications()
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
// SetMobileSignalHandler setup geth callback to notify about new signal
|
||||
// used for gomobile builds
|
||||
func SetMobileSignalHandler(handler SignalHandler) {
|
||||
|
|
|
@ -436,6 +436,22 @@ func (db *Database) GetAccounts() ([]Account, error) {
|
|||
return accounts, nil
|
||||
}
|
||||
|
||||
func (db *Database) GetAccountByAddress(address types.Address) (rst *Account, err error) {
|
||||
row := db.db.QueryRow("SELECT address, wallet, chat, type, storage, pubkey, path, name, color FROM accounts WHERE address = ? COLLATE NOCASE", address)
|
||||
|
||||
acc := &Account{}
|
||||
pubkey := []byte{}
|
||||
err = row.Scan(
|
||||
&acc.Address, &acc.Wallet, &acc.Chat, &acc.Type, &acc.Storage,
|
||||
&pubkey, &acc.Path, &acc.Name, &acc.Color)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acc.PublicKey = pubkey
|
||||
return acc, nil
|
||||
}
|
||||
|
||||
func (db *Database) SaveAccounts(accounts []Account) (err error) {
|
||||
var (
|
||||
tx *sql.Tx
|
||||
|
|
|
@ -206,6 +206,24 @@ func TestGetAccounts(t *testing.T) {
|
|||
require.Equal(t, accounts, rst)
|
||||
}
|
||||
|
||||
func TestGetAccountByAddress(t *testing.T) {
|
||||
db, stop := setupTestDB(t)
|
||||
defer stop()
|
||||
address := types.Address{0x01}
|
||||
account := Account{Address: address, Chat: true, Wallet: true}
|
||||
dilute := []Account{
|
||||
{Address: types.Address{0x02}, PublicKey: types.HexBytes{0x01, 0x02}},
|
||||
{Address: types.Address{0x03}, PublicKey: types.HexBytes{0x02, 0x03}},
|
||||
}
|
||||
|
||||
accounts := append(dilute, account)
|
||||
|
||||
require.NoError(t, db.SaveAccounts(accounts))
|
||||
rst, err := db.GetAccountByAddress(address)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &account, rst)
|
||||
}
|
||||
|
||||
func TestAddressExists(t *testing.T) {
|
||||
db, stop := setupTestDB(t)
|
||||
defer stop()
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/status-im/status-go/peers"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/browsers"
|
||||
localnotifications "github.com/status-im/status-go/services/local-notifications"
|
||||
"github.com/status-im/status-go/services/peer"
|
||||
"github.com/status-im/status-go/services/permissions"
|
||||
"github.com/status-im/status-go/services/shhext"
|
||||
|
@ -671,6 +672,17 @@ func (n *StatusNode) WalletService() (s *wallet.Service, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// LocalNotificationsService returns localnotifications.Service instance if it was started.
|
||||
func (n *StatusNode) LocalNotificationsService() (s *localnotifications.Service, err error) {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
err = n.gethService(&s)
|
||||
if err == node.ErrServiceUnknown {
|
||||
err = ErrServiceUnknown
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BrowsersService returns browsers.Service instance if it was started.
|
||||
func (n *StatusNode) BrowsersService() (s *browsers.Service, err error) {
|
||||
n.mu.RLock()
|
||||
|
|
|
@ -436,6 +436,9 @@ type NodeConfig struct {
|
|||
// WalletConfig extra configuration for wallet.Service.
|
||||
WalletConfig WalletConfig
|
||||
|
||||
// WalleLocalNotificationsConfig extra configuration for localnotifications.Service.
|
||||
LocalNotificationsConfig LocalNotificationsConfig
|
||||
|
||||
// BrowsersConfig extra configuration for browsers.Service.
|
||||
BrowsersConfig BrowsersConfig
|
||||
|
||||
|
@ -469,6 +472,11 @@ type WalletConfig struct {
|
|||
Enabled bool
|
||||
}
|
||||
|
||||
// LocalNotificationsConfig extra configuration for localnotifications.Service.
|
||||
type LocalNotificationsConfig struct {
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// BrowsersConfig extra configuration for browsers.Service.
|
||||
type BrowsersConfig struct {
|
||||
Enabled bool
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package localnotifications
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
func NewAPI(s *Service) *API {
|
||||
return &API{s}
|
||||
}
|
||||
|
||||
type API struct {
|
||||
s *Service
|
||||
}
|
||||
|
||||
func (api *API) NotificationPreferences(ctx context.Context) ([]NotificationPreference, error) {
|
||||
return api.s.db.GetPreferences()
|
||||
}
|
||||
|
||||
func (api *API) SwitchWalletNotifications(ctx context.Context, preference bool) error {
|
||||
log.Debug("Switch Transaction Notification")
|
||||
err := api.s.db.ChangeWalletPreference(preference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if preference {
|
||||
api.s.StartWalletWatcher()
|
||||
} else {
|
||||
api.s.StopWalletWatcher()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
package localnotifications
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"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/services/wallet"
|
||||
"github.com/status-im/status-go/signal"
|
||||
)
|
||||
|
||||
type PushCategory string
|
||||
|
||||
type transactionState string
|
||||
|
||||
const walletDeeplinkPrefix = "status-im://wallet/"
|
||||
|
||||
const (
|
||||
failed transactionState = "failed"
|
||||
inbound transactionState = "inbound"
|
||||
outbound transactionState = "outbound"
|
||||
)
|
||||
|
||||
type notificationBody struct {
|
||||
State transactionState `json:"state"`
|
||||
From common.Address `json:"from"`
|
||||
To common.Address `json:"to"`
|
||||
FromAccount *accounts.Account `json:"fromAccount,omitempty"`
|
||||
ToAccount *accounts.Account `json:"toAccount,omitempty"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
ERC20 bool `json:"erc20"`
|
||||
Contract common.Address `json:"contract"`
|
||||
Network uint64 `json:"network"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
ID common.Hash `json:"id"`
|
||||
Platform float32 `json:"platform,omitempty"`
|
||||
Body notificationBody `json:"body"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// TransactionEvent - structure used to pass messages from wallet to bus
|
||||
type TransactionEvent struct {
|
||||
Type string `json:"type"`
|
||||
BlockNumber *big.Int `json:"block-number"`
|
||||
Accounts []common.Address `json:"accounts"`
|
||||
NewTransactionsPerAccount map[common.Address]int `json:"new-transactions"`
|
||||
ERC20 bool `json:"erc20"`
|
||||
}
|
||||
|
||||
// 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
|
||||
transmitter *transmitter
|
||||
walletTransmitter *transmitter
|
||||
db *Database
|
||||
walletDB *wallet.Database
|
||||
accountsDB *accounts.Database
|
||||
}
|
||||
|
||||
func NewService(appDB *sql.DB, network uint64) *Service {
|
||||
db := NewDB(appDB, network)
|
||||
walletDB := wallet.NewDB(appDB, network)
|
||||
accountsDB := accounts.NewDB(appDB)
|
||||
trans := &transmitter{}
|
||||
walletTrans := &transmitter{}
|
||||
|
||||
return &Service{
|
||||
db: db,
|
||||
walletDB: walletDB,
|
||||
accountsDB: accountsDB,
|
||||
transmitter: trans,
|
||||
walletTransmitter: walletTrans,
|
||||
}
|
||||
}
|
||||
|
||||
func pushMessage(notification *Notification) {
|
||||
log.Info("Pushing a new push notification", "info", notification)
|
||||
signal.SendLocalNotifications(notification)
|
||||
}
|
||||
|
||||
func (s *Service) buildTransactionNotification(rawTransfer wallet.Transfer) *Notification {
|
||||
log.Info("Handled a new transfer in buildTransactionNotification", "info", rawTransfer)
|
||||
|
||||
var deeplink string
|
||||
var state transactionState
|
||||
transfer := wallet.CastToTransferView(rawTransfer)
|
||||
|
||||
switch {
|
||||
case transfer.TxStatus == hexutil.Uint64(0):
|
||||
state = failed
|
||||
case transfer.Address == transfer.To:
|
||||
state = inbound
|
||||
default:
|
||||
state = outbound
|
||||
}
|
||||
|
||||
from, err := s.accountsDB.GetAccountByAddress(types.Address(transfer.From))
|
||||
|
||||
if err != nil {
|
||||
log.Debug("Could not select From account by address", "error", err)
|
||||
}
|
||||
|
||||
to, err := s.accountsDB.GetAccountByAddress(types.Address(transfer.To))
|
||||
|
||||
if err != nil {
|
||||
log.Debug("Could not select To account by address", "error", err)
|
||||
}
|
||||
|
||||
if from != nil {
|
||||
deeplink = walletDeeplinkPrefix + from.Address.String()
|
||||
} else if to != nil {
|
||||
deeplink = walletDeeplinkPrefix + to.Address.String()
|
||||
}
|
||||
|
||||
body := notificationBody{
|
||||
State: state,
|
||||
From: transfer.From,
|
||||
To: transfer.Address,
|
||||
FromAccount: from,
|
||||
ToAccount: to,
|
||||
Value: transfer.Value,
|
||||
ERC20: string(transfer.Type) == "erc20",
|
||||
Contract: transfer.Contract,
|
||||
Network: transfer.NetworkID,
|
||||
}
|
||||
|
||||
return &Notification{
|
||||
ID: transfer.ID,
|
||||
Body: body,
|
||||
Deeplink: deeplink,
|
||||
Category: "transaction",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) transactionsHandler(payload TransactionEvent) {
|
||||
log.Info("Handled a new transaction", "info", payload)
|
||||
|
||||
limit := 20
|
||||
if payload.BlockNumber != nil {
|
||||
for _, address := range payload.Accounts {
|
||||
log.Info("Handled transfer for address", "info", address)
|
||||
transfers, err := s.walletDB.GetTransfersByAddressAndBlock(address, payload.BlockNumber, int64(limit))
|
||||
if err != nil {
|
||||
log.Error("Could not fetch transfers", "error", err)
|
||||
}
|
||||
|
||||
for _, transaction := range transfers {
|
||||
n := s.buildTransactionNotification(transaction)
|
||||
pushMessage(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SubscribeWallet - Subscribes to wallet signals
|
||||
func (s *Service) SubscribeWallet(publisher *event.Feed) error {
|
||||
s.walletTransmitter.publisher = publisher
|
||||
|
||||
preference, err := s.db.GetWalletPreference()
|
||||
|
||||
if err != nil {
|
||||
log.Error("Failed to get wallet preference", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if preference.Enabled {
|
||||
s.StartWalletWatcher()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartWalletWatcher - Forward wallet events to notifications
|
||||
func (s *Service) StartWalletWatcher() {
|
||||
if s.walletTransmitter.quit != nil {
|
||||
// already running, nothing to do
|
||||
return
|
||||
}
|
||||
|
||||
if s.walletTransmitter.publisher == nil {
|
||||
log.Error("wallet publisher was not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
s.walletTransmitter.quit = make(chan struct{})
|
||||
events := make(chan wallet.Event, 10)
|
||||
sub := s.walletTransmitter.publisher.Subscribe(events)
|
||||
|
||||
s.walletTransmitter.wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer s.walletTransmitter.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-s.walletTransmitter.quit:
|
||||
sub.Unsubscribe()
|
||||
return
|
||||
case err := <-sub.Err():
|
||||
// technically event.Feed cannot send an error to subscription.Err channel.
|
||||
// the only time we will get an event is when that channel is closed.
|
||||
if err != nil {
|
||||
log.Error("wallet signals transmitter failed with", "error", err)
|
||||
}
|
||||
return
|
||||
case event := <-events:
|
||||
if event.Type == wallet.EventNewBlock {
|
||||
s.transmitter.publisher.Send(TransactionEvent{
|
||||
Type: string(event.Type),
|
||||
BlockNumber: event.BlockNumber,
|
||||
Accounts: event.Accounts,
|
||||
NewTransactionsPerAccount: event.NewTransactionsPerAccount,
|
||||
ERC20: event.ERC20,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// StopWalletWatcher - stops watching for new wallet events
|
||||
func (s *Service) StopWalletWatcher() {
|
||||
if s.walletTransmitter.quit != nil {
|
||||
close(s.walletTransmitter.quit)
|
||||
s.walletTransmitter.wg.Wait()
|
||||
s.walletTransmitter.quit = nil
|
||||
}
|
||||
}
|
||||
|
||||
// IsWatchingWallet - check if local-notifications are subscribed to wallet updates
|
||||
func (s *Service) IsWatchingWallet() bool {
|
||||
return s.walletTransmitter.quit != nil
|
||||
}
|
||||
|
||||
// Start Worker which processes all incoming messages
|
||||
func (s *Service) Start(_ *p2p.Server) 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 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),
|
||||
Public: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Protocols returns list of p2p protocols.
|
||||
func (s *Service) Protocols() []p2p.Protocol {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) IsStarted() bool {
|
||||
return s.started
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package localnotifications
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet"
|
||||
"github.com/status-im/status-go/signal"
|
||||
"github.com/status-im/status-go/t/utils"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
func createWalletDb(t *testing.T, db *sql.DB) (*wallet.Database, func()) {
|
||||
return wallet.NewDB(db, 1777), func() {
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceStartStop(t *testing.T) {
|
||||
db, stop := setupAppTestDb(t)
|
||||
defer stop()
|
||||
|
||||
s := NewService(db, 1777)
|
||||
require.NoError(t, s.Start(nil))
|
||||
require.Equal(t, true, s.IsStarted())
|
||||
|
||||
require.NoError(t, s.Stop())
|
||||
require.Equal(t, false, s.IsStarted())
|
||||
}
|
||||
|
||||
func TestWalletSubscription(t *testing.T) {
|
||||
db, stop := setupAppTestDb(t)
|
||||
defer stop()
|
||||
|
||||
feed := &event.Feed{}
|
||||
s := NewService(db, 1777)
|
||||
require.NoError(t, s.Start(nil))
|
||||
require.Equal(t, true, s.IsStarted())
|
||||
|
||||
require.NoError(t, s.SubscribeWallet(feed))
|
||||
require.Equal(t, false, s.IsWatchingWallet())
|
||||
|
||||
s.StartWalletWatcher()
|
||||
require.Equal(t, true, s.IsWatchingWallet())
|
||||
|
||||
s.StopWalletWatcher()
|
||||
require.Equal(t, false, s.IsWatchingWallet())
|
||||
|
||||
require.NoError(t, s.Stop())
|
||||
require.Equal(t, false, s.IsStarted())
|
||||
}
|
||||
|
||||
func TestTransactionNotification(t *testing.T) {
|
||||
db, stop := setupAppTestDb(t)
|
||||
defer stop()
|
||||
|
||||
walletDb, stop := createWalletDb(t, db)
|
||||
defer stop()
|
||||
|
||||
s := NewService(db, 1777)
|
||||
require.NoError(t, s.Start(nil))
|
||||
require.Equal(t, true, s.IsStarted())
|
||||
|
||||
var signalEvent []byte
|
||||
|
||||
signalHandler := signal.MobileSignalHandler(func(s []byte) {
|
||||
signalEvent = s
|
||||
})
|
||||
|
||||
signal.SetMobileSignalHandler(signalHandler)
|
||||
|
||||
feed := &event.Feed{}
|
||||
require.NoError(t, s.SubscribeWallet(feed))
|
||||
|
||||
s.StartWalletWatcher()
|
||||
|
||||
header := &wallet.DBHeader{
|
||||
Number: big.NewInt(1),
|
||||
Hash: common.Hash{1},
|
||||
Address: common.Address{1},
|
||||
}
|
||||
tx := types.NewTransaction(1, common.Address{1}, nil, 10, big.NewInt(10), nil)
|
||||
receipt := types.NewReceipt(nil, false, 100)
|
||||
receipt.Logs = []*types.Log{}
|
||||
transfers := []wallet.Transfer{
|
||||
{
|
||||
ID: common.Hash{1},
|
||||
Type: wallet.TransferType("eth"),
|
||||
BlockHash: header.Hash,
|
||||
BlockNumber: header.Number,
|
||||
Transaction: tx,
|
||||
Receipt: receipt,
|
||||
Address: header.Address,
|
||||
},
|
||||
}
|
||||
require.NoError(t, walletDb.ProcessBlocks(header.Address, big.NewInt(1), big.NewInt(1), []*wallet.DBHeader{header}))
|
||||
require.NoError(t, walletDb.ProcessTranfers(transfers, []*wallet.DBHeader{}))
|
||||
|
||||
feed.Send(wallet.Event{
|
||||
Type: wallet.EventNewBlock,
|
||||
BlockNumber: header.Number,
|
||||
Accounts: []common.Address{header.Address},
|
||||
})
|
||||
|
||||
require.NoError(t, utils.Eventually(func() error {
|
||||
if signalEvent == nil {
|
||||
return fmt.Errorf("Signal was not handled")
|
||||
}
|
||||
notification := struct {
|
||||
Type string
|
||||
Event Notification
|
||||
}{}
|
||||
|
||||
require.NoError(t, json.Unmarshal(signalEvent, ¬ification))
|
||||
|
||||
if notification.Type != "local-notifications" {
|
||||
return fmt.Errorf("Wrong signal was sent")
|
||||
}
|
||||
if notification.Event.Body.To != header.Address {
|
||||
return fmt.Errorf("Transaction to address is wrong")
|
||||
}
|
||||
return nil
|
||||
}, 2*time.Second, 100*time.Millisecond))
|
||||
|
||||
require.NoError(t, s.Stop())
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package localnotifications
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
db *sql.DB
|
||||
network uint64
|
||||
}
|
||||
|
||||
type NotificationPreference struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Service string `json:"service"`
|
||||
Event string `json:"event,omitempty"`
|
||||
Identifier string `json:"identifier,omitempty"`
|
||||
}
|
||||
|
||||
func NewDB(db *sql.DB, network uint64) *Database {
|
||||
return &Database{db: db, network: network}
|
||||
}
|
||||
|
||||
func (db *Database) GetPreferences() (rst []NotificationPreference, err error) {
|
||||
rows, err := db.db.Query("SELECT service, event, identifier, enabled FROM local_notifications_preferences")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
pref := NotificationPreference{}
|
||||
err = rows.Scan(&pref.Service, &pref.Event, &pref.Identifier, &pref.Enabled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rst = append(rst, pref)
|
||||
}
|
||||
return rst, nil
|
||||
}
|
||||
|
||||
func (db *Database) GetWalletPreference() (rst NotificationPreference, err error) {
|
||||
pref := db.db.QueryRow("SELECT service, event, identifier, enabled FROM local_notifications_preferences WHERE service = 'wallet' AND event = 'transaction' AND identifier = 'all'")
|
||||
|
||||
err = pref.Scan(&rst.Service, &rst.Event, &rst.Identifier, &rst.Enabled)
|
||||
return
|
||||
}
|
||||
|
||||
func (db *Database) ChangeWalletPreference(preference bool) error {
|
||||
_, err := db.db.Exec("INSERT OR REPLACE INTO local_notifications_preferences (service, event, identifier, enabled) VALUES ('wallet', 'transaction', 'all', ?)", preference)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package localnotifications
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/status-im/status-go/appdatabase"
|
||||
)
|
||||
|
||||
func setupAppTestDb(t *testing.T) (*sql.DB, func()) {
|
||||
tmpfile, err := ioutil.TempFile("", "local-notifications-tests-")
|
||||
require.NoError(t, err)
|
||||
db, err := appdatabase.InitializeDB(tmpfile.Name(), "local-notifications-tests")
|
||||
require.NoError(t, err)
|
||||
return db, func() {
|
||||
require.NoError(t, os.Remove(tmpfile.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
func setupTestDB(t *testing.T, db *sql.DB) (*Database, func()) {
|
||||
return NewDB(db, 1777), func() {
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalletPreferences(t *testing.T) {
|
||||
appDB, appStop := setupAppTestDb(t)
|
||||
defer appStop()
|
||||
|
||||
db, stop := setupTestDB(t, appDB)
|
||||
defer stop()
|
||||
|
||||
enabled := true
|
||||
require.NoError(t, db.ChangeWalletPreference(enabled))
|
||||
rst, err := db.GetWalletPreference()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, enabled, rst.Enabled)
|
||||
|
||||
enabled = false
|
||||
require.NoError(t, db.ChangeWalletPreference(enabled))
|
||||
rst, err = db.GetWalletPreference()
|
||||
require.Equal(t, enabled, rst.Enabled)
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPreferences(t *testing.T) {
|
||||
appDB, appStop := setupAppTestDb(t)
|
||||
defer appStop()
|
||||
|
||||
db, stop := setupTestDB(t, appDB)
|
||||
defer stop()
|
||||
|
||||
enabled := true
|
||||
|
||||
require.NoError(t, db.ChangeWalletPreference(enabled))
|
||||
rst, err := db.GetPreferences()
|
||||
|
||||
require.Equal(t, 1, len(rst))
|
||||
require.Equal(t, enabled, rst[0].Enabled)
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
|
@ -250,6 +250,23 @@ func (db *Database) GetTransfersByAddress(address common.Address, toBlock *big.I
|
|||
return query.Scan(rows)
|
||||
}
|
||||
|
||||
// GetTransfersByAddressAndBlock loads transfers for a given address and block.
|
||||
func (db *Database) GetTransfersByAddressAndBlock(address common.Address, block *big.Int, limit int64) (rst []Transfer, err error) {
|
||||
query := newTransfersQuery().
|
||||
FilterNetwork(db.network).
|
||||
FilterAddress(address).
|
||||
FilterBlockNumber(block).
|
||||
FilterLoaded(1).
|
||||
Limit(limit)
|
||||
|
||||
rows, err := db.db.Query(query.String(), query.Args()...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
return query.Scan(rows)
|
||||
}
|
||||
|
||||
// GetBlocksByAddress loads blocks for a given address.
|
||||
func (db *Database) GetBlocksByAddress(address common.Address, limit int) (rst []*big.Int, err error) {
|
||||
query := `SELECT blk_number FROM blocks
|
||||
|
|
|
@ -122,11 +122,11 @@ func TestDBReorgTransfers(t *testing.T) {
|
|||
replacedTX := types.NewTransaction(2, common.Address{1}, nil, 10, big.NewInt(10), nil)
|
||||
require.NoError(t, db.ProcessBlocks(original.Address, original.Number, original.Number, []*DBHeader{original}))
|
||||
require.NoError(t, db.ProcessTranfers([]Transfer{
|
||||
{ethTransfer, common.Hash{1}, *originalTX.To(), original.Number, original.Hash, 100, originalTX, true, common.Address{1}, rcpt, nil},
|
||||
{ethTransfer, common.Hash{1}, *originalTX.To(), original.Number, original.Hash, 100, originalTX, true, 1777, common.Address{1}, rcpt, nil},
|
||||
}, []*DBHeader{}))
|
||||
require.NoError(t, db.ProcessBlocks(replaced.Address, replaced.Number, replaced.Number, []*DBHeader{replaced}))
|
||||
require.NoError(t, db.ProcessTranfers([]Transfer{
|
||||
{ethTransfer, common.Hash{2}, *replacedTX.To(), replaced.Number, replaced.Hash, 100, replacedTX, true, common.Address{1}, rcpt, nil},
|
||||
{ethTransfer, common.Hash{2}, *replacedTX.To(), replaced.Number, replaced.Hash, 100, replacedTX, true, 1777, common.Address{1}, rcpt, nil},
|
||||
}, []*DBHeader{original}))
|
||||
|
||||
all, err := db.GetTransfers(big.NewInt(0), nil)
|
||||
|
|
|
@ -41,6 +41,7 @@ type Transfer struct {
|
|||
Timestamp uint64 `json:"timestamp"`
|
||||
Transaction *types.Transaction `json:"transaction"`
|
||||
Loaded bool
|
||||
NetworkID uint64
|
||||
// From is derived from tx signature in order to offload this computation from UI component.
|
||||
From common.Address `json:"from"`
|
||||
Receipt *types.Receipt `json:"receipt"`
|
||||
|
|
|
@ -18,9 +18,11 @@ import (
|
|||
func NewService(db *Database, accountsFeed *event.Feed) *Service {
|
||||
feed := &event.Feed{}
|
||||
return &Service{
|
||||
db: db,
|
||||
feed: feed,
|
||||
signals: &SignalsTransmitter{publisher: feed},
|
||||
db: db,
|
||||
feed: feed,
|
||||
signals: &SignalsTransmitter{
|
||||
publisher: feed,
|
||||
},
|
||||
accountsFeed: accountsFeed,
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +46,11 @@ func (s *Service) Start(*p2p.Server) error {
|
|||
return s.signals.Start()
|
||||
}
|
||||
|
||||
// GetFeed returns signals feed.
|
||||
func (s *Service) GetFeed() *event.Feed {
|
||||
return s.feed
|
||||
}
|
||||
|
||||
// StartReactor separately because it requires known ethereum address, which will become available only after login.
|
||||
func (s *Service) StartReactor(client *ethclient.Client, accounts []common.Address, chain *big.Int) error {
|
||||
reactor := NewReactor(s.db, s.feed, client, chain)
|
||||
|
|
|
@ -32,6 +32,7 @@ func castToTransferView(t Transfer) TransferView {
|
|||
view.TxStatus = hexutil.Uint64(t.Receipt.Status)
|
||||
view.Input = hexutil.Bytes(t.Transaction.Data())
|
||||
view.TxHash = t.Transaction.Hash()
|
||||
view.NetworkID = t.NetworkID
|
||||
switch t.Type {
|
||||
case ethTransfer:
|
||||
view.From = t.From
|
||||
|
@ -48,6 +49,11 @@ func castToTransferView(t Transfer) TransferView {
|
|||
return view
|
||||
}
|
||||
|
||||
// CastToTransferView transforms a raw Transfer into an enriched one
|
||||
func CastToTransferView(t Transfer) TransferView {
|
||||
return castToTransferView(t)
|
||||
}
|
||||
|
||||
func parseLog(ethlog *types.Log) (from, to common.Address, amount *big.Int) {
|
||||
if len(ethlog.Topics) < 3 {
|
||||
log.Warn("not enough topics for erc20 transfer", "topics", ethlog.Topics)
|
||||
|
@ -91,4 +97,5 @@ type TransferView struct {
|
|||
From common.Address `json:"from"`
|
||||
To common.Address `json:"to"`
|
||||
Contract common.Address `json:"contract"`
|
||||
NetworkID uint64
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
const baseTransfersQuery = "SELECT hash, type, blk_hash, blk_number, timestamp, address, tx, sender, receipt, log FROM transfers"
|
||||
const baseTransfersQuery = "SELECT hash, type, blk_hash, blk_number, timestamp, address, tx, sender, receipt, log, network_id FROM transfers"
|
||||
|
||||
func newTransfersQuery() *transfersQuery {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
@ -84,6 +84,14 @@ func (q *transfersQuery) FilterBlockHash(blockHash common.Hash) *transfersQuery
|
|||
return q
|
||||
}
|
||||
|
||||
func (q *transfersQuery) FilterBlockNumber(blockNumber *big.Int) *transfersQuery {
|
||||
q.andOrWhere()
|
||||
q.added = true
|
||||
q.buf.WriteString(" blk_number = ?")
|
||||
q.args = append(q.args, (*SQLBigInt)(blockNumber))
|
||||
return q
|
||||
}
|
||||
|
||||
func (q *transfersQuery) Limit(pageSize int64) *transfersQuery {
|
||||
q.buf.WriteString(" ORDER BY blk_number DESC, hash ASC ")
|
||||
q.buf.WriteString(" LIMIT ?")
|
||||
|
@ -110,7 +118,7 @@ func (q *transfersQuery) Scan(rows *sql.Rows) (rst []Transfer, err error) {
|
|||
err = rows.Scan(
|
||||
&transfer.ID, &transfer.Type, &transfer.BlockHash,
|
||||
(*SQLBigInt)(transfer.BlockNumber), &transfer.Timestamp, &transfer.Address,
|
||||
&JSONBlob{transfer.Transaction}, &transfer.From, &JSONBlob{transfer.Receipt}, &JSONBlob{transfer.Log})
|
||||
&JSONBlob{transfer.Transaction}, &transfer.From, &JSONBlob{transfer.Receipt}, &JSONBlob{transfer.Log}, &transfer.NetworkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package signal
|
||||
|
||||
const (
|
||||
notificationEvent = "local-notifications"
|
||||
)
|
||||
|
||||
// SendLocalNotifications sends event with a local notification.
|
||||
func SendLocalNotifications(event interface{}) {
|
||||
send(notificationEvent, event)
|
||||
}
|
Loading…
Reference in New Issue