status-go/protocol/messenger_identity.go
Icaro Motta dbed69d155
perf(login)!: Set-up messenger filters outside login flow (#5229)
Fixes the slow login in mobile devices when users have joined large communities,
such as the Status one. A user would get stuck for almost 20s in some devices.

We identified that the step to set-up filters in the messenger is potentially
expensive and that it is not critical to happen before the node.login signal is
emitted. The solution presented in this PR is to set-up filters inside
messenger.Start(), which the client already calls immediately after login.

With this change, users of the mobile app can login pretty fast even when they
joined large communities. They can immediately interact with other parts of the
app even if filter initialization is running in the background, like Wallet,
Activity Center, Settings, and Profile.

Breaking changes: in the mobile repository, we had to change where the endpoint
wakuext_startMessenger was called and the order of a few events to process
chats. So essentially ordering, but no data changes.

- Root issue https://github.com/status-im/status-mobile/issues/20059
- Related mobile PR https://github.com/status-im/status-mobile/pull/20173
2024-06-10 12:02:42 -03:00

198 lines
4.7 KiB
Go

package protocol
import (
"errors"
"fmt"
"runtime"
"strings"
"unicode/utf8"
utils "github.com/status-im/status-go/common"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/server"
)
const (
maxBioLength = 240
)
var ErrInvalidBioLength = errors.New("invalid bio length")
var ErrDisplayNameDupeOfCommunityMember = errors.New("display name duplicates on of community members")
func (m *Messenger) SetDisplayName(displayName string) error {
currDisplayName, err := m.settings.DisplayName()
if err != nil {
return err
}
if utils.IsENSName(displayName) || currDisplayName == displayName {
return nil // Do nothing
}
if err = utils.ValidateDisplayName(&displayName); err != nil {
return err
}
isDupe, err := m.IsDisplayNameDupeOfCommunityMember(displayName)
if err != nil {
return err
}
if isDupe {
return ErrDisplayNameDupeOfCommunityMember
}
m.account.Name = displayName
err = m.multiAccounts.UpdateDisplayName(m.account.KeyUID, displayName)
if err != nil {
return err
}
err = m.settings.SaveSettingField(settings.DisplayName, displayName)
if err != nil {
return err
}
err = m.UpdateKeypairName(m.account.KeyUID, displayName)
if err != nil {
return err
}
err = m.resetLastPublishedTimeForChatIdentity()
if err != nil {
return err
}
return m.publishContactCode()
}
func (m *Messenger) SaveSyncDisplayName(displayName string, clock uint64) error {
err := m.settings.SaveSyncSetting(settings.DisplayName, displayName, clock)
if err != nil {
return err
}
preferredName, err := m.settings.GetPreferredUsername()
if err != nil {
return err
}
preferredNameClock, err := m.settings.GetSettingLastSynced(settings.PreferredName)
if err != nil {
return err
}
// When either the display name or preferred name changes, m.account.Name should be updated.
// However, a race condition can occur during BackupData, where m.account.Name could be incorrectly updated.
// The final value of m.account.Name depends on which backup message(BackedUpProfile/BackedUpSettings) arrives later.
// So we should check the clock of the preferred name and only update m.account.Name if it's older than the display name.
// Yet even if the preferred name clock is older, but the preferred name was empty, we should still update m.account.Name.
if preferredNameClock < clock || preferredName == "" {
m.account.Name = displayName
return m.multiAccounts.SaveAccount(*m.account)
}
return nil
}
func ValidateBio(bio *string) error {
if utf8.RuneCountInString(*bio) > maxBioLength {
return ErrInvalidBioLength
}
return nil
}
func (m *Messenger) SetBio(bio string) error {
currentBio, err := m.settings.Bio()
if err != nil {
return err
}
if currentBio == bio {
return nil // Do nothing
}
if err = ValidateBio(&bio); err != nil {
return err
}
if err = m.settings.SaveSettingField(settings.Bio, bio); err != nil {
return err
}
if err = m.resetLastPublishedTimeForChatIdentity(); err != nil {
return err
}
return m.publishContactCode()
}
func (m *Messenger) setInstallationHostname() error {
imd, err := m.getOurInstallationMetadata()
if err != nil {
return err
}
// If the name and device are already set, don't do anything
if len(imd.Name) != 0 && len(imd.DeviceType) != 0 {
return nil
}
if len(imd.Name) == 0 {
deviceName, err := m.settings.DeviceName()
if err != nil {
return err
}
if deviceName != "" {
imd.Name = deviceName
} else {
hn, err := server.GetDeviceName()
if err != nil {
return err
}
// NOTE: imd.Name is always empty in this else branch, which leads to the
// result of Sprintf having a trailing whitespace.
imd.Name = fmt.Sprintf("%s %s", hn, imd.Name)
}
}
if len(imd.DeviceType) == 0 {
imd.DeviceType = runtime.GOOS
}
return m.setInstallationMetadata(m.installationID, imd)
}
func (m *Messenger) getOurInstallationMetadata() (*multidevice.InstallationMetadata, error) {
ourInstallation, ok := m.allInstallations.Load(m.installationID)
if !ok {
return nil, fmt.Errorf("messenger's installationID is not set or not loadable")
}
if ourInstallation.InstallationMetadata == nil {
return new(multidevice.InstallationMetadata), nil
}
return ourInstallation.InstallationMetadata, nil
}
func (m *Messenger) SetInstallationDeviceType(deviceType string) error {
if strings.TrimSpace(deviceType) == "" {
return errors.New("device type is empty")
}
imd, err := m.getOurInstallationMetadata()
if err != nil {
return err
}
// If the name is already set, don't do anything
if len(imd.DeviceType) != 0 {
return nil
}
imd.DeviceType = deviceType
return m.setInstallationMetadata(m.installationID, imd)
}