From 8b452eadc67160b92f807f2393a0aa1069bcc5f6 Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Tue, 3 Dec 2024 16:19:51 -0700 Subject: [PATCH 1/9] Also use an RPC_KEYBOARD if present. --- text_input/rpc_keyboard.h | 162 +++++++++++++++++ text_input/rpc_keyboard_stub.c | 150 ++++++++++++++++ text_input/uart_text_input.c | 312 +++++++++++++++++++++++++++------ 3 files changed, 572 insertions(+), 52 deletions(-) create mode 100644 text_input/rpc_keyboard.h create mode 100644 text_input/rpc_keyboard_stub.c diff --git a/text_input/rpc_keyboard.h b/text_input/rpc_keyboard.h new file mode 100644 index 00000000000..1db8fd570ca --- /dev/null +++ b/text_input/rpc_keyboard.h @@ -0,0 +1,162 @@ +#pragma once + +#include +#include +#include + +#define RECORD_RPC_KEYBOARD "rpckeyboard" + +#define RPC_KEYBOARD_KEY_RIGHT '\x13' +#define RPC_KEYBOARD_KEY_LEFT '\x14' +#define RPC_KEYBOARD_KEY_ENTER '\x0D' +#define RPC_KEYBOARD_KEY_BACKSPACE '\x08' + +typedef enum +{ + // Unknown error occurred + RpcKeyboardChatpadStatusError, + // The chatpad worker is stopped + RpcKeyboardChatpadStatusStopped, + // The chatpad worker is started, but not ready + RpcKeyboardChatpadStatusStarted, + // The chatpad worker is ready and got response from chatpad + RpcKeyboardChatpadStatusReady, +} RpcKeyboardChatpadStatus; + +typedef struct RpcKeyboard RpcKeyboard; + +typedef enum +{ + // Replacement text was provided by the user + RpcKeyboardEventTypeTextEntered, + // A single character was provided by the user + RpcKeyboardEventTypeCharEntered, + // A macro was entered by the user + RpcKeyboardEventTypeMacroEntered, +} RpcKeyboardEventType; + +typedef struct +{ + // The mutex to protect the data, call furi_mutex_acquire/furi_mutex_release. + FuriMutex *mutex; + // The text message, macro or character. + char message[256]; + // The length of the message. + uint16_t length; + // The newline enabled flag, allow newline to submit text. + bool newline_enabled; +} RpcKeyboardEventData; + +typedef struct +{ + RpcKeyboardEventType type; + RpcKeyboardEventData data; +} RpcKeyboardEvent; + +typedef FuriPubSub *(*RpcKeyboardGetPubsub)(RpcKeyboard *rpc_keyboard); +typedef void (*RpcKeyboardNewlineEnable)(RpcKeyboard *rpc_keyboard, bool enable); +typedef void (*RpcKeyboardPublishCharFn)(RpcKeyboard *keyboard, char character); +typedef void (*RpcKeyboardPublishMacroFn)(RpcKeyboard *rpc_keyboard, char macro); +typedef char *(*RpcKeyboardGetMacroFn)(RpcKeyboard *rpc_keyboard, char macro); +typedef void (*RpcKeyboardSetMacroFn)(RpcKeyboard *rpc_keyboard, char macro, char *value); +typedef void (*RpcKeyboardChatpadStartFn)(RpcKeyboard *rpc_keyboard); +typedef void (*RpcKeyboardChatpadStopFn)(RpcKeyboard *rpc_keyboard); +typedef RpcKeyboardChatpadStatus (*RpcKeyboardChatpadStatusFn)(RpcKeyboard *rpc_keyboard); + +typedef struct RpcKeyboardFunctions RpcKeyboardFunctions; +struct RpcKeyboardFunctions +{ + uint16_t major; + uint16_t minor; + RpcKeyboardGetPubsub fn_get_pubsub; + RpcKeyboardNewlineEnable fn_newline_enable; + RpcKeyboardPublishCharFn fn_publish_char; + RpcKeyboardPublishMacroFn fn_publish_macro; + RpcKeyboardGetMacroFn fn_get_macro; + RpcKeyboardSetMacroFn fn_set_macro; + RpcKeyboardChatpadStartFn fn_chatpad_start; + RpcKeyboardChatpadStopFn fn_chatpad_stop; + RpcKeyboardChatpadStatusFn fn_chatpad_status; +}; + +/** + * @brief STARTUP - Register the remote keyboard. + */ +void rpc_keyboard_register(void); + +/** + * @brief UNUSED - Unregister the remote keyboard. + */ +void rpc_keyboard_release(void); + +/** + * @brief Get the pubsub object for the remote keyboard. + * @details This function returns the pubsub object, use to subscribe to keyboard events. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @return FuriPubSub* pointer to the pubsub object. + */ +FuriPubSub *rpc_keyboard_get_pubsub(RpcKeyboard *rpc_keyboard); + +/** + * @brief Enable or disable newline character submitting the text. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] enable true to enable, false to disable. + */ +void rpc_keyboard_newline_enable(RpcKeyboard *rpc_keyboard, bool enable); + +/** + * @brief Publish the replacement text to the remote keyboard. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] bytes pointer to the text buffer. + * @param[in] buffer_size size of the text buffer. + */ +void rpc_keyboard_publish_text(RpcKeyboard *rpc_keyboard, uint8_t *bytes, uint32_t buffer_size); + +/** + * @brief Publish a single key pressed on the remote keyboard. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] character the character that was pressed. + */ +void rpc_keyboard_publish_char(RpcKeyboard *rpc_keyboard, char character); + +/** + * @brief Publish a macro key pressed on the remote keyboard. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] character the macro key that was pressed. + */ +void rpc_keyboard_publish_macro(RpcKeyboard *rpc_keyboard, char macro); + +/** + * @brief Get the macro text associated with a macro key. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] macro the macro key. + * @return char* pointer to the macro text. NULL if the macro key is not set. User must free the memory. + */ +char *rpc_keyboard_get_macro(RpcKeyboard *rpc_keyboard, char macro); + +/** + * @brief Set the macro text associated with a macro key. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] macro the macro key. + * @param[in] value the macro text. + */ +void rpc_keyboard_set_macro(RpcKeyboard *rpc_keyboard, char macro, char *value); + +/** + * @brief Initializes the chatpad and starts listening for keypresses. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + */ +void rpc_keyboard_chatpad_start(RpcKeyboard *rpc_keyboard); + +/** + * @brief Stops the chatpad & frees resources. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + */ +void rpc_keyboard_chatpad_stop(RpcKeyboard *rpc_keyboard); + +/** + * @brief Get the status of the chatpad. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @return RpcKeyboardChatpadStatus the status of the chatpad. + */ +RpcKeyboardChatpadStatus rpc_keyboard_chatpad_status(RpcKeyboard *rpc_keyboard); diff --git a/text_input/rpc_keyboard_stub.c b/text_input/rpc_keyboard_stub.c new file mode 100644 index 00000000000..eb7afb52bce --- /dev/null +++ b/text_input/rpc_keyboard_stub.c @@ -0,0 +1,150 @@ +#include "rpc_keyboard.h" + +#include + +static bool rpc_keyboard_functions_check_version(RpcKeyboardFunctions *stub) +{ + furi_check(stub); + if (stub->major == 1 && stub->minor > 2) + { + return true; + } + FURI_LOG_D("RpcKeyboard", "Unsupported version %d.%d", stub->major, stub->minor); + return false; +} + +/** + * @brief Get the pubsub object for the remote keyboard. + * @details This function returns the pubsub object, use to subscribe to keyboard events. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @return FuriPubSub* pointer to the pubsub object. + */ +FuriPubSub *rpc_keyboard_get_pubsub(RpcKeyboard *rpc_keyboard) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return NULL; + } + return stub->fn_get_pubsub((RpcKeyboard *)rpc_keyboard); +} + +/** + * @brief Enable or disable newline character submitting the text. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] enable true to enable, false to disable. + */ +void rpc_keyboard_newline_enable(RpcKeyboard *rpc_keyboard, bool enable) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return; + } + stub->fn_newline_enable((RpcKeyboard *)rpc_keyboard, enable); +} + +/** + * @brief Publish a single key pressed on the remote keyboard. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] character the character that was pressed. + */ +void rpc_keyboard_publish_char(RpcKeyboard *rpc_keyboard, char character) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return; + } + stub->fn_publish_char((RpcKeyboard *)rpc_keyboard, character); +} + +/** + * @brief Publish a macro key pressed on the remote keyboard. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] character the macro key that was pressed. + */ +void rpc_keyboard_publish_macro(RpcKeyboard *rpc_keyboard, char macro) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return; + } + stub->fn_publish_macro((RpcKeyboard *)rpc_keyboard, macro); +} + +/** + * @brief Get the macro text associated with a macro key. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] macro the macro key. + * @return char* pointer to the macro text. NULL if the macro key is not set. User must free the memory. + */ +char *rpc_keyboard_get_macro(RpcKeyboard *rpc_keyboard, char macro) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return NULL; + } + return stub->fn_get_macro((RpcKeyboard *)rpc_keyboard, macro); +} + +/** + * @brief Set the macro text associated with a macro key. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @param[in] macro the macro key. + * @param[in] value the macro text. + */ +void rpc_keyboard_set_macro(RpcKeyboard *rpc_keyboard, char macro, char *value) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return; + } + stub->fn_set_macro((RpcKeyboard *)rpc_keyboard, macro, value); +} + +/** + * @brief Initializes the chatpad and starts listening for keypresses. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + */ +void rpc_keyboard_chatpad_start(RpcKeyboard *rpc_keyboard) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return; + } + stub->fn_chatpad_start((RpcKeyboard *)rpc_keyboard); +} + +/** + * @brief Stops the chatpad & frees resources. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + */ +void rpc_keyboard_chatpad_stop(RpcKeyboard *rpc_keyboard) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return; + } + stub->fn_chatpad_stop((RpcKeyboard *)rpc_keyboard); +} + +/** + * @brief Get the status of the chatpad. + * @param[in] rpc_keyboard pointer to the RECORD_RPC_KEYBOARD. + * @return RpcKeyboardChatpadStatus the status of the chatpad. + */ +RpcKeyboardChatpadStatus rpc_keyboard_chatpad_status(RpcKeyboard *rpc_keyboard) +{ + RpcKeyboardFunctions *stub = (RpcKeyboardFunctions *)rpc_keyboard; + if (!rpc_keyboard_functions_check_version(stub)) + { + return RpcKeyboardChatpadStatusError; + } + return stub->fn_chatpad_status((RpcKeyboard *)rpc_keyboard); +} diff --git a/text_input/uart_text_input.c b/text_input/uart_text_input.c index 572167cc43a..ef14dbd5ef4 100644 --- a/text_input/uart_text_input.c +++ b/text_input/uart_text_input.c @@ -4,6 +4,7 @@ #include #include "flip_social_icons.h" #include +#include "rpc_keyboard.h" struct UART_TextInput { @@ -25,6 +26,9 @@ typedef struct size_t text_buffer_size; bool clear_default_text; + FuriPubSubSubscription *keyboard_subscription; + bool invoke_callback; + UART_TextInputCallback callback; void *callback_context; @@ -281,6 +285,21 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) uint8_t needed_string_width = canvas_width(canvas) - 8; uint8_t start_pos = 4; + if (model->invoke_callback) + { + model->invoke_callback = false; + if (model->validator_callback && (!model->validator_callback(model->text_buffer, model->validator_text, model->validator_callback_context))) + { + model->valadator_message_visible = true; + } + else if (model->callback != 0) + { + // We hijack the current thread to invoke the callback (we aren't doing a draw). + model->callback(model->callback_context); + return; + } + } + const char *text = model->text_buffer; canvas_clear(canvas); @@ -329,17 +348,17 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) if (model->selected_row == row && model->selected_column == column) { canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, + canvas, + keyboard_origin_x + keys[column].x, keyboard_origin_y + keys[column].y, &I_KeySaveSelected_24x11); } else { canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, &I_KeySave_24x11); } } @@ -349,17 +368,17 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) if (model->selected_row == row && model->selected_column == column) { canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, &I_KeyBackspaceSelected_16x9); } else { canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, &I_KeyBackspace_16x9); } } @@ -369,10 +388,10 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) { canvas_set_color(canvas, ColorBlack); canvas_draw_box( - canvas, - keyboard_origin_x + keys[column].x - 1, - keyboard_origin_y + keys[column].y - 8, - 7, + canvas, + keyboard_origin_x + keys[column].x - 1, + keyboard_origin_y + keys[column].y - 8, + 7, 10); canvas_set_color(canvas, ColorWhite); } @@ -383,17 +402,17 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) if (0 == strcmp(model->header, mode_AT)) { canvas_draw_glyph( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, char_to_uppercase(keys[column].text)); } else { canvas_draw_glyph( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, keys[column].text); } } @@ -413,7 +432,7 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) } } -static void +static void uart_text_input_handle_up(UART_TextInput *uart_text_input, UART_TextInputModel *model) { UNUSED(uart_text_input); @@ -427,7 +446,7 @@ uart_text_input_handle_up(UART_TextInput *uart_text_input, UART_TextInputModel * } } -static void +static void uart_text_input_handle_down(UART_TextInput *uart_text_input, UART_TextInputModel *model) { UNUSED(uart_text_input); @@ -441,7 +460,7 @@ uart_text_input_handle_down(UART_TextInput *uart_text_input, UART_TextInputModel } } -static void +static void uart_text_input_handle_left(UART_TextInput *uart_text_input, UART_TextInputModel *model) { UNUSED(uart_text_input); @@ -455,7 +474,7 @@ uart_text_input_handle_left(UART_TextInput *uart_text_input, UART_TextInputModel } } -static void +static void uart_text_input_handle_right(UART_TextInput *uart_text_input, UART_TextInputModel *model) { UNUSED(uart_text_input); @@ -470,8 +489,8 @@ uart_text_input_handle_right(UART_TextInput *uart_text_input, UART_TextInputMode } static void uart_text_input_handle_ok( - UART_TextInput *uart_text_input, - UART_TextInputModel *model, + UART_TextInput *uart_text_input, + UART_TextInputModel *model, bool shift) { char selected = get_selected_char(model); @@ -537,7 +556,7 @@ static bool uart_text_input_view_input_callback(InputEvent *event, void *context // Acquire model UART_TextInputModel *model = view_get_model(uart_text_input->view); - if ((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + if ((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && model->valadator_message_visible) { model->valadator_message_visible = false; @@ -634,12 +653,199 @@ void uart_text_input_timer_callback(void *context) UART_TextInput *uart_text_input = context; with_view_model( - uart_text_input->view, + uart_text_input->view, + UART_TextInputModel * model, + { model->valadator_message_visible = false; }, + true); +} + +static void text_input_keyboard_callback_line(UART_TextInput *text_input, const RpcKeyboardEvent *event) +{ + with_view_model( + text_input->view, + UART_TextInputModel * model, + { + if (model->text_buffer != NULL && model->text_buffer_size > 0) + { + if (event->data.length > 0) + { + furi_mutex_acquire(event->data.mutex, FuriWaitForever); + size_t len = event->data.length; + if (len >= model->text_buffer_size) + { + len = model->text_buffer_size - 1; + } + + bool newline = false; + bool substitutions = false; + size_t copy_index = 0; + for (size_t i = 0; i < len; i++) + { + char ch = event->data.message[i]; + if ((ch >= 0x20 && ch <= 0x7E) || ch == '\n' || ch == '\r') + { + model->text_buffer[copy_index++] = ch; + if (ch == '\n' || ch == '\r') + { + newline = event->data.newline_enabled && !substitutions; // TODO: No min-length check? + break; + } + } + } + model->text_buffer[copy_index] = '\0'; + furi_mutex_release(event->data.mutex); + FURI_LOG_D("text_input", "copy: %d, %d, %s", len, copy_index, model->text_buffer); + + // Set focus on Save + model->selected_row = 3; + model->selected_column = 8; + + // Hijack the next draw to invoke the callback if newline is true. + model->invoke_callback = newline; + } + } + }, + true); +} + +static void text_input_keyboard_type_key(UART_TextInput *text_input, char selected) +{ + with_view_model( + text_input->view, UART_TextInputModel * model, - { model->valadator_message_visible = false; }, + { + size_t text_length = strlen(model->text_buffer); + char search_key = isupper(selected) ? tolower(selected) : selected == ' ' ? '_' + : selected; + bool found = false; + for (int row = 0; row < keyboard_row_count; row++) + { + const UART_TextInputKey *keys = get_row(row); + for (int column = 0; column < get_row_size(row); column++) + { + if (keys[column].text == search_key) + { + model->selected_row = row; + model->selected_column = column; + found = true; + } + } + } + if (!found) + { + // Set focus on Backspace + model->selected_row = 2; + model->selected_column = 9; + } + + if (selected == ENTER_KEY) + { + if (model->validator_callback && (!model->validator_callback(model->text_buffer, model->validator_text, model->validator_callback_context))) + { + model->valadator_message_visible = true; + furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4); + } + else if (model->callback != 0) + { // TODO: no min-length check + model->callback(model->callback_context); + } + } + else if (selected == BACKSPACE_KEY) + { + uart_text_input_backspace_cb(model); + } + else + { + if (model->clear_default_text) + { + text_length = 0; + } + if (selected == RPC_KEYBOARD_KEY_LEFT || selected == RPC_KEYBOARD_KEY_RIGHT) + { + // ignore these keys for now + } + else if (text_length < (model->text_buffer_size - 1)) + { + model->text_buffer[text_length] = selected; + model->text_buffer[text_length + 1] = 0; + } + } + model->clear_default_text = false; + }, true); } +static void text_input_keyboard_callback(const void *message, void *context) +{ + UART_TextInput *text_input = context; + const RpcKeyboardEvent *event = message; + + if (event == NULL) + { + return; + } + + switch (event->type) + { + case RpcKeyboardEventTypeTextEntered: + text_input_keyboard_callback_line(text_input, event); + break; + case RpcKeyboardEventTypeCharEntered: + char ch = event->data.message[0]; + FURI_LOG_I("text_input", "char: %c", ch); + text_input_keyboard_type_key(text_input, ch); + break; + case RpcKeyboardEventTypeMacroEntered: + furi_mutex_acquire(event->data.mutex, FuriWaitForever); + FURI_LOG_I("text_input", "macro: %s", event->data.message); + for (size_t i = 0; i < event->data.length; i++) + { + text_input_keyboard_type_key(text_input, event->data.message[i]); + } + furi_mutex_release(event->data.mutex); + break; + } +} + +static void text_input_view_enter_callback(void *context) +{ + furi_assert(context); + UART_TextInput *text_input = context; + if (furi_record_exists(RECORD_RPC_KEYBOARD)) + { + RpcKeyboard *rpc_keyboard = furi_record_open(RECORD_RPC_KEYBOARD); + FuriPubSub *rpc_keyboard_pubsub = rpc_keyboard_get_pubsub(rpc_keyboard); + if (rpc_keyboard_pubsub != NULL) + { + with_view_model(text_input->view, UART_TextInputModel * model, { model->keyboard_subscription = furi_pubsub_subscribe(rpc_keyboard_pubsub, text_input_keyboard_callback, text_input); }, false); + } + furi_record_close(RECORD_RPC_KEYBOARD); + } +} + +static void text_input_view_exit_callback(void *context) +{ + furi_assert(context); + UART_TextInput *text_input = context; + if (furi_record_exists(RECORD_RPC_KEYBOARD)) + { + RpcKeyboard *rpc_keyboard = furi_record_open(RECORD_RPC_KEYBOARD); + FuriPubSub *rpc_keyboard_pubsub = rpc_keyboard_get_pubsub(rpc_keyboard); + if (rpc_keyboard_pubsub != NULL) + { + with_view_model( + text_input->view, + UART_TextInputModel * model, + { + furi_pubsub_unsubscribe(rpc_keyboard_pubsub, model->keyboard_subscription); + model->keyboard_subscription = NULL; + }, + false); + } + furi_record_close(RECORD_RPC_KEYBOARD); + } +} + UART_TextInput *uart_text_input_alloc() { UART_TextInput *uart_text_input = malloc(sizeof(UART_TextInput)); @@ -648,14 +854,16 @@ UART_TextInput *uart_text_input_alloc() view_allocate_model(uart_text_input->view, ViewModelTypeLocking, sizeof(UART_TextInputModel)); view_set_draw_callback(uart_text_input->view, uart_text_input_view_draw_callback); view_set_input_callback(uart_text_input->view, uart_text_input_view_input_callback); + view_set_enter_callback(uart_text_input->view, text_input_view_enter_callback); + view_set_exit_callback(uart_text_input->view, text_input_view_exit_callback); - uart_text_input->timer = + uart_text_input->timer = furi_timer_alloc(uart_text_input_timer_callback, FuriTimerTypeOnce, uart_text_input); with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { model->validator_text = furi_string_alloc(); }, + uart_text_input->view, + UART_TextInputModel * model, + { model->validator_text = furi_string_alloc(); }, false); uart_text_input_reset(uart_text_input); @@ -667,9 +875,9 @@ void uart_text_input_free(UART_TextInput *uart_text_input) { furi_assert(uart_text_input); with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { furi_string_free(model->validator_text); }, + uart_text_input->view, + UART_TextInputModel * model, + { furi_string_free(model->validator_text); }, false); // Send stop command @@ -713,11 +921,11 @@ View *uart_text_input_get_view(UART_TextInput *uart_text_input) } void uart_text_input_set_result_callback( - UART_TextInput *uart_text_input, - UART_TextInputCallback callback, - void *callback_context, - char *text_buffer, - size_t text_buffer_size, + UART_TextInput *uart_text_input, + UART_TextInputCallback callback, + void *callback_context, + char *text_buffer, + size_t text_buffer_size, bool clear_default_text) { with_view_model( @@ -732,7 +940,7 @@ void uart_text_input_set_result_callback( if (text_buffer && text_buffer[0] != '\0') { // Set focus on Save - model->selected_row = 2; + model->selected_row = 3; model->selected_column = 8; } }, @@ -740,8 +948,8 @@ void uart_text_input_set_result_callback( } void uart_text_input_set_validator( - UART_TextInput *uart_text_input, - UART_TextInputValidatorCallback callback, + UART_TextInput *uart_text_input, + UART_TextInputValidatorCallback callback, void *callback_context) { with_view_model( @@ -754,14 +962,14 @@ void uart_text_input_set_validator( true); } -UART_TextInputValidatorCallback +UART_TextInputValidatorCallback uart_text_input_get_validator_callback(UART_TextInput *uart_text_input) { UART_TextInputValidatorCallback validator_callback = NULL; with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { validator_callback = model->validator_callback; }, + uart_text_input->view, + UART_TextInputModel * model, + { validator_callback = model->validator_callback; }, false); return validator_callback; } @@ -770,9 +978,9 @@ void *uart_text_input_get_validator_callback_context(UART_TextInput *uart_text_i { void *validator_callback_context = NULL; with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { validator_callback_context = model->validator_callback_context; }, + uart_text_input->view, + UART_TextInputModel * model, + { validator_callback_context = model->validator_callback_context; }, false); return validator_callback_context; } From 6781c58a786ff16c7bf39d7d76100a50ba73baf3 Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Tue, 3 Dec 2024 16:22:55 -0700 Subject: [PATCH 2/9] Update version to 0.8 --- alloc/flip_social_alloc.c | 4 ++-- application.fam | 2 +- assets/CHANGELOG.md | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/alloc/flip_social_alloc.c b/alloc/flip_social_alloc.c index b0c97bc3b3b..0af1f8ef8cf 100644 --- a/alloc/flip_social_alloc.c +++ b/alloc/flip_social_alloc.c @@ -176,11 +176,11 @@ FlipSocialApp *flip_social_app_alloc() } // Allocate Submenu(s) - if (!easy_flipper_set_submenu(&app->submenu_logged_out, FlipSocialViewLoggedOutSubmenu, "FlipSocial v0.7", flip_social_callback_exit_app, &app->view_dispatcher)) + if (!easy_flipper_set_submenu(&app->submenu_logged_out, FlipSocialViewLoggedOutSubmenu, "FlipSocial v0.8", flip_social_callback_exit_app, &app->view_dispatcher)) { return NULL; } - if (!easy_flipper_set_submenu(&app->submenu_logged_in, FlipSocialViewLoggedInSubmenu, "FlipSocial v0.7", flip_social_callback_exit_app, &app->view_dispatcher)) + if (!easy_flipper_set_submenu(&app->submenu_logged_in, FlipSocialViewLoggedInSubmenu, "FlipSocial v0.8", flip_social_callback_exit_app, &app->view_dispatcher)) { return NULL; } diff --git a/application.fam b/application.fam index 52f19b48cbe..d0a1258316e 100644 --- a/application.fam +++ b/application.fam @@ -9,6 +9,6 @@ App( fap_icon_assets="assets", fap_author="jblanked", fap_weburl="https://github.com/jblanked/FlipSocial", - fap_version="0.7", + fap_version="0.8", fap_description="Social media platform for the Flipper Zero.", ) diff --git a/assets/CHANGELOG.md b/assets/CHANGELOG.md index 01350f3d732..f3bc5bc84d0 100644 --- a/assets/CHANGELOG.md +++ b/assets/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.8 +- Add support for RPC_KEYBOARD + ## 0.7 - Improved memory allocation - Increased the max explore users from 50 to 100 From 092a879e51eca979d90b4603d5b1ce5fa3d32e4a Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Tue, 3 Dec 2024 16:28:53 -0700 Subject: [PATCH 3/9] fix whitespace --- text_input/uart_text_input.c | 98 ++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/text_input/uart_text_input.c b/text_input/uart_text_input.c index ef14dbd5ef4..b8e8fc9f283 100644 --- a/text_input/uart_text_input.c +++ b/text_input/uart_text_input.c @@ -348,17 +348,17 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) if (model->selected_row == row && model->selected_column == column) { canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, + canvas, + keyboard_origin_x + keys[column].x, keyboard_origin_y + keys[column].y, &I_KeySaveSelected_24x11); } else { canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, &I_KeySave_24x11); } } @@ -368,17 +368,17 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) if (model->selected_row == row && model->selected_column == column) { canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, &I_KeyBackspaceSelected_16x9); } else { canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, &I_KeyBackspace_16x9); } } @@ -388,10 +388,10 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) { canvas_set_color(canvas, ColorBlack); canvas_draw_box( - canvas, - keyboard_origin_x + keys[column].x - 1, - keyboard_origin_y + keys[column].y - 8, - 7, + canvas, + keyboard_origin_x + keys[column].x - 1, + keyboard_origin_y + keys[column].y - 8, + 7, 10); canvas_set_color(canvas, ColorWhite); } @@ -402,17 +402,17 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) if (0 == strcmp(model->header, mode_AT)) { canvas_draw_glyph( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, char_to_uppercase(keys[column].text)); } else { canvas_draw_glyph( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, keys[column].text); } } @@ -432,7 +432,7 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) } } -static void +static void uart_text_input_handle_up(UART_TextInput *uart_text_input, UART_TextInputModel *model) { UNUSED(uart_text_input); @@ -446,7 +446,7 @@ uart_text_input_handle_up(UART_TextInput *uart_text_input, UART_TextInputModel * } } -static void +static void uart_text_input_handle_down(UART_TextInput *uart_text_input, UART_TextInputModel *model) { UNUSED(uart_text_input); @@ -460,7 +460,7 @@ uart_text_input_handle_down(UART_TextInput *uart_text_input, UART_TextInputModel } } -static void +static void uart_text_input_handle_left(UART_TextInput *uart_text_input, UART_TextInputModel *model) { UNUSED(uart_text_input); @@ -474,7 +474,7 @@ uart_text_input_handle_left(UART_TextInput *uart_text_input, UART_TextInputModel } } -static void +static void uart_text_input_handle_right(UART_TextInput *uart_text_input, UART_TextInputModel *model) { UNUSED(uart_text_input); @@ -489,8 +489,8 @@ uart_text_input_handle_right(UART_TextInput *uart_text_input, UART_TextInputMode } static void uart_text_input_handle_ok( - UART_TextInput *uart_text_input, - UART_TextInputModel *model, + UART_TextInput *uart_text_input, + UART_TextInputModel *model, bool shift) { char selected = get_selected_char(model); @@ -556,7 +556,7 @@ static bool uart_text_input_view_input_callback(InputEvent *event, void *context // Acquire model UART_TextInputModel *model = view_get_model(uart_text_input->view); - if ((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + if ((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && model->valadator_message_visible) { model->valadator_message_visible = false; @@ -857,13 +857,13 @@ UART_TextInput *uart_text_input_alloc() view_set_enter_callback(uart_text_input->view, text_input_view_enter_callback); view_set_exit_callback(uart_text_input->view, text_input_view_exit_callback); - uart_text_input->timer = + uart_text_input->timer = furi_timer_alloc(uart_text_input_timer_callback, FuriTimerTypeOnce, uart_text_input); with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { model->validator_text = furi_string_alloc(); }, + uart_text_input->view, + UART_TextInputModel * model, + { model->validator_text = furi_string_alloc(); }, false); uart_text_input_reset(uart_text_input); @@ -875,9 +875,9 @@ void uart_text_input_free(UART_TextInput *uart_text_input) { furi_assert(uart_text_input); with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { furi_string_free(model->validator_text); }, + uart_text_input->view, + UART_TextInputModel * model, + { furi_string_free(model->validator_text); }, false); // Send stop command @@ -921,11 +921,11 @@ View *uart_text_input_get_view(UART_TextInput *uart_text_input) } void uart_text_input_set_result_callback( - UART_TextInput *uart_text_input, - UART_TextInputCallback callback, - void *callback_context, - char *text_buffer, - size_t text_buffer_size, + UART_TextInput *uart_text_input, + UART_TextInputCallback callback, + void *callback_context, + char *text_buffer, + size_t text_buffer_size, bool clear_default_text) { with_view_model( @@ -948,8 +948,8 @@ void uart_text_input_set_result_callback( } void uart_text_input_set_validator( - UART_TextInput *uart_text_input, - UART_TextInputValidatorCallback callback, + UART_TextInput *uart_text_input, + UART_TextInputValidatorCallback callback, void *callback_context) { with_view_model( @@ -962,14 +962,14 @@ void uart_text_input_set_validator( true); } -UART_TextInputValidatorCallback +UART_TextInputValidatorCallback uart_text_input_get_validator_callback(UART_TextInput *uart_text_input) { UART_TextInputValidatorCallback validator_callback = NULL; with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { validator_callback = model->validator_callback; }, + uart_text_input->view, + UART_TextInputModel * model, + { validator_callback = model->validator_callback; }, false); return validator_callback; } @@ -978,9 +978,9 @@ void *uart_text_input_get_validator_callback_context(UART_TextInput *uart_text_i { void *validator_callback_context = NULL; with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { validator_callback_context = model->validator_callback_context; }, + uart_text_input->view, + UART_TextInputModel * model, + { validator_callback_context = model->validator_callback_context; }, false); return validator_callback_context; } From 3c8b188d6432a233ad8b574d7ad96993ca3ec7b9 Mon Sep 17 00:00:00 2001 From: jblanked <82678820+jblanked@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:11:59 -0500 Subject: [PATCH 4/9] FlipSocial - v0.8 new features - Introduced a bio feature in the Profile section: View and update your bio. - Enhanced the Explore view: Clicking on a user now displays their bio and friend count. You can also search for users. - Improved the Messages view: Users can now search for a contact to send messages. --- README.md | 4 +- alloc/flip_social_alloc.c | 52 +++++- assets/CHANGELOG.md | 7 +- assets/README.md | 4 +- callback/flip_social_callback.c | 263 ++++++++++++++++++++++++----- callback/flip_social_callback.h | 5 +- explore/flip_social_explore.c | 36 +++- explore/flip_social_explore.h | 1 + flip_social.c | 27 +++ flip_social.h | 33 +++- flip_storage/flip_social_storage.c | 28 ++- flip_storage/flip_social_storage.h | 3 + 12 files changed, 400 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 7546d8621a6..7f29ee2754f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The highlight of this app is customizable pre-saves, which, as explained below, FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in the WebCrawler app: https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP ## Requirements -- WiFi Developer Board or Raspberry Pi Pico W with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP +- WiFi Developer Board, Raspberry Pi, or ESP32 Device with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP - WiFi Access Point @@ -60,7 +60,7 @@ FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in - Loading screens. **v0.8** -- Improve User Profile (Bio, friend count, block) +- Improve User Profile - Improve Explore Page **v1.0** diff --git a/alloc/flip_social_alloc.c b/alloc/flip_social_alloc.c index 0af1f8ef8cf..dc394621ea8 100644 --- a/alloc/flip_social_alloc.c +++ b/alloc/flip_social_alloc.c @@ -41,6 +41,7 @@ FlipSocialApp *flip_social_app_alloc() app->register_password_logged_out_temp_buffer_size = MAX_USER_LENGTH; app->register_password_2_logged_out_temp_buffer_size = MAX_USER_LENGTH; app->change_password_logged_in_temp_buffer_size = MAX_USER_LENGTH; + app->change_bio_logged_in_temp_buffer_size = MAX_MESSAGE_LENGTH; app->compose_pre_save_logged_in_temp_buffer_size = MAX_MESSAGE_LENGTH; app->wifi_ssid_logged_in_temp_buffer_size = MAX_USER_LENGTH; app->wifi_password_logged_in_temp_buffer_size = MAX_USER_LENGTH; @@ -48,6 +49,8 @@ FlipSocialApp *flip_social_app_alloc() app->login_username_logged_in_temp_buffer_size = MAX_USER_LENGTH; app->messages_new_message_logged_in_temp_buffer_size = MAX_MESSAGE_LENGTH; app->message_user_choice_logged_in_temp_buffer_size = MAX_MESSAGE_LENGTH; + app->explore_logged_in_temp_buffer_size = MAX_USER_LENGTH; + app->message_users_logged_in_temp_buffer_size = MAX_USER_LENGTH; if (!easy_flipper_set_buffer(&app->wifi_ssid_logged_out_temp_buffer, app->wifi_ssid_logged_out_temp_buffer_size)) { return NULL; @@ -80,6 +83,10 @@ FlipSocialApp *flip_social_app_alloc() { return NULL; } + if (!easy_flipper_set_buffer(&app->change_bio_logged_in_temp_buffer, app->change_bio_logged_in_temp_buffer_size)) + { + return NULL; + } if (!easy_flipper_set_buffer(&app->compose_pre_save_logged_in_temp_buffer, app->compose_pre_save_logged_in_temp_buffer_size)) { return NULL; @@ -133,6 +140,10 @@ FlipSocialApp *flip_social_app_alloc() { return NULL; } + if (!easy_flipper_set_buffer(&app->change_bio_logged_in, app->change_bio_logged_in_temp_buffer_size)) + { + return NULL; + } if (!easy_flipper_set_buffer(&app->compose_pre_save_logged_in, app->compose_pre_save_logged_in_temp_buffer_size)) { return NULL; @@ -174,6 +185,22 @@ FlipSocialApp *flip_social_app_alloc() { return NULL; } + if (!easy_flipper_set_buffer(&app->explore_logged_in, app->explore_logged_in_temp_buffer_size)) + { + return NULL; + } + if (!easy_flipper_set_buffer(&app->explore_logged_in_temp_buffer, app->explore_logged_in_temp_buffer_size)) + { + return NULL; + } + if (!easy_flipper_set_buffer(&app->message_users_logged_in, app->message_users_logged_in_temp_buffer_size)) + { + return NULL; + } + if (!easy_flipper_set_buffer(&app->message_users_logged_in_temp_buffer, app->message_users_logged_in_temp_buffer_size)) + { + return NULL; + } // Allocate Submenu(s) if (!easy_flipper_set_submenu(&app->submenu_logged_out, FlipSocialViewLoggedOutSubmenu, "FlipSocial v0.8", flip_social_callback_exit_app, &app->view_dispatcher)) @@ -242,7 +269,8 @@ FlipSocialApp *flip_social_app_alloc() app->variable_item_logged_out_register_button = variable_item_list_add(app->variable_item_list_logged_out_register, "Register", 0, NULL, app); // app->variable_item_logged_in_profile_username = variable_item_list_add(app->variable_item_list_logged_in_profile, "Username", 1, NULL, app); - app->variable_item_logged_in_profile_change_password = variable_item_list_add(app->variable_item_list_logged_in_profile, "Change Password", 1, NULL, app); + app->variable_item_logged_in_profile_change_password = variable_item_list_add(app->variable_item_list_logged_in_profile, "Password", 1, NULL, app); + app->variable_item_logged_in_profile_change_bio = variable_item_list_add(app->variable_item_list_logged_in_profile, "Bio", 1, NULL, app); app->variable_item_logged_in_profile_friends = variable_item_list_add(app->variable_item_list_logged_in_profile, "Friends", 0, NULL, app); // app->variable_item_logged_in_settings_about = variable_item_list_add(app->variable_item_list_logged_in_settings, "About", 0, NULL, app); @@ -281,7 +309,11 @@ FlipSocialApp *flip_social_app_alloc() return NULL; } // - if (!easy_flipper_set_uart_text_input(&app->text_input_logged_in_change_password, FlipSocialViewLoggedInChangePasswordInput, "Enter New Password", app->change_password_logged_in_temp_buffer, app->change_password_logged_in_temp_buffer_size, flip_social_logged_in_profile_change_password_updated, flip_social_callback_to_profile_logged_in, &app->view_dispatcher, app)) + if (!easy_flipper_set_uart_text_input(&app->text_input_logged_in_change_password, FlipSocialViewLoggedInChangePasswordInput, "Password", app->change_password_logged_in_temp_buffer, app->change_password_logged_in_temp_buffer_size, flip_social_logged_in_profile_change_password_updated, flip_social_callback_to_profile_logged_in, &app->view_dispatcher, app)) + { + return NULL; + } + if (!easy_flipper_set_uart_text_input(&app->text_input_logged_in_change_bio, FlipSocialViewLoggedInChangeBioInput, "Bio", app->change_bio_logged_in_temp_buffer, app->change_bio_logged_in_temp_buffer_size, flip_social_logged_in_profile_change_bio_updated, flip_social_callback_to_profile_logged_in, &app->view_dispatcher, app)) { return NULL; } @@ -306,6 +338,14 @@ FlipSocialApp *flip_social_app_alloc() { return NULL; } + if (!easy_flipper_set_uart_text_input(&app->text_input_logged_in_explore, FlipSocialViewLoggedInExploreInput, "Enter Username or Keyword", app->explore_logged_in_temp_buffer, app->explore_logged_in_temp_buffer_size, flip_social_logged_in_explore_updated, flip_social_callback_to_submenu_logged_in, &app->view_dispatcher, app)) + { + return NULL; + } + if (!easy_flipper_set_uart_text_input(&app->text_input_logged_in_message_users, FlipSocialViewLoggedInMessageUsersInput, "Enter Username or Keyword", app->message_users_logged_in_temp_buffer, app->message_users_logged_in_temp_buffer_size, flip_social_logged_in_message_users_updated, flip_social_callback_to_submenu_logged_in, &app->view_dispatcher, app)) + { + return NULL; + } // Load the settings if (!load_settings(app->wifi_ssid_logged_out, @@ -320,6 +360,8 @@ FlipSocialApp *flip_social_app_alloc() app->login_password_logged_out_temp_buffer_size, app->change_password_logged_in, app->change_password_logged_in_temp_buffer_size, + app->change_bio_logged_in, + app->change_bio_logged_in_temp_buffer_size, app->is_logged_in, app->is_logged_in_size)) @@ -377,6 +419,11 @@ FlipSocialApp *flip_social_app_alloc() strncpy(app->change_password_logged_in_temp_buffer, app->change_password_logged_in, app->change_password_logged_in_temp_buffer_size - 1); app->change_password_logged_in_temp_buffer[app->change_password_logged_in_temp_buffer_size - 1] = '\0'; } + if (app->change_bio_logged_in && app->change_bio_logged_in_temp_buffer) + { + strncpy(app->change_bio_logged_in_temp_buffer, app->change_bio_logged_in, app->change_bio_logged_in_temp_buffer_size - 1); + app->change_bio_logged_in_temp_buffer[app->change_bio_logged_in_temp_buffer_size - 1] = '\0'; + } if (app->compose_pre_save_logged_in && app->compose_pre_save_logged_in_temp_buffer) { strncpy(app->compose_pre_save_logged_in_temp_buffer, app->compose_pre_save_logged_in, app->compose_pre_save_logged_in_temp_buffer_size - 1); @@ -470,6 +517,7 @@ FlipSocialApp *flip_social_app_alloc() variable_item_set_current_value_text(app->variable_item_logged_out_wifi_settings_ssid, app->wifi_ssid_logged_out); variable_item_set_current_value_text(app->variable_item_logged_out_login_username, app->login_username_logged_out); variable_item_set_current_value_text(app->variable_item_logged_in_profile_username, app->login_username_logged_in); + variable_item_set_current_value_text(app->variable_item_logged_in_profile_change_bio, app->change_bio_logged_in); // if (app->is_logged_in != NULL && strcmp(app->is_logged_in, "true") == 0) diff --git a/assets/CHANGELOG.md b/assets/CHANGELOG.md index f3bc5bc84d0..e41468ce849 100644 --- a/assets/CHANGELOG.md +++ b/assets/CHANGELOG.md @@ -1,5 +1,8 @@ -## 0.8 -- Add support for RPC_KEYBOARD +## 0.8 - New Features +- Added support for RPC_KEYBOARD (thanks to Derek Jamison). +- Introduced a bio feature in the Profile section: View and update your bio. +- Enhanced the Explore view: Clicking on a user now displays their bio and friend count. You can also search for users. +- Improved the Messages view: Users can now search for a contact to send messages. ## 0.7 - Improved memory allocation diff --git a/assets/README.md b/assets/README.md index 7546d8621a6..7f29ee2754f 100644 --- a/assets/README.md +++ b/assets/README.md @@ -6,7 +6,7 @@ The highlight of this app is customizable pre-saves, which, as explained below, FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in the WebCrawler app: https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP ## Requirements -- WiFi Developer Board or Raspberry Pi Pico W with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP +- WiFi Developer Board, Raspberry Pi, or ESP32 Device with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP - WiFi Access Point @@ -60,7 +60,7 @@ FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in - Loading screens. **v0.8** -- Improve User Profile (Bio, friend count, block) +- Improve User Profile - Improve Explore Page **v1.0** diff --git a/callback/flip_social_callback.c b/callback/flip_social_callback.c index c59a144cc8a..a896e2b33d6 100644 --- a/callback/flip_social_callback.c +++ b/callback/flip_social_callback.c @@ -106,7 +106,7 @@ static void flip_social_free_friends(void) } } -void flip_social_request_error_draw(Canvas *canvas) +static void flip_social_request_error_draw(Canvas *canvas) { if (canvas == NULL) { @@ -200,7 +200,7 @@ static char *flip_social_login_parse(DataLoaderModel *model) strcpy(app_instance->change_password_logged_in, app_instance->login_password_logged_out); } - save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); // send user to the logged in submenu view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInSubmenu); @@ -281,7 +281,7 @@ static char *flip_social_register_parse(DataLoaderModel *model) auth_headers_alloc(); // save the credentials - save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); // send user to the logged in submenu view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInSubmenu); @@ -980,6 +980,48 @@ bool feed_dialog_alloc() } return false; } +static bool flip_social_get_user_info() +{ + char url[256]; + snprintf(url, sizeof(url), "https://www.flipsocial.net/api/user/users/%s/extended/", flip_social_explore->usernames[flip_social_explore->index]); + if (!flipper_http_get_request_with_headers(url, auth_headers)) + { + FURI_LOG_E(TAG, "Failed to send HTTP request for user info"); + fhttp.state = ISSUE; + return false; + } + fhttp.state = RECEIVING; + return true; +} +static bool flip_social_parse_user_info() +{ + if (fhttp.last_response == NULL) + { + FURI_LOG_E(TAG, "Response is NULL"); + return false; + } + if (!app_instance->explore_user_bio) + { + FURI_LOG_E(TAG, "App instance is NULL"); + return false; + } + char *bio = get_json_value("bio", fhttp.last_response, 32); + char *friends = get_json_value("friends", fhttp.last_response, 32); + if (bio && friends) + { + if (strlen(bio) != 0) + { + snprintf(app_instance->explore_user_bio, MAX_MESSAGE_LENGTH, "%s (%s friends)", bio, friends); + } + else + { + snprintf(app_instance->explore_user_bio, MAX_MESSAGE_LENGTH, "%s friends", friends); + } + free(bio); + return true; + } + return false; +} /** * @brief Handle ALL submenu item selections. @@ -1030,12 +1072,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index) &app->view_dispatcher); // view dispatcher break; case FlipSocialSubmenuLoggedInIndexMessagesNewMessage: - flipper_http_loading_task( - flip_social_get_explore, // get the explore users - flip_social_parse_json_message_user_choices, // parse the explore users - FlipSocialViewLoggedInMessagesUserChoices, // switch to the user choices if successful - FlipSocialViewLoggedInSubmenu, // switch back to the main submenu if failed - &app->view_dispatcher); // view dispatcher + view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessageUsersInput); break; case FlipSocialSubmenuLoggedInIndexFeed: free_flip_social_group(); @@ -1048,12 +1085,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index) break; case FlipSocialSubmenuExploreIndex: free_flip_social_group(); - flipper_http_loading_task( - flip_social_get_explore, // get the explore users - flip_social_parse_json_explore, // parse the explore users - FlipSocialViewLoggedInExploreSubmenu, // switch to the explore submenu if successful - FlipSocialViewLoggedInSubmenu, // switch back to the main submenu if failed - &app->view_dispatcher); // view dispatcher + view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInExploreInput); break; case FlipSocialSubmenuLoggedInIndexCompose: free_pre_saved_messages(); @@ -1071,7 +1103,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index) free_flip_social_group(); app->is_logged_in = "false"; - save_settings(app->wifi_ssid_logged_out, app->wifi_password_logged_out, app->login_username_logged_out, app->login_username_logged_in, app->login_password_logged_out, app->change_password_logged_in, app->is_logged_in); + save_settings(app->wifi_ssid_logged_out, app->wifi_password_logged_out, app->login_username_logged_out, app->login_username_logged_in, app->login_password_logged_out, app->change_password_logged_in, app->change_bio_logged_in, app->is_logged_in); view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutSubmenu); break; @@ -1124,30 +1156,70 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index) return; } flip_social_explore->index = index - FlipSocialSubmenuExploreIndexStartIndex; - flip_social_free_explore_dialog(); - if (!app->dialog_explore) + // loading task to get the user info + if (app->explore_user_bio) { - if (!easy_flipper_set_dialog_ex( - &app->dialog_explore, - FlipSocialViewExploreDialog, - "User Options", - 0, - 0, - flip_social_explore->usernames[flip_social_explore->index], - 0, - 10, - "Remove", - "Add", - NULL, - explore_dialog_callback, - flip_social_callback_to_explore_logged_in, - &app->view_dispatcher, - app)) + free(app->explore_user_bio); + app->explore_user_bio = NULL; + } + if (!easy_flipper_set_buffer(&app->explore_user_bio, MAX_MESSAGE_LENGTH)) + { + return; + } + if (flipper_http_process_response_async(flip_social_get_user_info, flip_social_parse_user_info)) + { + flip_social_free_explore_dialog(); + if (!app->dialog_explore) { - return; + if (!easy_flipper_set_dialog_ex( + &app->dialog_explore, + FlipSocialViewExploreDialog, + flip_social_explore->usernames[flip_social_explore->index], + 0, + 0, + app->explore_user_bio, + 0, + 10, + "Remove", // remove if user is a friend (future update) + "Add", // add if user is not a friend (future update) + NULL, + explore_dialog_callback, + flip_social_callback_to_explore_logged_in, + &app->view_dispatcher, + app)) + { + return; + } } + view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewExploreDialog); + } + else + { + flip_social_free_explore_dialog(); + if (!app->dialog_explore) + { + if (!easy_flipper_set_dialog_ex( + &app->dialog_explore, + FlipSocialViewExploreDialog, + flip_social_explore->usernames[flip_social_explore->index], + 0, + 0, + "", + 0, + 10, + "Remove", // remove if user is a friend (future update) + "Add", // add if user is not a friend (future update) + NULL, + explore_dialog_callback, + flip_social_callback_to_explore_logged_in, + &app->view_dispatcher, + app)) + { + return; + } + } + view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewExploreDialog); } - view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewExploreDialog); } // handle the friends selection @@ -1263,7 +1335,7 @@ void flip_social_logged_out_wifi_settings_ssid_updated(void *context) } // Save the settings - save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutWifiSettings); } @@ -1308,7 +1380,7 @@ void flip_social_logged_out_wifi_settings_password_updated(void *context) } // Save the settings - save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutWifiSettings); } @@ -1371,7 +1443,7 @@ void flip_social_logged_out_login_username_updated(void *context) } // Save the settings - save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutLogin); } @@ -1408,7 +1480,7 @@ void flip_social_logged_out_login_password_updated(void *context) } // Save the settings - save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutLogin); } @@ -1592,7 +1664,7 @@ void flip_social_logged_in_wifi_settings_ssid_updated(void *context) } // Save the settings - save_settings(app_instance->wifi_ssid_logged_in, app_instance->wifi_password_logged_in, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_in, app_instance->wifi_password_logged_in, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); // update the wifi settings if (strlen(app->wifi_ssid_logged_in) > 0 && strlen(app->wifi_password_logged_in) > 0) @@ -1638,7 +1710,7 @@ void flip_social_logged_in_wifi_settings_password_updated(void *context) } // Save the settings - save_settings(app_instance->wifi_ssid_logged_in, app_instance->wifi_password_logged_in, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_in, app_instance->wifi_password_logged_in, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); // update the wifi settings if (strlen(app->wifi_ssid_logged_in) > 0 && strlen(app->wifi_password_logged_in) > 0) @@ -1772,7 +1844,44 @@ void flip_social_logged_in_profile_change_password_updated(void *context) return; } // Save the settings - save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in); + save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProfile); +} + +void flip_social_logged_in_profile_change_bio_updated(void *context) +{ + FlipSocialApp *app = (FlipSocialApp *)context; + if (!app) + { + FURI_LOG_E(TAG, "FlipSocialApp is NULL"); + return; + } + + // Store the entered message + strncpy(app->change_bio_logged_in, app->change_bio_logged_in_temp_buffer, app->change_bio_logged_in_temp_buffer_size); + + // Ensure null-termination + app->change_bio_logged_in[app->change_bio_logged_in_temp_buffer_size - 1] = '\0'; + + // Update the message item text + if (app->variable_item_logged_in_profile_change_bio) + { + variable_item_set_current_value_text(app->variable_item_logged_in_profile_change_bio, app->change_bio_logged_in); + } + + // send post request to change bio + auth_headers_alloc(); + char payload[256]; + snprintf(payload, sizeof(payload), "{\"username\":\"%s\",\"bio\":\"%s\"}", app->login_username_logged_out, app->change_bio_logged_in); + if (!flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/change-bio/", auth_headers, payload)) + { + FURI_LOG_E(TAG, "Failed to send post request to change bio"); + FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board"); + return; + } + // Save the settings + save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in); view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProfile); } @@ -1799,7 +1908,10 @@ void flip_social_text_input_logged_in_profile_item_selected(void *context, uint3 case 1: // Change Password view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInChangePasswordInput); break; - case 2: // Friends + case 2: // Change Bio + view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInChangeBioInput); + break; + case 3: // Friends flip_social_free_friends(); free_flip_social_group(); if (!app->submenu_friends) @@ -1968,6 +2080,69 @@ void flip_social_logged_in_messages_new_message_updated(void *context) view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesSubmenu); } +void flip_social_logged_in_explore_updated(void *context) +{ + FlipSocialApp *app = (FlipSocialApp *)context; + if (!app) + { + FURI_LOG_E(TAG, "FlipSocialApp is NULL"); + return; + } + + // check if the message is empty + if (app->explore_logged_in_temp_buffer_size == 0) + { + FURI_LOG_E(TAG, "Message is empty"); + strncpy(app->explore_logged_in, "a", 2); + } + else + { + // Store the entered message + strncpy(app->explore_logged_in, app->explore_logged_in_temp_buffer, app->explore_logged_in_temp_buffer_size); + } + + // Ensure null-termination + app->explore_logged_in[app->explore_logged_in_temp_buffer_size - 1] = '\0'; + + flipper_http_loading_task( + flip_social_get_explore, // get the explore users + flip_social_parse_json_explore, // parse the explore users + FlipSocialViewLoggedInExploreSubmenu, // switch to the explore submenu if successful + FlipSocialViewLoggedInSubmenu, // switch back to the main submenu if failed + &app->view_dispatcher); // view dispatcher +} +void flip_social_logged_in_message_users_updated(void *context) +{ + FlipSocialApp *app = (FlipSocialApp *)context; + if (!app) + { + FURI_LOG_E(TAG, "FlipSocialApp is NULL"); + return; + } + + // check if the message is empty + if (app->message_users_logged_in_temp_buffer_size == 0) + { + FURI_LOG_E(TAG, "Message is empty"); + strncpy(app->message_users_logged_in, "a", 2); + } + else + { + // Store the entered message + strncpy(app->message_users_logged_in, app->message_users_logged_in_temp_buffer, app->message_users_logged_in_temp_buffer_size); + } + + // Ensure null-termination + app->message_users_logged_in[app->message_users_logged_in_temp_buffer_size - 1] = '\0'; + + flipper_http_loading_task( + flip_social_get_explore_2, // get the explore users + flip_social_parse_json_message_user_choices, // parse the explore users + FlipSocialViewLoggedInMessagesUserChoices, // switch to the user choices if successful + FlipSocialViewLoggedInSubmenu, // switch back to the main submenu if failed + &app->view_dispatcher); // view dispatcher +} + static void flip_social_widget_set_text(char *message, Widget **widget) { if (widget == NULL) diff --git a/callback/flip_social_callback.h b/callback/flip_social_callback.h index c7008473fab..722dee08305 100644 --- a/callback/flip_social_callback.h +++ b/callback/flip_social_callback.h @@ -8,8 +8,6 @@ #include #include -void flip_social_request_error_draw(Canvas *canvas); - /** * @brief Navigation callback to go back to the submenu Logged out. * @param context The context - unused @@ -223,6 +221,7 @@ void flip_social_logged_in_profile_change_password_updated(void *context); * @param index The index of the selected item. * @return void */ +void flip_social_logged_in_profile_change_bio_updated(void *context); void flip_social_text_input_logged_in_profile_item_selected(void *context, uint32_t index); /** @@ -253,6 +252,8 @@ void flip_social_logged_in_messages_new_message_updated(void *context); * @return void */ void flip_social_text_input_logged_out_register_item_selected(void *context, uint32_t index); +void flip_social_logged_in_explore_updated(void *context); +void flip_social_logged_in_message_users_updated(void *context); // Add edits by Derek Jamison typedef enum DataState DataState; diff --git a/explore/flip_social_explore.c b/explore/flip_social_explore.c index bc616ce9705..23568caad84 100644 --- a/explore/flip_social_explore.c +++ b/explore/flip_social_explore.c @@ -66,14 +66,45 @@ bool flip_social_get_explore(void) FURI_LOG_E(TAG, "HTTP state is INACTIVE"); return false; } + char *keyword = !app_instance->explore_logged_in || strlen(app_instance->explore_logged_in) == 0 ? "a" : app_instance->explore_logged_in; snprintf( fhttp.file_path, sizeof(fhttp.file_path), - STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/users.json"); + STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/%s.json", + keyword); fhttp.save_received_data = true; auth_headers_alloc(); - if (!flipper_http_get_request_with_headers("https://www.flipsocial.net/api/user/users/", auth_headers)) + char url[256]; + snprintf(url, sizeof(url), "https://www.flipsocial.net/api/user/explore/%s/%d/", keyword, MAX_EXPLORE_USERS); + if (!flipper_http_get_request_with_headers(url, auth_headers)) + { + FURI_LOG_E(TAG, "Failed to send HTTP request for explore"); + fhttp.state = ISSUE; + return false; + } + fhttp.state = RECEIVING; + return true; +} +bool flip_social_get_explore_2(void) +{ + if (fhttp.state == INACTIVE) + { + FURI_LOG_E(TAG, "HTTP state is INACTIVE"); + return false; + } + char *keyword = !app_instance->message_users_logged_in || strlen(app_instance->message_users_logged_in) == 0 ? "a" : app_instance->message_users_logged_in; + snprintf( + fhttp.file_path, + sizeof(fhttp.file_path), + STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/%s.json", + keyword); + + fhttp.save_received_data = true; + auth_headers_alloc(); + char url[256]; + snprintf(url, sizeof(url), "https://www.flipsocial.net/api/user/explore/%s/%d/", keyword, MAX_EXPLORE_USERS); + if (!flipper_http_get_request_with_headers(url, auth_headers)) { FURI_LOG_E(TAG, "Failed to send HTTP request for explore"); fhttp.state = ISSUE; @@ -116,7 +147,6 @@ bool flip_social_parse_json_explore() // set submenu submenu_reset(app_instance->submenu_explore); submenu_set_header(app_instance->submenu_explore, "Explore"); - // Parse the JSON array of usernames for (size_t i = 0; i < MAX_EXPLORE_USERS; i++) { diff --git a/explore/flip_social_explore.h b/explore/flip_social_explore.h index aed55b2dd1d..c177a790189 100644 --- a/explore/flip_social_explore.h +++ b/explore/flip_social_explore.h @@ -5,5 +5,6 @@ FlipSocialModel *flip_social_explore_alloc(); void flip_social_free_explore(); bool flip_social_get_explore(); +bool flip_social_get_explore_2(void); bool flip_social_parse_json_explore(); #endif \ No newline at end of file diff --git a/flip_social.c b/flip_social.c index 8677c233e6c..dd8890e321e 100644 --- a/flip_social.c +++ b/flip_social.c @@ -132,6 +132,11 @@ void flip_social_app_free(FlipSocialApp *app) view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInChangePasswordInput); uart_text_input_free(app->text_input_logged_in_change_password); } + if (app->text_input_logged_in_change_bio) + { + view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInChangeBioInput); + uart_text_input_free(app->text_input_logged_in_change_bio); + } if (app->text_input_logged_in_compose_pre_save_input) { view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInComposeAddPreSaveInput); @@ -157,6 +162,16 @@ void flip_social_app_free(FlipSocialApp *app) view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesNewMessageUserChoicesInput); uart_text_input_free(app->text_input_logged_in_messages_new_message_user_choices); } + if (app->text_input_logged_in_explore) + { + view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInExploreInput); + uart_text_input_free(app->text_input_logged_in_explore); + } + if (app->text_input_logged_in_message_users) + { + view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInMessageUsersInput); + uart_text_input_free(app->text_input_logged_in_message_users); + } // Free Widget(s) if (app->widget_result) @@ -209,10 +224,20 @@ void flip_social_app_free(FlipSocialApp *app) free(app->change_password_logged_in); if (app->change_password_logged_in_temp_buffer) free(app->change_password_logged_in_temp_buffer); + if (app->change_bio_logged_in) + free(app->change_bio_logged_in); if (app->compose_pre_save_logged_in) free(app->compose_pre_save_logged_in); if (app->compose_pre_save_logged_in_temp_buffer) free(app->compose_pre_save_logged_in_temp_buffer); + if (app->explore_logged_in) + free(app->explore_logged_in); + if (app->explore_logged_in_temp_buffer) + free(app->explore_logged_in_temp_buffer); + if (app->message_users_logged_in) + free(app->message_users_logged_in); + if (app->message_users_logged_in_temp_buffer) + free(app->message_users_logged_in_temp_buffer); if (app->wifi_ssid_logged_in) free(app->wifi_ssid_logged_in); if (app->wifi_ssid_logged_in_temp_buffer) @@ -239,6 +264,8 @@ void flip_social_app_free(FlipSocialApp *app) free(last_explore_response); if (selected_message) free(selected_message); + if (app->explore_user_bio) + free(app->explore_user_bio); if (app->input_event && app->input_event_queue) furi_pubsub_unsubscribe(app->input_event_queue, app->input_event); diff --git a/flip_social.h b/flip_social.h index 51e54ff37ce..a367666121b 100644 --- a/flip_social.h +++ b/flip_social.h @@ -11,7 +11,7 @@ #define MAX_PRE_SAVED_MESSAGES 20 // Maximum number of pre-saved messages #define MAX_MESSAGE_LENGTH 100 // Maximum length of a message in the feed -#define MAX_EXPLORE_USERS 100 // Maximum number of users to explore +#define MAX_EXPLORE_USERS 50 // Maximum number of users to explore #define MAX_USER_LENGTH 32 // Maximum length of a username #define MAX_FRIENDS 50 // Maximum number of friends #define MAX_TOKENS 640 // Adjust based on expected JSON tokens @@ -125,12 +125,15 @@ typedef enum FlipSocialViewLoggedInCompose, // The compose screen FlipSocialViewLoggedInSettings, // The settings screen // + FlipSocialViewLoggedInChangeBioInput, // Text input screen for bio input on profile screen FlipSocialViewLoggedInChangePasswordInput, // Text input screen for password input on change password screen FlipSocialViewLoggedInComposeAddPreSaveInput, // Text input screen for add text input on compose screen // FlipSocialViewLoggedInMessagesNewMessageInput, // Text input screen for new message input on messages screen FlipSocialViewLoggedInMessagesNewMessageUserChoicesInput, // Text input screen for new message input on messages screen FlipSocialViewLoggedInMessagesUserChoices, // the view after clicking [New Message] - select a user to message, then direct to input view + FlipSocialViewLoggedInExploreInput, // Text input screen for explore input on explore screen + FlipSocialViewLoggedInMessageUsersInput, // FlipSocialViewLoggedInSettingsAbout, // The about screen FlipSocialViewLoggedInSettingsWifi, // The wifi settings screen @@ -189,12 +192,16 @@ typedef struct UART_TextInput *text_input_logged_out_register_password_2; // Text input for password 2 input on register screen // UART_TextInput *text_input_logged_in_change_password; // Text input for password input on change password screen + UART_TextInput *text_input_logged_in_change_bio; // Text input for bio input on profile screen UART_TextInput *text_input_logged_in_compose_pre_save_input; // Text input for pre save input on compose screen UART_TextInput *text_input_logged_in_wifi_settings_ssid; // Text input for ssid input on wifi settings screen UART_TextInput *text_input_logged_in_wifi_settings_password; // Text input for password input on wifi settings screen // UART_TextInput *text_input_logged_in_messages_new_message; // Text input for new message input on messages screen UART_TextInput *text_input_logged_in_messages_new_message_user_choices; // + // + UART_TextInput *text_input_logged_in_explore; // Text input for explore input on explore screen + UART_TextInput *text_input_logged_in_message_users; VariableItem *variable_item_logged_out_wifi_settings_ssid; // Reference to the ssid configuration item VariableItem *variable_item_logged_out_wifi_settings_password; // Reference to the password configuration item @@ -208,10 +215,12 @@ typedef struct // VariableItem *variable_item_logged_in_profile_username; // Reference to the username configuration item VariableItem *variable_item_logged_in_profile_change_password; // Reference to the change password configuration item - VariableItem *variable_item_logged_in_settings_about; // Reference to the about configuration item - VariableItem *variable_item_logged_in_settings_wifi; // Reference to the wifi settings configuration item - VariableItem *variable_item_logged_in_wifi_settings_ssid; // Reference to the ssid configuration item - VariableItem *variable_item_logged_in_wifi_settings_password; // Reference to the password configuration item + VariableItem *variable_item_logged_in_profile_change_bio; // Reference to the change bio configuration item + // + VariableItem *variable_item_logged_in_settings_about; // Reference to the about configuration item + VariableItem *variable_item_logged_in_settings_wifi; // Reference to the wifi settings configuration item + VariableItem *variable_item_logged_in_wifi_settings_ssid; // Reference to the ssid configuration item + VariableItem *variable_item_logged_in_wifi_settings_password; // Reference to the password configuration item // VariableItem *variable_item_logged_in_profile_friends; // Reference to the friends configuration item // @@ -227,6 +236,10 @@ typedef struct char *login_username_logged_in_temp_buffer; // Temporary buffer for login username text input uint32_t login_username_logged_in_temp_buffer_size; // Size of the login username temporary buffer + char *change_bio_logged_in; // Store the entered bio + char *change_bio_logged_in_temp_buffer; // Temporary buffer for bio text input + uint32_t change_bio_logged_in_temp_buffer_size; // Size of the bio temporary buffer + // char *wifi_ssid_logged_out; // Store the entered wifi ssid char *wifi_ssid_logged_out_temp_buffer; // Temporary buffer for wifi ssid text input uint32_t wifi_ssid_logged_out_temp_buffer_size; // Size of the wifi ssid temporary buffer @@ -280,6 +293,14 @@ typedef struct char *message_user_choice_logged_in; // Store the entered message to send to the selected user char *message_user_choice_logged_in_temp_buffer; // Temporary buffer for message to send to the selected user uint32_t message_user_choice_logged_in_temp_buffer_size; // Size of the message to send to the selected user temporary buffer + // + char *explore_logged_in; // Store the entered explore + char *explore_logged_in_temp_buffer; // Temporary buffer for explore text input + uint32_t explore_logged_in_temp_buffer_size; // Size of the explore temporary buffer + + char *message_users_logged_in; // Store the entered message users + char *message_users_logged_in_temp_buffer; // Temporary buffer for message users text input + uint32_t message_users_logged_in_temp_buffer_size; // Size of the message users temporary buffer Loading *loading; // The loading screen DialogEx *dialog_explore; @@ -287,6 +308,8 @@ typedef struct DialogEx *dialog_messages; DialogEx *dialog_compose; DialogEx *dialog_feed; + + char *explore_user_bio; // Store the bio of the selected user } FlipSocialApp; void flip_social_app_free(FlipSocialApp *app); diff --git a/flip_storage/flip_social_storage.c b/flip_storage/flip_social_storage.c index b0baacba036..dea5b2a71fe 100644 --- a/flip_storage/flip_social_storage.c +++ b/flip_storage/flip_social_storage.c @@ -47,7 +47,6 @@ void save_playlist(const PreSavedPlaylist *playlist) furi_record_close(RECORD_STORAGE); } -// Function to load the playlist // Function to load the playlist bool load_playlist(PreSavedPlaylist *playlist) { @@ -143,6 +142,7 @@ void save_settings( const char *login_username_logged_in, const char *login_password_logged_out, const char *change_password_logged_in, + const char *change_bio_logged_in, const char *is_logged_in) { // Create the directory for saving settings @@ -219,6 +219,14 @@ void save_settings( FURI_LOG_E(TAG, "Failed to write is_logged_in"); } + // Save the change_bio_logged_in length and data + size_t change_bio_length = strlen(change_bio_logged_in) + 1; // Include null terminator + if (storage_file_write(file, &change_bio_length, sizeof(size_t)) != sizeof(size_t) || + storage_file_write(file, change_bio_logged_in, change_bio_length) != change_bio_length) + { + FURI_LOG_E(TAG, "Failed to write change_bio_logged_in"); + } + storage_file_close(file); storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -237,6 +245,8 @@ bool load_settings( size_t password_out_size, char *change_password_logged_in, size_t change_password_size, + char *change_bio_logged_in, + size_t change_bio_size, char *is_logged_in, size_t is_logged_in_size) { @@ -363,6 +373,22 @@ bool load_settings( is_logged_in[is_logged_in_length - 1] = '\0'; // Ensure null-termination } + // Load the change_bio_logged_in + size_t change_bio_length; + if (storage_file_read(file, &change_bio_length, sizeof(size_t)) != sizeof(size_t) || change_bio_length > change_bio_size || + storage_file_read(file, change_bio_logged_in, change_bio_length) != change_bio_length) + { + FURI_LOG_E(TAG, "Failed to read change_bio_logged_in"); + // storage_file_close(file); + // storage_file_free(file); + // furi_record_close(RECORD_STORAGE); + // return false; + } + else + { + change_bio_logged_in[change_bio_length - 1] = '\0'; // Ensure null-termination + } + storage_file_close(file); storage_file_free(file); furi_record_close(RECORD_STORAGE); diff --git a/flip_storage/flip_social_storage.h b/flip_storage/flip_social_storage.h index 26ac2fc6d27..94a1fbd76d5 100644 --- a/flip_storage/flip_social_storage.h +++ b/flip_storage/flip_social_storage.h @@ -16,6 +16,7 @@ void save_settings( const char *login_username_logged_in, const char *login_password_logged_out, const char *change_password_logged_in, + const char *change_bio_logged_in, const char *is_logged_in); bool load_settings( @@ -31,6 +32,8 @@ bool load_settings( size_t password_out_size, char *change_password_logged_in, size_t change_password_size, + char *change_bio_logged_in, + size_t change_bio_size, char *is_logged_in, size_t is_logged_in_size); From b0dc8d12ba68545686b5126f3f9f2ced07fbfebe Mon Sep 17 00:00:00 2001 From: jblanked <82678820+jblanked@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:25:12 -0500 Subject: [PATCH 5/9] Update token count - There's a new bug that occurs when you click on any user in the Messages --- alloc/flip_social_alloc.c | 4 ---- callback/flip_social_callback.c | 2 -- explore/flip_social_explore.c | 2 +- feed/flip_social_feed.c | 10 ++++----- flip_social.c | 6 ------ flip_social.h | 6 +----- messages/flip_social_messages.c | 37 +++++++-------------------------- 7 files changed, 15 insertions(+), 52 deletions(-) diff --git a/alloc/flip_social_alloc.c b/alloc/flip_social_alloc.c index dc394621ea8..1ea7cce42d3 100644 --- a/alloc/flip_social_alloc.c +++ b/alloc/flip_social_alloc.c @@ -181,10 +181,6 @@ FlipSocialApp *flip_social_app_alloc() { return NULL; } - if (!easy_flipper_set_buffer(&last_explore_response, app->message_user_choice_logged_in_temp_buffer_size)) - { - return NULL; - } if (!easy_flipper_set_buffer(&app->explore_logged_in, app->explore_logged_in_temp_buffer_size)) { return NULL; diff --git a/callback/flip_social_callback.c b/callback/flip_social_callback.c index a896e2b33d6..7d092001039 100644 --- a/callback/flip_social_callback.c +++ b/callback/flip_social_callback.c @@ -481,7 +481,6 @@ uint32_t flip_social_callback_to_explore_logged_in(void *context) { UNUSED(context); flip_social_dialog_stop = false; - last_explore_response = ""; flip_social_dialog_shown = false; if (flip_social_explore) { @@ -499,7 +498,6 @@ uint32_t flip_social_callback_to_friends_logged_in(void *context) { UNUSED(context); flip_social_dialog_stop = false; - last_explore_response = ""; flip_social_dialog_shown = false; flip_social_friends->index = 0; return FlipSocialViewLoggedInFriendsSubmenu; diff --git a/explore/flip_social_explore.c b/explore/flip_social_explore.c index 23568caad84..ee18bbc9025 100644 --- a/explore/flip_social_explore.c +++ b/explore/flip_social_explore.c @@ -150,7 +150,7 @@ bool flip_social_parse_json_explore() // Parse the JSON array of usernames for (size_t i = 0; i < MAX_EXPLORE_USERS; i++) { - char *username = get_json_array_value("users", i, data_cstr, MAX_TOKENS); // currently its 330 tokens + char *username = get_json_array_value("users", i, data_cstr, 64); // currently its 53 tokens (with max explore users at 50) if (username == NULL) { break; diff --git a/feed/flip_social_feed.c b/feed/flip_social_feed.c index 9e7a5abda6c..379c44167e3 100644 --- a/feed/flip_social_feed.c +++ b/feed/flip_social_feed.c @@ -167,11 +167,11 @@ bool flip_social_load_feed_post(int post_id) } // Extract individual fields from the JSON object - char *username = get_json_value("username", data_cstr, 40); - char *message = get_json_value("message", data_cstr, 40); - char *flipped = get_json_value("flipped", data_cstr, 40); - char *flips = get_json_value("flip_count", data_cstr, 40); - char *id = get_json_value("id", data_cstr, 40); + char *username = get_json_value("username", data_cstr, 16); + char *message = get_json_value("message", data_cstr, 16); + char *flipped = get_json_value("flipped", data_cstr, 16); + char *flips = get_json_value("flip_count", data_cstr, 16); + char *id = get_json_value("id", data_cstr, 16); if (username == NULL || message == NULL || flipped == NULL || id == NULL) { diff --git a/flip_social.c b/flip_social.c index dd8890e321e..d343f4ff7fe 100644 --- a/flip_social.c +++ b/flip_social.c @@ -15,7 +15,6 @@ bool flip_social_register_success = false; bool flip_social_dialog_shown = false; bool flip_social_dialog_stop = false; bool flip_social_send_message = false; -char *last_explore_response = NULL; char *selected_message = NULL; char auth_headers[256] = {0}; @@ -260,16 +259,11 @@ void flip_social_app_free(FlipSocialApp *app) free(app->message_user_choice_logged_in); if (app->message_user_choice_logged_in_temp_buffer) free(app->message_user_choice_logged_in_temp_buffer); - if (last_explore_response) - free(last_explore_response); if (selected_message) free(selected_message); if (app->explore_user_bio) free(app->explore_user_bio); - if (app->input_event && app->input_event_queue) - furi_pubsub_unsubscribe(app->input_event_queue, app->input_event); - // DeInit UART flipper_http_deinit(); diff --git a/flip_social.h b/flip_social.h index a367666121b..ceb302f8f02 100644 --- a/flip_social.h +++ b/flip_social.h @@ -17,7 +17,7 @@ #define MAX_TOKENS 640 // Adjust based on expected JSON tokens #define MAX_FEED_ITEMS 50 // Maximum number of feed items #define MAX_LINE_LENGTH 30 -#define MAX_MESSAGE_USERS 20 // Maximum number of users to display in the submenu +#define MAX_MESSAGE_USERS 40 // Maximum number of users to display in the submenu #define MAX_MESSAGES 20 // Maximum number of meesages between each user #define SETTINGS_PATH STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/settings.bin" @@ -224,9 +224,6 @@ typedef struct // VariableItem *variable_item_logged_in_profile_friends; // Reference to the friends configuration item // - FuriPubSub *input_event_queue; - FuriPubSubSubscription *input_event; - PreSavedPlaylist pre_saved_messages; // Pre-saved messages for the feed screen char *is_logged_in; // Store the login status @@ -329,7 +326,6 @@ extern bool flip_social_register_success; extern bool flip_social_dialog_shown; extern bool flip_social_dialog_stop; extern bool flip_social_send_message; -extern char *last_explore_response; extern char *selected_message; extern char auth_headers[256]; diff --git a/messages/flip_social_messages.c b/messages/flip_social_messages.c index 28e439c45e9..a2b311522ff 100644 --- a/messages/flip_social_messages.c +++ b/messages/flip_social_messages.c @@ -154,7 +154,7 @@ bool flip_social_get_message_users() fhttp.save_received_data = true; auth_headers_alloc(); - snprintf(command, 128, "https://www.flipsocial.net/api/messages/%s/get/list/", app_instance->login_username_logged_out); + snprintf(command, 128, "https://www.flipsocial.net/api/messages/%s/get/list/%d/", app_instance->login_username_logged_out, MAX_MESSAGE_USERS); if (!flipper_http_get_request_with_headers(command, auth_headers)) { FURI_LOG_E(TAG, "Failed to send HTTP request for messages"); @@ -183,7 +183,7 @@ bool flip_social_get_messages_with_user() FURI_LOG_E(TAG, "Username is NULL"); return false; } - char command[128]; + char command[256]; snprintf( fhttp.file_path, sizeof(fhttp.file_path), @@ -192,7 +192,7 @@ bool flip_social_get_messages_with_user() fhttp.save_received_data = true; auth_headers_alloc(); - snprintf(command, 128, "https://www.flipsocial.net/api/messages/%s/get/%s/", app_instance->login_username_logged_out, flip_social_message_users->usernames[flip_social_message_users->index]); + snprintf(command, sizeof(command), "https://www.flipsocial.net/api/messages/%s/get/%s/%d/", app_instance->login_username_logged_out, flip_social_message_users->usernames[flip_social_message_users->index], MAX_MESSAGES); if (!flipper_http_get_request_with_headers(command, auth_headers)) { FURI_LOG_E(TAG, "Failed to send HTTP request for messages"); @@ -231,18 +231,11 @@ bool flip_social_parse_json_message_users() return false; } - // Remove newlines - char *pos = data_cstr; - while ((pos = strchr(pos, '\n')) != NULL) - { - *pos = ' '; - } - // Initialize message users count flip_social_message_users->count = 0; // Extract the users array from the JSON - char *json_users = get_json_value("users", data_cstr, MAX_TOKENS); + char *json_users = get_json_value("users", data_cstr, 64); if (json_users == NULL) { FURI_LOG_E(TAG, "Failed to parse users array."); @@ -324,18 +317,11 @@ bool flip_social_parse_json_message_user_choices() return false; } - // Remove newlines - char *pos = data_cstr; - while ((pos = strchr(pos, '\n')) != NULL) - { - *pos = ' '; - } - // Initialize explore count flip_social_explore->count = 0; // Extract the users array from the JSON - char *json_users = get_json_value("users", data_cstr, MAX_TOKENS); + char *json_users = get_json_value("users", data_cstr, 64); if (json_users == NULL) { FURI_LOG_E(TAG, "Failed to parse users array."); @@ -417,13 +403,6 @@ bool flip_social_parse_json_messages() return false; } - // Remove newlines - char *pos = data_cstr; - while ((pos = strchr(pos, '\n')) != NULL) - { - *pos = ' '; - } - // Initialize messages count flip_social_messages->count = 0; @@ -431,15 +410,15 @@ bool flip_social_parse_json_messages() for (int i = 0; i < MAX_MESSAGES; i++) { // Parse each item in the array - char *item = get_json_array_value("conversations", i, data_cstr, MAX_TOKENS); + char *item = get_json_array_value("conversations", i, data_cstr, 128); if (item == NULL) { break; } // Extract individual fields from the JSON object - char *sender = get_json_value("sender", item, 64); - char *content = get_json_value("content", item, 64); + char *sender = get_json_value("sender", item, 32); + char *content = get_json_value("content", item, 32); if (sender == NULL || content == NULL) { From a37f0242f5e74f3fd9126539a059ff3614e9cb87 Mon Sep 17 00:00:00 2001 From: jblanked <82678820+jblanked@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:28:00 -0500 Subject: [PATCH 6/9] Update uart_text_input.c --- text_input/uart_text_input.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text_input/uart_text_input.c b/text_input/uart_text_input.c index b8e8fc9f283..1dd3a6d3ee7 100644 --- a/text_input/uart_text_input.c +++ b/text_input/uart_text_input.c @@ -295,7 +295,7 @@ static void uart_text_input_view_draw_callback(Canvas *canvas, void *_model) else if (model->callback != 0) { // We hijack the current thread to invoke the callback (we aren't doing a draw). - model->callback(model->callback_context); + // model->callback(model->callback_context); return; } } @@ -653,9 +653,9 @@ void uart_text_input_timer_callback(void *context) UART_TextInput *uart_text_input = context; with_view_model( - uart_text_input->view, - UART_TextInputModel * model, - { model->valadator_message_visible = false; }, + uart_text_input->view, + UART_TextInputModel * model, + { model->valadator_message_visible = false; }, true); } From 8169bd332fe668c8d7e9f7482f65505fb482cb6f Mon Sep 17 00:00:00 2001 From: jblanked <82678820+jblanked@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:44:01 -0500 Subject: [PATCH 7/9] Remove draw --- draw/flip_social_draw.c | 461 ---------------------------------------- draw/flip_social_draw.h | 33 --- 2 files changed, 494 deletions(-) delete mode 100644 draw/flip_social_draw.c delete mode 100644 draw/flip_social_draw.h diff --git a/draw/flip_social_draw.c b/draw/flip_social_draw.c deleted file mode 100644 index a2d83073da3..00000000000 --- a/draw/flip_social_draw.c +++ /dev/null @@ -1,461 +0,0 @@ -#include "flip_social_draw.h" -Action action = ActionNone; -bool flip_social_board_is_active(Canvas *canvas) -{ - if (fhttp.state == INACTIVE) - { - canvas_draw_str(canvas, 0, 7, "Wifi Dev Board disconnected."); - canvas_draw_str(canvas, 0, 17, "Please connect to the board."); - canvas_draw_str(canvas, 0, 32, "If your board is connected,"); - canvas_draw_str(canvas, 0, 42, "make sure you have flashed"); - canvas_draw_str(canvas, 0, 52, "your WiFi Devboard with the"); - canvas_draw_str(canvas, 0, 62, "latest FlipperHTTP flash."); - return false; - } - return true; -} - -void on_input(const void *event, void *ctx) -{ - UNUSED(ctx); - - InputKey key = ((InputEvent *)event)->key; - InputType type = ((InputEvent *)event)->type; - - if (type != InputTypeRelease) - { - return; - } - - switch (key) - { - case InputKeyOk: - action = ActionFlip; - break; - case InputKeyBack: - action = ActionBack; - break; - case InputKeyRight: - action = ActionNext; - break; - case InputKeyLeft: - action = ActionPrev; - break; - case InputKeyUp: - action = ActionPrev; - break; - case InputKeyDown: - action = ActionNext; - break; - default: - action = ActionNone; - break; - } -} - -// Function to draw the message on the canvas with word wrapping -void draw_user_message(Canvas *canvas, const char *user_message, int x, int y) -{ - if (user_message == NULL) - { - FURI_LOG_E(TAG, "User message is NULL."); - return; - } - - size_t msg_length = strlen(user_message); - size_t start = 0; - int line_num = 0; - char line[MAX_LINE_LENGTH + 1]; // Buffer for the current line (+1 for null terminator) - - while (start < msg_length && line_num < 4) - { - size_t remaining = msg_length - start; - size_t len = (remaining > MAX_LINE_LENGTH) ? MAX_LINE_LENGTH : remaining; - - if (remaining > MAX_LINE_LENGTH) - { - // Find the last space within the first 'len' characters - size_t last_space = len; - while (last_space > 0 && user_message[start + last_space - 1] != ' ') - { - last_space--; - } - - if (last_space > 0) - { - len = last_space; // Adjust len to the position of the last space - } - } - - // Copy the substring to 'line' and null-terminate it - memcpy(line, user_message + start, len); - line[len] = '\0'; // Ensure the string is null-terminated - - // Draw the string on the canvas - // Adjust the y-coordinate based on the line number - canvas_draw_str_aligned(canvas, x, y + line_num * 10, AlignLeft, AlignTop, line); - - // Update the start position for the next line - start += len; - - // Skip any spaces to avoid leading spaces on the next line - while (start < msg_length && user_message[start] == ' ') - { - start++; - } - - // Increment the line number - line_num++; - } -} - -void flip_social_callback_draw_compose(Canvas *canvas, void *model) -{ - UNUSED(model); - if (!canvas) - { - FURI_LOG_E(TAG, "Canvas is NULL"); - return; - } - if (!app_instance) - { - FURI_LOG_E(TAG, "FlipSocialApp is NULL"); - return; - } - if (!selected_message) - { - FURI_LOG_E(TAG, "Selected message is NULL"); - return; - } - - if (strlen(selected_message) > MAX_MESSAGE_LENGTH) - { - FURI_LOG_E(TAG, "Message is too long"); - return; - } - - if (!flip_social_dialog_shown) - { - flip_social_dialog_shown = true; - app_instance->input_event_queue = furi_record_open(RECORD_INPUT_EVENTS); - app_instance->input_event = furi_pubsub_subscribe(app_instance->input_event_queue, on_input, NULL); - auth_headers_alloc(); - } - - draw_user_message(canvas, selected_message, 0, 2); - - canvas_draw_icon(canvas, 0, 53, &I_ButtonLeft_4x7); - canvas_draw_str_aligned(canvas, 7, 54, AlignLeft, AlignTop, "Delete"); - canvas_draw_icon(canvas, 52, 53, &I_ButtonBACK_10x8); - canvas_draw_str_aligned(canvas, 64, 54, AlignLeft, AlignTop, "Back"); - canvas_draw_icon(canvas, 100, 53, &I_ButtonRight_4x7); - canvas_draw_str_aligned(canvas, 107, 54, AlignLeft, AlignTop, "Post"); - - // handle action - switch (action) - { - case ActionNone: - break; - case ActionBack: - flip_social_dialog_stop = true; - break; - case ActionNext: - // send selected_message - if (selected_message && app_instance->login_username_logged_in) - { - if (strlen(selected_message) > MAX_MESSAGE_LENGTH) - { - FURI_LOG_E(TAG, "Message is too long"); - return; - } - // Send the selected_message - char command[256]; - snprintf(command, sizeof(command), "{\"username\":\"%s\",\"content\":\"%s\"}", - app_instance->login_username_logged_in, selected_message); - if (!flipper_http_post_request_with_headers( - "https://www.flipsocial.net/api/feed/post/", - auth_headers, - command)) - { - FURI_LOG_E(TAG, "Failed to send HTTP request for feed"); - fhttp.state = ISSUE; - return; - } - - fhttp.state = RECEIVING; - furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS); - } - else - { - FURI_LOG_E(TAG, "Message or username is NULL"); - return; - } - while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0) - { - // Wait for the feed to be received - furi_delay_ms(100); - - // Draw the resulting string on the canvas - canvas_draw_str(canvas, 0, 30, "Receiving.."); - } - flip_social_dialog_stop = true; - furi_timer_stop(fhttp.get_timeout_timer); - break; - case ActionPrev: - // delete message - app_instance->pre_saved_messages.messages[app_instance->pre_saved_messages.index] = NULL; - - for (uint32_t i = app_instance->pre_saved_messages.index; i < app_instance->pre_saved_messages.count - 1; i++) - { - app_instance->pre_saved_messages.messages[i] = app_instance->pre_saved_messages.messages[i + 1]; - } - app_instance->pre_saved_messages.count--; - - // add the item to the submenu - submenu_reset(app_instance->submenu_compose); - - submenu_add_item(app_instance->submenu_compose, "Add Pre-Save", FlipSocialSubmenuComposeIndexAddPreSave, flip_social_callback_submenu_choices, app_instance); - - for (uint32_t i = 0; i < app_instance->pre_saved_messages.count; i++) - { - submenu_add_item(app_instance->submenu_compose, app_instance->pre_saved_messages.messages[i], FlipSocialSubemnuComposeIndexStartIndex + i, flip_social_callback_submenu_choices, app_instance); - } - - // save playlist - save_playlist(&app_instance->pre_saved_messages); - - flip_social_dialog_stop = true; - break; - default: - action = ActionNone; - break; - } - - if (flip_social_dialog_stop) - { - furi_pubsub_unsubscribe(app_instance->input_event_queue, app_instance->input_event); - flip_social_dialog_shown = false; - flip_social_dialog_stop = false; - if (action == ActionNext) - { - canvas_clear(canvas); - canvas_draw_str(canvas, 0, 10, "Sent successfully!"); - canvas_draw_str(canvas, 0, 50, "Loading feed :D"); - canvas_draw_str(canvas, 0, 60, "Please wait..."); - action = ActionNone; - if (!flip_social_load_initial_feed()) - { - FURI_LOG_E(TAG, "Failed to load initial feed"); - return; - } - } - else if (action == ActionBack) - { - action = ActionNone; - view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInSubmenu); - } - else - { - action = ActionNone; - view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInCompose); - } - } -} -// function to draw the dialog canvas -void flip_social_canvas_draw_message(Canvas *canvas, char *user_username, char *user_message, bool is_flipped, bool show_prev, bool show_next, int flip_count) -{ - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, user_username); - canvas_set_font(canvas, FontSecondary); - - char flip_count_str[12]; - if (flip_count == 1) - { - snprintf(flip_count_str, sizeof(flip_count_str), "%d Flip", flip_count); - canvas_draw_str_aligned(canvas, 106, 54, AlignLeft, AlignTop, flip_count_str); - } - else - { - snprintf(flip_count_str, sizeof(flip_count_str), "%d Flips", flip_count); - - if (flip_count < 10) - { - canvas_draw_str_aligned(canvas, 100, 54, AlignLeft, AlignTop, flip_count_str); - } - else if (flip_count < 100) - { - canvas_draw_str_aligned(canvas, 94, 54, AlignLeft, AlignTop, flip_count_str); - } - else - { - canvas_draw_str_aligned(canvas, 88, 54, AlignLeft, AlignTop, flip_count_str); - } - } - - draw_user_message(canvas, user_message, 0, 12); - - // combine and shift icons/labels around if not show_prev or show_next - if (show_prev && show_next && !is_flipped) - { - canvas_draw_icon(canvas, 0, 54, &I_ButtonLeft_4x7); - canvas_draw_str_aligned(canvas, 6, 54, AlignLeft, AlignTop, "Prev"); - canvas_draw_icon(canvas, 30, 54, &I_ButtonRight_4x7); - canvas_draw_str_aligned(canvas, 36, 54, AlignLeft, AlignTop, "Next"); - canvas_draw_icon(canvas, 58, 54, &I_ButtonOK_7x7); - canvas_draw_str_aligned(canvas, 67, 54, AlignLeft, AlignTop, "Flip"); - } - else if (show_prev && !show_next && !is_flipped) - { - canvas_draw_icon(canvas, 0, 54, &I_ButtonLeft_4x7); - canvas_draw_str_aligned(canvas, 6, 54, AlignLeft, AlignTop, "Prev"); - canvas_draw_icon(canvas, 28, 54, &I_ButtonOK_7x7); - canvas_draw_str_aligned(canvas, 37, 54, AlignLeft, AlignTop, "Flip"); - } - else if (!show_prev && show_next && !is_flipped) - { - canvas_draw_icon(canvas, 0, 54, &I_ButtonRight_4x7); - canvas_draw_str_aligned(canvas, 6, 54, AlignLeft, AlignTop, "Next"); - canvas_draw_icon(canvas, 28, 54, &I_ButtonOK_7x7); - canvas_draw_str_aligned(canvas, 37, 54, AlignLeft, AlignTop, "Flip"); - } - else if (show_prev && show_next && is_flipped) - { - canvas_draw_icon(canvas, 0, 54, &I_ButtonLeft_4x7); - canvas_draw_str_aligned(canvas, 6, 54, AlignLeft, AlignTop, "Prev"); - canvas_draw_icon(canvas, 28, 54, &I_ButtonRight_4x7); - canvas_draw_str_aligned(canvas, 34, 54, AlignLeft, AlignTop, "Next"); - canvas_draw_icon(canvas, 54, 54, &I_ButtonOK_7x7); - canvas_draw_str_aligned(canvas, 63, 54, AlignLeft, AlignTop, "UnFlip"); - } - else if (show_prev && !show_next && is_flipped) - { - canvas_draw_icon(canvas, 0, 54, &I_ButtonLeft_4x7); - canvas_draw_str_aligned(canvas, 6, 54, AlignLeft, AlignTop, "Prev"); - canvas_draw_icon(canvas, 28, 54, &I_ButtonOK_7x7); - canvas_draw_str_aligned(canvas, 37, 54, AlignLeft, AlignTop, "UnFlip"); - } - else if (!show_prev && show_next && is_flipped) - { - canvas_draw_icon(canvas, 0, 54, &I_ButtonRight_4x7); - canvas_draw_str_aligned(canvas, 6, 54, AlignLeft, AlignTop, "Next"); - canvas_draw_icon(canvas, 28, 54, &I_ButtonOK_7x7); - canvas_draw_str_aligned(canvas, 37, 54, AlignLeft, AlignTop, "UnFlip"); - } - else if (!show_prev && !show_next && is_flipped) - { - canvas_draw_icon(canvas, 0, 54, &I_ButtonOK_7x7); - canvas_draw_str_aligned(canvas, 9, 54, AlignLeft, AlignTop, "UnFlip"); - } - else - { - canvas_draw_icon(canvas, 0, 54, &I_ButtonOK_7x7); - canvas_draw_str_aligned(canvas, 9, 54, AlignLeft, AlignTop, "Flip"); - } -} -// Callback function to handle the feed dialog -void flip_social_callback_draw_feed(Canvas *canvas, void *model) -{ - UNUSED(model); - if (!canvas) - { - FURI_LOG_E(TAG, "Canvas is NULL"); - return; - } - if (!app_instance) - { - FURI_LOG_E(TAG, "FlipSocialApp is NULL"); - return; - } - - if (!flip_social_dialog_shown) - { - flip_social_dialog_shown = true; - app_instance->input_event_queue = furi_record_open(RECORD_INPUT_EVENTS); - app_instance->input_event = furi_pubsub_subscribe(app_instance->input_event_queue, on_input, NULL); - auth_headers_alloc(); - } - - // handle action - switch (action) - { - case ActionNone: - flip_social_canvas_draw_message(canvas, flip_feed_item->username, flip_feed_item->message, flip_feed_item->is_flipped, flip_feed_info->index > 0, flip_feed_info->index < flip_feed_info->count - 1, flip_feed_item->flips); - break; - case ActionNext: - canvas_clear(canvas); - if (flip_feed_info->index < flip_feed_info->count - 1) - { - flip_feed_info->index++; - } - // load the next feed item - if (!flip_social_load_feed_post(flip_feed_info->ids[flip_feed_info->index])) - { - FURI_LOG_E(TAG, "Failed to load nexy feed post"); - fhttp.state = ISSUE; - return; - } - flip_social_canvas_draw_message(canvas, flip_feed_item->username, flip_feed_item->message, flip_feed_item->is_flipped, flip_feed_info->index > 0, flip_feed_info->index < flip_feed_info->count - 1, flip_feed_item->flips); - action = ActionNone; - break; - case ActionPrev: - canvas_clear(canvas); - if (flip_feed_info->index > 0) - { - flip_feed_info->index--; - } - // load the previous feed item - if (!flip_social_load_feed_post(flip_feed_info->ids[flip_feed_info->index])) - { - FURI_LOG_E(TAG, "Failed to load nexy feed post"); - fhttp.state = ISSUE; - return; - } - flip_social_canvas_draw_message(canvas, flip_feed_item->username, flip_feed_item->message, flip_feed_item->is_flipped, flip_feed_info->index > 0, flip_feed_info->index < flip_feed_info->count - 1, flip_feed_item->flips); - action = ActionNone; - break; - case ActionFlip: - canvas_clear(canvas); - // Moved to above the is_flipped check - if (!flip_feed_item->is_flipped) - { - // increase the flip count - flip_feed_item->flips++; - } - else - { - // decrease the flip count - flip_feed_item->flips--; - } - // change the flip status - flip_feed_item->is_flipped = !flip_feed_item->is_flipped; - // send post request to flip the message - if (app_instance->login_username_logged_in == NULL) - { - FURI_LOG_E(TAG, "Username is NULL"); - return; - } - char payload[256]; - snprintf(payload, sizeof(payload), "{\"username\":\"%s\",\"post_id\":\"%u\"}", app_instance->login_username_logged_in, flip_feed_item->id); - flipper_http_post_request_with_headers("https://www.flipsocial.net/api/feed/flip/", auth_headers, payload); - flip_social_canvas_draw_message(canvas, flip_feed_item->username, flip_feed_item->message, flip_feed_item->is_flipped, flip_feed_info->index > 0, flip_feed_info->index < flip_feed_info->count - 1, flip_feed_item->flips); - action = ActionNone; - break; - case ActionBack: - canvas_clear(canvas); - flip_social_dialog_stop = true; - flip_feed_info->index = 0; - action = ActionNone; - break; - default: - break; - } - - if (flip_social_dialog_stop) - { - furi_pubsub_unsubscribe(app_instance->input_event_queue, app_instance->input_event); - flip_social_dialog_shown = false; - flip_social_dialog_stop = false; - action = ActionNone; - } -} \ No newline at end of file diff --git a/draw/flip_social_draw.h b/draw/flip_social_draw.h deleted file mode 100644 index a90d00f0f8c..00000000000 --- a/draw/flip_social_draw.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef FLIP_SOCIAL_DRAW_H -#define FLIP_SOCIAL_DRAW_H -#include -#include -#include -#include - -typedef enum -{ - ActionNone, - ActionBack, - ActionNext, - ActionPrev, - ActionFlip, -} Action; - -extern Action action; - -bool flip_social_board_is_active(Canvas *canvas); - -void flip_social_handle_error(Canvas *canvas); - -void on_input(const void *event, void *ctx); -// Function to draw the message on the canvas with word wrapping -void draw_user_message(Canvas *canvas, const char *user_message, int x, int y); - -void flip_social_callback_draw_compose(Canvas *canvas, void *model); -// function to draw the dialog canvas -void flip_social_canvas_draw_message(Canvas *canvas, char *user_username, char *user_message, bool is_flipped, bool show_prev, bool show_next, int flip_count); -// Callback function to handle the feed dialog -void flip_social_callback_draw_feed(Canvas *canvas, void *model); - -#endif \ No newline at end of file From 0cfcf385e97b2a2133d32d7c55ea7e03293fc3a9 Mon Sep 17 00:00:00 2001 From: jblanked <82678820+jblanked@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:44:12 -0500 Subject: [PATCH 8/9] print token count --- jsmn/jsmn.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jsmn/jsmn.c b/jsmn/jsmn.c index b85ab83b537..0d6ea8cd46a 100644 --- a/jsmn/jsmn.c +++ b/jsmn/jsmn.c @@ -473,6 +473,9 @@ char *get_json_value(char *key, char *json_data, uint32_t max_tokens) return NULL; } + // print amount of tokens + FURI_LOG_I("JSMM.H", "Amount of tokens: %d", ret); + // Ensure that the root element is an object if (ret < 1 || tokens[0].type != JSMN_OBJECT) { @@ -548,6 +551,9 @@ char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t return NULL; } + // print amount of tokens + FURI_LOG_I("JSMM.H", "Amount of tokens: %d", ret); + // Ensure the root element is an array if (ret < 1 || tokens[0].type != JSMN_ARRAY) { From 9bb3b3d9aed3de43d712836bd80f6c7967a5043d Mon Sep 17 00:00:00 2001 From: jblanked <82678820+jblanked@users.noreply.github.com> Date: Wed, 4 Dec 2024 21:53:59 -0500 Subject: [PATCH 9/9] Fix Out of Memory error (thanks Derek) --- flip_social.h | 2 +- friends/flip_social_friends.c | 2 +- jsmn/jsmn.c | 6 ------ messages/flip_social_messages.c | 8 ++++---- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/flip_social.h b/flip_social.h index ceb302f8f02..f7b65d0c1a8 100644 --- a/flip_social.h +++ b/flip_social.h @@ -14,7 +14,7 @@ #define MAX_EXPLORE_USERS 50 // Maximum number of users to explore #define MAX_USER_LENGTH 32 // Maximum length of a username #define MAX_FRIENDS 50 // Maximum number of friends -#define MAX_TOKENS 640 // Adjust based on expected JSON tokens +#define MAX_TOKENS 576 // Adjust based on expected JSON tokens #define MAX_FEED_ITEMS 50 // Maximum number of feed items #define MAX_LINE_LENGTH 30 #define MAX_MESSAGE_USERS 40 // Maximum number of users to display in the submenu diff --git a/friends/flip_social_friends.c b/friends/flip_social_friends.c index 10d2cbd29e8..e564afab02a 100644 --- a/friends/flip_social_friends.c +++ b/friends/flip_social_friends.c @@ -117,7 +117,7 @@ bool flip_social_parse_json_friends() submenu_set_header(app_instance->submenu_friends, "Friends"); // Extract the users array from the JSON - char *json_users = get_json_value("friends", data_cstr, MAX_TOKENS); + char *json_users = get_json_value("friends", data_cstr, 128); if (json_users == NULL) { FURI_LOG_E(TAG, "Failed to parse friends array."); diff --git a/jsmn/jsmn.c b/jsmn/jsmn.c index 0d6ea8cd46a..b85ab83b537 100644 --- a/jsmn/jsmn.c +++ b/jsmn/jsmn.c @@ -473,9 +473,6 @@ char *get_json_value(char *key, char *json_data, uint32_t max_tokens) return NULL; } - // print amount of tokens - FURI_LOG_I("JSMM.H", "Amount of tokens: %d", ret); - // Ensure that the root element is an object if (ret < 1 || tokens[0].type != JSMN_OBJECT) { @@ -551,9 +548,6 @@ char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t return NULL; } - // print amount of tokens - FURI_LOG_I("JSMM.H", "Amount of tokens: %d", ret); - // Ensure the root element is an array if (ret < 1 || tokens[0].type != JSMN_ARRAY) { diff --git a/messages/flip_social_messages.c b/messages/flip_social_messages.c index a2b311522ff..c1ba402cbee 100644 --- a/messages/flip_social_messages.c +++ b/messages/flip_social_messages.c @@ -40,7 +40,7 @@ FlipSocialMessage *flip_social_user_messages_alloc() FURI_LOG_E(TAG, "Failed to allocate memory for messages"); return NULL; } - for (size_t i = 0; i < MAX_MESSAGE_USERS; i++) + for (size_t i = 0; i < MAX_MESSAGES; i++) { if (messages->usernames[i] == NULL) { @@ -410,15 +410,15 @@ bool flip_social_parse_json_messages() for (int i = 0; i < MAX_MESSAGES; i++) { // Parse each item in the array - char *item = get_json_array_value("conversations", i, data_cstr, 128); + char *item = get_json_array_value("conversations", i, data_cstr, 64); if (item == NULL) { break; } // Extract individual fields from the JSON object - char *sender = get_json_value("sender", item, 32); - char *content = get_json_value("content", item, 32); + char *sender = get_json_value("sender", item, 8); + char *content = get_json_value("content", item, 8); if (sender == NULL || content == NULL) {