Skip to content

Commit 2c8b782

Browse files
committed
feat(task): implement task component
1 parent c72c6d5 commit 2c8b782

File tree

3 files changed

+188
-1
lines changed

3 files changed

+188
-1
lines changed

example/task.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:commander_ui/src/commander.dart';
2+
import 'package:commander_ui/src/level.dart';
3+
4+
enum Shape { square, circle, triangle }
5+
6+
Future<void> sleep() => Future.delayed(Duration(seconds: 1));
7+
8+
Future<String> sleepWithValue() => Future.delayed(Duration(seconds: 1), () => 'Hello World !');
9+
10+
Future<void> main() async {
11+
final commander = Commander(level: Level.verbose);
12+
print('Hello World !');
13+
14+
final successTask = await commander.task('I am an success task', colored: true);
15+
await successTask.step('Success step 1', callback: sleepWithValue);
16+
await successTask.step('Success step 2', callback: sleep);
17+
successTask.success('Success task data are available !');
18+
19+
final warnTask = await commander.task('I am an warn task');
20+
await warnTask.step('Warn step 1', callback: sleepWithValue);
21+
await warnTask.step('Warn step 2', callback: sleep);
22+
await warnTask.step('Warn step 3', callback: sleep);
23+
warnTask.warn('Warn task !');
24+
25+
final errorTask = await commander.task('I am an error task');
26+
await errorTask.step('Error step 1', callback: sleepWithValue);
27+
await errorTask.step('Error step 2', callback: sleep);
28+
await errorTask.step('Error step 3', callback: sleep);
29+
errorTask.error('Error task !');
30+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import 'dart:async';
2+
import 'dart:io';
3+
4+
import 'package:commander_ui/src/application/terminals/terminal.dart';
5+
import 'package:commander_ui/src/application/utils/terminal_tools.dart';
6+
import 'package:commander_ui/src/domains/models/component.dart';
7+
import 'package:mansion/mansion.dart';
8+
9+
final List<String> _loadingSteps = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
10+
11+
final class Task with TerminalTools implements Component<StepManager> {
12+
final Terminal _terminal;
13+
final bool _colored;
14+
15+
Task(this._terminal, {bool colored = true}) : _colored = colored;
16+
17+
@override
18+
Future<StepManager> handle() async {
19+
return StepManager(_terminal, _colored);
20+
}
21+
}
22+
23+
final class StepManager with TerminalTools {
24+
final Terminal _terminal;
25+
(int, int)? _position;
26+
Timer? _timer;
27+
int _loadingStep = 0;
28+
bool isInitialStep = true;
29+
final bool _colored;
30+
31+
StepManager(this._terminal, this._colored);
32+
33+
Future<T> step<T>(String message, {FutureOr<T> Function()? callback}) {
34+
if (isInitialStep) {
35+
createSpace(_terminal, 1);
36+
_position = readCursorPosition(_terminal);
37+
isInitialStep = false;
38+
}
39+
40+
final task = StepTask<T>(message, callback);
41+
42+
stdout.writeAnsi(CursorVisibility.hide);
43+
if (_timer != null) {
44+
_timer?.cancel();
45+
}
46+
47+
_timer = Timer.periodic(Duration(milliseconds: 100), (timer) {
48+
_drawLoader(() {
49+
final buffer = StringBuffer();
50+
51+
buffer.writeAnsiAll([
52+
CursorPosition.moveTo(_position!.$2, _position!.$1),
53+
SetStyles(Style.foreground(Color.green)),
54+
Print(_loadingSteps[_loadingStep]),
55+
SetStyles.reset
56+
]);
57+
58+
task.render(buffer);
59+
});
60+
});
61+
62+
return task.start();
63+
}
64+
65+
void success(String message) {
66+
final buffer = StringBuffer();
67+
68+
buffer.writeAnsiAll([
69+
CursorPosition.moveTo(_position!.$2, _position!.$1),
70+
SetStyles(Style.foreground(Color.green)),
71+
Print('✔ '),
72+
if (!_colored) SetStyles.reset,
73+
..._messageSequence(message),
74+
]);
75+
76+
_timer?.cancel();
77+
stdout.write(buffer.toString());
78+
}
79+
80+
void warn(String message) {
81+
final buffer = StringBuffer();
82+
83+
buffer.writeAnsiAll([
84+
CursorPosition.moveTo(_position!.$2, _position!.$1),
85+
SetStyles(Style.foreground(Color.yellow)),
86+
Print('⚠ '),
87+
if (!_colored) SetStyles.reset,
88+
..._messageSequence(message),
89+
]);
90+
91+
_timer?.cancel();
92+
stdout.write(buffer.toString());
93+
}
94+
95+
void error(String message) {
96+
final buffer = StringBuffer();
97+
98+
buffer.writeAnsiAll([
99+
CursorPosition.moveTo(_position!.$2, _position!.$1),
100+
SetStyles(Style.foreground(Color.red)),
101+
Print('✘ '),
102+
if (!_colored) SetStyles.reset,
103+
..._messageSequence(message),
104+
]);
105+
106+
_timer?.cancel();
107+
stdout.write(buffer.toString());
108+
}
109+
110+
List<Sequence> _messageSequence(String message) {
111+
return [Print(message), SetStyles.reset, AsciiControl.lineFeed];
112+
}
113+
114+
void _drawLoader(void Function() render) {
115+
if (_loadingStep == _loadingSteps.length - 1) {
116+
_loadingStep = 0;
117+
}
118+
119+
render();
120+
_loadingStep += 1;
121+
}
122+
}
123+
124+
final class StepTask<T> {
125+
final _completer = Completer<T>();
126+
127+
final String _message;
128+
final FutureOr<void> Function()? _callback;
129+
130+
StepTask(this._message, this._callback);
131+
132+
Future<T> start() async {
133+
if (_callback case Future<void> Function() callback) {
134+
callback().then((value) {
135+
_completer.complete(value as T);
136+
});
137+
} else {
138+
await Future.delayed(Duration(milliseconds: 100), _completer.complete);
139+
}
140+
141+
return _completer.future;
142+
}
143+
144+
void render(StringBuffer buffer) {
145+
buffer.writeAnsiAll([
146+
SetStyles(Style.foreground(Color.brightBlack)),
147+
Print(' $_message'),
148+
SetStyles.reset,
149+
AsciiControl.lineFeed,
150+
]);
151+
152+
stdout.write(buffer.toString());
153+
}
154+
}

lib/src/commander.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:io' as io;
44
import 'package:commander_ui/src/application/components/ask.dart';
55
import 'package:commander_ui/src/application/components/checkbox.dart';
66
import 'package:commander_ui/src/application/components/select.dart';
7+
import 'package:commander_ui/src/application/components/task.dart';
78
import 'package:commander_ui/src/application/components/swap.dart';
89
import 'package:commander_ui/src/application/terminals/terminal.dart';
910
import 'package:commander_ui/src/application/utils/terminal_tools.dart';
@@ -30,7 +31,6 @@ class Commander with TerminalTools {
3031

3132
final _queue = <String?>[];
3233

33-
Never _exit(int code) => io.exit(code);
3434
final _terminal = Terminal();
3535

3636
/// Write message via `stdout.write`.
@@ -76,4 +76,7 @@ class Commander with TerminalTools {
7676
Future<bool> swap<T>(String message, {bool defaultValue = false, String placeholder = ''}) =>
7777
Swap<T>(_terminal, message: message, defaultValue: defaultValue, placeholder: placeholder)
7878
.handle();
79+
80+
Future<StepManager> task<T>(String message, {bool colored = false}) =>
81+
Task(_terminal, colored: colored).handle();
7982
}

0 commit comments

Comments
 (0)