Skip to content

Commit 4a72069

Browse files
committed
refactor(webview): consolidate view-local state persistence
1 parent 92fb602 commit 4a72069

3 files changed

Lines changed: 35 additions & 21 deletions

File tree

src/core/webview/ClineProvider.ts

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -566,17 +566,8 @@ export class ClineProvider
566566
* Save a single view-local state value. Only non-secret selections are persisted durably.
567567
*/
568568
private async saveViewState(key: keyof ExtensionState, value: any): Promise<void> {
569-
if (key === "mode") {
570-
await this.savePersistedViewState({ mode: value })
571-
} else if (key === "currentApiConfigName") {
572-
await this.savePersistedViewState({ currentApiConfigName: value })
573-
}
574-
575-
if (value === undefined || value === null) {
576-
delete this.viewLocalState[key]
577-
} else {
578-
this.viewLocalState[key] = value
579-
}
569+
await this._saveViewLocalStateFromMutation({ [key]: value } as Partial<RooCodeSettings> &
570+
Partial<ExtensionState>)
580571

581572
this.log(`[saveViewState] Saved ${String(key)} for viewId ${this.viewId}`)
582573
}
@@ -1855,11 +1846,9 @@ export class ClineProvider
18551846
this.updateGlobalState("currentApiConfigName", name),
18561847
this.providerSettingsManager.setModeConfig(mode, id),
18571848
this.contextProxy.setProviderSettings(providerSettings),
1858-
this.saveViewState("currentApiConfigName", name),
1859-
this.saveViewState("apiConfiguration", providerSettings),
18601849
])
18611850

1862-
this._updateViewLocalStateFromMutation({
1851+
await this._saveViewLocalStateFromMutation({
18631852
listApiConfigMeta,
18641853
currentApiConfigName: name,
18651854
apiConfiguration: providerSettings,
@@ -1959,11 +1948,9 @@ export class ClineProvider
19591948
this.contextProxy.setValue("listApiConfigMeta", listApiConfigMeta),
19601949
this.contextProxy.setValue("currentApiConfigName", name),
19611950
this.contextProxy.setProviderSettings(providerSettings),
1962-
this.saveViewState("currentApiConfigName", name),
1963-
this.saveViewState("apiConfiguration", providerSettings),
19641951
])
19651952

1966-
this._updateViewLocalStateFromMutation({
1953+
await this._saveViewLocalStateFromMutation({
19671954
listApiConfigMeta,
19681955
currentApiConfigName: name,
19691956
apiConfiguration: providerSettings,
@@ -3102,8 +3089,7 @@ export class ClineProvider
31023089

31033090
public async setValue<K extends keyof RooCodeSettings>(key: K, value: RooCodeSettings[K]) {
31043091
await this.contextProxy.setValue(key, value)
3105-
this._updateViewLocalStateFromMutation({ [key]: value })
3106-
await this._persistViewLocalStateFromMutation({ [key]: value })
3092+
await this._saveViewLocalStateFromMutation({ [key]: value })
31073093
}
31083094

31093095
public getValue<K extends keyof RooCodeSettings>(key: K) {
@@ -3116,8 +3102,14 @@ export class ClineProvider
31163102

31173103
public async setValues(values: RooCodeSettings) {
31183104
await this.contextProxy.setValues(values)
3119-
this._updateViewLocalStateFromMutation(values)
3105+
await this._saveViewLocalStateFromMutation(values)
3106+
}
3107+
3108+
private async _saveViewLocalStateFromMutation(
3109+
values: Partial<RooCodeSettings> & Partial<ExtensionState>,
3110+
): Promise<void> {
31203111
await this._persistViewLocalStateFromMutation(values)
3112+
this._updateViewLocalStateFromMutation(values)
31213113
}
31223114

31233115
/**

src/core/webview/__tests__/ClineProvider.parallelMode.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,24 @@ describe("ClineProvider - Parallel Mode Support", () => {
844844

845845
await provider.dispose()
846846
})
847+
it("should not update viewLocalState when durable view-state persistence fails", async () => {
848+
const provider = new ClineProvider(mockContext, mockOutputChannel, "sidebar", new ContextProxy(mockContext))
849+
const providerAccess = provider as unknown as {
850+
setViewStateId: (viewStateId: string) => Promise<void>
851+
saveViewState: (key: keyof ExtensionState, value: unknown) => Promise<void>
852+
viewLocalState: Partial<ExtensionState>
853+
}
854+
vi.spyOn(provider.contextProxy, "setValue").mockRejectedValueOnce(new Error("persist failed"))
855+
856+
await providerAccess.setViewStateId("stable-sidebar-view")
857+
858+
await expect(providerAccess.saveViewState("mode", "architect")).rejects.toThrow("persist failed")
859+
expect(providerAccess.viewLocalState).not.toHaveProperty("mode")
860+
expect(provider.contextProxy.getValue("viewStates")).toBeUndefined()
861+
862+
await provider.dispose()
863+
})
864+
847865
it("should merge concurrent persisted updates from separate provider instances without lost viewStates", async () => {
848866
const provider1 = new ClineProvider(
849867
mockContext,
@@ -1175,6 +1193,7 @@ describe("ClineProvider - Parallel Mode Support", () => {
11751193
vi.spyOn(provider.providerSettingsManager, "listConfig").mockResolvedValueOnce([
11761194
{ id: "new-profile-id", name: "new-profile", apiProvider: "openrouter" },
11771195
] as any)
1196+
const saveViewStateSpy = vi.spyOn(provider as any, "saveViewState")
11781197
;(provider as any).viewLocalState = {
11791198
currentApiConfigName: "stale-profile",
11801199
apiConfiguration: { apiProvider: "anthropic" },
@@ -1183,6 +1202,7 @@ describe("ClineProvider - Parallel Mode Support", () => {
11831202
await provider.activateProviderProfile({ name: "new-profile" })
11841203
const state = await provider.getState()
11851204

1205+
expect(saveViewStateSpy).not.toHaveBeenCalled()
11861206
expect(state.currentApiConfigName).toBe("new-profile")
11871207
expect(state.apiConfiguration).toMatchObject({
11881208
apiProvider: "openrouter",
@@ -1197,6 +1217,7 @@ describe("ClineProvider - Parallel Mode Support", () => {
11971217
vi.spyOn(provider.providerSettingsManager, "listConfig").mockResolvedValue([
11981218
{ id: "test-id", name: "saved-profile", apiProvider: "bedrock" },
11991219
] as any)
1220+
const saveViewStateSpy = vi.spyOn(provider as any, "saveViewState")
12001221
;(provider as any).viewLocalState = {
12011222
currentApiConfigName: "stale-profile",
12021223
apiConfiguration: { apiProvider: "anthropic" },
@@ -1208,6 +1229,7 @@ describe("ClineProvider - Parallel Mode Support", () => {
12081229
} as any)
12091230
const state = await provider.getState()
12101231

1232+
expect(saveViewStateSpy).not.toHaveBeenCalled()
12111233
expect(state.currentApiConfigName).toBe("saved-profile")
12121234
expect(state.apiConfiguration).toMatchObject({
12131235
apiProvider: "bedrock",

src/eslint-suppressions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,7 @@
10861086
},
10871087
"core/webview/__tests__/ClineProvider.parallelMode.spec.ts": {
10881088
"@typescript-eslint/no-explicit-any": {
1089-
"count": 141
1089+
"count": 143
10901090
}
10911091
},
10921092
"core/webview/__tests__/ClineProvider.spec.ts": {

0 commit comments

Comments
 (0)