Skip to content

Commit 3b7a2b6

Browse files
committed
feat(status-bar): improve status bar display and actions
- The status bar will always show the LS instance status (running, stopping, etc) if the LS CLI is installed — it doesn’t need to have the full setup complete - The status bar commands will always allow starting and stopping LS if the LS CLI is installed — it doesn’t need to have the full setup complete - The status bar will be red if any part of the setup is incomplete
1 parent 505f11a commit 3b7a2b6

File tree

4 files changed

+61
-40
lines changed

4 files changed

+61
-40
lines changed

src/plugins/status-bar.ts

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,31 @@ export default createPlugin(
1515
}) => {
1616
context.subscriptions.push(
1717
commands.registerCommand("localstack.showCommands", async () => {
18-
const shouldShowLocalStackStart = async () =>
19-
(await checkLocalstackInstalled(outputChannel)) &&
18+
const shouldShowLocalStackStart = () =>
19+
setupStatusTracker.statuses().isInstalled &&
2020
localStackStatusTracker.status() === "stopped";
21-
const shouldShowLocalStackStop = async () =>
22-
(await checkLocalstackInstalled(outputChannel)) &&
21+
const shouldShowLocalStackStop = () =>
22+
setupStatusTracker.statuses().isInstalled &&
2323
localStackStatusTracker.status() === "running";
2424
const shouldShowRunSetupWizard = () =>
2525
setupStatusTracker.status() === "setup_required";
2626

27-
const getCommands = async () => {
27+
const getCommands = () => {
2828
const commands: (QuickPickItem & { command: string })[] = [];
2929
commands.push({
3030
label: "Manage",
3131
command: "",
3232
kind: QuickPickItemKind.Separator,
3333
});
3434

35-
if (await shouldShowLocalStackStart()) {
35+
if (shouldShowLocalStackStart()) {
3636
commands.push({
3737
label: "Start LocalStack",
3838
command: "localstack.start",
3939
});
4040
}
4141

42-
if (await shouldShowLocalStackStop()) {
42+
if (shouldShowLocalStackStop()) {
4343
commands.push({
4444
label: "Stop LocalStack",
4545
command: "localstack.stop",
@@ -59,7 +59,7 @@ export default createPlugin(
5959

6060
if (shouldShowRunSetupWizard()) {
6161
commands.push({
62-
label: "Run LocalStack setup Wizard",
62+
label: "Run LocalStack Setup Wizard",
6363
command: "localstack.setup",
6464
});
6565
}
@@ -81,30 +81,30 @@ export default createPlugin(
8181
commands.registerCommand("localstack.refreshStatusBar", () => {
8282
// TODO
8383
const setupStatus = setupStatusTracker.status();
84-
85-
if (setupStatus === "setup_required") {
86-
statusBarItem.command = "localstack.showCommands";
87-
statusBarItem.text = "$(error) LocalStack";
88-
statusBarItem.backgroundColor = new ThemeColor(
89-
"statusBarItem.errorBackground",
90-
);
91-
} else {
92-
statusBarItem.command = "localstack.showCommands";
93-
statusBarItem.backgroundColor = undefined;
94-
const localStackStatus = localStackStatusTracker.status();
95-
if (
96-
localStackStatus === "starting" ||
97-
localStackStatus === "stopping"
98-
) {
99-
statusBarItem.text = `$(sync~spin) LocalStack (${localStackStatus})`;
100-
} else if (
101-
localStackStatus === "running" ||
102-
localStackStatus === "stopped"
103-
) {
104-
statusBarItem.text = `$(localstack-logo) LocalStack (${localStackStatus})`;
105-
}
106-
}
107-
84+
const localStackStatus = localStackStatusTracker.status();
85+
const localStackInstalled = setupStatusTracker.statuses().isInstalled;
86+
87+
statusBarItem.command = "localstack.showCommands";
88+
statusBarItem.backgroundColor =
89+
setupStatus === "setup_required"
90+
? new ThemeColor("statusBarItem.errorBackground")
91+
: undefined;
92+
93+
const shouldSpin =
94+
localStackStatus === "starting" || localStackStatus === "stopping";
95+
const icon =
96+
setupStatus === "setup_required"
97+
? "$(error)"
98+
: shouldSpin
99+
? "$(sync~spin)"
100+
: "$(localstack-logo)";
101+
102+
const statusText = localStackInstalled
103+
? `${localStackStatus}`
104+
: "not installed";
105+
statusBarItem.text = `${icon} LocalStack: ${statusText}`;
106+
107+
statusBarItem.tooltip = "Show LocalStack commands";
108108
statusBarItem.show();
109109
}),
110110
);
@@ -118,6 +118,7 @@ export default createPlugin(
118118
});
119119
}
120120
};
121+
121122
context.subscriptions.push({
122123
dispose() {
123124
clearImmediate(refreshStatusBarImmediateId);
@@ -127,10 +128,12 @@ export default createPlugin(
127128
refreshStatusBarImmediate();
128129

129130
localStackStatusTracker.onChange(() => {
131+
outputChannel.trace("[status-bar]: localStackStatusTracker changed");
130132
refreshStatusBarImmediate();
131133
});
132134

133135
setupStatusTracker.onChange(() => {
136+
outputChannel.trace("[status-bar]: setupStatusTracker changed");
134137
refreshStatusBarImmediate();
135138
});
136139
},

src/utils/promises.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,8 @@ export function minDelay<T>(
3232
MIN_TIME_BETWEEN_STEPS_MS,
3333
);
3434
}
35+
36+
/**
37+
* Extracts the resolved type from a Promise.
38+
*/
39+
export type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

src/utils/setup-status.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import ms from "ms";
22
import type { Disposable, LogOutputChannel } from "vscode";
33

44
import { createEmitter } from "./emitter.ts";
5-
import { checkIsSetupRequired } from "./setup.ts";
5+
import type { UnwrapPromise } from "./promises.ts";
6+
import { checkSetupStatus } from "./setup.ts";
67
import type { TimeTracker } from "./time-tracker.ts";
78

89
export type SetupStatus = "ok" | "setup_required";
910

1011
export interface SetupStatusTracker extends Disposable {
1112
status(): SetupStatus;
13+
statuses(): UnwrapPromise<ReturnType<typeof checkSetupStatus>>;
1214
onChange(callback: (status: SetupStatus) => void): void;
1315
}
1416

@@ -20,6 +22,7 @@ export async function createSetupStatusTracker(
2022
timeTracker: TimeTracker,
2123
): Promise<SetupStatusTracker> {
2224
const start = Date.now();
25+
let statuses: UnwrapPromise<ReturnType<typeof checkSetupStatus>> | undefined;
2326
let status: SetupStatus | undefined;
2427
const emitter = createEmitter<SetupStatus>(outputChannel);
2528
const end = Date.now();
@@ -29,13 +32,18 @@ export async function createSetupStatusTracker(
2932

3033
let timeout: NodeJS.Timeout | undefined;
3134
const startChecking = async () => {
32-
const setupRequired = await checkIsSetupRequired(outputChannel);
35+
statuses = await checkSetupStatus(outputChannel);
36+
37+
const setupRequired = Object.values(statuses).some(
38+
(check) => check === false,
39+
);
3340
const newStatus = setupRequired ? "setup_required" : "ok";
3441
if (status !== newStatus) {
3542
status = newStatus;
3643
await emitter.emit(status);
3744
}
3845

46+
// TODO: Find a smarter way to check the status (e.g. watch for changes in AWS credentials or LocalStack installation)
3947
timeout = setTimeout(() => void startChecking(), 1_000);
4048
};
4149

@@ -48,6 +56,10 @@ export async function createSetupStatusTracker(
4856
// biome-ignore lint/style/noNonNullAssertion: false positive
4957
return status!;
5058
},
59+
statuses() {
60+
// biome-ignore lint/style/noNonNullAssertion: false positive
61+
return statuses!;
62+
},
5163
onChange(callback) {
5264
emitter.on(callback);
5365
if (status) {

src/utils/setup.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import { checkIsProfileConfigured } from "./configure-aws.ts";
55
import { checkLocalstackInstalled } from "./install.ts";
66
import { checkIsLicenseValid } from "./license.ts";
77

8-
export async function checkIsSetupRequired(
9-
outputChannel: LogOutputChannel,
10-
): Promise<boolean> {
8+
export async function checkSetupStatus(outputChannel: LogOutputChannel) {
119
const [isInstalled, isAuthenticated, isLicenseValid, isProfileConfigured] =
1210
await Promise.all([
1311
checkLocalstackInstalled(outputChannel),
@@ -16,7 +14,10 @@ export async function checkIsSetupRequired(
1614
checkIsProfileConfigured(),
1715
]);
1816

19-
return (
20-
!isInstalled || !isAuthenticated || !isLicenseValid || !isProfileConfigured
21-
);
17+
return {
18+
isInstalled,
19+
isAuthenticated,
20+
isLicenseValid,
21+
isProfileConfigured,
22+
};
2223
}

0 commit comments

Comments
 (0)