diff --git a/src/avatars.ts b/src/avatars.ts index a63cd47..e597eeb 100644 --- a/src/avatars.ts +++ b/src/avatars.ts @@ -4,7 +4,7 @@ import { configuration } from './configuration'; import { Container } from './container'; import type { GitRevisionReference } from './git/models/reference'; import { getGitHubNoReplyAddressParts } from './git/remotes/github'; -import { StorageKeys } from './storage'; +import type { StoredAvatar } from './storage'; import { debounce } from './system/function'; import { filterMap } from './system/iterable'; import { base64, equalsIgnoreCase, md5 } from './system/string'; @@ -20,18 +20,18 @@ _onDidFetchAvatar.event( ? [ ...filterMap(avatarCache, ([key, avatar]) => avatar.uri != null - ? [ + ? ([ key, { uri: avatar.uri.toString(), timestamp: avatar.timestamp, }, - ] + ] as [string, StoredAvatar]) : undefined, ), ] : undefined; - void Container.instance.storage.store(StorageKeys.Avatars, avatars); + void Container.instance.storage.store('avatars', avatars); }, 1000), ); @@ -46,11 +46,6 @@ interface Avatar { retries: number; } -interface SerializedAvatar { - uri: string; - timestamp: number; -} - let avatarCache: Map | undefined; const avatarQueue = new Map>(); @@ -140,7 +135,7 @@ function createOrUpdateAvatar( function ensureAvatarCache(cache: Map | undefined): asserts cache is Map { if (cache == null) { const avatars: [string, Avatar][] | undefined = Container.instance.storage - .get<[string, SerializedAvatar][]>(StorageKeys.Avatars) + .get('avatars') ?.map<[string, Avatar]>(([key, avatar]) => [ key, { @@ -249,7 +244,7 @@ export function getPresenceDataUri(status: ContactPresenceStatus) { export function resetAvatarCache(reset: 'all' | 'failed' | 'fallback') { switch (reset) { case 'all': - void Container.instance.storage.delete(StorageKeys.Avatars); + void Container.instance.storage.delete('avatars'); avatarCache?.clear(); avatarQueue.clear(); break; diff --git a/src/commands/gitCommands.utils.ts b/src/commands/gitCommands.utils.ts index b95ff5e..ba08392 100644 --- a/src/commands/gitCommands.utils.ts +++ b/src/commands/gitCommands.utils.ts @@ -4,7 +4,6 @@ import { ContextKeys } from '../constants'; import type { Container } from '../container'; import { getContext } from '../context'; import type { Usage } from '../storage'; -import { WorkspaceStorageKeys } from '../storage'; import { BranchGitCommand } from './git/branch'; import { CherryPickGitCommand } from './git/cherry-pick'; import { CoAuthorsGitCommand } from './git/coauthors'; @@ -99,7 +98,7 @@ export class PickCommandStep implements QuickPickStep { ].filter((i: T | undefined): i is T => i != null); if (configuration.get('gitCommands.sortBy') === GitCommandSorting.Usage) { - const usage = this.container.storage.getWorkspace(WorkspaceStorageKeys.GitCommandPaletteUsage); + const usage = this.container.storage.getWorkspace('gitComandPalette:usage'); if (usage != null) { this.items.sort((a, b) => (usage[b.key] ?? 0) - (usage[a.key] ?? 0)); } @@ -139,12 +138,12 @@ export class PickCommandStep implements QuickPickStep { } private async updateCommandUsage(id: string, timestamp: number) { - let usage = this.container.storage.getWorkspace(WorkspaceStorageKeys.GitCommandPaletteUsage); + let usage = this.container.storage.getWorkspace(`gitComandPalette:usage`); if (usage === undefined) { usage = Object.create(null) as Usage; } usage[id] = timestamp; - await this.container.storage.storeWorkspace(WorkspaceStorageKeys.GitCommandPaletteUsage, usage); + await this.container.storage.storeWorkspace(`gitComandPalette:usage`, usage); } } diff --git a/src/container.ts b/src/container.ts index 058b8f7..8cb0fd3 100644 --- a/src/container.ts +++ b/src/container.ts @@ -25,7 +25,7 @@ import { GraphWebview } from './plus/webviews/graph/graphWebview'; import { TimelineWebview } from './plus/webviews/timeline/timelineWebview'; import { TimelineWebviewView } from './plus/webviews/timeline/timelineWebviewView'; import { StatusBarController } from './statusbar/statusBarController'; -import { Storage } from './storage'; +import type { Storage } from './storage'; import { executeCommand } from './system/command'; import { log } from './system/decorators/log'; import { memoize } from './system/decorators/memoize'; @@ -68,10 +68,10 @@ export class Container { }, }); - static create(context: ExtensionContext, insiders: boolean) { + static create(context: ExtensionContext, storage: Storage, insiders: boolean) { if (Container.#instance != null) throw new Error('Container is already initialized'); - Container.#instance = new Container(context, insiders); + Container.#instance = new Container(context, storage, insiders); return Container.#instance; } @@ -138,12 +138,12 @@ export class Container { private _configAffectedByModeRegex: RegExp | undefined; private _terminalLinks: GitTerminalLinkProvider | undefined; - private constructor(context: ExtensionContext, insiders: boolean) { + private constructor(context: ExtensionContext, storage: Storage, insiders: boolean) { this._context = context; this._insiders = insiders; this.ensureModeApplied(); - context.subscriptions.push((this._storage = new Storage(this._context))); + context.subscriptions.push((this._storage = storage)); context.subscriptions.push(configuration.onWillChange(this.onConfigurationChanging, this)); diff --git a/src/env/node/git/localGitProvider.ts b/src/env/node/git/localGitProvider.ts index 3897fb0..e9dd556 100644 --- a/src/env/node/git/localGitProvider.ts +++ b/src/env/node/git/localGitProvider.ts @@ -91,7 +91,6 @@ import { showGitMissingErrorMessage, showGitVersionUnsupportedErrorMessage, } from '../../../messages'; -import { WorkspaceStorageKeys } from '../../../storage'; import { countStringLength, filterMap } from '../../../system/array'; import { TimedCancellationSource } from '../../../system/cancellation'; import { gate } from '../../../system/decorators/gate'; @@ -264,8 +263,7 @@ export class LocalGitProvider implements GitProvider, Disposable { void subscribeToScmOpenCloseRepository.call(this); const potentialGitPaths = - configuration.getAny('git.path') ?? - this.container.storage.getWorkspace(WorkspaceStorageKeys.GitPath, undefined); + configuration.getAny('git.path') ?? this.container.storage.getWorkspace('gitPath'); const start = hrtime(); @@ -289,7 +287,7 @@ export class LocalGitProvider implements GitProvider, Disposable { const location = await any(findGitPromise, findGitFromSCMPromise); // Save the found git path, but let things settle first to not impact startup performance setTimeout(() => { - void this.container.storage.storeWorkspace(WorkspaceStorageKeys.GitPath, location.path); + void this.container.storage.storeWorkspace('gitPath', location.path); }, 1000); if (scope != null) { diff --git a/src/extension.ts b/src/extension.ts index 3b9d8e5..9180e71 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -19,7 +19,7 @@ import { showWhatsNewMessage, } from './messages'; import { registerPartnerActionRunners } from './partners'; -import { StorageKeys, SyncedStorageKeys } from './storage'; +import { DeprecatedStorageKeys, Storage, SyncedStorageKeys } from './storage'; import { executeCommand, executeCoreCommand, registerCommands } from './system/command'; import { setDefaultDateLocales } from './system/date'; import { once } from './system/event'; @@ -94,10 +94,9 @@ export async function activate(context: ExtensionContext): Promise(SyncedStorageKeys.Version); - const localVersion = - context.globalState.get(StorageKeys.Version) ?? - context.globalState.get(StorageKeys.Deprecated_Version); + const storage = new Storage(context); + const syncedVersion = storage.get('synced:version'); + const localVersion = storage.get('version') ?? context.globalState.get(DeprecatedStorageKeys.Version); let previousVersion: string | undefined; if (localVersion == null || syncedVersion == null) { @@ -110,13 +109,13 @@ export async function activate(context: ExtensionContext): Promise( - SyncedStorageKeys.HomeViewWelcomeVisible, + exitMessage = `syncedVersion=${syncedVersion}, localVersion=${localVersion}, previousVersion=${previousVersion}, welcome=${storage.get( + 'views:welcome:visible', )}`; } if (previousVersion == null) { - void context.globalState.update(SyncedStorageKeys.HomeViewWelcomeVisible, true); + void storage.store('views:welcome:visible', true); } Configuration.configure(context); @@ -132,7 +131,7 @@ export async function activate(context: ExtensionContext): Promise { context.subscriptions.push(...registerCommands(container)); registerBuiltInActionRunners(container); @@ -140,11 +139,11 @@ export async function activate(context: ExtensionContext): Promise { if (!e.focused) return; disposable.dispose(); // If the window is now focused and we are pending the welcome, clear the pending state and show the welcome - if (container.storage.get(StorageKeys.PendingWelcomeOnFocus) === true) { - void container.storage.delete(StorageKeys.PendingWelcomeOnFocus); + if (container.storage.get('pendingWelcomeOnFocus') === true) { + void container.storage.delete('pendingWelcomeOnFocus'); if (configuration.get('showWelcomeOnInstall')) { void executeCommand(Commands.ShowWelcomePage); } @@ -289,19 +288,19 @@ async function showWelcomeOrWhatsNew(container: Container, version: string, prev if (configuration.get('showWhatsNewAfterUpgrades')) { if (window.state.focused) { - await container.storage.delete(StorageKeys.PendingWhatsNewOnFocus); + await container.storage.delete('pendingWhatsNewOnFocus'); await showWhatsNewMessage(version); } else { // Save pending on window getting focus - await container.storage.store(StorageKeys.PendingWhatsNewOnFocus, true); + await container.storage.store('pendingWhatsNewOnFocus', true); const disposable = window.onDidChangeWindowState(e => { if (!e.focused) return; disposable.dispose(); // If the window is now focused and we are pending the what's new, clear the pending state and show the what's new - if (container.storage.get(StorageKeys.PendingWhatsNewOnFocus) === true) { - void container.storage.delete(StorageKeys.PendingWhatsNewOnFocus); + if (container.storage.get('pendingWhatsNewOnFocus') === true) { + void container.storage.delete('pendingWhatsNewOnFocus'); if (configuration.get('showWhatsNewAfterUpgrades')) { void showWhatsNewMessage(version); } diff --git a/src/git/gitProviderService.ts b/src/git/gitProviderService.ts index c640506..9ee246d 100644 --- a/src/git/gitProviderService.ts +++ b/src/git/gitProviderService.ts @@ -21,7 +21,6 @@ import { Logger } from '../logger'; import type { SubscriptionChangeEvent } from '../plus/subscription/subscriptionService'; import type { RepoComparisonKey } from '../repositories'; import { asRepoComparisonKey, Repositories } from '../repositories'; -import { WorkspaceStorageKeys } from '../storage'; import type { FreeSubscriptionPlans, RequiredSubscriptionPlans, Subscription } from '../subscription'; import { getSubscriptionPlanPriority, isSubscriptionPaidPlan, SubscriptionPlanId } from '../subscription'; import { groupByFilterMap, groupByMap } from '../system/array'; @@ -707,9 +706,7 @@ export class GitProviderService implements Disposable { let disabled = !enabled; // If we think we should be disabled during startup, check if we have a saved value from the last time this repo was loaded if (!enabled && this._initializing) { - disabled = !( - this.container.storage.getWorkspace(WorkspaceStorageKeys.AssumeRepositoriesOnStartup) ?? true - ); + disabled = !(this.container.storage.getWorkspace('assumeRepositoriesOnStartup') ?? true); } if (this._context.enabled === enabled && this._context.disabled === disabled) return; @@ -729,7 +726,7 @@ export class GitProviderService implements Disposable { await Promise.allSettled(promises); if (!this._initializing) { - void this.container.storage.storeWorkspace(WorkspaceStorageKeys.AssumeRepositoriesOnStartup, enabled); + void this.container.storage.storeWorkspace('assumeRepositoriesOnStartup', enabled); } } diff --git a/src/git/models/branch.ts b/src/git/models/branch.ts index 0241023..dfae790 100644 --- a/src/git/models/branch.ts +++ b/src/git/models/branch.ts @@ -1,7 +1,5 @@ import { BranchSorting, configuration, DateStyle } from '../../configuration'; import { Container } from '../../container'; -import type { Starred } from '../../storage'; -import { WorkspaceStorageKeys } from '../../storage'; import { formatDate, fromNow } from '../../system/date'; import { debug } from '../../system/decorators/log'; import { memoize } from '../../system/decorators/memoize'; @@ -181,7 +179,7 @@ export class GitBranch implements GitBranchReference { } get starred() { - const starred = Container.instance.storage.getWorkspace(WorkspaceStorageKeys.StarredBranches); + const starred = Container.instance.storage.getWorkspace('starred:branches'); return starred !== undefined && starred[this.id] === true; } diff --git a/src/git/models/reference.ts b/src/git/models/reference.ts index 00cc55b..7d3d50a 100644 --- a/src/git/models/reference.ts +++ b/src/git/models/reference.ts @@ -53,25 +53,22 @@ export namespace GitRevision { export function shorten( ref: string | undefined, - { - force, - strings = {}, - }: { + options?: { force?: boolean; strings?: { uncommitted?: string; uncommittedStaged?: string; working?: string }; - } = {}, + }, ) { if (ref === deletedOrMissing) return '(deleted)'; - if (!ref) return strings.working ?? ''; + if (!ref) return options?.strings?.working ?? ''; if (isUncommitted(ref)) { return isUncommittedStaged(ref) - ? strings.uncommittedStaged ?? 'Index' - : strings.uncommitted ?? 'Working Tree'; + ? options?.strings?.uncommittedStaged ?? 'Index' + : options?.strings?.uncommitted ?? 'Working Tree'; } if (GitRevision.isRange(ref)) return ref; - if (!force && !isShaLike(ref)) return ref; + if (!options?.force && !isShaLike(ref)) return ref; // Don't allow shas to be shortened to less than 5 characters const len = Math.max(5, configuration.get('advanced.abbreviatedShaLength')); diff --git a/src/git/models/remote.ts b/src/git/models/remote.ts index 6929a39..a037e0c 100644 --- a/src/git/models/remote.ts +++ b/src/git/models/remote.ts @@ -1,5 +1,4 @@ import { Container } from '../../container'; -import { WorkspaceStorageKeys } from '../../storage'; import { sortCompare } from '../../system/string'; import type { RemoteProvider } from '../remotes/provider'; import { RichRemoteProvider } from '../remotes/provider'; @@ -60,7 +59,7 @@ export class GitRemote(WorkspaceStorageKeys.DefaultRemote); + const defaultRemote = Container.instance.storage.getWorkspace('remote:default'); return this.id === defaultRemote; } diff --git a/src/git/models/repository.ts b/src/git/models/repository.ts index d2ff4d3..919cf0f 100644 --- a/src/git/models/repository.ts +++ b/src/git/models/repository.ts @@ -9,8 +9,7 @@ import type { FeatureAccess, Features, PlusFeatures } from '../../features'; import { Logger } from '../../logger'; import { showCreatePullRequestPrompt, showGenericErrorMessage } from '../../messages'; import { asRepoComparisonKey } from '../../repositories'; -import type { Starred } from '../../storage'; -import { WorkspaceStorageKeys } from '../../storage'; +import type { StoredStarred } from '../../storage'; import { filterMap, groupByMap } from '../../system/array'; import { executeActionCommand, executeCoreGitCommand } from '../../system/command'; import { formatDate, fromNow } from '../../system/date'; @@ -856,13 +855,13 @@ export class Repository implements Disposable { } async setRemoteAsDefault(remote: GitRemote, value: boolean = true) { - await this.container.storage.storeWorkspace(WorkspaceStorageKeys.DefaultRemote, value ? remote.id : undefined); + await this.container.storage.storeWorkspace('remote:default', value ? remote.id : undefined); this.fireChange(RepositoryChange.Remotes, RepositoryChange.RemoteProviders); } get starred() { - const starred = this.container.storage.getWorkspace(WorkspaceStorageKeys.StarredRepositories); + const starred = this.container.storage.getWorkspace('starred:repositories'); return starred != null && starred[this.id] === true; } @@ -932,18 +931,19 @@ export class Repository implements Disposable { private async updateStarred(star: boolean, branch?: GitBranch) { if (branch != null) { - await this.updateStarredCore(WorkspaceStorageKeys.StarredBranches, branch.id, star); + await this.updateStarredCore('branches', branch.id, star); } else { - await this.updateStarredCore(WorkspaceStorageKeys.StarredRepositories, this.id, star); + await this.updateStarredCore('repositories', this.id, star); } this.fireChange(RepositoryChange.Starred); } - private async updateStarredCore(key: WorkspaceStorageKeys, id: string, star: boolean) { - let starred = this.container.storage.getWorkspace(key); + private async updateStarredCore(key: 'branches' | 'repositories', id: string, star: boolean) { + const storageKey = `starred:${key}` as const; + let starred = this.container.storage.getWorkspace(storageKey); if (starred === undefined) { - starred = Object.create(null) as Starred; + starred = Object.create(null) as StoredStarred; } if (star) { @@ -952,7 +952,7 @@ export class Repository implements Disposable { const { [id]: _, ...rest } = starred; starred = rest; } - await this.container.storage.storeWorkspace(key, starred); + await this.container.storage.storeWorkspace(storageKey, starred); this.fireChange(RepositoryChange.Starred); } diff --git a/src/git/remotes/provider.ts b/src/git/remotes/provider.ts index 09e0431..024c31b 100644 --- a/src/git/remotes/provider.ts +++ b/src/git/remotes/provider.ts @@ -10,7 +10,6 @@ import { AuthenticationError, ProviderRequestClientError } from '../../errors'; import { Logger } from '../../logger'; import { showIntegrationDisconnectedTooManyFailedRequestsWarningMessage } from '../../messages'; import type { IntegrationAuthenticationSessionDescriptor } from '../../plus/integrationAuthentication'; -import { WorkspaceStorageKeys } from '../../storage'; import { gate } from '../../system/decorators/gate'; import { debug, getLogScope, log } from '../../system/decorators/log'; import { encodeUrl } from '../../system/encoding'; @@ -310,8 +309,8 @@ export abstract class RichRemoteProvider extends RemoteProvider { return this.custom ? `${this.name}:${this.domain}` : this.name; } - private get connectedKey(): `${WorkspaceStorageKeys.ConnectedPrefix}${string}` { - return `${WorkspaceStorageKeys.ConnectedPrefix}${this.key}`; + private get connectedKey(): `connected:${string}` { + return `connected:${this.key}`; } get maybeConnected(): boolean | undefined { @@ -664,7 +663,7 @@ export abstract class RichRemoteProvider extends RemoteProvider { if (createIfNeeded) { await container.storage.deleteWorkspace(this.connectedKey); - } else if (container.storage.getWorkspace(this.connectedKey) === false) { + } else if (container.storage.getWorkspace(this.connectedKey) === false) { return undefined; } diff --git a/src/plus/subscription/authenticationProvider.ts b/src/plus/subscription/authenticationProvider.ts index 52aae95..397e326 100644 --- a/src/plus/subscription/authenticationProvider.ts +++ b/src/plus/subscription/authenticationProvider.ts @@ -7,7 +7,6 @@ import { authentication, Disposable, EventEmitter, extensions, window } from 'vs import { uuid } from '@env/crypto'; import type { Container } from '../../container'; import { Logger } from '../../logger'; -import { StorageKeys } from '../../storage'; import { debug, getLogScope } from '../../system/decorators/log'; import type { ServerConnection } from './serverConnection'; @@ -173,7 +172,7 @@ export class SubscriptionAuthenticationProvider implements AuthenticationProvide private _migrated: boolean | undefined; async tryMigrateSession(): Promise { if (this._migrated == null) { - this._migrated = this.container.storage.get(StorageKeys.MigratedAuthentication, false); + this._migrated = this.container.storage.get('plus:migratedAuthentication', false); } if (this._migrated) return undefined; @@ -209,7 +208,7 @@ export class SubscriptionAuthenticationProvider implements AuthenticationProvide Logger.error(ex, 'Unable to migrate authentication'); } finally { this._migrated = true; - void this.container.storage.store(StorageKeys.MigratedAuthentication, true); + void this.container.storage.store('plus:migratedAuthentication', true); } return session; } diff --git a/src/plus/subscription/subscriptionService.ts b/src/plus/subscription/subscriptionService.ts index d9c722d..67969b7 100644 --- a/src/plus/subscription/subscriptionService.ts +++ b/src/plus/subscription/subscriptionService.ts @@ -28,7 +28,6 @@ import { setContext } from '../../context'; import { AccountValidationError } from '../../errors'; import type { RepositoriesChangeEvent } from '../../git/gitProviderService'; import { Logger } from '../../logger'; -import { StorageKeys } from '../../storage'; import type { Subscription } from '../../subscription'; import { computeSubscriptionState, @@ -795,7 +794,7 @@ export class SubscriptionService implements Disposable { } private getStoredSubscription(): Subscription | undefined { - const storedSubscription = this.container.storage.get>(StorageKeys.Subscription); + const storedSubscription = this.container.storage.get('premium:subscription'); const subscription = storedSubscription?.data; if (subscription != null) { @@ -812,7 +811,7 @@ export class SubscriptionService implements Disposable { } private async storeSubscription(subscription: Subscription): Promise { - return this.container.storage.store>(StorageKeys.Subscription, { + return this.container.storage.store('premium:subscription', { v: 1, data: subscription, }); @@ -970,8 +969,3 @@ interface GKUser { status: 'activated' | 'pending'; firstGitLensCheckIn?: string; } - -interface Stored { - v: SchemaVersion; - data: T; -} diff --git a/src/plus/webviews/graph/graphWebview.ts b/src/plus/webviews/graph/graphWebview.ts index aecb699..8230137 100644 --- a/src/plus/webviews/graph/graphWebview.ts +++ b/src/plus/webviews/graph/graphWebview.ts @@ -2,6 +2,7 @@ import type { CommitType } from '@gitkraken/gitkraken-components'; import { commitNodeType, mergeNodeType, stashNodeType } from '@gitkraken/gitkraken-components'; import type { Disposable, Event } from 'vscode'; import { EventEmitter, ViewColumn, window } from 'vscode'; +import type { GraphColumnConfig } from '../../../configuration'; import { configuration } from '../../../configuration'; import { Commands } from '../../../constants'; import type { Container } from '../../../container'; @@ -14,19 +15,11 @@ import type { GitRemote } from '../../../git/models/remote'; import type { Repository, RepositoryChangeEvent } from '../../../git/models/repository'; import type { GitTag } from '../../../git/models/tag'; import { RepositoryPicker } from '../../../quickpicks/repositoryPicker'; -import { WorkspaceStorageKeys } from '../../../storage'; import type { IpcMessage } from '../../../webviews/protocol'; import { onIpc } from '../../../webviews/protocol'; import { WebviewWithConfigBase } from '../../../webviews/webviewWithConfigBase'; import { ensurePlusFeaturesEnabled } from '../../subscription/utils'; -import type { - GraphColumnConfig, - GraphColumnConfigDictionary, - GraphCommit, - GraphConfig as GraphConfigWithColumns, - GraphRepository, - State, -} from './protocol'; +import type { GraphCommit, GraphCompositeConfig, GraphRepository, State } from './protocol'; import { ColumnChangeCommandType, DidChangeCommitsNotificationType, @@ -87,17 +80,13 @@ export class GraphWebview extends WebviewWithConfigBase { private dismissPreview() { this.previewBanner = false; - void this.container.storage.storeWorkspace(WorkspaceStorageKeys.GraphPreview, false); + void this.container.storage.storeWorkspace('graph:preview', false); } private changeColumn(name: string, config: GraphColumnConfig) { - const columns = - this.container.storage.getWorkspace(WorkspaceStorageKeys.GraphColumns) ?? {}; + const columns = this.container.storage.getWorkspace('graph:columns') ?? {}; columns[name] = config; - void this.container.storage.storeWorkspace( - WorkspaceStorageKeys.GraphColumns, - columns, - ); + void this.container.storage.storeWorkspace('graph:columns', columns); void this.notifyDidChangeConfig(); } @@ -274,13 +263,11 @@ export class GraphWebview extends WebviewWithConfigBase { return repositories.find(r => r.path === repoPath); } - private getConfig(): GraphConfigWithColumns { + private getConfig(): GraphCompositeConfig { const settings = configuration.get('graph'); - const config: GraphConfigWithColumns = { + const config: GraphCompositeConfig = { ...settings, - columns: this.container.storage.getWorkspace( - WorkspaceStorageKeys.GraphColumns, - ), + columns: this.container.storage.getWorkspace('graph:columns'), }; return config; } @@ -300,7 +287,7 @@ export class GraphWebview extends WebviewWithConfigBase { } if (this.previewBanner == null) { - this.previewBanner = (await this.container.storage.getWorkspace(WorkspaceStorageKeys.GraphPreview)) ?? true; + this.previewBanner = this.container.storage.getWorkspace('graph:preview') ?? true; } if (this.selectedRepository === undefined) { diff --git a/src/plus/webviews/graph/protocol.ts b/src/plus/webviews/graph/protocol.ts index 71bbb9b..e8f5859 100644 --- a/src/plus/webviews/graph/protocol.ts +++ b/src/plus/webviews/graph/protocol.ts @@ -1,10 +1,11 @@ +import type { GraphColumnConfig, GraphConfig } from '../../../config'; import { IpcCommandType, IpcNotificationType } from '../../../webviews/protocol'; export interface State { repositories?: GraphRepository[]; selectedRepository?: string; commits?: GraphCommit[]; - config?: GraphConfig; + config?: GraphCompositeConfig; remotes?: GraphRemote[]; tags?: GraphTag[]; branches?: GraphBranch[]; @@ -27,19 +28,10 @@ export type GraphRemote = Record; export type GraphTag = Record; export type GraphBranch = Record; -export interface GraphColumnConfig { - width: number; -} - -export interface GraphColumnConfigDictionary { - [key: string]: GraphColumnConfig; -} - -export interface GraphConfig { - defaultLimit: number; - pageLimit: number; - columns?: GraphColumnConfigDictionary; - columnColors: string[]; +export interface GraphCompositeConfig extends GraphConfig { + columns?: { + [key: string]: GraphColumnConfig; + }; } export interface CommitListCallback { @@ -77,7 +69,7 @@ export interface DidChangeParams { export const DidChangeNotificationType = new IpcNotificationType('graph/didChange'); export interface DidChangeConfigParams { - config: GraphConfig; + config: GraphCompositeConfig; } export const DidChangeConfigNotificationType = new IpcNotificationType('graph/didChangeConfig'); diff --git a/src/storage.ts b/src/storage.ts index 15173d6..ab1939d 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -1,15 +1,25 @@ import type { Disposable, Event, ExtensionContext, SecretStorageChangeEvent } from 'vscode'; import { EventEmitter } from 'vscode'; -import type { ViewShowBranchComparison } from './config'; +import type { GraphColumnConfig, ViewShowBranchComparison } from './config'; import type { SearchPattern } from './git/search'; - -export interface StorageChangeEvent { - /** - * The key of the stored value that has changed. - */ - readonly key: string; - readonly workspace: boolean; -} +import type { Subscription } from './subscription'; +import type { CompletedActions } from './webviews/home/protocol'; + +export type StorageChangeEvent = + | { + /** + * The key of the stored value that has changed. + */ + readonly key: GlobalStoragePath; + readonly workspace: false; + } + | { + /** + * The key of the stored value that has changed. + */ + readonly key: WorkspaceStoragePath; + readonly workspace: true; + }; export class Storage implements Disposable { private _onDidChange = new EventEmitter(); @@ -31,19 +41,25 @@ export class Storage implements Disposable { this._disposable.dispose(); } - get(key: StorageKeys | SyncedStorageKeys): T | undefined; - get(key: StorageKeys | SyncedStorageKeys, defaultValue: T): T; - get(key: StorageKeys | SyncedStorageKeys, defaultValue?: T): T | undefined { - return this.context.globalState.get(key, defaultValue); + get(key: T): GlobalStoragePathValue; + get( + key: T, + defaultValue: NonNullable>, + ): NonNullable>; + get( + key: T, + defaultValue?: GlobalStoragePathValue, + ): GlobalStoragePathValue | undefined { + return this.context.globalState.get(`gitlens:${key}`, defaultValue); } - async delete(key: StorageKeys | SyncedStorageKeys): Promise { - await this.context.globalState.update(key, undefined); + async delete(key: T): Promise { + await this.context.globalState.update(`gitlens:${key}`, undefined); this._onDidChange.fire({ key: key, workspace: false }); } - async store(key: StorageKeys | SyncedStorageKeys, value: T): Promise { - await this.context.globalState.update(key, value); + async store(key: T, value: GlobalStoragePathValue): Promise { + await this.context.globalState.update(`gitlens:${key}`, value); this._onDidChange.fire({ key: key, workspace: false }); } @@ -59,100 +75,149 @@ export class Storage implements Disposable { return this.context.secrets.store(key, value); } - getWorkspace(key: WorkspaceStorageKeys | `${WorkspaceStorageKeys.ConnectedPrefix}${string}`): T | undefined; - getWorkspace(key: WorkspaceStorageKeys | `${WorkspaceStorageKeys.ConnectedPrefix}${string}`, defaultValue: T): T; - getWorkspace( - key: WorkspaceStorageKeys | `${WorkspaceStorageKeys.ConnectedPrefix}${string}`, - defaultValue?: T, - ): T | undefined { - return this.context.workspaceState.get(key, defaultValue); + getWorkspace(key: T): WorkspaceStoragePathValue; + getWorkspace( + key: T, + defaultValue: NonNullable>, + ): NonNullable>; + getWorkspace( + key: T, + defaultValue?: WorkspaceStoragePathValue, + ): WorkspaceStoragePathValue | undefined { + return this.context.workspaceState.get(`gitlens:${key}`, defaultValue); } - async deleteWorkspace( - key: WorkspaceStorageKeys | `${WorkspaceStorageKeys.ConnectedPrefix}${string}`, - ): Promise { - await this.context.workspaceState.update(key, undefined); + async deleteWorkspace(key: T): Promise { + await this.context.workspaceState.update(`gitlens:${key}`, undefined); this._onDidChange.fire({ key: key, workspace: true }); } - async storeWorkspace( - key: WorkspaceStorageKeys | `${WorkspaceStorageKeys.ConnectedPrefix}${string}`, - value: T, - ): Promise { - await this.context.workspaceState.update(key, value); + async storeWorkspace(key: T, value: WorkspaceStoragePathValue): Promise { + await this.context.workspaceState.update(`gitlens:${key}`, value); this._onDidChange.fire({ key: key, workspace: true }); } } export type SecretKeys = string; -export const enum StorageKeys { - Avatars = 'gitlens:avatars', - PendingWelcomeOnFocus = 'gitlens:pendingWelcomeOnFocus', - PendingWhatsNewOnFocus = 'gitlens:pendingWhatsNewOnFocus', - HomeViewActionsCompleted = 'gitlens:home:actions:completed', - - Version = 'gitlens:version', - - MigratedAuthentication = 'gitlens:plus:migratedAuthentication', - Subscription = 'gitlens:premium:subscription', // Don't change this key name as its the stored subscription - - Deprecated_Version = 'gitlensVersion', +export const enum DeprecatedStorageKeys { + /** @deprecated use `gitlens:version` */ + Version = 'gitlensVersion', + /** @deprecated */ + DisallowConnectionPrefix = 'gitlens:disallow:connection:', } export const enum SyncedStorageKeys { Version = 'gitlens:synced:version', HomeViewWelcomeVisible = 'gitlens:views:welcome:visible', +} - Deprecated_DisallowConnectionPrefix = 'gitlens:disallow:connection:', +export interface GlobalStorage { + avatars?: [string, StoredAvatar][]; + home: { + actions: { + completed?: CompletedActions[]; + }; + }; + pendingWelcomeOnFocus?: boolean; + pendingWhatsNewOnFocus?: boolean; + plus: { + migratedAuthentication?: boolean; + }; + // Don't change this key name ('premium`) as its the stored subscription + premium: { + subscription?: Stored; + }; + synced: { + version?: string; + }; + version?: string; + views: { + welcome: { + visible?: boolean; + }; + }; } -export const enum WorkspaceStorageKeys { - AssumeRepositoriesOnStartup = 'gitlens:assumeRepositoriesOnStartup', - GitPath = 'gitlens:gitPath', +export interface WorkspaceStorage { + assumeRepositoriesOnStartup?: boolean; + branch: { + comparisons?: StoredBranchComparisons; + }; + connected: { + [key: string]: boolean; + }; + gitComandPalette: { + usage?: Usage; + }; + gitPath?: string; + graph: { + columns?: { + [key: string]: GraphColumnConfig; + }; + preview?: boolean; + }; + remote: { + default?: string; + }; + starred: { + branches?: StoredStarred; + repositories?: StoredStarred; + }; + views: { + repositories: { + autoRefresh?: boolean; + }; + searchAndCompare: { + keepResults?: boolean; + pinned?: StoredPinnedItems; + }; + commitDetails: { + autolinksExpanded?: boolean; + }; + }; + + pinned: { + /** @deprecated use `gitlens:views:searchAndCompare:pinned` */ + comparisons?: DeprecatedPinnedComparisons; + }; +} - BranchComparisons = 'gitlens:branch:comparisons', - ConnectedPrefix = 'gitlens:connected:', - DefaultRemote = 'gitlens:remote:default', - GitCommandPaletteUsage = 'gitlens:gitComandPalette:usage', - StarredBranches = 'gitlens:starred:branches', - StarredRepositories = 'gitlens:starred:repositories', - ViewsRepositoriesAutoRefresh = 'gitlens:views:repositories:autoRefresh', - ViewsSearchAndCompareKeepResults = 'gitlens:views:searchAndCompare:keepResults', - ViewsSearchAndComparePinnedItems = 'gitlens:views:searchAndCompare:pinned', - ViewsCommitDetailsAutolinksExpanded = 'gitlens:views:commitDetails:autolinksExpanded', - GraphColumns = 'gitlens:graph:columns', - GraphPreview = 'gitlens:graph:preview', +export interface Stored { + v: SchemaVersion; + data: T; +} - Deprecated_DisallowConnectionPrefix = 'gitlens:disallow:connection:', - Deprecated_PinnedComparisons = 'gitlens:pinned:comparisons', +export interface StoredAvatar { + uri: string; + timestamp: number; } -export interface BranchComparison { +export interface StoredBranchComparison { ref: string; notation: '..' | '...' | undefined; type: Exclude | undefined; } -export interface BranchComparisons { - [id: string]: string | BranchComparison; +export interface StoredBranchComparisons { + [id: string]: string | StoredBranchComparison; } -export interface NamedRef { +export interface StoredNamedRef { label?: string; ref: string; } -export interface PinnedComparison { +export interface StoredPinnedComparison { type: 'comparison'; timestamp: number; path: string; - ref1: NamedRef; - ref2: NamedRef; + ref1: StoredNamedRef; + ref2: StoredNamedRef; notation?: '..' | '...'; } -export interface PinnedSearch { +export interface StoredPinnedSearch { type: 'search'; timestamp: number; path: string; @@ -168,16 +233,53 @@ export interface PinnedSearch { search: SearchPattern; } -export type PinnedItem = PinnedComparison | PinnedSearch; +export type StoredPinnedItem = StoredPinnedComparison | StoredPinnedSearch; -export interface PinnedItems { - [id: string]: PinnedItem; +export interface StoredPinnedItems { + [id: string]: StoredPinnedItem; } -export interface Starred { +export interface StoredStarred { [id: string]: boolean; } export interface Usage { [id: string]: number; } + +interface DeprecatedPinnedComparison { + path: string; + ref1: StoredNamedRef; + ref2: StoredNamedRef; + notation?: '..' | '...'; +} + +interface DeprecatedPinnedComparisons { + [id: string]: DeprecatedPinnedComparison; +} + +type SubPath = Key extends string + ? T[Key] extends Record + ? + | `${Key}:${SubPath>}` + | `${Key}:${Exclude & string}` + : never + : never; + +type Path = SubPath | keyof T; + +type PathValue> = P extends `${infer Key}:${infer Rest}` + ? Key extends keyof T + ? Rest extends Path + ? PathValue + : never + : never + : P extends keyof T + ? T[P] + : never; + +type GlobalStoragePath = Path; +type GlobalStoragePathValue

= PathValue; + +type WorkspaceStoragePath = Path; +type WorkspaceStoragePathValue

= PathValue; diff --git a/src/views/nodes/compareBranchNode.ts b/src/views/nodes/compareBranchNode.ts index 4afc592..5db3da6 100644 --- a/src/views/nodes/compareBranchNode.ts +++ b/src/views/nodes/compareBranchNode.ts @@ -6,8 +6,7 @@ import type { GitBranch } from '../../git/models/branch'; import { GitRevision } from '../../git/models/reference'; import { CommandQuickPickItem } from '../../quickpicks/items/common'; import { ReferencePicker } from '../../quickpicks/referencePicker'; -import type { BranchComparison, BranchComparisons } from '../../storage'; -import { WorkspaceStorageKeys } from '../../storage'; +import type { StoredBranchComparison, StoredBranchComparisons } from '../../storage'; import { gate } from '../../system/decorators/gate'; import { debug, log } from '../../system/decorators/log'; import { getSettledValue } from '../../system/promise'; @@ -30,7 +29,7 @@ export class CompareBranchNode extends ViewNode( - WorkspaceStorageKeys.BranchComparisons, - ); + const comparisons = this.view.container.storage.getWorkspace('branch:comparisons'); const id = `${this.branch.id}${this.branch.current ? '+current' : ''}`; const compareWith = comparisons?.[id]; @@ -387,16 +384,14 @@ export class CompareBranchNode extends ViewNode( - WorkspaceStorageKeys.BranchComparisons, - ); + let comparisons = this.view.container.storage.getWorkspace('branch:comparisons'); if (comparisons == null) { if (compareWith == null) return; - comparisons = Object.create(null) as BranchComparisons; + comparisons = Object.create(null) as StoredBranchComparisons; } const id = `${this.branch.id}${this.branch.current ? '+current' : ''}`; @@ -409,6 +404,6 @@ export class CompareBranchNode extends ViewNode { diff --git a/src/views/nodes/compareResultsNode.ts b/src/views/nodes/compareResultsNode.ts index 47182dd..e825b58 100644 --- a/src/views/nodes/compareResultsNode.ts +++ b/src/views/nodes/compareResultsNode.ts @@ -1,7 +1,7 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState, window } from 'vscode'; import { GitUri } from '../../git/gitUri'; import { GitRevision } from '../../git/models/reference'; -import type { NamedRef } from '../../storage'; +import type { StoredNamedRef } from '../../storage'; import { gate } from '../../system/decorators/gate'; import { debug, log } from '../../system/decorators/log'; import { getSettledValue } from '../../system/promise'; @@ -33,8 +33,8 @@ export class CompareResultsNode extends ViewNode { view: SearchAndCompareView, parent: ViewNode, public readonly repoPath: string, - private _ref: NamedRef, - private _compareWith: NamedRef, + private _ref: StoredNamedRef, + private _compareWith: StoredNamedRef, private _pinned: number = 0, ) { super(GitUri.fromRepoPath(repoPath), view, parent); @@ -176,7 +176,7 @@ export class CompareResultsNode extends ViewNode { @gate() @debug() async getDiffRefs(): Promise<[string, string]> { - return Promise.resolve([this._compareWith.ref, this._ref.ref]); + return Promise.resolve<[string, string]>([this._compareWith.ref, this._ref.ref]); } @log() diff --git a/src/views/repositoriesView.ts b/src/views/repositoriesView.ts index 16209dc..d45f8bd 100644 --- a/src/views/repositoriesView.ts +++ b/src/views/repositoriesView.ts @@ -18,7 +18,6 @@ import type { import { GitReference } from '../git/models/reference'; import type { GitRemote } from '../git/models/remote'; import type { GitWorktree } from '../git/models/worktree'; -import { WorkspaceStorageKeys } from '../storage'; import { executeCommand } from '../system/command'; import { gate } from '../system/decorators/gate'; import { BranchesNode } from './nodes/branchesNode'; @@ -279,10 +278,7 @@ export class RepositoriesView extends ViewBase(WorkspaceStorageKeys.ViewsRepositoriesAutoRefresh, true) - ); + return this.config.autoRefresh && this.container.storage.getWorkspace('views:repositories:autoRefresh', true); } findBranch(branch: GitBranchReference, token?: CancellationToken) { @@ -855,15 +851,9 @@ export class RepositoriesView extends ViewBase( - WorkspaceStorageKeys.ViewsRepositoriesAutoRefresh, - true, - ); + workspaceEnabled = this.container.storage.getWorkspace('views:repositories:autoRefresh', true); } else { - await this.container.storage.storeWorkspace( - WorkspaceStorageKeys.ViewsRepositoriesAutoRefresh, - workspaceEnabled, - ); + await this.container.storage.storeWorkspace('views:repositories:autoRefresh', workspaceEnabled); } } diff --git a/src/views/searchAndCompareView.ts b/src/views/searchAndCompareView.ts index 1459bbe..472e4c9 100644 --- a/src/views/searchAndCompareView.ts +++ b/src/views/searchAndCompareView.ts @@ -11,8 +11,7 @@ import { GitRevision } from '../git/models/reference'; import type { SearchPattern } from '../git/search'; import { ReferencePicker, ReferencesQuickPickIncludes } from '../quickpicks/referencePicker'; import { RepositoryPicker } from '../quickpicks/repositoryPicker'; -import type { NamedRef, PinnedItem, PinnedItems } from '../storage'; -import { WorkspaceStorageKeys } from '../storage'; +import type { StoredNamedRef, StoredPinnedItem, StoredPinnedItems } from '../storage'; import { filterMap } from '../system/array'; import { executeCommand } from '../system/command'; import { gate } from '../system/decorators/gate'; @@ -25,17 +24,6 @@ import { SearchResultsNode } from './nodes/searchResultsNode'; import { ContextValues, RepositoryFolderNode, ViewNode } from './nodes/viewNode'; import { ViewBase } from './viewBase'; -interface DeprecatedPinnedComparison { - path: string; - ref1: NamedRef; - ref2: NamedRef; - notation?: '..' | '...'; -} - -interface DeprecatedPinnedComparisons { - [id: string]: DeprecatedPinnedComparison; -} - export class SearchAndCompareViewNode extends ViewNode { protected override splatted = true; private comparePicker: ComparePickerNode | undefined; @@ -134,7 +122,7 @@ export class SearchAndCompareViewNode extends ViewNode { await Promise.all(promises); } - async compareWithSelected(repoPath?: string, ref?: string | NamedRef) { + async compareWithSelected(repoPath?: string, ref?: string | StoredNamedRef) { const selectedRef = this.comparePicker?.selectedRef; if (selectedRef == null) return; @@ -176,7 +164,7 @@ export class SearchAndCompareViewNode extends ViewNode { await this.view.compare(repoPath, selectedRef.ref, ref); } - async selectForCompare(repoPath?: string, ref?: string | NamedRef, options?: { prompt?: boolean }) { + async selectForCompare(repoPath?: string, ref?: string | StoredNamedRef, options?: { prompt?: boolean }) { if (repoPath == null) { repoPath = (await RepositoryPicker.getRepositoryOrShow('Compare'))?.path; } @@ -232,7 +220,7 @@ export class SearchAndCompareViewNode extends ViewNode { } } - private getRefName(ref: string | NamedRef) { + private getRefName(ref: string | StoredNamedRef): string { return typeof ref === 'string' ? GitRevision.shorten(ref, { strings: { working: 'Working Tree' } })! : ref.label ?? GitRevision.shorten(ref.ref)!; @@ -356,10 +344,7 @@ export class SearchAndCompareView extends ViewBase( - WorkspaceStorageKeys.ViewsSearchAndCompareKeepResults, - true, - ); + return this.container.storage.getWorkspace('views:searchAndCompare:keepResults', true); } clear() { @@ -380,7 +365,7 @@ export class SearchAndCompareView extends ViewBase( - WorkspaceStorageKeys.ViewsSearchAndComparePinnedItems, - ); + let savedPins = this.container.storage.getWorkspace('views:searchAndCompare:pinned'); if (savedPins == null) { // Migrate any deprecated pinned items - const deprecatedPins = this.container.storage.getWorkspace( - WorkspaceStorageKeys.Deprecated_PinnedComparisons, - ); + const deprecatedPins = this.container.storage.getWorkspace('pinned:comparisons'); if (deprecatedPins == null) return []; - savedPins = Object.create(null) as PinnedItems; + savedPins = Object.create(null) as StoredPinnedItems; for (const p of Object.values(deprecatedPins)) { savedPins[CompareResultsNode.getPinnableId(p.path, p.ref1.ref, p.ref2.ref)] = { type: 'comparison', @@ -458,14 +439,11 @@ export class SearchAndCompareView extends ViewBase( - WorkspaceStorageKeys.ViewsSearchAndComparePinnedItems, - ); + async updatePinned(id: string, pin?: StoredPinnedItem) { + let pinned = this.container.storage.getWorkspace('views:searchAndCompare:pinned'); if (pinned == null) { - pinned = Object.create(null) as PinnedItems; + pinned = Object.create(null) as StoredPinnedItems; } if (pin != null) { @@ -528,7 +501,7 @@ export class SearchAndCompareView extends ViewBase { +const getGraphColSettingsModel = (config?: GraphCompositeConfig): GKGraphColumnsSettings => { const columnsSettings: GKGraphColumnsSettings = { ...defaultGraphColumnsSettings }; if (config?.columns !== undefined) { for (const column of Object.keys(config.columns)) { @@ -382,7 +382,9 @@ export function GraphWrapper({ )} -

Preview
+
+ Preview +
); diff --git a/src/webviews/apps/plus/graph/graph.tsx b/src/webviews/apps/plus/graph/graph.tsx index ffa45c7..2b76c8f 100644 --- a/src/webviews/apps/plus/graph/graph.tsx +++ b/src/webviews/apps/plus/graph/graph.tsx @@ -2,14 +2,8 @@ import type { CssVariables } from '@gitkraken/gitkraken-components'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import type { GraphConfig } from '../../../../config'; -import type { - CommitListCallback, - GraphColumnConfig, - GraphCommit, - GraphRepository, - State, -} from '../../../../plus/webviews/graph/protocol'; +import type { GraphColumnConfig, GraphConfig } from '../../../../config'; +import type { CommitListCallback, GraphCommit, GraphRepository, State } from '../../../../plus/webviews/graph/protocol'; import { ColumnChangeCommandType, DidChangeCommitsNotificationType, diff --git a/src/webviews/commitDetails/commitDetailsWebviewView.ts b/src/webviews/commitDetails/commitDetailsWebviewView.ts index 7284d5a..11f682f 100644 --- a/src/webviews/commitDetails/commitDetailsWebviewView.ts +++ b/src/webviews/commitDetails/commitDetailsWebviewView.ts @@ -10,7 +10,6 @@ import { GitFile } from '../../git/models/file'; import type { IssueOrPullRequest } from '../../git/models/issue'; import type { PullRequest } from '../../git/models/pullRequest'; import type { GraphSelectionChangeEvent } from '../../plus/webviews/graph/graphWebview'; -import { WorkspaceStorageKeys } from '../../storage'; import { executeCommand } from '../../system/command'; import { debug } from '../../system/decorators/log'; import type { Deferrable } from '../../system/function'; @@ -70,9 +69,7 @@ export class CommitDetailsWebviewView extends WebviewViewBase { commands.registerCommand(`${this.id}.refresh`, () => this.refresh(), this), commands.registerCommand('gitlens.home.toggleWelcome', async () => { const welcomeVisible = !this.welcomeVisible; - await this.container.storage.store(SyncedStorageKeys.HomeViewWelcomeVisible, welcomeVisible); + await this.container.storage.store('views:welcome:visible', welcomeVisible); if (welcomeVisible) { - await this.container.storage.store(StorageKeys.HomeViewActionsCompleted, []); + await this.container.storage.store('home:actions:completed', []); } void this.notifyDidChangeData(); }), commands.registerCommand('gitlens.home.showSCM', async () => { - const completedActions = this.container.storage.get( - StorageKeys.HomeViewActionsCompleted, - [], - ); + const completedActions = this.container.storage.get('home:actions:completed', []); if (!completedActions.includes(CompletedActions.OpenedSCM)) { completedActions.push(CompletedActions.OpenedSCM); - await this.container.storage.store(StorageKeys.HomeViewActionsCompleted, completedActions); + await this.container.storage.store('home:actions:completed', completedActions); void this.notifyDidChangeData(); } @@ -74,14 +70,12 @@ export class HomeWebviewView extends WebviewViewBase { } private get welcomeVisible(): boolean { - return this.container.storage.get(SyncedStorageKeys.HomeViewWelcomeVisible, true); + return this.container.storage.get('views:welcome:visible', true); } private async getState(subscription?: Subscription): Promise { // Make sure to make a copy of the array otherwise it will be live to the storage value - const completedActions = [ - ...this.container.storage.get(StorageKeys.HomeViewActionsCompleted, []), - ]; + const completedActions = [...this.container.storage.get('home:actions:completed', [])]; if (!this.welcomeVisible) { completedActions.push(CompletedActions.DismissedWelcome); }