Skip to content

Commit 8c744ce

Browse files
committed
feat: WIP refactored default reporter to use a new statement management library (jotai) instead of being event based
1 parent 539cecc commit 8c744ce

File tree

8 files changed

+116
-30
lines changed

8 files changed

+116
-30
lines changed

.eslintrc.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
"unicorn/no-array-for-each": "off",
1919
"unicorn/prefer-object-from-entries": "off",
2020
"unicorn/prefer-type-error": "off",
21-
"no-await-in-loop": "off",
2221
"quotes": [
2322
"error",
2423
"single"
25-
]
24+
],
25+
"no-await-in-loop": "off"
2626
},
2727
"ignorePatterns": [
2828
"*.test.ts"

codify.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@
1919
"firefox"
2020
]
2121
},
22+
{ "type": "terraform" },
2223
{ "type": "alias", "alias": "gcdsdd", "value": "git clone" }
2324
]

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"debug": "^4.3.4",
1717
"ink": "^5",
1818
"ink-form": "^2.0.1",
19+
"jotai": "^2.11.1",
1920
"js-yaml": "^4.1.0",
2021
"js-yaml-source-map": "^0.2.2",
2122
"json-source-map": "^0.6.1",

src/ui/components/default-component.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { PasswordInput, Select } from '@inkjs/ui';
22
import chalk from 'chalk';
33
import { Box, Static, Text } from 'ink';
4+
import { useAtom } from 'jotai';
45
import { EventEmitter } from 'node:events';
56
import React, { useLayoutEffect, useState } from 'react';
67

78
import { Plan } from '../../entities/plan.js';
89
import { ImportResult, RequiredParameters } from '../../orchestrators/import.js';
910
import { RenderEvent, RenderState } from '../reporters/reporter.js';
11+
import { RenderStatus, store } from '../store/index.js';
1012
import { ImportResultComponent } from './import/import-result.js';
1113
import { ImportParametersForm } from './import/index.js';
1214
import { PlanComponent } from './plan/plan.js';
@@ -20,7 +22,7 @@ export function DefaultComponent(props: {
2022
const { emitter } = props;
2123

2224
const [state, setState] = useState(RenderState.GENERATING_PLAN);
23-
const [progressState, setProgressState] = useState(null as ProgressState | null);
25+
// const [progressState, setProgressState] = useState(null as ProgressState | null);
2426
const [hideProgress, setHideProgress] = useState(false);
2527
const [plan, setPlan] = useState(null as Plan | null);
2628
const [showSudoPrompt, setShowPromptSudo] = useState(false);
@@ -29,19 +31,22 @@ export function DefaultComponent(props: {
2931
const [importResult, setImportResult] = useState<ImportResult | null>(null);
3032
const [sudoAttemptCount, setSudoAttemptCount] = useState(0);
3133
const [confirmationMessage, setConfirmationMessage] = useState('');
34+
35+
const [{ status: renderStatus, data: renderData }] = useAtom(store.renderState);
36+
const [progressState] = useAtom(store.progressState);
3237

3338
// Use layoutEffect runs before the first render, whereas useEffect runs after
3439
useLayoutEffect(() => {
3540
emitter.on(RenderEvent.STATE_TRANSITION, (obj) => {
3641
switch (obj.nextState) {
3742
case RenderState.DISPLAY_PLAN: {
38-
setProgressState(null);
43+
// setProgressState(null);
3944
setPlan(obj.plan);
4045
break;
4146
}
4247

4348
case RenderState.DISPLAY_IMPORT_RESULT: {
44-
setProgressState(null);
49+
// setProgressState(null);
4550
setImportResult(obj.importResult);
4651
break;
4752
}
@@ -61,7 +66,7 @@ export function DefaultComponent(props: {
6166
});
6267

6368
emitter.on(RenderEvent.PROGRESS_UPDATE, (state: ProgressState) => {
64-
setProgressState(structuredClone(state));
69+
// setProgressState(structuredClone(state));
6570
});
6671

6772
emitter.on(RenderEvent.PROMPT_SUDO, (attemptCount) => {
@@ -94,21 +99,28 @@ export function DefaultComponent(props: {
9499
})
95100
}, []);
96101

102+
// console.log(renderStatus);
103+
// console.log(renderData);
104+
//
105+
// console.log(renderStatus);
106+
// console.log(progressState);
107+
// console.log(renderData);
108+
97109
return <Box flexDirection="column">
98110
{
99-
([RenderState.APPLY_COMPLETE, RenderState.APPLYING, RenderState.GENERATING_PLAN].includes(state)) && progressState && !hideProgress && (
111+
renderStatus === RenderStatus.PROGRESS && progressState && !hideProgress && (
100112
<ProgressDisplay emitter={spinnerEmitter} eventType="data" progress={progressState}/>
101113
)
102114
}
103115
{
104-
state >= RenderState.DISPLAY_PLAN && plan && <Static items={[plan]}>{
116+
renderStatus === RenderStatus.DISPLAY_PLAN && <Static items={[renderData as Plan]}>{
105117
(plan, idx) => <PlanComponent key={idx} plan={plan}/>
106118
}</Static>
107119
}
108120
{
109-
state === RenderState.PROMPT_CONFIRMATION && (
121+
renderStatus === RenderStatus.PROMPT_CONFIRMATION && (
110122
<Box flexDirection="column">
111-
<Text>{confirmationMessage}</Text>
123+
<Text>{renderData as string}</Text>
112124
<Select onChange={(value) => emitter.emit(RenderEvent.PROMPT_CONFIRMATION_RESULT, value === 'yes')} options={[
113125
{ label: 'Yes', value: 'yes' },
114126
{ label: 'No', value: 'no' },
@@ -117,7 +129,7 @@ export function DefaultComponent(props: {
117129
)
118130
}
119131
{
120-
state === RenderState.APPLY_COMPLETE && (
132+
renderStatus === RenderStatus.APPLY_COMPLETE && (
121133
<Box flexDirection="column">
122134
<Text> </Text>
123135
<Text>🎉 Finished applying 🎉</Text>

src/ui/reporters/default-reporter.tsx

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ImportResult, RequiredParameters, UserSuppliedParameters } from '../../
1010
import { SudoUtils } from '../../utils/sudo.js';
1111
import { DefaultComponent } from '../components/default-component.js';
1212
import { ProgressState, ProgressStatus } from '../components/progress/progress-display.js';
13+
import { RenderStatus, store } from '../store/index.js';
1314
import { DisplayPlanStateTransition, RenderEvent, RenderState, Reporter } from './reporter.js';
1415

1516
const ProgressLabelMapping = {
@@ -81,31 +82,37 @@ export class DefaultReporter implements Reporter {
8182
}
8283

8384
displayPlan(plan: Plan): void {
85+
store.set(store.renderState, { status: RenderStatus.DISPLAY_PLAN, data: plan });
86+
store.set(store.progressState, null);
8487
this.progressState = null;
8588

86-
this.renderEmitter.emit(RenderEvent.STATE_TRANSITION, {
87-
nextState: RenderState.DISPLAY_PLAN,
88-
plan,
89-
} as DisplayPlanStateTransition);
89+
// this.renderEmitter.emit(RenderEvent.STATE_TRANSITION, {
90+
// nextState: RenderState.DISPLAY_PLAN,
91+
// plan,
92+
// } as DisplayPlanStateTransition);
9093
}
9194

9295
async promptConfirmation(message: string): Promise<boolean> {
9396
const result = await Promise.all([
9497
new Promise<boolean>((resolve) => {
9598
this.renderEmitter.once(RenderEvent.PROMPT_CONFIRMATION_RESULT, (isConfirmed) => resolve(isConfirmed as boolean));
9699
}),
97-
this.renderEmitter.emit(RenderEvent.STATE_TRANSITION, {
98-
nextState: RenderState.PROMPT_CONFIRMATION,
99-
message,
100-
}),
100+
101+
store.set(store.renderState, { status: RenderStatus.PROMPT_CONFIRMATION, data: message })
102+
// this.renderEmitter.emit(RenderEvent.STATE_TRANSITION, {
103+
// nextState: RenderState.PROMPT_CONFIRMATION,
104+
// message,
105+
// }),
101106
])
102107

103108
const continueApply = result[0];
104109

105110
if (continueApply) {
106-
this.renderEmitter.emit(RenderEvent.STATE_TRANSITION, {
107-
nextState: RenderState.APPLYING,
108-
});
111+
// this.renderEmitter.emit(RenderEvent.STATE_TRANSITION, {
112+
// nextState: RenderState.APPLYING,
113+
// });
114+
115+
store.set(store.renderState, { status: RenderStatus.PROGRESS });
109116

110117
this.log(`${message} -> "Yes"`)
111118
}
@@ -114,9 +121,11 @@ export class DefaultReporter implements Reporter {
114121
}
115122

116123
displayApplyComplete(messages: string[]): Promise<void> | void {
117-
this.renderEmitter.emit(RenderEvent.STATE_TRANSITION, {
118-
nextState: RenderState.APPLY_COMPLETE,
119-
});
124+
store.set(store.renderState, { status: RenderStatus.APPLY_COMPLETE });
125+
126+
// this.renderEmitter.emit(RenderEvent.STATE_TRANSITION, {
127+
// nextState: RenderState.APPLY_COMPLETE,
128+
// });
120129
}
121130

122131
private log(args: string): void {
@@ -134,7 +143,9 @@ export class DefaultReporter implements Reporter {
134143
};
135144

136145
this.log(`${label} started`)
137-
this.renderEmitter.emit(RenderEvent.PROGRESS_UPDATE, this.progressState);
146+
147+
store.set(store.progressState, structuredClone(this.progressState));
148+
// this.renderEmitter.emit(RenderEvent.PROGRESS_UPDATE, this.progressState);
138149
}
139150

140151
private onProcessFinishEvent(name: ProcessName): void {
@@ -143,8 +154,9 @@ export class DefaultReporter implements Reporter {
143154
this.progressState!.status = ProgressStatus.FINISHED;
144155

145156
this.log(`${label} finished successfully`)
146-
this.renderEmitter.emit(RenderEvent.PROGRESS_UPDATE, this.progressState);
147157

158+
store.internal.set(store.progressState, structuredClone(this.progressState));
159+
// this.renderEmitter.emit(RenderEvent.PROGRESS_UPDATE, this.progressState);
148160
}
149161

150162
private onSubprocessStartEvent(name: SubProcessName, additionalName?: string): void {
@@ -160,7 +172,9 @@ export class DefaultReporter implements Reporter {
160172
});
161173

162174
this.log(`${label} started`)
163-
this.renderEmitter.emit(RenderEvent.PROGRESS_UPDATE, this.progressState);
175+
176+
store.set(store.progressState, structuredClone(this.progressState));
177+
// this.renderEmitter.emit(RenderEvent.PROGRESS_UPDATE, this.progressState);
164178
}
165179

166180
private onSubprocessFinishEvent(name: SubProcessName, additionalName?: string): void {
@@ -180,7 +194,8 @@ export class DefaultReporter implements Reporter {
180194
subProgress.status = ProgressStatus.FINISHED;
181195

182196
this.log(`${label} finished successfully`)
183-
this.renderEmitter.emit(RenderEvent.PROGRESS_UPDATE, this.progressState);
197+
store.set(store.progressState, structuredClone(this.progressState));
198+
// this.renderEmitter.emit(RenderEvent.PROGRESS_UPDATE, this.progressState);
184199
}
185200

186201
private async getUserPassword(): Promise<string> {

src/ui/reporters/reporter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export const ReporterFactory = {
6464
create(type: ReporterType): Reporter {
6565
switch (type) {
6666
case ReporterType.DEBUG: {
67-
return new DebugReporter();
67+
return new DefaultReporter();
6868
}
6969

7070
case ReporterType.PLAIN: {

src/ui/store/index.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { atom, createStore, getDefaultStore, Setter, Getter, Atom, WritableAtom } from 'jotai'
2+
3+
import { ProgressState } from '../components/progress/progress-display.js';
4+
5+
export interface RenderState {
6+
status: RenderStatus;
7+
data?: unknown;
8+
}
9+
10+
export enum RenderStatus {
11+
PROGRESS,
12+
DISPLAY_PLAN,
13+
DISPLAY_IMPORT_RESULT,
14+
IMPORT_PROMPT,
15+
PROMPT_CONFIRMATION,
16+
APPLY_COMPLETE,
17+
SUDO_PROMPT,
18+
}
19+
20+
export const store = new class {
21+
private internal = getDefaultStore()
22+
23+
renderState = atom(<RenderState>{ status: RenderStatus.PROGRESS })
24+
renderStatus = atom((get) => get(this.renderState).status)
25+
renderData = atom((get) => get(this.renderState).data)
26+
27+
progressState = atom(null as ProgressState | null)
28+
29+
get<Value>(atom: Atom<Value>): Value {
30+
return this.internal.get(atom);
31+
}
32+
33+
set<Value, Args extends unknown[], Result>(atom: WritableAtom<Value, Args, Result>, ...args: Args): Result {
34+
return this.internal.set(atom, ...args);
35+
}
36+
}

0 commit comments

Comments
 (0)