2021-04-07 12:57:14 +00:00
package protocol
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"strings"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
)
func ( db sqlitePersistence ) DeleteActivityCenterNotification ( id [ ] byte ) error {
_ , err := db . db . Exec ( ` DELETE FROM activity_center_notifications WHERE id = ? ` , id )
return err
}
2021-08-23 14:23:55 +00:00
func ( db sqlitePersistence ) DeleteActivityCenterNotificationForMessage ( chatID string , messageID string ) error {
var tx * sql . Tx
var err error
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
2021-11-25 15:21:42 +00:00
_ , notifications , err := db . buildActivityCenterQuery ( tx , "" , 0 , nil , chatID , "" , ActivityCenterNotificationNoType )
2021-08-23 14:23:55 +00:00
if err != nil {
return err
}
var ids [ ] types . HexBytes
for _ , notification := range notifications {
if notification . Message != nil && notification . Message . ID == messageID {
ids = append ( ids , notification . ID )
}
}
if len ( ids ) > 0 {
idsArgs := make ( [ ] interface { } , 0 , len ( ids ) )
for _ , id := range ids {
idsArgs = append ( idsArgs , id )
}
inVector := strings . Repeat ( "?, " , len ( ids ) - 1 ) + "?"
2021-12-07 14:34:43 +00:00
query := "UPDATE activity_center_notifications SET read = 1, dismissed = 1 WHERE id IN (" + inVector + ")" // nolint: gosec
2021-08-23 14:23:55 +00:00
_ , err = tx . Exec ( query , idsArgs ... )
return err
}
return nil
}
2021-04-07 12:57:14 +00:00
func ( db sqlitePersistence ) SaveActivityCenterNotification ( notification * ActivityCenterNotification ) error {
var tx * sql . Tx
var err error
err = notification . Valid ( )
if err != nil {
return err
}
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
2021-11-19 14:32:04 +00:00
if notification . Type == ActivityCenterNotificationTypeNewOneToOne ||
notification . Type == ActivityCenterNotificationTypeNewPrivateGroupChat {
2021-04-07 12:57:14 +00:00
// Delete other notifications so it pop us again if not currently dismissed
2021-06-29 08:27:11 +00:00
_ , err = tx . Exec ( ` DELETE FROM activity_center_notifications WHERE id = ? AND (dismissed OR accepted) ` , notification . ID )
2021-04-07 12:57:14 +00:00
if err != nil {
return err
}
}
2021-05-29 17:05:25 +00:00
// encode message
var encodedMessage [ ] byte
if notification . Message != nil {
encodedMessage , err = json . Marshal ( notification . Message )
if err != nil {
return err
}
}
2021-07-15 20:21:44 +00:00
// encode message
var encodedReplyMessage [ ] byte
if notification . ReplyMessage != nil {
encodedReplyMessage , err = json . Marshal ( notification . ReplyMessage )
if err != nil {
return err
}
}
_ , err = tx . Exec ( ` INSERT INTO activity_center_notifications (id, timestamp, notification_type, chat_id, message, reply_message, author) VALUES (?,?,?,?,?,?,?) ` , notification . ID , notification . Timestamp , notification . Type , notification . ChatID , encodedMessage , encodedReplyMessage , notification . Author )
2021-04-07 12:57:14 +00:00
return err
}
func ( db sqlitePersistence ) unmarshalActivityCenterNotificationRows ( rows * sql . Rows ) ( string , [ ] * ActivityCenterNotification , error ) {
var notifications [ ] * ActivityCenterNotification
latestCursor := ""
for rows . Next ( ) {
var chatID sql . NullString
var lastMessageBytes [ ] byte
2021-05-29 17:05:25 +00:00
var messageBytes [ ] byte
2021-07-15 20:21:44 +00:00
var replyMessageBytes [ ] byte
2021-04-07 12:57:14 +00:00
var name sql . NullString
2021-06-17 15:08:28 +00:00
var author sql . NullString
2021-04-07 12:57:14 +00:00
notification := & ActivityCenterNotification { }
err := rows . Scan (
& notification . ID ,
& notification . Timestamp ,
& notification . Type ,
& chatID ,
& notification . Read ,
& notification . Accepted ,
& notification . Dismissed ,
2021-05-29 17:05:25 +00:00
& messageBytes ,
2021-04-07 12:57:14 +00:00
& lastMessageBytes ,
2021-07-15 20:21:44 +00:00
& replyMessageBytes ,
2021-04-07 12:57:14 +00:00
& name ,
2021-06-17 15:08:28 +00:00
& author ,
2021-04-07 12:57:14 +00:00
& latestCursor )
if err != nil {
return "" , nil , err
}
if chatID . Valid {
notification . ChatID = chatID . String
}
if name . Valid {
notification . Name = name . String
}
2021-06-17 15:08:28 +00:00
if author . Valid {
2021-06-21 15:39:00 +00:00
notification . Author = author . String
2021-06-17 15:08:28 +00:00
}
2021-04-07 12:57:14 +00:00
// Restore last message
if lastMessageBytes != nil {
2021-05-29 17:05:25 +00:00
lastMessage := & common . Message { }
if err = json . Unmarshal ( lastMessageBytes , lastMessage ) ; err != nil {
return "" , nil , err
}
notification . LastMessage = lastMessage
}
// Restore message
if messageBytes != nil {
2021-04-07 12:57:14 +00:00
message := & common . Message { }
2021-05-29 17:05:25 +00:00
if err = json . Unmarshal ( messageBytes , message ) ; err != nil {
2021-04-07 12:57:14 +00:00
return "" , nil , err
}
2021-05-29 17:05:25 +00:00
notification . Message = message
2021-04-07 12:57:14 +00:00
}
2021-07-15 20:21:44 +00:00
// Restore reply message
if replyMessageBytes != nil {
replyMessage := & common . Message { }
if err = json . Unmarshal ( replyMessageBytes , replyMessage ) ; err != nil {
return "" , nil , err
}
notification . ReplyMessage = replyMessage
}
2021-04-07 12:57:14 +00:00
notifications = append ( notifications , notification )
}
return latestCursor , notifications , nil
}
2021-11-25 15:21:42 +00:00
func ( db sqlitePersistence ) buildActivityCenterQuery ( tx * sql . Tx , cursor string , limit int , ids [ ] types . HexBytes , chatID string , author string , activityCenterType ActivityCenterType ) ( string , [ ] * ActivityCenterNotification , error ) {
2021-04-07 12:57:14 +00:00
var args [ ] interface { }
2021-11-25 15:21:42 +00:00
2021-04-07 12:57:14 +00:00
cursorWhere := ""
inQueryWhere := ""
2021-08-23 14:23:55 +00:00
inChatWhere := ""
2021-11-25 15:21:42 +00:00
fromAuthorWhere := ""
ofTypeWhere := ""
2021-04-07 12:57:14 +00:00
if cursor != "" {
cursorWhere = "AND cursor <= ?" //nolint: goconst
args = append ( args , cursor )
}
if len ( ids ) != 0 {
inVector := strings . Repeat ( "?, " , len ( ids ) - 1 ) + "?"
inQueryWhere = fmt . Sprintf ( " AND a.id IN (%s)" , inVector )
for _ , id := range ids {
args = append ( args , id )
}
}
2021-08-23 14:23:55 +00:00
if chatID != "" {
inChatWhere = "AND a.chat_id = ?" //nolint: goconst
args = append ( args , chatID )
}
2021-11-25 15:21:42 +00:00
if author != "" {
fromAuthorWhere = " AND author = ?"
args = append ( args , author )
}
if activityCenterType != ActivityCenterNotificationNoType {
ofTypeWhere = " AND notification_type = ?"
args = append ( args , activityCenterType )
}
2021-04-07 12:57:14 +00:00
query := fmt . Sprintf ( // nolint: gosec
`
SELECT
a . id ,
a . timestamp ,
a . notification_type ,
a . chat_id ,
a . read ,
a . accepted ,
a . dismissed ,
2021-05-29 17:05:25 +00:00
a . message ,
2021-04-07 12:57:14 +00:00
c . last_message ,
2021-07-15 20:21:44 +00:00
a . reply_message ,
2021-04-07 12:57:14 +00:00
c . name ,
2021-06-17 15:08:28 +00:00
a . author ,
2021-04-07 12:57:14 +00:00
substr ( ' 0000000000000000000000000000000000000000000000000000000000000000 ' || a . timestamp , - 64 , 64 ) || a . id as cursor
FROM activity_center_notifications a
LEFT JOIN chats c
ON
c . id = a . chat_id
WHERE NOT a . dismissed AND NOT a . accepted
% s
% s
2021-08-23 14:23:55 +00:00
% s
2021-11-25 15:21:42 +00:00
% s
% s
ORDER BY cursor DESC ` , cursorWhere , inQueryWhere , inChatWhere , fromAuthorWhere , ofTypeWhere )
2021-04-07 12:57:14 +00:00
if limit != 0 {
args = append ( args , limit )
query += ` LIMIT ? `
}
rows , err := tx . Query ( query , args ... )
if err != nil {
return "" , nil , err
}
return db . unmarshalActivityCenterNotificationRows ( rows )
}
2021-12-02 14:23:02 +00:00
func ( db sqlitePersistence ) runActivityCenterIDQuery ( query string ) ( [ ] [ ] byte , error ) {
rows , err := db . db . Query ( query )
if err != nil {
return nil , err
}
var ids [ ] [ ] byte
for rows . Next ( ) {
var id [ ] byte
err = rows . Scan ( & id )
if err != nil {
return nil , err
}
ids = append ( ids , id )
}
return ids , nil
}
func ( db sqlitePersistence ) GetNotReadActivityCenterNotificationIds ( ) ( [ ] [ ] byte , error ) {
return db . runActivityCenterIDQuery ( "SELECT a.id FROM activity_center_notifications a WHERE NOT a.read" )
}
func ( db sqlitePersistence ) GetToProcessActivityCenterNotificationIds ( ) ( [ ] [ ] byte , error ) {
return db . runActivityCenterIDQuery ( "SELECT a.id FROM activity_center_notifications a WHERE NOT a.dismissed AND NOT a.accepted" )
}
2021-12-07 14:34:43 +00:00
func ( db sqlitePersistence ) HasPendingNotificationsForChat ( chatID string ) ( bool , error ) {
2021-12-10 14:34:16 +00:00
rows , err := db . db . Query ( "SELECT 1 FROM activity_center_notifications a WHERE a.chat_id = ? AND NOT a.dismissed AND NOT a.accepted" , chatID )
2021-12-07 14:34:43 +00:00
if err != nil {
return false , err
}
return rows . Next ( ) , nil
}
2021-12-02 14:23:02 +00:00
func ( db sqlitePersistence ) GetActivityCenterNotificationsByID ( ids [ ] types . HexBytes ) ( [ ] * ActivityCenterNotification , error ) {
idsArgs := make ( [ ] interface { } , 0 , len ( ids ) )
for _ , id := range ids {
idsArgs = append ( idsArgs , id )
}
inVector := strings . Repeat ( "?, " , len ( ids ) - 1 ) + "?"
rows , err := db . db . Query ( "SELECT a.id, a.read, a.accepted, a.dismissed FROM activity_center_notifications a WHERE a.id IN (" + inVector + ")" , idsArgs ... ) // nolint: gosec
if err != nil {
return nil , err
}
var notifications [ ] * ActivityCenterNotification
for rows . Next ( ) {
notification := & ActivityCenterNotification { }
err := rows . Scan (
& notification . ID ,
& notification . Read ,
& notification . Accepted ,
& notification . Dismissed )
if err != nil {
return nil , err
}
notifications = append ( notifications , notification )
}
return notifications , nil
}
2021-04-07 12:57:14 +00:00
func ( db sqlitePersistence ) ActivityCenterNotifications ( currCursor string , limit uint64 ) ( string , [ ] * ActivityCenterNotification , error ) {
var tx * sql . Tx
var err error
// We fetch limit + 1 to check for pagination
incrementedLimit := int ( limit ) + 1
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return "" , nil , err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
2021-11-25 15:21:42 +00:00
latestCursor , notifications , err := db . buildActivityCenterQuery ( tx , currCursor , incrementedLimit , nil , "" , "" , ActivityCenterNotificationNoType )
2021-04-07 12:57:14 +00:00
if err != nil {
return "" , nil , err
}
if len ( notifications ) == incrementedLimit {
notifications = notifications [ 0 : limit ]
} else {
latestCursor = ""
}
return latestCursor , notifications , nil
}
func ( db sqlitePersistence ) DismissAllActivityCenterNotifications ( ) error {
2021-12-07 14:34:43 +00:00
_ , err := db . db . Exec ( ` UPDATE activity_center_notifications SET read = 1, dismissed = 1 WHERE NOT dismissed AND NOT accepted ` )
2021-04-07 12:57:14 +00:00
return err
}
2021-12-06 12:44:40 +00:00
func ( db sqlitePersistence ) DismissAllActivityCenterNotificationsFromUser ( userPublicKey string ) error {
2021-12-07 14:34:43 +00:00
_ , err := db . db . Exec ( ` UPDATE activity_center_notifications SET read = 1, dismissed = 1 WHERE NOT dismissed AND NOT accepted AND author = ? ` , userPublicKey )
2021-12-06 12:44:40 +00:00
return err
}
2021-04-07 12:57:14 +00:00
func ( db sqlitePersistence ) DismissActivityCenterNotifications ( ids [ ] types . HexBytes ) error {
idsArgs := make ( [ ] interface { } , 0 , len ( ids ) )
for _ , id := range ids {
idsArgs = append ( idsArgs , id )
}
inVector := strings . Repeat ( "?, " , len ( ids ) - 1 ) + "?"
2021-12-07 14:34:43 +00:00
query := "UPDATE activity_center_notifications SET read = 1, dismissed = 1 WHERE id IN (" + inVector + ")" // nolint: gosec
2021-04-07 12:57:14 +00:00
_ , err := db . db . Exec ( query , idsArgs ... )
return err
}
func ( db sqlitePersistence ) AcceptAllActivityCenterNotifications ( ) ( [ ] * ActivityCenterNotification , error ) {
var tx * sql . Tx
var err error
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return nil , err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
2021-11-25 15:21:42 +00:00
_ , notifications , err := db . buildActivityCenterQuery ( tx , "" , 0 , nil , "" , "" , ActivityCenterNotificationNoType )
2021-04-07 12:57:14 +00:00
2021-12-07 14:34:43 +00:00
_ , err = tx . Exec ( ` UPDATE activity_center_notifications SET read = 1, accepted = 1 WHERE NOT accepted AND NOT dismissed ` )
2021-04-07 12:57:14 +00:00
if err != nil {
return nil , err
}
return notifications , nil
}
func ( db sqlitePersistence ) AcceptActivityCenterNotifications ( ids [ ] types . HexBytes ) ( [ ] * ActivityCenterNotification , error ) {
var tx * sql . Tx
var err error
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return nil , err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
2021-11-25 15:21:42 +00:00
_ , notifications , err := db . buildActivityCenterQuery ( tx , "" , 0 , ids , "" , "" , ActivityCenterNotificationNoType )
2021-04-07 12:57:14 +00:00
if err != nil {
return nil , err
}
idsArgs := make ( [ ] interface { } , 0 , len ( ids ) )
for _ , id := range ids {
idsArgs = append ( idsArgs , id )
}
inVector := strings . Repeat ( "?, " , len ( ids ) - 1 ) + "?"
2021-12-07 14:34:43 +00:00
query := "UPDATE activity_center_notifications SET read = 1, accepted = 1 WHERE id IN (" + inVector + ")" // nolint: gosec
2021-04-07 12:57:14 +00:00
_ , err = tx . Exec ( query , idsArgs ... )
return notifications , err
}
2021-11-25 15:21:42 +00:00
func ( db sqlitePersistence ) AcceptActivityCenterNotificationsForInvitesFromUser ( userPublicKey string ) ( [ ] * ActivityCenterNotification , error ) {
var tx * sql . Tx
var err error
tx , err = db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return nil , err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
_ , notifications , err := db . buildActivityCenterQuery ( tx , "" , 0 , nil , "" , userPublicKey , ActivityCenterNotificationTypeNewPrivateGroupChat )
if err != nil {
return nil , err
}
2021-12-07 14:34:43 +00:00
_ , err = tx . Exec ( ` UPDATE activity_center_notifications SET read = 1, accepted = 1 WHERE NOT accepted AND NOT dismissed AND author = ? AND notification_type = ? ` , userPublicKey , ActivityCenterNotificationTypeNewPrivateGroupChat )
2021-11-25 15:21:42 +00:00
if err != nil {
return nil , err
}
return notifications , nil
}
2021-04-07 12:57:14 +00:00
func ( db sqlitePersistence ) MarkAllActivityCenterNotificationsRead ( ) error {
_ , err := db . db . Exec ( ` UPDATE activity_center_notifications SET read = 1 WHERE NOT read ` )
return err
}
2021-06-11 16:47:53 +00:00
func ( db sqlitePersistence ) MarkActivityCenterNotificationsRead ( ids [ ] types . HexBytes ) error {
idsArgs := make ( [ ] interface { } , 0 , len ( ids ) )
for _ , id := range ids {
idsArgs = append ( idsArgs , id )
}
inVector := strings . Repeat ( "?, " , len ( ids ) - 1 ) + "?"
query := "UPDATE activity_center_notifications SET read = 1 WHERE id IN (" + inVector + ")" // nolint: gosec
_ , err := db . db . Exec ( query , idsArgs ... )
return err
}
2021-09-24 10:57:15 +00:00
func ( db sqlitePersistence ) MarkActivityCenterNotificationsUnread ( ids [ ] types . HexBytes ) error {
idsArgs := make ( [ ] interface { } , 0 , len ( ids ) )
for _ , id := range ids {
idsArgs = append ( idsArgs , id )
}
inVector := strings . Repeat ( "?, " , len ( ids ) - 1 ) + "?"
query := "UPDATE activity_center_notifications SET read = 0 WHERE id IN (" + inVector + ")" // nolint: gosec
_ , err := db . db . Exec ( query , idsArgs ... )
return err
}
2021-04-07 12:57:14 +00:00
func ( db sqlitePersistence ) UnreadActivityCenterNotificationsCount ( ) ( uint64 , error ) {
var count uint64
2021-07-06 16:07:41 +00:00
err := db . db . QueryRow ( ` SELECT COUNT(1) FROM activity_center_notifications WHERE NOT read AND NOT dismissed AND NOT accepted ` ) . Scan ( & count )
2021-04-07 12:57:14 +00:00
return count , err
}