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 }