github.com/status-im/status-go@v1.1.0/protocol/messenger_sync_settings.go (about)

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  
     7  	"go.uber.org/zap"
     8  
     9  	"github.com/status-im/status-go/multiaccounts/errors"
    10  	"github.com/status-im/status-go/multiaccounts/settings"
    11  	"github.com/status-im/status-go/protocol/common"
    12  	"github.com/status-im/status-go/protocol/protobuf"
    13  )
    14  
    15  // syncSettings syncs all settings that are syncable
    16  func (m *Messenger) prepareSyncSettingsMessages(currentClock uint64, prepareForBackup bool) (resultRaw []*common.RawMessage, resultSync []*protobuf.SyncSetting, errors []error) {
    17  	s, err := m.settings.GetSettings()
    18  	if err != nil {
    19  		errors = append(errors, err)
    20  		return
    21  	}
    22  
    23  	logger := m.logger.Named("prepareSyncSettings")
    24  	// Do not use the network clock, use the db value
    25  	_, chat := m.getLastClockWithRelatedChat()
    26  
    27  	for _, sf := range settings.SettingFieldRegister {
    28  		if sf.CanSync(settings.FromStruct) {
    29  			// DisplayName is backed up via `protobuf.BackedUpProfile` message.
    30  			if prepareForBackup && sf.SyncProtobufFactory().SyncSettingProtobufType() == protobuf.SyncSetting_DISPLAY_NAME {
    31  				continue
    32  			}
    33  
    34  			// Pull clock from the db
    35  			clock, err := m.settings.GetSettingLastSynced(sf)
    36  			if err != nil {
    37  				logger.Error("m.settings.GetSettingLastSynced", zap.Error(err), zap.Any("SettingField", sf))
    38  				errors = append(errors, err)
    39  				return
    40  			}
    41  			if clock == 0 {
    42  				clock = currentClock
    43  			}
    44  
    45  			// Build protobuf
    46  			rm, sm, err := sf.SyncProtobufFactory().FromStruct()(s, clock, chat.ID)
    47  			if err != nil {
    48  				// Collect errors to give other sync messages a chance to send
    49  				logger.Error("SyncProtobufFactory.Struct", zap.Error(err))
    50  				errors = append(errors, err)
    51  			}
    52  
    53  			resultRaw = append(resultRaw, rm)
    54  			resultSync = append(resultSync, sm)
    55  		}
    56  	}
    57  	return
    58  }
    59  
    60  func (m *Messenger) syncSettings(rawMessageHandler RawMessageHandler) error {
    61  	logger := m.logger.Named("syncSettings")
    62  
    63  	clock, _ := m.getLastClockWithRelatedChat()
    64  	rawMessages, _, errors := m.prepareSyncSettingsMessages(clock, false)
    65  
    66  	if len(errors) != 0 {
    67  		// return just the first error, the others have been logged
    68  		return errors[0]
    69  	}
    70  
    71  	for _, rm := range rawMessages {
    72  		_, err := rawMessageHandler(context.Background(), *rm)
    73  		if err != nil {
    74  			logger.Error("dispatchMessage", zap.Error(err))
    75  			return err
    76  		}
    77  		logger.Debug("dispatchMessage success", zap.Any("rm", rm))
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  // extractSyncSetting parses incoming *protobuf.SyncSetting and stores the setting data if needed
    84  func (m *Messenger) extractAndSaveSyncSetting(syncSetting *protobuf.SyncSetting) (*settings.SyncSettingField, error) {
    85  	sf, err := settings.GetFieldFromProtobufType(syncSetting.Type)
    86  	if err != nil {
    87  		m.logger.Error(
    88  			"extractSyncSetting - settings.GetFieldFromProtobufType",
    89  			zap.Error(err),
    90  			zap.Any("syncSetting", syncSetting),
    91  		)
    92  		return nil, err
    93  	}
    94  
    95  	spf := sf.SyncProtobufFactory()
    96  	if spf == nil {
    97  		m.logger.Warn("extractSyncSetting - received protobuf for setting with no SyncProtobufFactory", zap.Any("SettingField", sf))
    98  		return nil, nil
    99  	}
   100  	if spf.Inactive() {
   101  		m.logger.Warn("extractSyncSetting - received protobuf for inactive sync setting", zap.Any("SettingField", sf))
   102  		return nil, nil
   103  	}
   104  
   105  	value := spf.ExtractValueFromProtobuf()(syncSetting)
   106  
   107  	err = m.settings.SaveSyncSetting(sf, value, syncSetting.Clock)
   108  	if err == errors.ErrNewClockOlderThanCurrent {
   109  		m.logger.Info("extractSyncSetting - SaveSyncSetting :", zap.Error(err))
   110  		return nil, nil
   111  	}
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	if v, ok := value.([]byte); ok {
   117  		value = json.RawMessage(v)
   118  	}
   119  
   120  	return &settings.SyncSettingField{SettingField: sf, Value: value}, nil
   121  }
   122  
   123  // startSyncSettingsLoop watches the m.settings.SyncQueue and sends a sync message in response to a settings update
   124  func (m *Messenger) startSyncSettingsLoop() {
   125  	go func() {
   126  		logger := m.logger.Named("SyncSettingsLoop")
   127  
   128  		for {
   129  			select {
   130  			case s := <-m.settings.GetSyncQueue():
   131  				if s.CanSync(settings.FromInterface) {
   132  					logger.Debug("setting for sync received from settings.SyncQueue")
   133  
   134  					clock, chat := m.getLastClockWithRelatedChat()
   135  
   136  					// Only the messenger has access to the clock, so set the settings sync clock here.
   137  					err := m.settings.SetSettingLastSynced(s.SettingField, clock)
   138  					if err != nil {
   139  						logger.Error("m.settings.SetSettingLastSynced", zap.Error(err))
   140  						break
   141  					}
   142  					rm, _, err := s.SyncProtobufFactory().FromInterface()(s.Value, clock, chat.ID)
   143  					if err != nil {
   144  						logger.Error("SyncProtobufFactory().FromInterface", zap.Error(err), zap.Any("SyncSettingField", s))
   145  						break
   146  					}
   147  
   148  					_, err = m.dispatchMessage(context.Background(), *rm)
   149  					if err != nil {
   150  						logger.Error("dispatchMessage", zap.Error(err))
   151  						break
   152  					}
   153  
   154  					logger.Debug("message dispatched")
   155  				}
   156  			case <-m.quit:
   157  				return
   158  			}
   159  		}
   160  	}()
   161  }
   162  
   163  func (m *Messenger) startSettingsChangesLoop() {
   164  	channel := m.settings.SubscribeToChanges()
   165  	go func() {
   166  		for {
   167  			select {
   168  			case s := <-channel:
   169  				switch s.GetReactName() {
   170  				case settings.DisplayName.GetReactName():
   171  					m.selfContact.DisplayName = s.Value.(string)
   172  					m.publishSelfContactSubscriptions(&SelfContactChangeEvent{DisplayNameChanged: true})
   173  				case settings.PreferredName.GetReactName():
   174  					m.selfContact.EnsName = s.Value.(string)
   175  					m.publishSelfContactSubscriptions(&SelfContactChangeEvent{PreferredNameChanged: true})
   176  				case settings.Bio.GetReactName():
   177  					m.selfContact.Bio = s.Value.(string)
   178  					m.publishSelfContactSubscriptions(&SelfContactChangeEvent{BioChanged: true})
   179  				}
   180  			case <-m.quit:
   181  				return
   182  			}
   183  		}
   184  	}()
   185  }