Skip to content

Commit

Permalink
feat: on-demand contextual chatting (#211)
Browse files Browse the repository at this point in the history
* improve existing texts

* add hint bubble

* add more replicas and reset

* fix format
  • Loading branch information
Th3Un1q3 authored Jul 16, 2023
1 parent 52cc11a commit d329978
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 30 deletions.
1 change: 1 addition & 0 deletions flipp_pomodoro_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ typedef enum
FlippPomodoroAppCustomEventStageSkip = 100,
FlippPomodoroAppCustomEventStageComplete, // By Expiration
FlippPomodoroAppCustomEventTimerTick,
FlippPomodoroAppCustomEventTimerAskHint,
FlippPomodoroAppCustomEventStateUpdated,
FlippPomodoroAppCustomEventResumeTimer,
} FlippPomodoroAppCustomEvent;
Expand Down
6 changes: 3 additions & 3 deletions modules/flipp_pomodoro.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ PomodoroStage stages_sequence[] = {
};

char *current_stage_label[] = {
[FlippPomodoroStageFocus] = "Continue focus for:",
[FlippPomodoroStageRest] = "Keep rest for:",
[FlippPomodoroStageLongBreak] = "Long Break for:",
[FlippPomodoroStageFocus] = "Focusing...",
[FlippPomodoroStageRest] = "Short Break...",
[FlippPomodoroStageLongBreak] = "Long Break...",
};

char *next_stage_label[] = {
Expand Down
99 changes: 91 additions & 8 deletions scenes/flipp_pomodoro_scene_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,50 @@ enum
SceneEventNotConusmed = false
};

static char *work_hints[] = {
"Can you explain the problem as if I'm five?",
"Expected output vs. reality: what's the difference?",
"Ever thought of slicing the problem into bite-sized pieces?",
"What's the story when you walk through the code?",
"Any error messages gossiping about the issue?",
"What tricks have you tried to fix this?",
"Did you test the code, or just hoping for the best?",
"How's this code mingling with the rest of the app?",
"Any sneaky side effects causing mischief?",
"What are you assuming, and is it safe to do so?",
"Did you remember to invite all the edge cases to the party?",
"What happens in the isolation chamber (running code separately)?",
"Can you make the issue appear on command?",
"What's the scene at the crime spot when the error occurs?",
"Did you seek wisdom from the grand oracle (Google)?",
"What if you take a different path to solve this?",
"Did you take a coffee break to reboot your brain?"};

static char *break_hints[] = {
"Time to stretch! Remember, your body isn't made of code.",
"Hydrate or diedrate! Grab a glass of water.",
"Blink! Your eyes need a break too.",
"How about a quick dance-off with your shadow?",
"Ever tried chair yoga? Now's the time!",
"Time for a quick peek out the window. The outside world still exists!",
"Quick, think about kittens! Or puppies! Or baby turtles!",
"Time for a laugh. Look up a joke or two!",
"Sing a song. Bonus points for making up your own lyrics.",
"Do a quick tidy-up. A clean space is a happy space!",
"Time to play 'air' musical instrument for a minute.",
"How about a quick doodle? Unleash your inner Picasso!",
"Practice your superhero pose. Feel the power surge!",
"Quick, tell yourself a joke. Don't worry, I won't judge.",
"Time to practice your mime skills. Stuck in a box, anyone?",
"Ever tried juggling? Now's your chance!",
"Do a quick self high-five, you're doing great!"};

static char *random_string_of_list(char **hints, size_t num_hints)
{
int random_index = rand() % num_hints;
return hints[random_index];
}

void flipp_pomodoro_scene_timer_sync_view_state(void *ctx)
{
furi_assert(ctx);
Expand All @@ -34,6 +78,14 @@ void flipp_pomodoro_scene_timer_on_next_stage(void *ctx)
FlippPomodoroAppCustomEventStageSkip);
};

void flipp_pomodoro_scene_timer_on_ask_hint(void *ctx)
{
FlippPomodoroApp *app = ctx;
view_dispatcher_send_custom_event(
app->view_dispatcher,
FlippPomodoroAppCustomEventTimerAskHint);
}

void flipp_pomodoro_scene_timer_on_enter(void *ctx)
{
furi_assert(ctx);
Expand All @@ -48,24 +100,55 @@ void flipp_pomodoro_scene_timer_on_enter(void *ctx)

view_dispatcher_switch_to_view(app->view_dispatcher, FlippPomodoroAppViewTimer);
flipp_pomodoro_scene_timer_sync_view_state(app);

flipp_pomodoro_view_timer_set_callback_context(app->timer_view, app);

flipp_pomodoro_view_timer_set_on_ok_cb(
app->timer_view,
flipp_pomodoro_scene_timer_on_ask_hint);

flipp_pomodoro_view_timer_set_on_right_cb(
app->timer_view,
flipp_pomodoro_scene_timer_on_next_stage,
app);
flipp_pomodoro_scene_timer_on_next_stage);
};

void flipp_pomodoro_scene_timer_handle_custom_event(FlippPomodoroApp *app, FlippPomodoroAppCustomEvent custom_event)
char *flipp_pomodoro_scene_timer_get_contextual_hint(FlippPomodoroApp *app)
{
if (custom_event == FlippPomodoroAppCustomEventTimerTick && flipp_pomodoro__is_stage_expired(app->state))
switch (flipp_pomodoro__get_stage(app->state))
{
view_dispatcher_send_custom_event(
app->view_dispatcher,
FlippPomodoroAppCustomEventStageComplete);
case FlippPomodoroStageFocus:
return random_string_of_list(work_hints, sizeof(work_hints) / sizeof(work_hints[0]));
case FlippPomodoroStageRest:
case FlippPomodoroStageLongBreak:
return random_string_of_list(break_hints, sizeof(break_hints) / sizeof(break_hints[0]));
default:
return "What's up?";
}
}

if (custom_event == FlippPomodoroAppCustomEventStateUpdated)
void flipp_pomodoro_scene_timer_handle_custom_event(FlippPomodoroApp *app, FlippPomodoroAppCustomEvent custom_event)
{
switch (custom_event)
{
case FlippPomodoroAppCustomEventTimerTick:
if (flipp_pomodoro__is_stage_expired(app->state))
{
view_dispatcher_send_custom_event(
app->view_dispatcher,
FlippPomodoroAppCustomEventStageComplete);
}
break;
case FlippPomodoroAppCustomEventStateUpdated:
flipp_pomodoro_scene_timer_sync_view_state(app);
break;
case FlippPomodoroAppCustomEventTimerAskHint:
flipp_pomodoro_view_timer_display_hint(
flipp_pomodoro_view_timer_get_view(app->timer_view),
flipp_pomodoro_scene_timer_get_contextual_hint(app));
break;
default:
// optional: code to be executed if custom_event doesn't match any cases
break;
}
};

Expand Down
6 changes: 3 additions & 3 deletions views/flipp_pomodoro_info_view.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ static void flipp_pomodoro_info_view_draw_statistics(Canvas *canvas, FlippPomodo
{
FuriString *stats_string = furi_string_alloc();

furi_string_printf(stats_string, "So Long,\nand Thanks for All the Focus...\nand for completing\n%i pomodoro(s)", model->pomodoros_completed);
furi_string_printf(stats_string, "So Long,\nand Thanks for All the Focus...\nand for completing\n\e#%i\e# pomodoro(s)", model->pomodoros_completed);
const char *stats_string_formatted = furi_string_get_cstr(stats_string);

elements_text_box(
Expand Down Expand Up @@ -107,14 +107,14 @@ bool flipp_pomodoro_info_view_input_callback(InputEvent *event, void *ctx)
{
FlippPomodoroInfoView *info_view = ctx;

if (event->type == InputTypePress)
if (event->type == InputTypePress)
{
if (event->key == InputKeyRight && info_view->resume_timer_cb != NULL)
{
info_view->resume_timer_cb(info_view->user_action_cb_ctx);
return ViewInputConsumed;
}
else if (event->key == InputKeyLeft)
else if (event->key == InputKeyLeft)
{
flipp_pomodoro_info_view_toggle_mode(info_view);
return ViewInputConsumed;
Expand Down
126 changes: 111 additions & 15 deletions views/flipp_pomodoro_timer_view.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ struct FlippPomodoroTimerView
{
View *view;
FlippPomodoroTimerViewInputCb right_cb;
void *right_cb_ctx;
FlippPomodoroTimerViewInputCb ok_cb;
void *callback_context;
};

typedef struct
{
IconAnimation *icon;
FlippPomodoroState *state;
size_t scroll_counter;
char *current_hint;
} FlippPomodoroTimerViewModel;

static const Icon *stage_background_image[] = {
Expand Down Expand Up @@ -108,6 +111,58 @@ static void flipp_pomodoro_view_timer_draw_current_stage_label(Canvas *canvas, F
flipp_pomodoro__current_stage_label(state));
}

static void flipp_pomodoro_view_timer_draw_hint(Canvas *canvas, FlippPomodoroTimerViewModel *model)
{
size_t MAX_SCROLL_COUNTER = 300;
uint8_t SCROLL_DELAY_FRAMES = 3;

if (model->scroll_counter >= MAX_SCROLL_COUNTER || model->current_hint == NULL)
{
return;
}

uint8_t hint_width = 90;
uint8_t hint_height = 18;

uint8_t hint_x = canvas_width(canvas) - hint_width - 6;
uint8_t hint_y = 35;

FuriString *displayed_hint_string = furi_string_alloc();

furi_string_printf(
displayed_hint_string,
"%s",
model->current_hint);

size_t perfect_duration = furi_string_size(displayed_hint_string) * 1.5;

if (model->scroll_counter > perfect_duration)
{
model->scroll_counter = MAX_SCROLL_COUNTER;
furi_string_free(displayed_hint_string);
return;
}

size_t scroll_offset = (model->scroll_counter < SCROLL_DELAY_FRAMES) ? 0 : model->scroll_counter - SCROLL_DELAY_FRAMES;

canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, hint_x, hint_y, hint_width + 3, hint_height);
canvas_set_color(canvas, ColorBlack);

elements_bubble(canvas, hint_x, hint_y, hint_width, hint_height);

elements_scrollable_text_line(
canvas,
hint_x + 6,
hint_y + 12,
hint_width - 4,
displayed_hint_string,
scroll_offset,
true);
furi_string_free(displayed_hint_string);
model->scroll_counter++;
}

static void flipp_pomodoro_view_timer_draw_callback(Canvas *canvas, void *_model)
{
if (!_model)
Expand All @@ -128,10 +183,12 @@ static void flipp_pomodoro_view_timer_draw_callback(Canvas *canvas, void *_model
flipp_pomodoro__stage_remaining_duration(model->state));

flipp_pomodoro_view_timer_draw_current_stage_label(canvas, model->state);

canvas_set_color(canvas, ColorBlack);

canvas_set_font(canvas, FontSecondary);
elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state));
flipp_pomodoro_view_timer_draw_hint(canvas, model);
};

bool flipp_pomodoro_view_timer_input_callback(InputEvent *event, void *ctx)
Expand All @@ -140,19 +197,24 @@ bool flipp_pomodoro_view_timer_input_callback(InputEvent *event, void *ctx)
furi_assert(event);
FlippPomodoroTimerView *timer = ctx;

const bool should_trigger_right_event_cb = (event->type == InputTypePress) &&
(event->key == InputKeyRight) &&
(timer->right_cb != NULL);
const bool is_press_event = event->type == InputTypePress;

if (should_trigger_right_event_cb)
if (!is_press_event)
{
furi_assert(timer->right_cb);
furi_assert(timer->right_cb_ctx);
timer->right_cb(timer->right_cb_ctx);
return ViewInputConsumed;
};
return ViewInputNotConusmed;
}

return ViewInputNotConusmed;
switch (event->key)
{
case InputKeyRight:
timer->right_cb(timer->callback_context);
return ViewInputConsumed;
case InputKeyOk:
timer->ok_cb(timer->callback_context);
return ViewInputConsumed;
default:
return ViewInputNotConusmed;
}
};

View *flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView *timer)
Expand All @@ -161,13 +223,24 @@ View *flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView *timer)
return timer->view;
};

void flipp_pomodoro_view_timer_display_hint(View *view, char *hint)
{
with_view_model(
view,
FlippPomodoroTimerViewModel * model,
{
model->scroll_counter = 0;
model->current_hint = hint;
},
true);
}

void flipp_pomodoro_view_timer_assign_animation(View *view)
{
with_view_model(
view,
FlippPomodoroTimerViewModel * model,
{
furi_assert(model->state);
if (model->icon)
{
icon_animation_free(model->icon);
Expand All @@ -186,21 +259,43 @@ FlippPomodoroTimerView *flipp_pomodoro_view_timer_alloc()
timer->view = view_alloc();

view_allocate_model(flipp_pomodoro_view_timer_get_view(timer), ViewModelTypeLockFree, sizeof(FlippPomodoroTimerViewModel));

view_set_context(flipp_pomodoro_view_timer_get_view(timer), timer);
view_set_draw_callback(timer->view, flipp_pomodoro_view_timer_draw_callback);
view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback);

with_view_model(
flipp_pomodoro_view_timer_get_view(timer),
FlippPomodoroTimerViewModel * model,
{
model->scroll_counter = 0;
},
false);

return timer;
};

void flipp_pomodoro_view_timer_set_on_right_cb(FlippPomodoroTimerView *timer, FlippPomodoroTimerViewInputCb right_cb, void *right_cb_ctx)
void flipp_pomodoro_view_timer_set_callback_context(FlippPomodoroTimerView *timer, void *callback_ctx)
{
furi_assert(timer);
furi_assert(callback_ctx);
timer->callback_context = callback_ctx;
}

void flipp_pomodoro_view_timer_set_on_right_cb(FlippPomodoroTimerView *timer, FlippPomodoroTimerViewInputCb right_cb)
{
furi_assert(timer);
furi_assert(right_cb);
furi_assert(right_cb_ctx);
timer->right_cb = right_cb;
timer->right_cb_ctx = right_cb_ctx;
};

void flipp_pomodoro_view_timer_set_on_ok_cb(FlippPomodoroTimerView *timer, FlippPomodoroTimerViewInputCb ok_kb)
{
furi_assert(ok_kb);
furi_assert(timer);
timer->ok_cb = ok_kb;
}

void flipp_pomodoro_view_timer_set_state(View *view, FlippPomodoroState *state)
{
furi_assert(view);
Expand All @@ -210,6 +305,7 @@ void flipp_pomodoro_view_timer_set_state(View *view, FlippPomodoroState *state)
FlippPomodoroTimerViewModel * model,
{
model->state = state;
model->current_hint = NULL;
},
false);
flipp_pomodoro_view_timer_assign_animation(view);
Expand Down
8 changes: 7 additions & 1 deletion views/flipp_pomodoro_timer_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView *timer);

void flipp_pomodoro_view_timer_set_state(View *view, FlippPomodoroState *state);

void flipp_pomodoro_view_timer_set_on_right_cb(FlippPomodoroTimerView *timer, FlippPomodoroTimerViewInputCb right_cb, void *right_cb_ctx);
void flipp_pomodoro_view_timer_set_callback_context(FlippPomodoroTimerView *timer, void *callback_ctx);

void flipp_pomodoro_view_timer_set_on_right_cb(FlippPomodoroTimerView *timer, FlippPomodoroTimerViewInputCb right_cb);

void flipp_pomodoro_view_timer_set_on_ok_cb(FlippPomodoroTimerView *timer, FlippPomodoroTimerViewInputCb ok_cb);

void flipp_pomodoro_view_timer_display_hint(View *view, char *hint);

0 comments on commit d329978

Please sign in to comment.