feat(sync)_: add AC notifications when initiating the sync fallback

Needed for https://github.com/status-im/status-desktop/issues/15750

Adds an AC notification when the syncing fails and the user is prompted to use a seed phrase instead.
There is one notification for the initiator (created) and one for the old account (received).
Once the flow is completed, ie the receiver presses Enable and sync,  the notifications are deleted
This commit is contained in:
Jonathan Rainville 2024-07-25 15:56:35 -04:00 committed by frank
parent 31538e7665
commit 837f682e0e
No known key found for this signature in database
GPG Key ID: B56FA1FC264D28FD
7 changed files with 302 additions and 163 deletions

View File

@ -40,6 +40,8 @@ const (
ActivityCenterNotificationTypeFirstCommunityTokenReceived ActivityCenterNotificationTypeFirstCommunityTokenReceived
ActivityCenterNotificationTypeCommunityBanned ActivityCenterNotificationTypeCommunityBanned
ActivityCenterNotificationTypeCommunityUnbanned ActivityCenterNotificationTypeCommunityUnbanned
ActivityCenterNotificationTypeNewInstallationReceived
ActivityCenterNotificationTypeNewInstallationCreated
) )
type ActivityCenterMembershipStatus int type ActivityCenterMembershipStatus int
@ -87,6 +89,7 @@ type ActivityCenterNotification struct {
MembershipStatus ActivityCenterMembershipStatus `json:"membershipStatus"` MembershipStatus ActivityCenterMembershipStatus `json:"membershipStatus"`
Name string `json:"name"` Name string `json:"name"`
Author string `json:"author"` Author string `json:"author"`
InstallationID string `json:"installationId"`
Type ActivityCenterType `json:"type"` Type ActivityCenterType `json:"type"`
LastMessage *common.Message `json:"lastMessage"` LastMessage *common.Message `json:"lastMessage"`
Message *common.Message `json:"message"` Message *common.Message `json:"message"`

View File

@ -152,9 +152,10 @@ func (db sqlitePersistence) SaveActivityCenterNotification(notification *Activit
dismissed, dismissed,
token_data, token_data,
deleted, deleted,
updated_at updated_at,
installation_id
) )
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
`, `,
notification.ID, notification.ID,
notification.Timestamp, notification.Timestamp,
@ -172,6 +173,7 @@ func (db sqlitePersistence) SaveActivityCenterNotification(notification *Activit
encodedTokenData, encodedTokenData,
notification.Deleted, notification.Deleted,
notification.UpdatedAt, notification.UpdatedAt,
notification.InstallationID,
) )
if err != nil { if err != nil {
return 0, err return 0, err
@ -273,6 +275,7 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRow(row *sql.Row)
var tokenDataBytes []byte var tokenDataBytes []byte
var name sql.NullString var name sql.NullString
var author sql.NullString var author sql.NullString
var installationID sql.NullString
notification := &ActivityCenterNotification{} notification := &ActivityCenterNotification{}
err := row.Scan( err := row.Scan(
&notification.ID, &notification.ID,
@ -292,7 +295,9 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRow(row *sql.Row)
&name, &name,
&author, &author,
&tokenDataBytes, &tokenDataBytes,
&notification.UpdatedAt) &notification.UpdatedAt,
&installationID,
)
if err != nil { if err != nil {
return nil, err return nil, err
@ -314,6 +319,10 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRow(row *sql.Row)
notification.Author = author.String notification.Author = author.String
} }
if installationID.Valid {
notification.InstallationID = installationID.String
}
if len(tokenDataBytes) > 0 { if len(tokenDataBytes) > 0 {
err = json.Unmarshal(tokenDataBytes, &notification.TokenData) err = json.Unmarshal(tokenDataBytes, &notification.TokenData)
if err != nil { if err != nil {
@ -363,6 +372,7 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRows(rows *sql.Ro
var tokenDataBytes []byte var tokenDataBytes []byte
var name sql.NullString var name sql.NullString
var author sql.NullString var author sql.NullString
var installationID sql.NullString
notification := &ActivityCenterNotification{} notification := &ActivityCenterNotification{}
err := rows.Scan( err := rows.Scan(
&notification.ID, &notification.ID,
@ -382,7 +392,9 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRows(rows *sql.Ro
&author, &author,
&tokenDataBytes, &tokenDataBytes,
&latestCursor, &latestCursor,
&notification.UpdatedAt) &notification.UpdatedAt,
&installationID,
)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -403,6 +415,10 @@ func (db sqlitePersistence) unmarshalActivityCenterNotificationRows(rows *sql.Ro
notification.Author = author.String notification.Author = author.String
} }
if installationID.Valid {
notification.InstallationID = installationID.String
}
if len(tokenDataBytes) > 0 { if len(tokenDataBytes) > 0 {
tokenData := &ActivityTokenData{} tokenData := &ActivityTokenData{}
if err = json.Unmarshal(tokenDataBytes, &tokenData); err != nil { if err = json.Unmarshal(tokenDataBytes, &tokenData); err != nil {
@ -543,7 +559,8 @@ func (db sqlitePersistence) buildActivityCenterQuery(tx *sql.Tx, params activity
a.author, a.author,
a.token_data, a.token_data,
substr('0000000000000000000000000000000000000000000000000000000000000000' || a.timestamp, -64, 64) || hex(a.id) as cursor, substr('0000000000000000000000000000000000000000000000000000000000000000' || a.timestamp, -64, 64) || hex(a.id) as cursor,
a.updated_at a.updated_at,
a.installation_id
FROM activity_center_notifications a FROM activity_center_notifications a
LEFT JOIN chats c LEFT JOIN chats c
ON ON
@ -664,7 +681,8 @@ func (db sqlitePersistence) GetActivityCenterNotificationsByID(ids []types.HexBy
a.author, a.author,
a.token_data, a.token_data,
substr('0000000000000000000000000000000000000000000000000000000000000000' || a.timestamp, -64, 64) || hex(a.id) as cursor, substr('0000000000000000000000000000000000000000000000000000000000000000' || a.timestamp, -64, 64) || hex(a.id) as cursor,
a.updated_at a.updated_at,
a.installation_id
FROM activity_center_notifications a FROM activity_center_notifications a
LEFT JOIN chats c LEFT JOIN chats c
ON ON
@ -704,7 +722,8 @@ func (db sqlitePersistence) GetActivityCenterNotificationByID(id types.HexBytes)
c.name, c.name,
a.author, a.author,
a.token_data, a.token_data,
a.updated_at a.updated_at,
a.installation_id
FROM activity_center_notifications a FROM activity_center_notifications a
LEFT JOIN chats c LEFT JOIN chats c
ON ON
@ -1338,7 +1357,8 @@ func (db sqlitePersistence) ActiveContactRequestNotification(contactID string) (
c.name, c.name,
a.author, a.author,
a.token_data, a.token_data,
a.updated_at a.updated_at,
a.installation_id
FROM activity_center_notifications a FROM activity_center_notifications a
LEFT JOIN chats c ON c.id = a.chat_id LEFT JOIN chats c ON c.id = a.chat_id
WHERE a.author = ? WHERE a.author = ?

View File

@ -3678,6 +3678,48 @@ func (m *Messenger) saveDataAndPrepareResponse(messageState *ReceivedMessageStat
} }
} }
if installation.Enabled {
// // Delete AC notif since the installation is now enabled
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(id))
if err != nil {
return false
}
if notification != nil {
updatedAt := m.GetCurrentTimeInMillis()
notification.UpdatedAt = updatedAt
notification.Deleted = true
// we shouldn't sync deleted notification here,
// as the same user on different devices will receive the same message(CommunityCancelRequestToJoin) ?
err = m.persistence.DeleteActivityCenterNotificationByID(types.FromHex(id), updatedAt)
if err != nil {
m.logger.Error("failed to delete notification from Activity Center", zap.Error(err))
return false
}
// sending signal to client to remove the activity center notification from UI
messageState.Response.AddActivityCenterNotification(notification)
}
} else {
// Add activity center notification when we receive a new installation
if id != m.installationID {
notification := &ActivityCenterNotification{
ID: types.FromHex(id),
Type: ActivityCenterNotificationTypeNewInstallationReceived,
InstallationID: id,
Timestamp: m.getTimesource().GetCurrentTime(),
Read: false,
Deleted: false,
UpdatedAt: m.GetCurrentTimeInMillis(),
}
err = m.addActivityCenterNotification(messageState.Response, notification, nil)
if err != nil {
return false
}
}
}
return true return true
}) })
if err != nil { if err != nil {

View File

@ -9,6 +9,7 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/images" "github.com/status-im/status-go/images"
"github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/encryption/multidevice" "github.com/status-im/status-go/protocol/encryption/multidevice"
@ -16,15 +17,45 @@ import (
"github.com/status-im/status-go/protocol/requests" "github.com/status-im/status-go/protocol/requests"
) )
func (m *Messenger) EnableAndSyncInstallation(request *requests.EnableAndSyncInstallation) error { func (m *Messenger) EnableAndSyncInstallation(request *requests.EnableAndSyncInstallation) (*MessengerResponse, error) {
if err := request.Validate(); err != nil { if err := request.Validate(); err != nil {
return err return nil, err
} }
err := m.EnableInstallation(request.InstallationID) err := m.EnableInstallation(request.InstallationID)
if err != nil { if err != nil {
return err return nil, err
} }
return m.SyncDevices(context.Background(), "", "", nil) response, err := m.SendPairInstallation(context.Background(), nil)
if err != nil {
return nil, err
}
err = m.SyncDevices(context.Background(), "", "", nil)
if err != nil {
return nil, err
}
// Delete AC notif
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(request.InstallationID))
if err != nil {
return nil, err
}
if notification != nil {
updatedAt := m.GetCurrentTimeInMillis()
notification.UpdatedAt = updatedAt
notification.Deleted = true
// we shouldn't sync deleted notification here,
// as the same user on different devices will receive the same message(CommunityCancelRequestToJoin) ?
err = m.persistence.DeleteActivityCenterNotificationByID(types.FromHex(request.InstallationID), updatedAt)
if err != nil {
m.logger.Error("failed to delete notification from Activity Center", zap.Error(err))
return nil, err
}
// sending signal to client to remove the activity center notification from UI
response.AddActivityCenterNotification(notification)
}
return response, nil
} }
func (m *Messenger) EnableInstallationAndPair(request *requests.EnableInstallationAndPair) (*MessengerResponse, error) { func (m *Messenger) EnableInstallationAndPair(request *requests.EnableInstallationAndPair) (*MessengerResponse, error) {
@ -53,7 +84,26 @@ func (m *Messenger) EnableInstallationAndPair(request *requests.EnableInstallati
i.Enabled = true i.Enabled = true
} }
m.allInstallations.Store(request.InstallationID, i) m.allInstallations.Store(request.InstallationID, i)
return m.SendPairInstallation(context.Background(), nil) response, err := m.SendPairInstallation(context.Background(), nil)
if err != nil {
return nil, err
}
notification := &ActivityCenterNotification{
ID: types.FromHex(request.InstallationID),
Type: ActivityCenterNotificationTypeNewInstallationCreated,
InstallationID: m.installationID, // Put our own installation ID, as we're the initiator of the pairing
Timestamp: m.getTimesource().GetCurrentTime(),
Read: false,
Deleted: false,
UpdatedAt: m.GetCurrentTimeInMillis(),
}
err = m.addActivityCenterNotification(response, notification, nil)
if err != nil {
return nil, err
}
return response, err
} }
// SendPairInstallation sends a pair installation message // SendPairInstallation sends a pair installation message

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
ALTER TABLE activity_center_notifications ADD COLUMN installation_id TEXT DEFAULT NULL;

View File

@ -1064,7 +1064,7 @@ func (api *PublicAPI) SyncDevices(ctx context.Context, name, picture string) err
return api.service.messenger.SyncDevices(ctx, name, picture, nil) return api.service.messenger.SyncDevices(ctx, name, picture, nil)
} }
func (api *PublicAPI) EnableAndSyncInstallation(request *requests.EnableAndSyncInstallation) error { func (api *PublicAPI) EnableAndSyncInstallation(request *requests.EnableAndSyncInstallation) (*protocol.MessengerResponse, error) {
return api.service.messenger.EnableAndSyncInstallation(request) return api.service.messenger.EnableAndSyncInstallation(request)
} }