@@ -31,6 +31,9 @@ final class AppModel: ObservableObject {
3131 self . isAwaitingConfigurationRecoveryCompletion =
3232 configurationStore. isConfigurationRecoveryCompletionPending
3333 configurationStore. seedDefaultConfigurationsIfNeeded ( )
34+ if !configurationStore. isConfigurationRecoveryRequired {
35+ refreshService. updateCurrentConfigurations ( configurationStore. configurations)
36+ }
3437
3538 refreshService. objectWillChange
3639 . sink { [ weak self] _ in
@@ -175,15 +178,26 @@ final class AppModel: ObservableObject {
175178 await refresh ( )
176179 }
177180
178- func discoverLocalCredentials( ) async {
181+ @discardableResult
182+ func discoverLocalCredentials( ) async -> Bool {
183+ let previousConfigurations = configurationStore. enabledConfigurations
179184 let discovery = await Task . detached ( priority: . utility) {
180185 LocalCredentialDiscovery . discover ( )
181186 } . value
182187
183188 configurationStore. applyLocalCredentialDiscoveries ( discovery)
189+ let didChangeRefreshInputs = refreshInputsChanged (
190+ from: previousConfigurations,
191+ to: configurationStore. enabledConfigurations
192+ )
193+ if didChangeRefreshInputs {
194+ invalidateAccounts ( )
195+ }
196+ return didChangeRefreshInputs
184197 }
185198
186199 func refresh( ) async {
200+ refreshService. updateCurrentConfigurations ( configurationStore. configurations)
187201 if refreshService. isRefreshing {
188202 pendingRefresh = true
189203 return
@@ -238,11 +252,24 @@ final class AppModel: ObservableObject {
238252 )
239253 }
240254
241- func handleAccountsChanged( ) async {
255+ func invalidateAccounts( ) {
256+ refreshService. updateCurrentConfigurations ( configurationStore. configurations)
257+ }
258+
259+ func refreshAfterAccountChange( ) async {
242260 updateAutoRefresh ( )
243261 await refresh ( )
244262 }
245263
264+ func refreshAfterCredentialChange( ) async {
265+ await refresh ( )
266+ }
267+
268+ func invalidateCredential( forAccountID accountID: String ) {
269+ refreshService. updateCurrentConfigurations ( configurationStore. configurations)
270+ refreshService. invalidateCredential ( forAccountID: accountID)
271+ }
272+
246273 func completeConfigurationRecoveryIfPossible( ) {
247274 guard isAwaitingConfigurationRecoveryCompletion,
248275 configurationStore. completeConfigurationRecovery ( )
@@ -251,13 +278,32 @@ final class AppModel: ObservableObject {
251278 }
252279
253280 isAwaitingConfigurationRecoveryCompletion = false
281+ refreshService. updateCurrentConfigurations ( configurationStore. configurations)
254282 historyStore. removeSnapshotsForMissingAccounts (
255283 validAccountIDs: Set ( configurationStore. configurations. map ( \. id) )
256284 )
257285 }
258286
259287 func refreshAccount( _ configuration: ProviderAccountConfiguration ) async -> ProviderUsageResult ? {
260- await refreshService. refresh ( configuration: configuration)
288+ refreshService. updateCurrentConfigurations ( configurationStore. configurations)
289+ guard
290+ let currentConfiguration = configurationStore. configuration ( accountID: configuration. id) ,
291+ currentConfiguration. isEnabled,
292+ refreshService. hasSameRefreshInputs ( configuration, currentConfiguration)
293+ else {
294+ return nil
295+ }
296+ guard let result = await refreshService. refresh ( configuration: currentConfiguration) else {
297+ return nil
298+ }
299+
300+ lastRefreshedAt = Date ( )
301+ recordUsageHistory ( )
302+ await processUsageAlerts (
303+ results: alertEligibleResults ( ) ,
304+ preserving: refreshService. incompleteRefreshAccountIDs
305+ )
306+ return result
261307 }
262308
263309 func quit( ) {
@@ -277,6 +323,34 @@ final class AppModel: ObservableObject {
277323 historyStore. record ( results: alertEligibleResults ( ) )
278324 }
279325
326+ private func refreshInputsChanged(
327+ from previousConfigurations: [ ProviderAccountConfiguration ] ,
328+ to currentConfigurations: [ ProviderAccountConfiguration ]
329+ ) -> Bool {
330+ let previousByID = previousConfigurations. reduce (
331+ into: [ String: ProviderAccountConfiguration] ( )
332+ ) { configurationsByID, configuration in
333+ configurationsByID [ configuration. id] = configuration
334+ }
335+ let currentByID = currentConfigurations. reduce (
336+ into: [ String: ProviderAccountConfiguration] ( )
337+ ) { configurationsByID, configuration in
338+ configurationsByID [ configuration. id] = configuration
339+ }
340+ guard previousByID. keys == currentByID. keys else {
341+ return true
342+ }
343+ return previousByID. contains { accountID, previousConfiguration in
344+ guard let currentConfiguration = currentByID [ accountID] else {
345+ return true
346+ }
347+ return !refreshService. hasSameRefreshInputs (
348+ previousConfiguration,
349+ currentConfiguration
350+ )
351+ }
352+ }
353+
280354 private func processUsageAlerts(
281355 results: [ ProviderUsageResult ] ,
282356 preserving preservedAccountIDs: Set < String >
0 commit comments