Skip to content

Commit c46f725

Browse files
authored
Fix "show toast on new install" logic (#4024)
1 parent de8d2ab commit c46f725

File tree

7 files changed

+106
-21
lines changed

7 files changed

+106
-21
lines changed

extension/src/extension.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { commands, env, ExtensionContext, ViewColumn } from 'vscode'
1+
import { commands, ExtensionContext, ViewColumn } from 'vscode'
22
import { DvcConfig } from './cli/dvc/config'
33
import { DvcExecutor } from './cli/dvc/executor'
44
import { DvcRunner } from './cli/dvc/runner'
@@ -254,9 +254,13 @@ class Extension extends Disposable {
254254
).contributes.walkthroughs[0].id
255255
)
256256

257-
registerPersistenceCommands(context.workspaceState, this.internalCommands)
257+
registerPersistenceCommands(
258+
context.workspaceState,
259+
context.globalState,
260+
this.internalCommands
261+
)
258262

259-
void showSetupOnFirstUse(env.isNewAppInstall)
263+
void showSetupOnFirstUse(context.globalState, context.workspaceState)
260264
this.dispose.track(recommendRedHatExtensionOnce())
261265

262266
this.dispose.track(new LanguageClient())

extension/src/persistence/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ export enum PersistenceKey {
1919
PLOT_SELECTED_METRICS = 'plotSelectedMetrics:',
2020
PLOT_TEMPLATE_ORDER = 'plotTemplateOrder:'
2121
}
22+
23+
export enum GlobalPersistenceKey {
24+
INSTALLED = 'dvc.installed'
25+
}

extension/src/persistence/register.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { InternalCommands } from '../commands/internal'
55

66
export const registerPersistenceCommands = (
77
workspaceState: Memento,
8+
globalState: Memento,
89
internalCommands: InternalCommands
910
) => {
1011
internalCommands.registerExternalCommand(RegisteredCommands.RESET_STATE, () =>
11-
resetPersistedState(workspaceState)
12+
resetPersistedState(workspaceState, globalState)
1213
)
1314
}

extension/src/persistence/util.test.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { commands } from 'vscode'
2-
import { PersistenceKey } from './constants'
2+
import { GlobalPersistenceKey, PersistenceKey } from './constants'
33
import { resetPersistedState } from './util'
44
import { buildMockMemento } from '../test/util'
55
import {
@@ -20,23 +20,28 @@ describe('Persistence util', () => {
2020
describe('resetPersistedState', () => {
2121
it('should reload the window', async () => {
2222
const workspaceState = buildMockMemento()
23+
const globalState = buildMockMemento()
2324

24-
await resetPersistedState(workspaceState)
25+
await resetPersistedState(workspaceState, globalState)
2526

2627
expect(mockedCommands.executeCommand).toHaveBeenCalledWith(
2728
'workbench.action.reloadWindow'
2829
)
2930
})
3031

3132
it('should reset all values from all dvc roots', async () => {
32-
const persistedState = {
33+
const persistedWorkspaceState = {
3334
[PersistenceKey.PLOT_HEIGHT + 'root1']: DEFAULT_HEIGHT,
3435
[PersistenceKey.PLOT_NB_ITEMS_PER_ROW_OR_WIDTH + 'root2']:
3536
DEFAULT_SECTION_NB_ITEMS_PER_ROW_OR_WIDTH
3637
}
37-
const workspaceState = buildMockMemento(persistedState)
38+
const persistedGlobalState = {
39+
[GlobalPersistenceKey.INSTALLED]: true
40+
}
41+
const workspaceState = buildMockMemento(persistedWorkspaceState)
42+
const globalState = buildMockMemento(persistedGlobalState)
3843

39-
await resetPersistedState(workspaceState)
44+
await resetPersistedState(workspaceState, globalState)
4045

4146
expect(
4247
workspaceState.get(PersistenceKey.PLOT_HEIGHT + 'root1')
@@ -46,6 +51,7 @@ describe('Persistence util', () => {
4651
PersistenceKey.PLOT_NB_ITEMS_PER_ROW_OR_WIDTH + 'root2'
4752
)
4853
).toBeUndefined()
54+
expect(globalState.get(GlobalPersistenceKey.INSTALLED)).toBeUndefined()
4955
})
5056
})
5157
})

extension/src/persistence/util.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { commands, Memento } from 'vscode'
22

3-
export const resetPersistedState = async (workspaceState: Memento) => {
3+
export const resetPersistedState = async (
4+
workspaceState: Memento,
5+
globalState: Memento
6+
) => {
47
for (const persistenceKey of workspaceState.keys()) {
58
await workspaceState.update(persistenceKey, undefined)
69
}
10+
for (const persistenceKey of globalState.keys()) {
11+
await globalState.update(persistenceKey, undefined)
12+
}
713

814
await commands.executeCommand('workbench.action.reloadWindow')
915
}

extension/src/setup/util.test.ts

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { ConfigKey, getConfigValue, setUserConfigValue } from '../vscode/config'
44
import { Toast } from '../vscode/toast'
55
import { Response } from '../vscode/response'
66
import { RegisteredCommands } from '../commands/external'
7+
import { buildMockMemento } from '../test/util'
8+
import { GlobalPersistenceKey, PersistenceKey } from '../persistence/constants'
9+
import { DEFAULT_HEIGHT } from '../plots/webview/contract'
710

811
jest.mock('vscode')
912
jest.mock('../vscode/toast')
@@ -25,19 +28,51 @@ beforeEach(() => {
2528
})
2629

2730
describe('showSetupOnFirstUse', () => {
31+
it('should set a global state key after a new install', async () => {
32+
const workspaceState = buildMockMemento()
33+
const globalState = buildMockMemento()
34+
35+
await showSetupOnFirstUse(globalState, workspaceState)
36+
37+
expect(globalState.get(GlobalPersistenceKey.INSTALLED)).toStrictEqual(true)
38+
})
39+
2840
it('should ask to show the setup page after a new install', async () => {
29-
await showSetupOnFirstUse(true)
41+
const workspaceState = buildMockMemento()
42+
const globalState = buildMockMemento()
43+
44+
await showSetupOnFirstUse(globalState, workspaceState)
45+
3046
expect(mockedAskShowOrCloseOrNever).toHaveBeenCalledTimes(1)
3147
})
3248

3349
it('should not ask to show the setup page when the install is not new', async () => {
34-
await showSetupOnFirstUse(false)
50+
const workspaceState = buildMockMemento()
51+
const globalState = buildMockMemento({
52+
[GlobalPersistenceKey.INSTALLED]: true
53+
})
54+
55+
await showSetupOnFirstUse(globalState, workspaceState)
56+
expect(mockedAskShowOrCloseOrNever).not.toHaveBeenCalled()
57+
})
58+
59+
it('should not ask to show the setup page when the workspace state has data even if the global install key is not set', async () => {
60+
const workspaceState = buildMockMemento({
61+
[PersistenceKey.PLOT_HEIGHT + 'root1']: DEFAULT_HEIGHT
62+
})
63+
const globalState = buildMockMemento()
64+
65+
await showSetupOnFirstUse(globalState, workspaceState)
66+
3567
expect(mockedAskShowOrCloseOrNever).not.toHaveBeenCalled()
3668
})
3769

3870
it('should not ask to show the setup page when the user has set a config option', async () => {
3971
mockedGetConfigValue.mockReturnValueOnce(true)
40-
await showSetupOnFirstUse(true)
72+
const workspaceState = buildMockMemento()
73+
const globalState = buildMockMemento()
74+
75+
await showSetupOnFirstUse(globalState, workspaceState)
4176
expect(mockedAskShowOrCloseOrNever).not.toHaveBeenCalled()
4277
expect(mockedGetConfigValue).toHaveBeenCalledTimes(1)
4378
expect(mockedGetConfigValue).toHaveBeenCalledWith(
@@ -47,7 +82,10 @@ describe('showSetupOnFirstUse', () => {
4782

4883
it('should set the config option if the user responds with never', async () => {
4984
mockedAskShowOrCloseOrNever.mockResolvedValueOnce(Response.NEVER)
50-
await showSetupOnFirstUse(true)
85+
const workspaceState = buildMockMemento()
86+
const globalState = buildMockMemento()
87+
88+
await showSetupOnFirstUse(globalState, workspaceState)
5189

5290
expect(mockedSetConfigValue).toHaveBeenCalledTimes(1)
5391
expect(mockedSetConfigValue).toHaveBeenCalledWith(
@@ -58,7 +96,10 @@ describe('showSetupOnFirstUse', () => {
5896

5997
it('should show the setup page if the user responds with show', async () => {
6098
mockedAskShowOrCloseOrNever.mockResolvedValueOnce(Response.SHOW)
61-
await showSetupOnFirstUse(true)
99+
const workspaceState = buildMockMemento()
100+
const globalState = buildMockMemento()
101+
102+
await showSetupOnFirstUse(globalState, workspaceState)
62103

63104
expect(mockedSetConfigValue).not.toHaveBeenCalled()
64105
expect(mockedExecuteCommand).toHaveBeenCalledWith(
@@ -68,15 +109,21 @@ describe('showSetupOnFirstUse', () => {
68109

69110
it('should take no action if the user closes the dialog', async () => {
70111
mockedAskShowOrCloseOrNever.mockResolvedValueOnce(undefined)
71-
await showSetupOnFirstUse(true)
112+
const workspaceState = buildMockMemento()
113+
const globalState = buildMockMemento()
114+
115+
await showSetupOnFirstUse(globalState, workspaceState)
72116

73117
expect(mockedSetConfigValue).not.toHaveBeenCalled()
74118
expect(mockedExecuteCommand).not.toHaveBeenCalled()
75119
})
76120

77121
it('should take no action if the user respond with close', async () => {
78122
mockedAskShowOrCloseOrNever.mockResolvedValueOnce(Response.CLOSE)
79-
await showSetupOnFirstUse(true)
123+
const workspaceState = buildMockMemento()
124+
const globalState = buildMockMemento()
125+
126+
await showSetupOnFirstUse(globalState, workspaceState)
80127

81128
expect(mockedSetConfigValue).not.toHaveBeenCalled()
82129
expect(mockedExecuteCommand).not.toHaveBeenCalled()

extension/src/setup/util.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { commands } from 'vscode'
1+
/* eslint-disable import/no-unused-modules */
2+
import { Memento, commands } from 'vscode'
23
import { RegisteredCommands } from '../commands/external'
34
import { ConfigKey, getConfigValue, setUserConfigValue } from '../vscode/config'
45
import { Response } from '../vscode/response'
56
import { Toast } from '../vscode/toast'
7+
import { GlobalPersistenceKey } from '../persistence/constants'
68

7-
export const showSetupOnFirstUse = async (
8-
isNewAppInstall: boolean
9-
): Promise<void> => {
9+
const showSetupToast = async (isNewAppInstall: boolean): Promise<void> => {
1010
if (
1111
!isNewAppInstall ||
1212
getConfigValue<boolean>(ConfigKey.DO_NOT_SHOW_SETUP_AFTER_INSTALL)
@@ -25,3 +25,20 @@ export const showSetupOnFirstUse = async (
2525
void setUserConfigValue(ConfigKey.DO_NOT_SHOW_SETUP_AFTER_INSTALL, true)
2626
}
2727
}
28+
29+
export const showSetupOnFirstUse = (
30+
globalState: Memento,
31+
workspaceState: Memento
32+
): Promise<void> | undefined => {
33+
const installed = globalState.get(GlobalPersistenceKey.INSTALLED, false)
34+
35+
if (installed) {
36+
return
37+
}
38+
39+
void globalState.update(GlobalPersistenceKey.INSTALLED, true)
40+
41+
// old users won't have the installedKey even if it's not a new install
42+
const workspaceKeys = workspaceState.keys()
43+
return showSetupToast(workspaceKeys.length === 0)
44+
}

0 commit comments

Comments
 (0)