rewrite askUserNameAndPassword() to use tim
This commit is contained in:
2
dependencies/tim
vendored
2
dependencies/tim
vendored
Submodule dependencies/tim updated: d417b5bbd5...3fb220ff54
@@ -18,11 +18,11 @@ static const str farewell_art = STR(
|
|||||||
"\\(_,J J L l`,)/\n"
|
"\\(_,J J L l`,)/\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
#define FPS 30
|
|
||||||
|
|
||||||
#define is_alias(LITERAL) str_equals(command, STR(LITERAL))
|
#define is_alias(LITERAL) str_equals(command, STR(LITERAL))
|
||||||
|
|
||||||
static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* password_out);
|
Result(bool) start_screen(Array(char) username_buf, Array(char) password_buf,
|
||||||
|
str* out_username, str* out_password);
|
||||||
static Result(void) ClientCLI_openUserDB(ClientCLI* self);
|
static Result(void) ClientCLI_openUserDB(ClientCLI* self);
|
||||||
static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop);
|
static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop);
|
||||||
static Result(SavedServer*) ClientCLI_joinNewServer(ClientCLI* self);
|
static Result(SavedServer*) ClientCLI_joinNewServer(ClientCLI* self);
|
||||||
@@ -48,48 +48,29 @@ void ClientCLI_construct(ClientCLI* self){
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result(void) ClientCLI_run(ClientCLI* self) {
|
Result(void) ClientCLI_run(ClientCLI* self) {
|
||||||
Deferral(FPS);
|
Deferral(32);
|
||||||
|
|
||||||
TimEditState e;
|
// ask username and password
|
||||||
TimEditState_init(&e, 32, "Greetings!");
|
Array(char) username_buf = Array_char_alloc(USERNAME_SIZE_MAX + 1);
|
||||||
bool edit_enabled = false;
|
Array(char) password_buf = Array_char_alloc(PASSWORD_SIZE_MAX + 1);
|
||||||
while(tim_run(30)){
|
Defer(
|
||||||
TimStyle c = { .brd = TimColor16_White, .bg = TimColor16_DarkBlue, .fg = TimColor16_White };
|
Array_char_destroy(&username_buf);
|
||||||
tim_frame(0, 0, ~0, ~0, c);
|
Array_char_destroy(&password_buf);
|
||||||
|
);
|
||||||
tim_label(e.s, A, 2, A, A, c);
|
str username = str_null, password = str_null;
|
||||||
|
try(bool start, i, start_screen(username_buf, password_buf, &username, &password));
|
||||||
if(edit_enabled){
|
if(!start){
|
||||||
TimKey key = tim_edit(&e, A, 5, tim->scopes[tim->scope].w - 4, c);
|
Return RESULT_VOID;
|
||||||
|
|
||||||
if(key == TimKey_Escape || key == TimKey_Enter)
|
|
||||||
edit_enabled = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(tim_button("[Enter] Edit text", A, 5, tim->scopes[tim->scope].w - 4, A, c) || tim_is_key_press(TimKey_Enter)){
|
|
||||||
edit_enabled = true;
|
|
||||||
tim->focus = &e;
|
|
||||||
tim->event.type = TimEvent_Void; // consume key event
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(tim_button("[Q] Quit", A, ~1, 10, A, c) || tim_is_key_press('q'))
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tim_reset_terminal();
|
||||||
term_clear();
|
term_clear();
|
||||||
printf(FMT_str"\n", greeting_art.len, greeting_art.data);
|
printf(FMT_str"\n", greeting_art.len, greeting_art.data);
|
||||||
|
|
||||||
// create Client
|
// create client
|
||||||
str username = str_null, password = str_null;
|
|
||||||
try_void(ClientCLI_askUserNameAndPassword(&username, &password));
|
|
||||||
Defer(
|
|
||||||
str_destroy(username);
|
|
||||||
str_destroy(password);
|
|
||||||
);
|
|
||||||
Client_free(self->client);
|
|
||||||
try(self->client, p, Client_create(username, password));
|
try(self->client, p, Client_create(username, password));
|
||||||
memset(password.data, 0, password.len);
|
// erase password from memory
|
||||||
|
Array_char_memset(&password_buf, 0);
|
||||||
|
|
||||||
// init db
|
// init db
|
||||||
try_void(ClientCLI_openUserDB(self));
|
try_void(ClientCLI_openUserDB(self));
|
||||||
@@ -120,49 +101,6 @@ Result(void) ClientCLI_run(ClientCLI* self) {
|
|||||||
Return RESULT_VOID;
|
Return RESULT_VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* password_out){
|
|
||||||
Deferral(8);
|
|
||||||
bool success = false;
|
|
||||||
|
|
||||||
// ask username
|
|
||||||
Array(char) username_buf = Array_char_alloc(128);
|
|
||||||
Defer(if(!success) Array_char_destroy(&username_buf));
|
|
||||||
str username = str_null;
|
|
||||||
while(true) {
|
|
||||||
printf("username: ");
|
|
||||||
try_void(term_readLine(username_buf.data, username_buf.len));
|
|
||||||
username = str_from_cstr(username_buf.data);
|
|
||||||
str_trim(&username, true);
|
|
||||||
str name_error_str = validateUsername_str(username);
|
|
||||||
if(name_error_str.data){
|
|
||||||
printf("ERROR: "FMT_str"\n",
|
|
||||||
name_error_str.len, name_error_str.data);
|
|
||||||
}
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ask password
|
|
||||||
Array(char) password_buf = Array_char_alloc(128);
|
|
||||||
Defer(if(!success) Array_char_destroy(&password_buf));
|
|
||||||
str password = str_null;
|
|
||||||
while(true) {
|
|
||||||
printf("password: ");
|
|
||||||
// TODO: hide password
|
|
||||||
try_void(term_readLineHidden(password_buf.data, password_buf.len));
|
|
||||||
password = str_from_cstr(password_buf.data);
|
|
||||||
str_trim(&password, true);
|
|
||||||
if(password.len < PASSWORD_SIZE_MIN || password.len > PASSWORD_SIZE_MAX){
|
|
||||||
printf("ERROR: password length (in bytes) must be >= %i and <= %i\n",
|
|
||||||
PASSWORD_SIZE_MIN, PASSWORD_SIZE_MAX);
|
|
||||||
}
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
*username_out = username;
|
|
||||||
*password_out = password;
|
|
||||||
success = true;
|
|
||||||
Return RESULT_VOID;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result(void) ClientCLI_openUserDB(ClientCLI* self){
|
static Result(void) ClientCLI_openUserDB(ClientCLI* self){
|
||||||
Deferral(8);
|
Deferral(8);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include "tlibc/collections/List.h"
|
#include "tlibc/collections/List.h"
|
||||||
#include "db/client_db.h"
|
#include "db/client_db.h"
|
||||||
|
|
||||||
|
#define FPS 30
|
||||||
|
|
||||||
typedef struct ClientCLI {
|
typedef struct ClientCLI {
|
||||||
Client* client;
|
Client* client;
|
||||||
tsqlite_connection* db;
|
tsqlite_connection* db;
|
||||||
|
|||||||
138
src/cli/ClientCLI/start_screen.c
Normal file
138
src/cli/ClientCLI/start_screen.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#include "ClientCLI.h"
|
||||||
|
#include "network/tcp-chat-protocol/v1.h"
|
||||||
|
#include "tim.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct TextInputState {
|
||||||
|
TimEditState edit;
|
||||||
|
cstr label;
|
||||||
|
TimStyle style_default;
|
||||||
|
TimStyle style_focused;
|
||||||
|
TimKey result_key;
|
||||||
|
} TextInputState;
|
||||||
|
|
||||||
|
void draw_item_text_input(bool is_selected, TimRect place, void* data){
|
||||||
|
TextInputState* ctx = data;
|
||||||
|
TimStyle style = is_selected ? ctx->style_focused : ctx->style_default;
|
||||||
|
ctx->result_key = tim_edit(&ctx->edit, place.x, place.y, place.w, style);
|
||||||
|
tim_label(ctx->label, place.x + 3, place.y, A, 1, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct StartScreenContext {
|
||||||
|
TimStyle style_default;
|
||||||
|
TimStyle style_focused;
|
||||||
|
TimStyle style_error;
|
||||||
|
TextInputState input_username;
|
||||||
|
TextInputState input_password;
|
||||||
|
bool exit;
|
||||||
|
bool start;
|
||||||
|
char* err_msg; // heap only
|
||||||
|
} StartScreenContext;
|
||||||
|
|
||||||
|
void draw_start_screen_buttons(bool is_selected, TimRect place, void* data){
|
||||||
|
StartScreenContext* ctx = data;
|
||||||
|
TimStyle style = is_selected ? ctx->style_focused : ctx->style_default;
|
||||||
|
i32 start_w = place.w / 2;
|
||||||
|
i32 dist = 1;
|
||||||
|
i32 quit_x = place.x + start_w + dist;
|
||||||
|
i32 quit_w = place.w - start_w - dist;
|
||||||
|
if(tim_button("[Enter] Start", place.x, place.y, start_w, A, style)
|
||||||
|
|| tim_is_key_press(TimKey_Enter))
|
||||||
|
{
|
||||||
|
ctx->start = true;
|
||||||
|
}
|
||||||
|
else if(tim_button("[Esc/Q] Quit", quit_x, place.y, quit_w, A, ctx->style_default)
|
||||||
|
|| tim_is_key_press('q'))
|
||||||
|
{
|
||||||
|
ctx->exit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(bool) start_screen(Array(char) username_buf, Array(char) password_buf,
|
||||||
|
str* out_username, str* out_password)
|
||||||
|
{
|
||||||
|
Deferral(16);
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
StartScreenContext ctx = {
|
||||||
|
.style_default = { .brd = TimColor16_Gray, .bg = TimColor16_DarkBlue, .fg = TimColor16_Gray },
|
||||||
|
.style_focused = { .brd = TimColor16_White, .bg = TimColor16_DarkCyan, .fg = TimColor16_White },
|
||||||
|
.style_error = { .brd = TimColor16_Gray, .bg = TimColor16_DarkRed, .fg = TimColor16_White },
|
||||||
|
};
|
||||||
|
|
||||||
|
TimEditState_construct(&ctx.input_username.edit, username_buf.data, username_buf.len, NULL);
|
||||||
|
ctx.input_username.label = "[username]";
|
||||||
|
ctx.input_username.style_default = ctx.style_default;
|
||||||
|
ctx.input_username.style_focused = ctx.style_focused;
|
||||||
|
|
||||||
|
TimEditState_construct(&ctx.input_password.edit, password_buf.data, password_buf.len, NULL);
|
||||||
|
ctx.input_password.edit.masked = true;
|
||||||
|
ctx.input_password.label = "[password]";
|
||||||
|
ctx.input_password.style_default = ctx.style_default;
|
||||||
|
ctx.input_password.style_focused = ctx.style_focused;
|
||||||
|
|
||||||
|
TimScrollItem items[] = {
|
||||||
|
// username input
|
||||||
|
{ .data = &ctx.input_username, .focus_target = &ctx.input_username.edit, .h = 3, .draw = draw_item_text_input },
|
||||||
|
// password input
|
||||||
|
{ .data = &ctx.input_password, .focus_target = &ctx.input_password.edit, .h = 3, .draw = draw_item_text_input },
|
||||||
|
// buttons
|
||||||
|
{ .data = &ctx, .focus_target = NULL, .h = 3, .draw = draw_start_screen_buttons },
|
||||||
|
};
|
||||||
|
|
||||||
|
TimScrollState scroll_list = { .items = items, .len = ARRAY_LEN(items) };
|
||||||
|
|
||||||
|
while(tim_run(FPS)){
|
||||||
|
tim_fill(tim_cell(" ", ctx.style_default.fg, ctx.style_default.bg), 0, 0, A, A);
|
||||||
|
tim_scope(A, A, 40, 14) {
|
||||||
|
tim_scroll(&scroll_list, 1, 1, ~1, ~1, ctx.style_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ctx.err_msg){
|
||||||
|
i32 below_scroll = tim->scopes[tim->scope].h/2 + 7;
|
||||||
|
tim_label("ERROR: ", A, below_scroll, A, A, ctx.style_error);
|
||||||
|
tim_label(ctx.err_msg, A, below_scroll + 1, A, A, ctx.style_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ctx.exit
|
||||||
|
|| ctx.input_username.result_key == TimKey_Escape
|
||||||
|
|| ctx.input_password.result_key == TimKey_Escape)
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ctx.start){
|
||||||
|
ctx.start = false;
|
||||||
|
free(ctx.err_msg);
|
||||||
|
ctx.err_msg = NULL;
|
||||||
|
|
||||||
|
// check username
|
||||||
|
str username = str_from_cstr(username_buf.data);
|
||||||
|
str_trim(&username, true);
|
||||||
|
str name_error_str = validateUsername_str(username);
|
||||||
|
if(name_error_str.data){
|
||||||
|
ctx.err_msg = name_error_str.data;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check password
|
||||||
|
str password = str_from_cstr(password_buf.data);
|
||||||
|
str_trim(&password, true);
|
||||||
|
if(password.len < PASSWORD_SIZE_MIN || password.len > PASSWORD_SIZE_MAX){
|
||||||
|
ctx.err_msg = sprintf_malloc(
|
||||||
|
"password length (in bytes) must be >= %i and <= %i",
|
||||||
|
PASSWORD_SIZE_MIN, PASSWORD_SIZE_MAX
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_username = username;
|
||||||
|
*out_password = password;
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Return RESULT_VALUE(i, result);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user