Explorar o código

Merge flip_social from https://github.com/jblanked/FlipSocial

Willy-JL hai 1 ano
pai
achega
db25407d22

BIN=BIN
flip_social/.DS_Store


+ 5 - 2
flip_social/README.md

@@ -3,7 +3,7 @@ The first social media app for Flipper Zero. Connect with other users directly o
 
 The highlight of this app is customizable pre-saves, which, as explained below, aim to address the challenges of typing with the directional pad.
 
-FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in the WebCrawler app:https://github.com/jblanked/FlipperHTTP
+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 Dev Board or Raspberry Pi Pico W for Flipper Zero with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP
@@ -51,9 +51,12 @@ FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in
 - Raspberry Pi Pico W Support
 
 **v0.6**
-- Improved User Profile (Bio, friend count, block)
+- Improved memory allocation
+- Fixed bugs in Direct Messaging View
+- Fixed bugs in Pre-Save View
 
 **v0.7**
+- Improved User Profile (Bio, friend count, block)
 - Improved Explore Page
 
 **v0.8**

+ 15 - 16
flip_social/flip_social_i.h → flip_social/alloc/flip_social_alloc.c

@@ -1,13 +1,6 @@
-// flip_social.i.h
-#ifndef FLIP_SOCIAL_I
-#define FLIP_SOCIAL_I
+#include <alloc/flip_social_alloc.h>
 
-/**
- * @brief Function to allocate resources for the FlipSocialApp.
- * @details Initializes all components and views of the application.
- * @return Pointer to the initialized FlipSocialApp, or NULL on failure.
- */
-static FlipSocialApp* flip_social_app_alloc() {
+FlipSocialApp* flip_social_app_alloc() {
     // Initiailize the app
     FlipSocialApp* app = (FlipSocialApp*)malloc(sizeof(FlipSocialApp));
 
@@ -34,13 +27,13 @@ static 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->compose_pre_save_logged_in_temp_buffer_size = 100;
+    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;
     app->is_logged_in_size = 8;
     app->login_username_logged_in_temp_buffer_size = MAX_USER_LENGTH;
-    app->messages_new_message_logged_in_temp_buffer_size = 100;
-    app->message_user_choice_logged_in_temp_buffer_size = 100;
+    app->messages_new_message_logged_in_temp_buffer_size = MAX_MESSAGE_LENGTH;
+    app->message_user_choice_logged_in_temp_buffer_size = MAX_MESSAGE_LENGTH;
     if(!easy_flipper_set_buffer(
            &app->wifi_ssid_logged_out_temp_buffer, app->wifi_ssid_logged_out_temp_buffer_size)) {
         return NULL;
@@ -175,12 +168,20 @@ static FlipSocialApp* flip_social_app_alloc() {
            app->message_user_choice_logged_in_temp_buffer_size)) {
         return NULL;
     }
+    if(!easy_flipper_set_buffer(
+           &selected_message, app->message_user_choice_logged_in_temp_buffer_size)) {
+        return NULL;
+    }
+    if(!easy_flipper_set_buffer(
+           &last_explore_response, app->message_user_choice_logged_in_temp_buffer_size)) {
+        return NULL;
+    }
 
     // Allocate Submenu(s)
     if(!easy_flipper_set_submenu(
            &app->submenu_logged_out,
            FlipSocialViewLoggedOutSubmenu,
-           "FlipSocial v0.5",
+           "FlipSocial v0.6",
            flip_social_callback_exit_app,
            &app->view_dispatcher)) {
         return NULL;
@@ -188,7 +189,7 @@ static FlipSocialApp* flip_social_app_alloc() {
     if(!easy_flipper_set_submenu(
            &app->submenu_logged_in,
            FlipSocialViewLoggedInSubmenu,
-           "FlipSocial v0.5",
+           "FlipSocial v0.6",
            flip_social_callback_exit_app,
            &app->view_dispatcher)) {
         return NULL;
@@ -937,5 +938,3 @@ static FlipSocialApp* flip_social_app_alloc() {
 
     return app;
 }
-
-#endif // FLIP_SOCIAL_I

+ 14 - 0
flip_social/alloc/flip_social_alloc.h

@@ -0,0 +1,14 @@
+#ifndef FLIP_SOCIAL_ALLOC_H
+#define FLIP_SOCIAL_ALLOC_H
+#include <flip_social.h>
+#include <callback/flip_social_callback.h>
+#include <flip_storage/flip_social_storage.h>
+#include <draw/flip_social_draw.h>
+
+/**
+ * @brief Function to allocate resources for the FlipSocialApp.
+ * @details Initializes all components and views of the application.
+ * @return Pointer to the initialized FlipSocialApp, or NULL on failure.
+ */
+FlipSocialApp* flip_social_app_alloc();
+#endif

+ 2 - 11
flip_social/app.c

@@ -1,15 +1,6 @@
 // app.c
-#include <jsmn.h> // Include cJSON
-#include <flip_social_e.h> // Include the FlipSocialApp structure
-#include <flip_social_storage.h> // Include the storage functions
-#include "flip_social_draw.h"
-#include "flip_social_feed.h"
-#include "flip_social_explore.h"
-#include "flip_social_friends.h"
-#include "flip_social_messages.h"
-#include <flip_social_callback.h> // Include the callback functions
-#include <flip_social_i.h> // Include the initialization functions
-#include <flip_social_free.h> // Include the cleanup functions
+#include <flip_social.h> // Include the FlipSocialApp structure
+#include <alloc/flip_social_alloc.h> // Include the allocation functions
 
 /**
  * @brief Entry point for the Hello World application.

BIN=BIN
flip_social/app.png


+ 1 - 1
flip_social/application.fam

@@ -9,6 +9,6 @@ App(
     fap_icon_assets="assets",
     fap_author="jblanked",
     fap_weburl="https://github.com/jblanked/FlipSocial",
-    fap_version="0.5",
+    fap_version="0.6",
     fap_description="Social media platform for the Flipper Zero.",
 )

+ 5 - 0
flip_social/assets/CHANGELOG.md

@@ -1,3 +1,8 @@
+## 0.6
+- Improved memory allocation
+- Fixed bugs in Direct Messaging View
+- Fixed bugs in Pre-Save View
+
 ## 0.5
 - Improved memory allocation
 - Improved Feed Page

+ 7 - 3
flip_social/assets/README.md

@@ -3,12 +3,13 @@ The first social media app for Flipper Zero. Connect with other users directly o
 
 The highlight of this app is customizable pre-saves, which, as explained below, aim to address the challenges of typing with the directional pad.
 
-FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in the WebCrawler app: [WebCrawler](https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP)
+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 Dev Board for Flipper Zero with FlipperHTTP Flash: [FlipperHTTP](https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP)
+- WiFi Dev Board or Raspberry Pi Pico W for Flipper Zero with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP
 - WiFi Access Point
 
+
 ## Features
 - Login/Logout
 - Registration
@@ -50,9 +51,12 @@ FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in
 - Raspberry Pi Pico W Support
 
 **v0.6**
-- Improved User Profile (Bio, friend count, block)
+- Improved memory allocation
+- Fixed bugs in Direct Messaging View
+- Fixed bugs in Pre-Save View
 
 **v0.7**
+- Improved User Profile (Bio, friend count, block)
 - Improved Explore Page
 
 **v0.8**

+ 69 - 51
flip_social/flip_social_callback.h → flip_social/callback/flip_social_callback.c

@@ -1,13 +1,11 @@
-// flip_social_callback.h
-#ifndef FLIP_SOCIAL_CALLBACK_H
-#define FLIP_SOCIAL_CALLBACK_H
+#include <callback/flip_social_callback.h>
 
 /**
  * @brief Navigation callback to go back to the submenu Logged out.
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedOutSubmenu)
  */
-static uint32_t flip_social_callback_to_submenu_logged_out(void* context) {
+uint32_t flip_social_callback_to_submenu_logged_out(void* context) {
     UNUSED(context);
     return FlipSocialViewLoggedOutSubmenu;
 }
@@ -17,7 +15,7 @@ static uint32_t flip_social_callback_to_submenu_logged_out(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInSubmenu)
  */
-static uint32_t flip_social_callback_to_submenu_logged_in(void* context) {
+uint32_t flip_social_callback_to_submenu_logged_in(void* context) {
     UNUSED(context);
     flip_social_free_explore();
     flip_social_free_feed();
@@ -32,7 +30,7 @@ static uint32_t flip_social_callback_to_submenu_logged_in(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedOutLogin)
  */
-static uint32_t flip_social_callback_to_login_logged_out(void* context) {
+uint32_t flip_social_callback_to_login_logged_out(void* context) {
     UNUSED(context);
     flip_social_sent_login_request = false;
     flip_social_login_success = false;
@@ -44,7 +42,7 @@ static uint32_t flip_social_callback_to_login_logged_out(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedOutRegister)
  */
-static uint32_t flip_social_callback_to_register_logged_out(void* context) {
+uint32_t flip_social_callback_to_register_logged_out(void* context) {
     UNUSED(context);
     flip_social_sent_register_request = false;
     flip_social_register_success = false;
@@ -56,7 +54,7 @@ static uint32_t flip_social_callback_to_register_logged_out(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedOutWifiSettings)
  */
-static uint32_t flip_social_callback_to_wifi_settings_logged_out(void* context) {
+uint32_t flip_social_callback_to_wifi_settings_logged_out(void* context) {
     UNUSED(context);
     return FlipSocialViewLoggedOutWifiSettings;
 }
@@ -66,7 +64,7 @@ static uint32_t flip_social_callback_to_wifi_settings_logged_out(void* context)
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInSettingsWifi)
  */
-static uint32_t flip_social_callback_to_wifi_settings_logged_in(void* context) {
+uint32_t flip_social_callback_to_wifi_settings_logged_in(void* context) {
     UNUSED(context);
     return FlipSocialViewLoggedInSettingsWifi;
 }
@@ -76,7 +74,7 @@ static uint32_t flip_social_callback_to_wifi_settings_logged_in(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInSettingsWifi)
  */
-static uint32_t flip_social_callback_to_settings_logged_in(void* context) {
+uint32_t flip_social_callback_to_settings_logged_in(void* context) {
     UNUSED(context);
     return FlipSocialViewLoggedInSettings;
 }
@@ -86,7 +84,7 @@ static uint32_t flip_social_callback_to_settings_logged_in(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInCompose)
  */
-static uint32_t flip_social_callback_to_compose_logged_in(void* context) {
+uint32_t flip_social_callback_to_compose_logged_in(void* context) {
     UNUSED(context);
     return FlipSocialViewLoggedInCompose;
 }
@@ -96,7 +94,7 @@ static uint32_t flip_social_callback_to_compose_logged_in(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInProfile)
  */
-static uint32_t flip_social_callback_to_profile_logged_in(void* context) {
+uint32_t flip_social_callback_to_profile_logged_in(void* context) {
     UNUSED(context);
     return FlipSocialViewLoggedInProfile;
 }
@@ -106,7 +104,7 @@ static uint32_t flip_social_callback_to_profile_logged_in(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInExploreSubmenu)
  */
-static uint32_t flip_social_callback_to_explore_logged_in(void* context) {
+uint32_t flip_social_callback_to_explore_logged_in(void* context) {
     UNUSED(context);
     flip_social_dialog_stop = true;
     last_explore_response = "";
@@ -121,7 +119,7 @@ static uint32_t flip_social_callback_to_explore_logged_in(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInFriendsSubmenu)
  */
-static uint32_t flip_social_callback_to_friends_logged_in(void* context) {
+uint32_t flip_social_callback_to_friends_logged_in(void* context) {
     UNUSED(context);
     flip_social_dialog_stop = true;
     last_explore_response = "";
@@ -136,7 +134,7 @@ static uint32_t flip_social_callback_to_friends_logged_in(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInMessagesSubmenu)
  */
-static uint32_t flip_social_callback_to_messages_logged_in(void* context) {
+uint32_t flip_social_callback_to_messages_logged_in(void* context) {
     UNUSED(context);
     return FlipSocialViewLoggedInMessagesSubmenu;
 }
@@ -146,7 +144,7 @@ static uint32_t flip_social_callback_to_messages_logged_in(void* context) {
  * @param context The context - unused
  * @return next view id (FlipSocialViewLoggedInMessagesUserChoices)
  */
-static uint32_t flip_social_callback_to_messages_user_choices(void* context) {
+uint32_t flip_social_callback_to_messages_user_choices(void* context) {
     UNUSED(context);
     return FlipSocialViewLoggedInMessagesUserChoices;
 }
@@ -156,7 +154,7 @@ static uint32_t flip_social_callback_to_messages_user_choices(void* context) {
  * @param context The context - unused
  * @return next view id (VIEW_NONE to exit the app)
  */
-static uint32_t flip_social_callback_exit_app(void* context) {
+uint32_t flip_social_callback_exit_app(void* context) {
     // Exit the application
     if(!context) {
         FURI_LOG_E(TAG, "Context is NULL");
@@ -172,7 +170,7 @@ static uint32_t flip_social_callback_exit_app(void* context) {
  * @param index The FlipSocialSubmenuIndex item that was clicked.
  * @return void
  */
-static void flip_social_callback_submenu_choices(void* context, uint32_t index) {
+void flip_social_callback_submenu_choices(void* context, uint32_t index) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -240,13 +238,13 @@ static void flip_social_callback_submenu_choices(void* context, uint32_t index)
         app->is_logged_in = "false";
 
         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);
+            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);
 
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutSubmenu);
         break;
@@ -255,10 +253,20 @@ static void flip_social_callback_submenu_choices(void* context, uint32_t index)
             app->view_dispatcher, FlipSocialViewLoggedInComposeAddPreSaveInput);
         break;
     default:
+        action = ActionNone;
         // Handle the pre-saved message selection (has a max of 25 items)
         if(index >= FlipSocialSubemnuComposeIndexStartIndex &&
            index < FlipSocialSubemnuComposeIndexStartIndex + MAX_PRE_SAVED_MESSAGES) {
-            flip_social_feed->index = index - FlipSocialSubemnuComposeIndexStartIndex;
+            app->pre_saved_messages.index = index - FlipSocialSubemnuComposeIndexStartIndex;
+            snprintf(
+                selected_message,
+                MAX_MESSAGE_LENGTH,
+                "%s",
+                app->pre_saved_messages.messages[app->pre_saved_messages.index]);
+            if(!selected_message) {
+                FURI_LOG_E(TAG, "Selected message is NULL");
+                return;
+            }
             view_dispatcher_switch_to_view(
                 app->view_dispatcher, FlipSocialViewLoggedInProcessCompose);
         }
@@ -267,6 +275,10 @@ static void flip_social_callback_submenu_choices(void* context, uint32_t index)
         else if(
             index >= FlipSocialSubmenuExploreIndexStartIndex &&
             index < FlipSocialSubmenuExploreIndexStartIndex + MAX_EXPLORE_USERS) {
+            if(!flip_social_explore) {
+                FURI_LOG_E(TAG, "FlipSocialExplore is NULL");
+                return;
+            }
             flip_social_explore->index = index - FlipSocialSubmenuExploreIndexStartIndex;
             view_dispatcher_switch_to_view(
                 app->view_dispatcher, FlipSocialViewLoggedInExploreProccess);
@@ -276,6 +288,10 @@ static void flip_social_callback_submenu_choices(void* context, uint32_t index)
         else if(
             index >= FlipSocialSubmenuLoggedInIndexFriendsStart &&
             index < FlipSocialSubmenuLoggedInIndexFriendsStart + MAX_FRIENDS) {
+            if(!flip_social_friends) {
+                FURI_LOG_E(TAG, "FlipSocialFriends is NULL");
+                return;
+            }
             flip_social_friends->index = index - FlipSocialSubmenuLoggedInIndexFriendsStart;
             view_dispatcher_switch_to_view(
                 app->view_dispatcher, FlipSocialViewLoggedInFriendsProcess);
@@ -285,6 +301,10 @@ static void flip_social_callback_submenu_choices(void* context, uint32_t index)
         else if(
             index >= FlipSocialSubmenuLoggedInIndexMessagesUsersStart &&
             index < FlipSocialSubmenuLoggedInIndexMessagesUsersStart + MAX_MESSAGE_USERS) {
+            if(!flip_social_message_users) {
+                FURI_LOG_E(TAG, "FlipSocialMessageUsers is NULL");
+                return;
+            }
             flip_social_message_users->index =
                 index - FlipSocialSubmenuLoggedInIndexMessagesUsersStart;
             if(flipper_http_process_response_async(
@@ -299,6 +319,10 @@ static void flip_social_callback_submenu_choices(void* context, uint32_t index)
             index >= FlipSocialSubmenuLoggedInIndexMessagesUserChoicesIndexStart &&
             index <
                 FlipSocialSubmenuLoggedInIndexMessagesUserChoicesIndexStart + MAX_EXPLORE_USERS) {
+            if(!flip_social_explore) {
+                FURI_LOG_E(TAG, "FlipSocialExplore is NULL");
+                return;
+            }
             flip_social_explore->index =
                 index - FlipSocialSubmenuLoggedInIndexMessagesUserChoicesIndexStart;
             view_dispatcher_switch_to_view(
@@ -316,7 +340,7 @@ static void flip_social_callback_submenu_choices(void* context, uint32_t index)
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_out_wifi_settings_ssid_updated(void* context) {
+void flip_social_logged_out_wifi_settings_ssid_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -371,7 +395,7 @@ static void flip_social_logged_out_wifi_settings_ssid_updated(void* context) {
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_out_wifi_settings_password_updated(void* context) {
+void flip_social_logged_out_wifi_settings_password_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -427,8 +451,7 @@ static void flip_social_logged_out_wifi_settings_password_updated(void* context)
  * @param index The index of the selected item.
  * @return void
  */
-static void
-    flip_social_text_input_logged_out_wifi_settings_item_selected(void* context, uint32_t index) {
+void flip_social_text_input_logged_out_wifi_settings_item_selected(void* context, uint32_t index) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -454,7 +477,7 @@ static void
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_out_login_username_updated(void* context) {
+void flip_social_logged_out_login_username_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -504,7 +527,7 @@ static void flip_social_logged_out_login_username_updated(void* context) {
  * @return void
  */
 
-static void flip_social_logged_out_login_password_updated(void* context) {
+void flip_social_logged_out_login_password_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -554,7 +577,7 @@ static void flip_social_logged_out_login_password_updated(void* context) {
  * @param index The index of the selected item.
  * @return void
  */
-static void flip_social_text_input_logged_out_login_item_selected(void* context, uint32_t index) {
+void flip_social_text_input_logged_out_login_item_selected(void* context, uint32_t index) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -583,7 +606,7 @@ static void flip_social_text_input_logged_out_login_item_selected(void* context,
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_out_register_username_updated(void* context) {
+void flip_social_logged_out_register_username_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -613,7 +636,7 @@ static void flip_social_logged_out_register_username_updated(void* context) {
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_out_register_password_updated(void* context) {
+void flip_social_logged_out_register_password_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -643,7 +666,7 @@ static void flip_social_logged_out_register_password_updated(void* context) {
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_out_register_password_2_updated(void* context) {
+void flip_social_logged_out_register_password_2_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -675,8 +698,7 @@ static void flip_social_logged_out_register_password_2_updated(void* context) {
  * @param index The index of the selected item.
  * @return void
  */
-static void
-    flip_social_text_input_logged_out_register_item_selected(void* context, uint32_t index) {
+void flip_social_text_input_logged_out_register_item_selected(void* context, uint32_t index) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -710,7 +732,7 @@ static void
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_in_wifi_settings_ssid_updated(void* context) {
+void flip_social_logged_in_wifi_settings_ssid_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -767,7 +789,7 @@ static void flip_social_logged_in_wifi_settings_ssid_updated(void* context) {
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_in_wifi_settings_password_updated(void* context) {
+void flip_social_logged_in_wifi_settings_password_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -825,8 +847,7 @@ static void flip_social_logged_in_wifi_settings_password_updated(void* context)
  * @param index The index of the selected item.
  * @return void
  */
-static void
-    flip_social_text_input_logged_in_wifi_settings_item_selected(void* context, uint32_t index) {
+void flip_social_text_input_logged_in_wifi_settings_item_selected(void* context, uint32_t index) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -852,7 +873,7 @@ static void
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_in_compose_pre_save_updated(void* context) {
+void flip_social_logged_in_compose_pre_save_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -911,7 +932,7 @@ static void flip_social_logged_in_compose_pre_save_updated(void* context) {
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_in_profile_change_password_updated(void* context) {
+void flip_social_logged_in_profile_change_password_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -982,7 +1003,7 @@ static void flip_social_logged_in_profile_change_password_updated(void* context)
  * @param index The index of the selected item.
  * @return void
  */
-static void flip_social_text_input_logged_in_profile_item_selected(void* context, uint32_t index) {
+void flip_social_text_input_logged_in_profile_item_selected(void* context, uint32_t index) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -1028,8 +1049,7 @@ static void flip_social_text_input_logged_in_profile_item_selected(void* context
  * @param index The index of the selected item.
  * @return void
  */
-static void
-    flip_social_text_input_logged_in_settings_item_selected(void* context, uint32_t index) {
+void flip_social_text_input_logged_in_settings_item_selected(void* context, uint32_t index) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -1052,7 +1072,7 @@ static void
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_in_messages_user_choice_message_updated(void* context) {
+void flip_social_logged_in_messages_user_choice_message_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -1127,7 +1147,7 @@ static void flip_social_logged_in_messages_user_choice_message_updated(void* con
  * @param context The context - FlipSocialApp object.
  * @return void
  */
-static void flip_social_logged_in_messages_new_message_updated(void* context) {
+void flip_social_logged_in_messages_new_message_updated(void* context) {
     FlipSocialApp* app = (FlipSocialApp*)context;
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
@@ -1186,5 +1206,3 @@ static void flip_social_logged_in_messages_new_message_updated(void* context) {
     furi_timer_stop(fhttp.get_timeout_timer);
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesSubmenu);
 }
-
-#endif // FLIP_SOCIAL_CALLBACK_H

+ 256 - 0
flip_social/callback/flip_social_callback.h

@@ -0,0 +1,256 @@
+#ifndef FLIP_SOCIAL_CALLBACK_H
+#define FLIP_SOCIAL_CALLBACK_H
+
+#include <flip_social.h>
+#include <messages/flip_social_messages.h>
+#include <friends/flip_social_friends.h>
+#include <explore/flip_social_explore.h>
+#include <feed/flip_social_feed.h>
+#include <draw/flip_social_draw.h>
+#include <flip_storage/flip_social_storage.h>
+
+/**
+ * @brief Navigation callback to go back to the submenu Logged out.
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedOutSubmenu)
+ */
+uint32_t flip_social_callback_to_submenu_logged_out(void* context);
+
+/**
+ * @brief Navigation callback to go back to the submenu Logged in.
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInSubmenu)
+ */
+uint32_t flip_social_callback_to_submenu_logged_in(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the (Logged out) Login screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedOutLogin)
+ */
+uint32_t flip_social_callback_to_login_logged_out(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the (Logged out) Register screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedOutRegister)
+ */
+uint32_t flip_social_callback_to_register_logged_out(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the (Logged out) Wifi Settings screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedOutWifiSettings)
+ */
+uint32_t flip_social_callback_to_wifi_settings_logged_out(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the (Logged in) Wifi Settings screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInSettingsWifi)
+ */
+uint32_t flip_social_callback_to_wifi_settings_logged_in(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the (Logged in) Settings screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInSettingsWifi)
+ */
+uint32_t flip_social_callback_to_settings_logged_in(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the (Logged in) Compose screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInCompose)
+ */
+uint32_t flip_social_callback_to_compose_logged_in(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the (Logged in) Profile screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInProfile)
+ */
+uint32_t flip_social_callback_to_profile_logged_in(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the Explore submenu
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInExploreSubmenu)
+ */
+uint32_t flip_social_callback_to_explore_logged_in(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the Friends submenu
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInFriendsSubmenu)
+ */
+uint32_t flip_social_callback_to_friends_logged_in(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the Messages submenu
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInMessagesSubmenu)
+ */
+uint32_t flip_social_callback_to_messages_logged_in(void* context);
+
+/**
+ * @brief Navigation callback to bring the user back to the User Choices screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInMessagesUserChoices)
+ */
+uint32_t flip_social_callback_to_messages_user_choices(void* context);
+
+/**
+ * @brief Navigation callback for exiting the application
+ * @param context The context - unused
+ * @return next view id (VIEW_NONE to exit the app)
+ */
+uint32_t flip_social_callback_exit_app(void* context);
+
+/**
+ * @brief Handle ALL submenu item selections.
+ * @param context The context - FlipSocialApp object.
+ * @param index The FlipSocialSubmenuIndex item that was clicked.
+ * @return void
+ */
+void flip_social_callback_submenu_choices(void* context, uint32_t index);
+
+/**
+ * @brief Text input callback for when the user finishes entering their SSID on the wifi settings (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_out_wifi_settings_ssid_updated(void* context);
+
+/**
+ * @brief Text input callback for when the user finishes entering their password on the wifi settings (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_out_wifi_settings_password_updated(void* context);
+
+/**
+ * @brief Callback when the user selects a menu item in the wifi settings (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @param index The index of the selected item.
+ * @return void
+ */
+void flip_social_text_input_logged_out_wifi_settings_item_selected(void* context, uint32_t index);
+
+/**
+ * @brief Text input callback for when the user finishes entering their username on the login (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_out_login_username_updated(void* context);
+
+/**
+ * @brief Text input callback for when the user finishes entering their password on the login (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_out_login_password_updated(void* context);
+
+/**
+ * @brief Callback when the user selects a menu item in the login (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @param index The index of the selected item.
+ * @return void
+ */
+void flip_social_text_input_logged_out_login_item_selected(void* context, uint32_t index);
+
+/**
+ * @brief Text input callback for when the user finishes entering their username on the register (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_out_register_username_updated(void* context);
+
+/**
+ * @brief Text input callback for when the user finishes entering their password on the register (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_out_register_password_updated(void* context);
+
+/**
+ * @brief Text input callback for when the user finishes entering their password 2 on the register (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_out_register_password_2_updated(void* context);
+
+/**
+ * @brief Text input callback for when the user finishes entering their SSID on the wifi settings (logged in) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_in_wifi_settings_ssid_updated(void* context);
+
+/**
+ * @brief Text input callback for when the user finishes entering their password on the wifi settings (logged in) screen.
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_in_wifi_settings_password_updated(void* context);
+
+/**
+ * @brief Callback when the user selects a menu item in the wifi settings (logged in) screen.
+ * @param context The context - FlipSocialApp object.
+ * @param index The index of the selected item.
+ * @return void
+ */
+void flip_social_text_input_logged_in_wifi_settings_item_selected(void* context, uint32_t index);
+
+/**
+ * @brief Text input callback for when the user finishes entering their message on the compose (logged in) screen for Add Text
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_in_compose_pre_save_updated(void* context);
+
+/**
+ * @brief Text input callback for when the user finishes entering their message on the profile (logged in) screen for change password
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_in_profile_change_password_updated(void* context);
+
+/**
+ * @brief Callback when a user selects a menu item in the profile (logged in) screen.
+ * @param context The context - FlipSocialApp object.
+ * @param index The index of the selected item.
+ * @return void
+ */
+void flip_social_text_input_logged_in_profile_item_selected(void* context, uint32_t index);
+
+/**
+ * @brief Callback when a user selects a menu item in the settings (logged in) screen.
+ * @param context The context - FlipSocialApp object.
+ * @param index The index of the selected item.
+ * @return void
+ */
+void flip_social_text_input_logged_in_settings_item_selected(void* context, uint32_t index);
+
+/**
+ * @brief Text input callback for when the user finishes entering their message to send to the selected user choice (user choice messages view)
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_in_messages_user_choice_message_updated(void* context);
+/**
+ * @brief Text input callback for when the user finishes entering their message to the selected user (messages view)
+ * @param context The context - FlipSocialApp object.
+ * @return void
+ */
+void flip_social_logged_in_messages_new_message_updated(void* context);
+
+/**
+ * @brief Callback when the user selects a menu item in the register (logged out) screen.
+ * @param context The context - FlipSocialApp object.
+ * @param index The index of the selected item.
+ * @return void
+ */
+void flip_social_text_input_logged_out_register_item_selected(void* context, uint32_t index);
+
+#endif

+ 61 - 57
flip_social/flip_social_draw.h → flip_social/draw/flip_social_draw.c

@@ -1,15 +1,5 @@
-#ifndef FLIP_SOCIAL_DRAW_H
-#define FLIP_SOCIAL_DRAW_H
-
-bool flip_social_sent_login_request = false;
-bool flip_social_sent_register_request = false;
-bool flip_social_login_success = false;
-bool flip_social_register_success = false;
-bool flip_social_dialog_shown = false;
-bool flip_social_dialog_stop = false;
-char* last_explore_response = "";
-static bool flip_social_update_friends();
-
+#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.");
@@ -24,14 +14,14 @@ bool flip_social_board_is_active(Canvas* canvas) {
 }
 
 void flip_social_handle_error(Canvas* canvas) {
-    if(fhttp.received_data != NULL) {
-        if(strstr(fhttp.received_data, "[ERROR] Not connected to Wifi. Failed to reconnect.") !=
+    if(fhttp.last_response != NULL) {
+        if(strstr(fhttp.last_response, "[ERROR] Not connected to Wifi. Failed to reconnect.") !=
            NULL) {
             canvas_clear(canvas);
             canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
             canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
             canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
-        } else if(strstr(fhttp.received_data, "[ERROR] Failed to connect to Wifi.") != NULL) {
+        } else if(strstr(fhttp.last_response, "[ERROR] Failed to connect to Wifi.") != NULL) {
             canvas_clear(canvas);
             canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
             canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
@@ -48,7 +38,7 @@ void flip_social_handle_error(Canvas* canvas) {
     }
 }
 
-static void on_input(const void* event, void* ctx) {
+void on_input(const void* event, void* ctx) {
     UNUSED(ctx);
 
     InputKey key = ((InputEvent*)event)->key;
@@ -132,7 +122,7 @@ void draw_user_message(Canvas* canvas, const char* user_message, int x, int y) {
     }
 }
 
-static void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
+void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
     UNUSED(model);
     if(!canvas) {
         FURI_LOG_E(TAG, "Canvas is NULL");
@@ -143,8 +133,6 @@ static void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
         return;
     }
 
-    char* message = app_instance->pre_saved_messages.messages[flip_social_feed->index];
-
     if(!flip_social_dialog_shown) {
         flip_social_dialog_shown = true;
         app_instance->input_event_queue = furi_record_open(RECORD_INPUT_EVENTS);
@@ -152,7 +140,7 @@ static void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
             furi_pubsub_subscribe(app_instance->input_event_queue, on_input, NULL);
     }
 
-    draw_user_message(canvas, message, 0, 2);
+    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");
@@ -169,16 +157,16 @@ static void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
         flip_social_dialog_stop = true;
         break;
     case ActionNext:
-        // send message
-        if(message && app_instance->login_username_logged_in) {
-            // Send the message
-            char command[128];
+        // send selected_message
+        if(selected_message && app_instance->login_username_logged_in) {
+            // Send the selected_message
+            char command[256];
             snprintf(
                 command,
                 sizeof(command),
                 "{\"username\":\"%s\",\"content\":\"%s\"}",
                 app_instance->login_username_logged_in,
-                message);
+                selected_message);
 
             bool success = flipper_http_post_request_with_headers(
                 "https://www.flipsocial.net/api/feed/post/",
@@ -187,15 +175,14 @@ static void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
 
             if(!success) {
                 FURI_LOG_E(TAG, "Failed to send HTTP request for feed");
-                furi_check(success); // Log the error with furi_check
-                return; // Exit early to avoid further errors
+                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");
-            furi_check(false); // Log as an error and return
             return;
         }
 
@@ -221,9 +208,10 @@ static void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
         break;
     case ActionPrev:
         // delete message
-        app_instance->pre_saved_messages.messages[flip_social_feed->index] = NULL;
+        app_instance->pre_saved_messages.messages[app_instance->pre_saved_messages.index] = NULL;
 
-        for(uint32_t i = flip_social_feed->index; i < app_instance->pre_saved_messages.count - 1;
+        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];
@@ -264,9 +252,23 @@ static void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
         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;
-            view_dispatcher_switch_to_view(
-                app_instance->view_dispatcher, FlipSocialViewLoggedInFeed);
+            if(flipper_http_process_response_async(
+                   flip_social_get_feed, flip_social_parse_json_feed)) {
+                view_dispatcher_switch_to_view(
+                    app_instance->view_dispatcher, FlipSocialViewLoggedInFeed);
+            } else {
+                // Set failure FlipSocialFeed object
+                if(!flip_social_temp_feed()) {
+                    return;
+                }
+                view_dispatcher_switch_to_view(
+                    app_instance->view_dispatcher, FlipSocialViewLoggedInFeed);
+            }
         } else if(action == ActionBack) {
             action = ActionNone;
             view_dispatcher_switch_to_view(
@@ -279,7 +281,7 @@ static void flip_social_callback_draw_compose(Canvas* canvas, void* model) {
     }
 }
 // function to draw the dialog canvas
-static void flip_social_canvas_draw_message(
+void flip_social_canvas_draw_message(
     Canvas* canvas,
     char* user_username,
     char* user_message,
@@ -354,7 +356,7 @@ static void flip_social_canvas_draw_message(
     }
 }
 // Callback function to handle the feed dialog
-static void flip_social_callback_draw_feed(Canvas* canvas, void* model) {
+void flip_social_callback_draw_feed(Canvas* canvas, void* model) {
     UNUSED(model);
     if(!canvas) {
         FURI_LOG_E(TAG, "Canvas is NULL");
@@ -476,7 +478,7 @@ static void flip_social_callback_draw_feed(Canvas* canvas, void* model) {
  * @param model The model - unused
  * @return void
  */
-static void flip_social_callback_draw_login(Canvas* canvas, void* model) {
+void flip_social_callback_draw_login(Canvas* canvas, void* model) {
     UNUSED(model);
     if(!canvas) {
         FURI_LOG_E(TAG, "Canvas is NULL");
@@ -529,10 +531,10 @@ static void flip_social_callback_draw_login(Canvas* canvas, void* model) {
         canvas_draw_str(canvas, 0, 17, "Request Sent!");
         canvas_draw_str(canvas, 0, 32, "Awaiting reponse...");
 
-        if(fhttp.state == IDLE && fhttp.received_data != NULL) {
+        if(fhttp.state == IDLE && fhttp.last_response != NULL) {
             // read response
-            if(strstr(fhttp.received_data, "[SUCCESS]") != NULL ||
-               strstr(fhttp.received_data, "User found") != NULL) {
+            if(strstr(fhttp.last_response, "[SUCCESS]") != NULL ||
+               strstr(fhttp.last_response, "User found") != NULL) {
                 canvas_draw_str(canvas, 0, 42, "Login successful!");
                 canvas_draw_str(canvas, 0, 62, "Welcome back!");
 
@@ -561,16 +563,16 @@ static void flip_social_callback_draw_login(Canvas* canvas, void* model) {
                 // send user to the logged in submenu
                 view_dispatcher_switch_to_view(
                     app_instance->view_dispatcher, FlipSocialViewLoggedInSubmenu);
-            } else if(strstr(fhttp.received_data, "User not found") != NULL) {
+            } else if(strstr(fhttp.last_response, "User not found") != NULL) {
                 canvas_clear(canvas);
                 canvas_draw_str(canvas, 0, 10, "Account not found...");
                 canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
             } else {
                 flip_social_handle_error(canvas);
             }
-        } else if((fhttp.state == ISSUE || fhttp.state == INACTIVE) && fhttp.received_data != NULL) {
+        } else if((fhttp.state == ISSUE || fhttp.state == INACTIVE) && fhttp.last_response != NULL) {
             flip_social_handle_error(canvas);
-        } else if(fhttp.state == IDLE && fhttp.received_data == NULL) {
+        } else if(fhttp.state == IDLE && fhttp.last_response == NULL) {
             flip_social_handle_error(canvas);
         }
     } else if(flip_social_sent_login_request && !flip_social_login_success) {
@@ -587,7 +589,7 @@ static void flip_social_callback_draw_login(Canvas* canvas, void* model) {
  * @param model The model - unused
  * @return void
  */
-static void flip_social_callback_draw_register(Canvas* canvas, void* model) {
+void flip_social_callback_draw_register(Canvas* canvas, void* model) {
     UNUSED(model);
     if(!canvas) {
         FURI_LOG_E(TAG, "Canvas is NULL");
@@ -653,9 +655,9 @@ static void flip_social_callback_draw_register(Canvas* canvas, void* model) {
 
         if(fhttp.state == IDLE) {
             // read response
-            if(fhttp.received_data != NULL &&
-               (strstr(fhttp.received_data, "[SUCCESS]") != NULL ||
-                strstr(fhttp.received_data, "User created") != NULL)) {
+            if(fhttp.last_response != NULL &&
+               (strstr(fhttp.last_response, "[SUCCESS]") != NULL ||
+                strstr(fhttp.last_response, "User created") != NULL)) {
                 canvas_draw_str(canvas, 0, 42, "Registeration successful!");
                 canvas_draw_str(canvas, 0, 62, "Welcome to FlipSocial!");
 
@@ -690,13 +692,13 @@ static void flip_social_callback_draw_register(Canvas* canvas, void* model) {
                 // send user to the logged in submenu
                 view_dispatcher_switch_to_view(
                     app_instance->view_dispatcher, FlipSocialViewLoggedInSubmenu);
-            } else if(strstr(fhttp.received_data, "Username or password not provided") != NULL) {
+            } else if(strstr(fhttp.last_response, "Username or password not provided") != NULL) {
                 canvas_clear(canvas);
                 canvas_draw_str(canvas, 0, 10, "Please enter your credentials.");
                 canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
             } else if(
-                strstr(fhttp.received_data, "User already exists") != NULL ||
-                strstr(fhttp.received_data, "Multiple users found") != NULL) {
+                strstr(fhttp.last_response, "User already exists") != NULL ||
+                strstr(fhttp.last_response, "Multiple users found") != NULL) {
                 canvas_draw_str(canvas, 0, 42, "Registration failed...");
                 canvas_draw_str(canvas, 0, 52, "Username already exists.");
                 canvas_draw_str(canvas, 0, 62, "Press BACK to return.");
@@ -717,7 +719,7 @@ static void flip_social_callback_draw_register(Canvas* canvas, void* model) {
 }
 
 // function to draw the dialog canvas
-static void flip_social_canvas_draw_explore(Canvas* canvas, char* user_username, char* content) {
+void flip_social_canvas_draw_explore(Canvas* canvas, char* user_username, char* content) {
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontPrimary);
     canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, user_username);
@@ -737,7 +739,7 @@ static void flip_social_canvas_draw_explore(Canvas* canvas, char* user_username,
 }
 
 // Callback function to handle the explore dialog
-static void flip_social_callback_draw_explore(Canvas* canvas, void* model) {
+void flip_social_callback_draw_explore(Canvas* canvas, void* model) {
     UNUSED(model);
     if(!canvas) {
         FURI_LOG_E(TAG, "Canvas is NULL");
@@ -816,7 +818,7 @@ static void flip_social_callback_draw_explore(Canvas* canvas, void* model) {
 }
 
 // Callback function to handle the friends dialog
-static void flip_social_callback_draw_friends(Canvas* canvas, void* model) {
+void flip_social_callback_draw_friends(Canvas* canvas, void* model) {
     UNUSED(model);
     if(!canvas) {
         FURI_LOG_E(TAG, "Canvas is NULL");
@@ -913,7 +915,7 @@ static void flip_social_callback_draw_friends(Canvas* canvas, void* model) {
     }
 }
 
-static void flip_social_canvas_draw_user_message(
+void flip_social_canvas_draw_user_message(
     Canvas* canvas,
     char* user_username,
     char* user_message,
@@ -942,7 +944,7 @@ static void flip_social_canvas_draw_user_message(
 }
 
 // Callback function to handle the messages dialog
-static void flip_social_callback_draw_messages(Canvas* canvas, void* model) {
+void flip_social_callback_draw_messages(Canvas* canvas, void* model) {
     UNUSED(model);
     if(!canvas) {
         FURI_LOG_E(TAG, "Canvas is NULL");
@@ -1007,18 +1009,22 @@ static void flip_social_callback_draw_messages(Canvas* canvas, void* model) {
     case ActionFlip:
         // go to the input view
         flip_social_dialog_stop = true;
+        flip_social_send_message = true;
+        action = ActionNone;
         break;
     default:
         action = ActionNone;
         break;
     }
 
-    if(flip_social_dialog_stop) {
+    if(flip_social_dialog_stop && flip_social_dialog_shown) {
         furi_pubsub_unsubscribe(app_instance->input_event_queue, app_instance->input_event);
         flip_social_dialog_shown = false;
         flip_social_dialog_stop = false;
-        if(action == ActionFlip) {
+        if(flip_social_send_message) {
+            FURI_LOG_I(TAG, "Switching to new message input view");
             action = ActionNone;
+            flip_social_send_message = false;
             view_dispatcher_switch_to_view(
                 app_instance->view_dispatcher, FlipSocialViewLoggedInMessagesNewMessageInput);
         } else {
@@ -1028,5 +1034,3 @@ static void flip_social_callback_draw_messages(Canvas* canvas, void* model) {
         }
     }
 }
-
-#endif // FLIP_SOCIAL_DRAW_H

+ 71 - 0
flip_social/draw/flip_social_draw.h

@@ -0,0 +1,71 @@
+#ifndef FLIP_SOCIAL_DRAW_H
+#define FLIP_SOCIAL_DRAW_H
+#include <flip_social.h>
+#include <flip_storage/flip_social_storage.h>
+#include <callback/flip_social_callback.h>
+#include <friends/flip_social_friends.h>
+
+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);
+/**
+ * @brief Navigation callback for asynchonously handling the login process.
+ * @param canvas The canvas to draw on.
+ * @param model The model - unused
+ * @return void
+ */
+void flip_social_callback_draw_login(Canvas* canvas, void* model);
+/**
+ * @brief Navigation callback for asynchonously handling the register process.
+ * @param canvas The canvas to draw on.
+ * @param model The model - unused
+ * @return void
+ */
+void flip_social_callback_draw_register(Canvas* canvas, void* model);
+
+// function to draw the dialog canvas
+void flip_social_canvas_draw_explore(Canvas* canvas, char* user_username, char* content);
+
+// Callback function to handle the explore dialog
+void flip_social_callback_draw_explore(Canvas* canvas, void* model);
+
+// Callback function to handle the friends dialog
+void flip_social_callback_draw_friends(Canvas* canvas, void* model);
+
+void flip_social_canvas_draw_user_message(
+    Canvas* canvas,
+    char* user_username,
+    char* user_message,
+    bool show_prev,
+    bool show_next);
+// Callback function to handle the messages dialog
+void flip_social_callback_draw_messages(Canvas* canvas, void* model);
+
+#endif

+ 1 - 23
flip_social/easy_flipper.h → flip_social/easy_flipper/easy_flipper.c

@@ -1,24 +1,4 @@
-#ifndef EASY_FLIPPER_H
-#define EASY_FLIPPER_H
-
-#include <malloc.h>
-#include <furi.h>
-#include <furi_hal.h>
-#include <gui/gui.h>
-#include <gui/view.h>
-#include <gui/modules/submenu.h>
-#include <gui/view_dispatcher.h>
-#include <gui/modules/menu.h>
-#include <gui/modules/submenu.h>
-#include <gui/modules/widget.h>
-#include <gui/modules/text_input.h>
-#include <gui/modules/text_box.h>
-#include <gui/modules/variable_item_list.h>
-#include <gui/modules/dialog_ex.h>
-#include <gui/modules/popup.h>
-#include <gui/modules/loading.h>
-
-#define EASY_TAG "EasyFlipper"
+#include <easy_flipper/easy_flipper.h>
 
 /**
  * @brief Navigation callback for exiting the application
@@ -530,5 +510,3 @@ bool easy_flipper_set_char_to_furi_string(FuriString** furi_string, char* buffer
     furi_string_set_str(*furi_string, buffer);
     return true;
 }
-
-#endif // EASY_FLIPPER_H

+ 261 - 0
flip_social/easy_flipper/easy_flipper.h

@@ -0,0 +1,261 @@
+#ifndef EASY_FLIPPER_H
+#define EASY_FLIPPER_H
+
+#include <malloc.h>
+#include <furi.h>
+#include <furi_hal.h>
+#include <gui/gui.h>
+#include <gui/view.h>
+#include <gui/modules/submenu.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/menu.h>
+#include <gui/modules/submenu.h>
+#include <gui/modules/widget.h>
+#include <gui/modules/text_input.h>
+#include <gui/modules/text_box.h>
+#include <gui/modules/variable_item_list.h>
+#include <gui/modules/dialog_ex.h>
+#include <gui/modules/popup.h>
+#include <gui/modules/loading.h>
+#include <stdio.h>
+#include <string.h>
+#include <jsmn/jsmn.h>
+
+#define EASY_TAG "EasyFlipper"
+
+/**
+ * @brief Navigation callback for exiting the application
+ * @param context The context - unused
+ * @return next view id (VIEW_NONE to exit the app)
+ */
+uint32_t easy_flipper_callback_exit_app(void* context);
+/**
+ * @brief Initialize a buffer
+ * @param buffer The buffer to initialize
+ * @param buffer_size The size of the buffer
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_buffer(char** buffer, uint32_t buffer_size);
+/**
+ * @brief Initialize a View object
+ * @param view The View object to initialize
+ * @param view_id The ID/Index of the view
+ * @param draw_callback The draw callback function (set to NULL if not needed)
+ * @param input_callback The input callback function (set to NULL if not needed)
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_view(
+    View** view,
+    int32_t view_id,
+    void draw_callback(Canvas*, void*),
+    bool input_callback(InputEvent*, void*),
+    uint32_t (*previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a ViewDispatcher object
+ * @param view_dispatcher The ViewDispatcher object to initialize
+ * @param gui The GUI object
+ * @param context The context to pass to the event callback
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_view_dispatcher(ViewDispatcher** view_dispatcher, Gui* gui, void* context);
+
+/**
+ * @brief Initialize a Submenu object
+ * @note This does not set the items in the submenu
+ * @param submenu The Submenu object to initialize
+ * @param view_id The ID/Index of the view
+ * @param title The title/header of the submenu
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_submenu(
+    Submenu** submenu,
+    int32_t view_id,
+    char* title,
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher);
+
+/**
+ * @brief Initialize a Menu object
+ * @note This does not set the items in the menu
+ * @param menu The Menu object to initialize
+ * @param view_id The ID/Index of the view
+ * @param item_callback The item callback function
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_menu(
+    Menu** menu,
+    int32_t view_id,
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher);
+
+/**
+ * @brief Initialize a Widget object
+ * @param widget The Widget object to initialize
+ * @param view_id The ID/Index of the view
+ * @param text The text to display in the widget
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_widget(
+    Widget** widget,
+    int32_t view_id,
+    char* text,
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher);
+
+/**
+ * @brief Initialize a VariableItemList object
+ * @note This does not set the items in the VariableItemList
+ * @param variable_item_list The VariableItemList object to initialize
+ * @param view_id The ID/Index of the view
+ * @param enter_callback The enter callback function (can be set to NULL)
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @param context The context to pass to the enter callback (usually the app)
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_variable_item_list(
+    VariableItemList** variable_item_list,
+    int32_t view_id,
+    void (*enter_callback)(void*, uint32_t),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a TextInput object
+ * @param text_input The TextInput object to initialize
+ * @param view_id The ID/Index of the view
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_text_input(
+    TextInput** text_input,
+    int32_t view_id,
+    char* header_text,
+    char* text_input_temp_buffer,
+    uint32_t text_input_buffer_size,
+    void (*result_callback)(void*),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a TextInput object with extra symbols
+ * @param uart_text_input The TextInput object to initialize
+ * @param view_id The ID/Index of the view
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_uart_text_input(
+    TextInput** uart_text_input,
+    int32_t view_id,
+    char* header_text,
+    char* uart_text_input_temp_buffer,
+    uint32_t uart_text_input_buffer_size,
+    void (*result_callback)(void*),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a DialogEx object
+ * @param dialog_ex The DialogEx object to initialize
+ * @param view_id The ID/Index of the view
+ * @param header The header of the dialog
+ * @param header_x The x coordinate of the header
+ * @param header_y The y coordinate of the header
+ * @param text The text of the dialog
+ * @param text_x The x coordinate of the dialog
+ * @param text_y The y coordinate of the dialog
+ * @param left_button_text The text of the left button
+ * @param right_button_text The text of the right button
+ * @param center_button_text The text of the center button
+ * @param result_callback The result callback function
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @param context The context to pass to the result callback
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_dialog_ex(
+    DialogEx** dialog_ex,
+    int32_t view_id,
+    char* header,
+    uint16_t header_x,
+    uint16_t header_y,
+    char* text,
+    uint16_t text_x,
+    uint16_t text_y,
+    char* left_button_text,
+    char* right_button_text,
+    char* center_button_text,
+    void (*result_callback)(DialogExResult, void*),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a Popup object
+ * @param popup The Popup object to initialize
+ * @param view_id The ID/Index of the view
+ * @param header The header of the dialog
+ * @param header_x The x coordinate of the header
+ * @param header_y The y coordinate of the header
+ * @param text The text of the dialog
+ * @param text_x The x coordinate of the dialog
+ * @param text_y The y coordinate of the dialog
+ * @param result_callback The result callback function
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @param context The context to pass to the result callback
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_popup(
+    Popup** popup,
+    int32_t view_id,
+    char* header,
+    uint16_t header_x,
+    uint16_t header_y,
+    char* text,
+    uint16_t text_x,
+    uint16_t text_y,
+    void (*result_callback)(void*),
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher,
+    void* context);
+
+/**
+ * @brief Initialize a Loading object
+ * @param loading The Loading object to initialize
+ * @param view_id The ID/Index of the view
+ * @param previous_callback The previous callback function (can be set to NULL)
+ * @param view_dispatcher The ViewDispatcher object
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_loading(
+    Loading** loading,
+    int32_t view_id,
+    uint32_t(previous_callback)(void*),
+    ViewDispatcher** view_dispatcher);
+
+/**
+ * @brief Set a char butter to a FuriString
+ * @param furi_string The FuriString object
+ * @param buffer The buffer to copy the string to
+ * @return true if successful, false otherwise
+ */
+bool easy_flipper_set_char_to_furi_string(FuriString** furi_string, char* buffer);
+
+#endif

+ 45 - 26
flip_social/flip_social_explore.h → flip_social/explore/flip_social_explore.c

@@ -1,7 +1,6 @@
-#ifndef FLIP_SOCIAL_EXPLORE_H
-#define FLIP_SOCIAL_EXPLORE_H
+#include "flip_social_explore.h"
 
-static FlipSocialModel* flip_social_explore_alloc() {
+FlipSocialModel* flip_social_explore_alloc() {
     // Allocate memory for each username only if not already allocated
     FlipSocialModel* explore = malloc(sizeof(FlipSocialModel));
     if(explore == NULL) {
@@ -20,33 +19,52 @@ static FlipSocialModel* flip_social_explore_alloc() {
     return explore;
 }
 
-static void flip_social_free_explore() {
+void flip_social_free_explore() {
     if(!flip_social_explore) {
         FURI_LOG_E(TAG, "Explore model is NULL");
         return;
     }
     for(int i = 0; i < flip_social_explore->count; i++) {
-        free(flip_social_explore->usernames[i]);
+        if(flip_social_explore->usernames[i]) {
+            free(flip_social_explore->usernames[i]);
+        }
     }
 }
 
 // for now we're just listing the current users
 // as the feed is upgraded, then we can port more to the explore view
-static bool flip_social_get_explore() {
+bool flip_social_get_explore() {
+    snprintf(
+        fhttp.file_path,
+        sizeof(fhttp.file_path),
+        STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/users.txt");
+
+    fhttp.save_received_data = true;
+    char* headers = jsmn("Content-Type", "application/json");
     // will return true unless the devboard is not connected
     bool success = flipper_http_get_request_with_headers(
-        "https://www.flipsocial.net/api/user/users/", jsmn("Content-Type", "application/json"));
+        "https://www.flipsocial.net/api/user/users/", headers);
+    free(headers);
     if(!success) {
         FURI_LOG_E(TAG, "Failed to send HTTP request for explore");
+        fhttp.state = ISSUE;
         return false;
     }
     fhttp.state = RECEIVING;
     return true;
 }
 
-static bool flip_social_parse_json_explore() {
-    if(fhttp.received_data == NULL) {
-        FURI_LOG_E(TAG, "No data received.");
+bool flip_social_parse_json_explore() {
+    // load the received data from the saved file
+    FuriString* user_data = flipper_http_load_from_file(fhttp.file_path);
+    if(user_data == NULL) {
+        FURI_LOG_E(TAG, "Failed to load received data from file.");
+        return false;
+    }
+    char* data_cstr = (char*)furi_string_get_cstr(user_data);
+    if(data_cstr == NULL) {
+        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
+        furi_string_free(user_data);
         return false;
     }
 
@@ -54,11 +72,13 @@ static bool flip_social_parse_json_explore() {
     flip_social_explore = flip_social_explore_alloc();
     if(flip_social_explore == NULL) {
         FURI_LOG_E(TAG, "Failed to allocate memory for explore usernames.");
+        furi_string_free(user_data);
+        free(data_cstr);
         return false;
     }
 
     // Remove newlines
-    char* pos = fhttp.received_data;
+    char* pos = data_cstr;
     while((pos = strchr(pos, '\n')) != NULL) {
         *pos = ' ';
     }
@@ -67,9 +87,11 @@ static bool flip_social_parse_json_explore() {
     flip_social_explore->count = 0;
 
     // Extract the users array from the JSON
-    char* json_users = get_json_value("users", fhttp.received_data, MAX_TOKENS);
+    char* json_users = get_json_value("users", data_cstr, MAX_TOKENS);
     if(json_users == NULL) {
         FURI_LOG_E(TAG, "Failed to parse users array.");
+        furi_string_free(user_data);
+        free(data_cstr);
         return false;
     }
 
@@ -84,12 +106,11 @@ static bool flip_social_parse_json_explore() {
         if(*(end - 1) == '"') *(end - 1) = '\0';
 
         // Copy username to pre-allocated memory
-        strncpy(
+        snprintf(
             flip_social_explore->usernames[flip_social_explore->count],
-            start,
-            MAX_USER_LENGTH - 1);
-        flip_social_explore->usernames[flip_social_explore->count][MAX_USER_LENGTH - 1] =
-            '\0'; // Ensure null termination
+            MAX_USER_LENGTH,
+            "%s",
+            start);
         flip_social_explore->count++;
         start = end + 1;
     }
@@ -100,12 +121,11 @@ static bool flip_social_parse_json_explore() {
         if(*(start + strlen(start) - 1) == ']') *(start + strlen(start) - 1) = '\0';
         if(*(start + strlen(start) - 1) == '"') *(start + strlen(start) - 1) = '\0';
 
-        strncpy(
+        snprintf(
             flip_social_explore->usernames[flip_social_explore->count],
-            start,
-            MAX_USER_LENGTH - 1);
-        flip_social_explore->usernames[flip_social_explore->count][MAX_USER_LENGTH - 1] =
-            '\0'; // Ensure null termination
+            MAX_USER_LENGTH,
+            "%s",
+            start);
         flip_social_explore->count++;
     }
 
@@ -125,8 +145,7 @@ static bool flip_social_parse_json_explore() {
     free(json_users);
     free(start);
     free(end);
-
-    return true;
+    furi_string_free(user_data);
+    free(data_cstr);
+    return flip_social_explore->count > 0;
 }
-
-#endif // FLIP_SOCIAL_EXPLORE_H

+ 9 - 0
flip_social/explore/flip_social_explore.h

@@ -0,0 +1,9 @@
+#ifndef FLIP_SOCIAL_EXPLORE_H
+#define FLIP_SOCIAL_EXPLORE_H
+#include "flip_social.h"
+#include <callback/flip_social_callback.h>
+FlipSocialModel* flip_social_explore_alloc();
+void flip_social_free_explore();
+bool flip_social_get_explore();
+bool flip_social_parse_json_explore();
+#endif

+ 35 - 21
flip_social/flip_social_feed.h → flip_social/feed/flip_social_feed.c

@@ -1,8 +1,7 @@
-#ifndef FLIP_SOCIAL_FEED_H
-#define FLIP_SOCIAL_FEED_H
+#include "flip_social_feed.h"
 
 // Set failure FlipSocialFeed object
-static bool flip_social_temp_feed() {
+bool flip_social_temp_feed() {
     if(flip_social_feed == NULL) {
         flip_social_feed = malloc(sizeof(FlipSocialFeed));
         if(flip_social_feed == NULL) {
@@ -54,7 +53,7 @@ static bool flip_social_temp_feed() {
 }
 
 // Allocate memory for each feed item if not already allocated
-static FlipSocialFeed* flip_social_feed_alloc() {
+FlipSocialFeed* flip_social_feed_alloc() {
     // Initialize the feed
     FlipSocialFeed* feed = (FlipSocialFeed*)malloc(sizeof(FlipSocialFeed));
     if(!feed) {
@@ -80,41 +79,59 @@ static FlipSocialFeed* flip_social_feed_alloc() {
     return feed;
 }
 
-static void flip_social_free_feed() {
+void flip_social_free_feed() {
     if(!flip_social_feed) {
         FURI_LOG_E(TAG, "Feed model is NULL");
         return;
     }
     for(uint32_t i = 0; i < flip_social_feed->count; i++) {
-        free(flip_social_feed->usernames[i]);
+        if(flip_social_feed->usernames[i]) {
+            free(flip_social_feed->usernames[i]);
+        }
     }
 }
 
-static bool flip_social_get_feed() {
+bool flip_social_get_feed() {
     // Get the feed from the server
     if(app_instance->login_username_logged_out == NULL) {
         FURI_LOG_E(TAG, "Username is NULL");
         return false;
     }
     char command[128];
+    snprintf(
+        fhttp.file_path,
+        sizeof(fhttp.file_path),
+        STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/feed.txt");
+
+    fhttp.save_received_data = true;
+    char* headers = jsmn("Content-Type", "application/json");
     snprintf(
         command,
         128,
         "https://www.flipsocial.net/api/feed/40/%s/extended/",
         app_instance->login_username_logged_out);
-    bool success =
-        flipper_http_get_request_with_headers(command, jsmn("Content-Type", "application/json"));
+    bool success = flipper_http_get_request_with_headers(command, headers);
+    free(headers);
     if(!success) {
         FURI_LOG_E(TAG, "Failed to send HTTP request for feed");
+        fhttp.state = ISSUE;
         return false;
     }
     fhttp.state = RECEIVING;
     return true;
 }
 
-static bool flip_social_parse_json_feed() {
-    if(fhttp.received_data == NULL) {
-        FURI_LOG_E(TAG, "No data received.");
+bool flip_social_parse_json_feed() {
+    // load the received data from the saved file
+    FuriString* feed_data = flipper_http_load_from_file(fhttp.file_path);
+    if(feed_data == NULL) {
+        FURI_LOG_E(TAG, "Failed to load received data from file.");
+        return false;
+    }
+    char* data_cstr = (char*)furi_string_get_cstr(feed_data);
+    if(data_cstr == NULL) {
+        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
+        furi_string_free(feed_data);
         return false;
     }
 
@@ -124,7 +141,7 @@ static bool flip_social_parse_json_feed() {
         return false;
     }
     // Remove newlines
-    char* pos = fhttp.received_data;
+    char* pos = data_cstr;
     while((pos = strchr(pos, '\n')) != NULL) {
         *pos = ' ';
     }
@@ -135,7 +152,7 @@ static bool flip_social_parse_json_feed() {
     // Iterate through the feed array
     for(int i = 0; i < MAX_FEED_ITEMS; i++) {
         // Parse each item in the array
-        char* item = get_json_array_value("feed", i, fhttp.received_data, MAX_TOKENS);
+        char* item = get_json_array_value("feed", i, data_cstr, MAX_TOKENS);
         if(item == NULL) {
             break;
         }
@@ -159,11 +176,8 @@ static bool flip_social_parse_json_feed() {
         }
 
         // Safely copy strings with bounds checking
-        strncpy(flip_social_feed->usernames[i], username, MAX_USER_LENGTH - 1);
-        flip_social_feed->usernames[i][MAX_USER_LENGTH - 1] = '\0';
-
-        strncpy(flip_social_feed->messages[i], message, MAX_MESSAGE_LENGTH - 1);
-        flip_social_feed->messages[i][MAX_MESSAGE_LENGTH - 1] = '\0';
+        snprintf(flip_social_feed->usernames[i], MAX_USER_LENGTH, "%s", username);
+        snprintf(flip_social_feed->messages[i], MAX_MESSAGE_LENGTH, "%s", message);
 
         // Store boolean and integer values
         flip_social_feed->is_flipped[i] = strstr(flipped, "true") != NULL;
@@ -181,7 +195,7 @@ static bool flip_social_parse_json_feed() {
         free(id);
     }
 
+    furi_string_free(feed_data);
+    free(data_cstr);
     return flip_social_feed->count > 0;
 }
-
-#endif // FLIP_SOCIAL_FEED_H

+ 11 - 0
flip_social/feed/flip_social_feed.h

@@ -0,0 +1,11 @@
+#ifndef FLIP_SOCIAL_FEED_H
+#define FLIP_SOCIAL_FEED_H
+#include "flip_social.h"
+#include <callback/flip_social_callback.h>
+
+bool flip_social_temp_feed();
+FlipSocialFeed* flip_social_feed_alloc();
+void flip_social_free_feed();
+bool flip_social_get_feed();
+bool flip_social_parse_json_feed();
+#endif

+ 22 - 16
flip_social/flip_social_free.h → flip_social/flip_social.c

@@ -1,6 +1,24 @@
-// flip_social_free.h
-#ifndef FLIP_SOCIAL_FREE_H
-#define FLIP_SOCIAL_FREE_H
+#include "flip_social.h"
+
+FlipSocialFeed* flip_social_feed = NULL; // Store the feed
+FlipSocialModel* flip_social_friends = NULL; // Store the friends
+FlipSocialModel2* flip_social_message_users =
+    NULL; // Store the users that have sent messages to the logged in user
+FlipSocialModel* flip_social_explore = NULL; // Store the users to explore
+FlipSocialMessage* flip_social_messages =
+    NULL; // Store the messages between the logged in user and the selected user
+
+FlipSocialApp* app_instance = NULL;
+
+bool flip_social_sent_login_request = false;
+bool flip_social_sent_register_request = false;
+bool flip_social_login_success = false;
+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;
 
 /**
  * @brief Function to free the resources used by FlipSocialApp.
@@ -8,7 +26,7 @@
  * @param app The FlipSocialApp object to free.
  * @return void
  */
-static void flip_social_app_free(FlipSocialApp* app) {
+void flip_social_app_free(FlipSocialApp* app) {
     if(!app) {
         FURI_LOG_E(TAG, "FlipSocialApp is NULL");
         return;
@@ -227,21 +245,9 @@ static void flip_social_app_free(FlipSocialApp* app) {
     if(app->input_event && app->input_event_queue)
         furi_pubsub_unsubscribe(app->input_event_queue, app->input_event);
 
-    // free received_data
-    if(fhttp.received_data) free(fhttp.received_data);
-
-    // free playlist and explore page
-    flip_social_free_explore();
-    flip_social_free_feed();
-    flip_social_free_friends();
-    flip_social_free_message_users();
-    flip_social_free_messages();
-
     // DeInit UART
     flipper_http_deinit();
 
     // Free the app structure
     if(app_instance) free(app_instance);
 }
-
-#endif // FLIP_SOCIAL_FREE_H

+ 28 - 36
flip_social/flip_social_e.h → flip_social/flip_social.h

@@ -1,10 +1,10 @@
-// flip_social_e.h
-#ifndef FLIP_SOCIAL_E
-#define FLIP_SOCIAL_E
-#include <easy_flipper.h>
+#ifndef FLIP_SOCIAL_H
+#define FLIP_SOCIAL_H
+
+#include <easy_flipper/easy_flipper.h>
 #include <dialogs/dialogs.h>
 #include <storage/storage.h>
-#include <flipper_http.h>
+#include <flipper_http/flipper_http.h>
 #include <input/input.h>
 #include <flip_social_icons.h>
 #define TAG "FlipSocial"
@@ -20,6 +20,10 @@
 #define MAX_MESSAGE_USERS      20 // 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"
+#define PRE_SAVED_MESSAGES_PATH \
+    STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/pre_saved_messages.txt"
+
 // Define the submenu items for our Hello World application
 typedef enum {
     FlipSocialSubmenuLoggedOutIndexLogin, // click to go to the login screen
@@ -51,20 +55,11 @@ typedef enum {
         300, // click to select a user to message
 } FlipSocialSubmenuIndex;
 
-typedef enum {
-    ActionNone,
-    ActionBack,
-    ActionNext,
-    ActionPrev,
-    ActionFlip,
-} Action;
-
-static Action action = ActionNone;
-
 // Define the ScriptPlaylist structure
 typedef struct {
     char* messages[MAX_PRE_SAVED_MESSAGES];
     size_t count;
+    size_t index;
 } PreSavedPlaylist;
 
 typedef struct {
@@ -309,29 +304,26 @@ typedef struct {
         message_user_choice_logged_in_temp_buffer_size; // Size of the message to send to the selected user temporary buffer
 } FlipSocialApp;
 
-static FlipSocialFeed* flip_social_feed = NULL; // Store the feed
-static FlipSocialModel* flip_social_friends = NULL; // Store the friends
-static FlipSocialModel2* flip_social_message_users =
-    NULL; // Store the users that have sent messages to the logged in user
-static FlipSocialModel* flip_social_explore = NULL; // Store the users to explore
-static FlipSocialMessage* flip_social_messages =
-    NULL; // Store the messages between the logged in user and the selected user
-
-// include strndup (otherwise NULL pointer dereference)
-char* strndup(const char* s, size_t n) {
-    char* result;
-    size_t len = strlen(s);
+void flip_social_app_free(FlipSocialApp* app);
 
-    if(n < len) len = n;
+extern FlipSocialFeed* flip_social_feed; // Store the feed
+extern FlipSocialModel* flip_social_friends; // Store the friends
+extern FlipSocialModel2*
+    flip_social_message_users; // Store the users that have sent messages to the logged in user
+extern FlipSocialModel* flip_social_explore; // Store the users to explore
+extern FlipSocialMessage*
+    flip_social_messages; // Store the messages between the logged in user and the selected user
 
-    result = (char*)malloc(len + 1);
-    if(!result) return NULL;
+extern FlipSocialApp* app_instance;
 
-    result[len] = '\0';
-    return (char*)memcpy(result, s, len);
-}
+extern bool flip_social_sent_login_request;
+extern bool flip_social_sent_register_request;
+extern bool flip_social_login_success;
+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;
 
-static FlipSocialApp* app_instance = NULL;
-static void flip_social_logged_in_compose_pre_save_updated(void* context);
-static void flip_social_callback_submenu_choices(void* context, uint32_t index);
 #endif

+ 3 - 14
flip_social/flip_social_storage.h → flip_social/flip_storage/flip_social_storage.c

@@ -1,13 +1,4 @@
-// flip_social_storage.h
-#ifndef FLIP_SOCIAL_STORAGE_H
-#define FLIP_SOCIAL_STORAGE_H
-
-#include <furi.h>
-#include <storage/storage.h>
-
-#define SETTINGS_PATH STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/settings.bin"
-#define PRE_SAVED_MESSAGES_PATH \
-    STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/pre_saved_messages.txt"
+#include "flip_social_storage.h"
 
 // Function to save the playlist
 void save_playlist(const PreSavedPlaylist* playlist) {
@@ -136,7 +127,7 @@ bool load_playlist(PreSavedPlaylist* playlist) {
 
     return true;
 }
-static void save_settings(
+void save_settings(
     const char* ssid,
     const char* password,
     const char* login_username_logged_out,
@@ -221,7 +212,7 @@ static void save_settings(
     furi_record_close(RECORD_STORAGE);
 }
 
-static bool load_settings(
+bool load_settings(
     char* ssid,
     size_t ssid_size,
     char* password,
@@ -353,5 +344,3 @@ static bool load_settings(
 
     return true;
 }
-
-#endif // FLIP_SOCIAL_STORAGE_H

+ 37 - 0
flip_social/flip_storage/flip_social_storage.h

@@ -0,0 +1,37 @@
+#ifndef FLIP_SOCIAL_STORAGE_H
+#define FLIP_SOCIAL_STORAGE_H
+
+#include "flip_social.h"
+
+// Function to save the playlist
+void save_playlist(const PreSavedPlaylist* playlist);
+
+// Function to load the playlist
+bool load_playlist(PreSavedPlaylist* playlist);
+
+void save_settings(
+    const char* ssid,
+    const char* password,
+    const char* login_username_logged_out,
+    const char* login_username_logged_in,
+    const char* login_password_logged_out,
+    const char* change_password_logged_in,
+    const char* is_logged_in);
+
+bool load_settings(
+    char* ssid,
+    size_t ssid_size,
+    char* password,
+    size_t password_size,
+    char* login_username_logged_out,
+    size_t username_out_size,
+    char* login_username_logged_in,
+    size_t username_in_size,
+    char* login_password_logged_out,
+    size_t password_out_size,
+    char* change_password_logged_in,
+    size_t change_password_size,
+    char* is_logged_in,
+    size_t is_logged_in_size);
+
+#endif

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 517 - 365
flip_social/flipper_http/flipper_http.c


+ 360 - 0
flip_social/flipper_http/flipper_http.h

@@ -0,0 +1,360 @@
+// flipper_http.h
+#ifndef FLIPPER_HTTP_H
+#define FLIPPER_HTTP_H
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_gpio.h>
+#include <furi_hal_serial.h>
+#include <storage/storage.h>
+
+// STORAGE_EXT_PATH_PREFIX is defined in the Furi SDK as /ext
+
+#define HTTP_TAG               "FlipSocial" // change this to your app name
+#define http_tag               "flip_social" // change this to your app id
+#define UART_CH                (FuriHalSerialIdUsart) // UART channel
+#define TIMEOUT_DURATION_TICKS (5 * 1000) // 5 seconds
+#define BAUDRATE               (115200) // UART baudrate
+#define RX_BUF_SIZE            1024 // UART RX buffer size
+#define RX_LINE_BUFFER_SIZE    8192 // UART RX line buffer size (increase for large responses)
+#define MAX_FILE_SHOW          8192 // Maximum data from file to show
+#define FILE_BUFFER_SIZE       512 // File buffer size
+
+// Forward declaration for callback
+typedef void (*FlipperHTTP_Callback)(const char* line, void* context);
+
+// State variable to track the UART state
+typedef enum {
+    INACTIVE, // Inactive state
+    IDLE, // Default state
+    RECEIVING, // Receiving data
+    SENDING, // Sending data
+    ISSUE, // Issue with connection
+} SerialState;
+
+// Event Flags for UART Worker Thread
+typedef enum {
+    WorkerEvtStop = (1 << 0),
+    WorkerEvtRxDone = (1 << 1),
+} WorkerEvtFlags;
+
+// FlipperHTTP Structure
+typedef struct {
+    FuriStreamBuffer* flipper_http_stream; // Stream buffer for UART communication
+    FuriHalSerialHandle* serial_handle; // Serial handle for UART communication
+    FuriThread* rx_thread; // Worker thread for UART
+    FuriThreadId rx_thread_id; // Worker thread ID
+    FlipperHTTP_Callback handle_rx_line_cb; // Callback for received lines
+    void* callback_context; // Context for the callback
+    SerialState state; // State of the UART
+
+    // variable to store the last received data from the UART
+    char* last_response;
+    char file_path[256]; // Path to save the received data
+
+    // Timer-related members
+    FuriTimer* get_timeout_timer; // Timer for HTTP request timeout
+
+    bool started_receiving_get; // Indicates if a GET request has started
+    bool just_started_get; // Indicates if GET data reception has just started
+
+    bool started_receiving_post; // Indicates if a POST request has started
+    bool just_started_post; // Indicates if POST data reception has just started
+
+    bool started_receiving_put; // Indicates if a PUT request has started
+    bool just_started_put; // Indicates if PUT data reception has just started
+
+    bool started_receiving_delete; // Indicates if a DELETE request has started
+    bool just_started_delete; // Indicates if DELETE data reception has just started
+
+    // Buffer to hold the raw bytes received from the UART
+    uint8_t* received_bytes;
+    size_t received_bytes_len; // Length of the received bytes
+    bool is_bytes_request; // Flag to indicate if the request is for bytes
+    bool save_bytes; // Flag to save the received data to a file
+    bool save_received_data; // Flag to save the received data to a file
+} FlipperHTTP;
+
+extern FlipperHTTP fhttp;
+// Global static array for the line buffer
+extern char rx_line_buffer[RX_LINE_BUFFER_SIZE];
+extern uint8_t file_buffer[FILE_BUFFER_SIZE];
+
+// fhttp.last_response holds the last received data from the UART
+
+// Function to append received data to file
+// make sure to initialize the file path before calling this function
+bool flipper_http_append_to_file(
+    const void* data,
+    size_t data_size,
+    bool start_new_file,
+    char* file_path);
+
+FuriString* flipper_http_load_from_file(char* file_path);
+
+// UART worker thread
+/**
+ * @brief      Worker thread to handle UART data asynchronously.
+ * @return     0
+ * @param      context   The context to pass to the callback.
+ * @note       This function will handle received data asynchronously via the callback.
+ */
+// UART worker thread
+int32_t flipper_http_worker(void* context);
+
+// Timer callback function
+/**
+ * @brief      Callback function for the GET timeout timer.
+ * @return     0
+ * @param      context   The context to pass to the callback.
+ * @note       This function will be called when the GET request times out.
+ */
+void get_timeout_timer_callback(void* context);
+
+// UART RX Handler Callback (Interrupt Context)
+/**
+ * @brief      A private callback function to handle received data asynchronously.
+ * @return     void
+ * @param      handle    The UART handle.
+ * @param      event     The event type.
+ * @param      context   The context to pass to the callback.
+ * @note       This function will handle received data asynchronously via the callback.
+ */
+void _flipper_http_rx_callback(
+    FuriHalSerialHandle* handle,
+    FuriHalSerialRxEvent event,
+    void* context);
+
+// UART initialization function
+/**
+ * @brief      Initialize UART.
+ * @return     true if the UART was initialized successfully, false otherwise.
+ * @param      callback  The callback function to handle received data (ex. flipper_http_rx_callback).
+ * @param      context   The context to pass to the callback.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_init(FlipperHTTP_Callback callback, void* context);
+
+// Deinitialize UART
+/**
+ * @brief      Deinitialize UART.
+ * @return     void
+ * @note       This function will stop the asynchronous RX, release the serial handle, and free the resources.
+ */
+void flipper_http_deinit();
+
+// Function to send data over UART with newline termination
+/**
+ * @brief      Send data over UART with newline termination.
+ * @return     true if the data was sent successfully, false otherwise.
+ * @param      data  The data to send over UART.
+ * @note       The data will be sent over UART with a newline character appended.
+ */
+bool flipper_http_send_data(const char* data);
+
+// Function to send a PING request
+/**
+ * @brief      Send a PING request to check if the Wifi Dev Board is connected.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ * @note       This is best used to check if the Wifi Dev Board is connected.
+ * @note       The state will remain INACTIVE until a PONG is received.
+ */
+bool flipper_http_ping();
+
+// Function to list available commands
+/**
+ * @brief      Send a command to list available commands.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_list_commands();
+
+// Function to turn on the LED
+/**
+ * @brief      Allow the LED to display while processing.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_led_on();
+
+// Function to turn off the LED
+/**
+ * @brief      Disable the LED from displaying while processing.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_led_off();
+
+// Function to parse JSON data
+/**
+ * @brief      Parse JSON data.
+ * @return     true if the JSON data was parsed successfully, false otherwise.
+ * @param      key       The key to parse from the JSON data.
+ * @param      json_data The JSON data to parse.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_parse_json(const char* key, const char* json_data);
+
+// Function to parse JSON array data
+/**
+ * @brief      Parse JSON array data.
+ * @return     true if the JSON array data was parsed successfully, false otherwise.
+ * @param      key       The key to parse from the JSON array data.
+ * @param      index     The index to parse from the JSON array data.
+ * @param      json_data The JSON array data to parse.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_parse_json_array(const char* key, int index, const char* json_data);
+
+// Function to scan for WiFi networks
+/**
+ * @brief      Send a command to scan for WiFi networks.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_scan_wifi();
+
+// Function to save WiFi settings (returns true if successful)
+/**
+ * @brief      Send a command to save WiFi settings.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_save_wifi(const char* ssid, const char* password);
+
+// Function to get IP address of WiFi Devboard
+/**
+ * @brief      Send a command to get the IP address of the WiFi Devboard
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_ip_address();
+
+// Function to get IP address of the connected WiFi network
+/**
+ * @brief      Send a command to get the IP address of the connected WiFi network.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_ip_wifi();
+
+// Function to disconnect from WiFi (returns true if successful)
+/**
+ * @brief      Send a command to disconnect from WiFi.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_disconnect_wifi();
+
+// Function to connect to WiFi (returns true if successful)
+/**
+ * @brief      Send a command to connect to WiFi.
+ * @return     true if the request was successful, false otherwise.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_connect_wifi();
+
+// Function to send a GET request
+/**
+ * @brief      Send a GET request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the GET request to.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_get_request(const char* url);
+
+// Function to send a GET request with headers
+/**
+ * @brief      Send a GET request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the GET request to.
+ * @param      headers  The headers to send with the GET request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_get_request_with_headers(const char* url, const char* headers);
+
+// Function to send a GET request with headers and return bytes
+/**
+ * @brief      Send a GET request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the GET request to.
+ * @param      headers  The headers to send with the GET request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_get_request_bytes(const char* url, const char* headers);
+
+// Function to send a POST request with headers
+/**
+ * @brief      Send a POST request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the POST request to.
+ * @param      headers  The headers to send with the POST request.
+ * @param      data  The data to send with the POST request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_post_request_with_headers(
+    const char* url,
+    const char* headers,
+    const char* payload);
+
+// Function to send a POST request with headers and return bytes
+/**
+ * @brief      Send a POST request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the POST request to.
+ * @param      headers  The headers to send with the POST request.
+ * @param      payload  The data to send with the POST request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_post_request_bytes(const char* url, const char* headers, const char* payload);
+
+// Function to send a PUT request with headers
+/**
+ * @brief      Send a PUT request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the PUT request to.
+ * @param      headers  The headers to send with the PUT request.
+ * @param      data  The data to send with the PUT request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_put_request_with_headers(
+    const char* url,
+    const char* headers,
+    const char* payload);
+
+// Function to send a DELETE request with headers
+/**
+ * @brief      Send a DELETE request to the specified URL.
+ * @return     true if the request was successful, false otherwise.
+ * @param      url  The URL to send the DELETE request to.
+ * @param      headers  The headers to send with the DELETE request.
+ * @param      data  The data to send with the DELETE request.
+ * @note       The received data will be handled asynchronously via the callback.
+ */
+bool flipper_http_delete_request_with_headers(
+    const char* url,
+    const char* headers,
+    const char* payload);
+
+// Function to handle received data asynchronously
+/**
+ * @brief      Callback function to handle received data asynchronously.
+ * @return     void
+ * @param      line     The received line.
+ * @param      context  The context passed to the callback.
+ * @note       The received data will be handled asynchronously via the callback and handles the state of the UART.
+ */
+void flipper_http_rx_callback(const char* line, void* context);
+
+// Function to trim leading and trailing spaces and newlines from a constant string
+char* trim(const char* str);
+/**
+ * @brief Process requests and parse JSON data asynchronously
+ * @param http_request The function to send the request
+ * @param parse_json The function to parse the JSON
+ * @return true if successful, false otherwise
+ */
+bool flipper_http_process_response_async(bool (*http_request)(void), bool (*parse_json)(void));
+
+#endif // FLIPPER_HTTP_H

+ 46 - 26
flip_social/flip_social_friends.h → flip_social/friends/flip_social_friends.c

@@ -1,7 +1,6 @@
-#ifndef FLIP_SOCIAL_FRIENDS
-#define FLIP_SOCIAL_FRIENDS
+#include "flip_social_friends.h"
 
-static FlipSocialModel* flip_social_friends_alloc() {
+FlipSocialModel* flip_social_friends_alloc() {
     // Allocate memory for each username only if not already allocated
     FlipSocialModel* friends = malloc(sizeof(FlipSocialModel));
     for(size_t i = 0; i < MAX_FRIENDS; i++) {
@@ -16,28 +15,37 @@ static FlipSocialModel* flip_social_friends_alloc() {
     return friends;
 }
 
-static void flip_social_free_friends() {
+void flip_social_free_friends() {
     if(!flip_social_friends) {
         FURI_LOG_E(TAG, "Friends model is NULL");
         return;
     }
     for(int i = 0; i < flip_social_friends->count; i++) {
-        free(flip_social_friends->usernames[i]);
+        if(flip_social_friends->usernames[i]) {
+            free(flip_social_friends->usernames[i]);
+        }
     }
 }
 
 // for now we're just listing the current users
 // as the feed is upgraded, then we can port more to the friends view
-static bool flip_social_get_friends() {
+bool flip_social_get_friends() {
     // will return true unless the devboard is not connected
     char url[100];
+    snprintf(
+        fhttp.file_path,
+        sizeof(fhttp.file_path),
+        STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/friends.txt");
+
+    fhttp.save_received_data = true;
+    char* headers = jsmn("Content-Type", "application/json");
     snprintf(
         url,
         100,
         "https://www.flipsocial.net/api/user/friends/%s/",
         app_instance->login_username_logged_in);
-    bool success =
-        flipper_http_get_request_with_headers(url, jsmn("Content-Type", "application/json"));
+    bool success = flipper_http_get_request_with_headers(url, headers);
+    free(headers);
     if(!success) {
         FURI_LOG_E(TAG, "Failed to send HTTP request for friends");
         return false;
@@ -46,7 +54,7 @@ static bool flip_social_get_friends() {
     return true;
 }
 
-static bool flip_social_update_friends() {
+bool flip_social_update_friends() {
     if(!app_instance->submenu_friends) {
         FURI_LOG_E(TAG, "Friends submenu is NULL");
         return false;
@@ -69,9 +77,17 @@ static bool flip_social_update_friends() {
     return true;
 }
 
-static bool flip_social_parse_json_friends() {
-    if(fhttp.received_data == NULL) {
-        FURI_LOG_E(TAG, "No data received.");
+bool flip_social_parse_json_friends() {
+    // load the received data from the saved file
+    FuriString* friend_data = flipper_http_load_from_file(fhttp.file_path);
+    if(friend_data == NULL) {
+        FURI_LOG_E(TAG, "Failed to load received data from file.");
+        return false;
+    }
+    char* data_cstr = (char*)furi_string_get_cstr(friend_data);
+    if(data_cstr == NULL) {
+        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
+        furi_string_free(friend_data);
         return false;
     }
 
@@ -79,11 +95,13 @@ static bool flip_social_parse_json_friends() {
     flip_social_friends = flip_social_friends_alloc();
     if(flip_social_friends == NULL) {
         FURI_LOG_E(TAG, "Failed to allocate memory for friends usernames.");
+        furi_string_free(friend_data);
+        free(data_cstr);
         return false;
     }
 
     // Remove newlines
-    char* pos = fhttp.received_data;
+    char* pos = data_cstr;
     while((pos = strchr(pos, '\n')) != NULL) {
         *pos = ' ';
     }
@@ -92,9 +110,11 @@ static bool flip_social_parse_json_friends() {
     flip_social_friends->count = 0;
 
     // Extract the users array from the JSON
-    char* json_users = get_json_value("friends", fhttp.received_data, MAX_TOKENS);
+    char* json_users = get_json_value("friends", data_cstr, MAX_TOKENS);
     if(json_users == NULL) {
         FURI_LOG_E(TAG, "Failed to parse friends array.");
+        furi_string_free(friend_data);
+        free(data_cstr);
         return false;
     }
 
@@ -109,12 +129,11 @@ static bool flip_social_parse_json_friends() {
         if(*(end - 1) == '"') *(end - 1) = '\0';
 
         // Copy username to pre-allocated memory
-        strncpy(
+        snprintf(
             flip_social_friends->usernames[flip_social_friends->count],
-            start,
-            MAX_USER_LENGTH - 1);
-        flip_social_friends->usernames[flip_social_friends->count][MAX_USER_LENGTH - 1] =
-            '\0'; // Ensure null termination
+            MAX_USER_LENGTH,
+            "%s",
+            start);
         flip_social_friends->count++;
         start = end + 1;
     }
@@ -125,18 +144,19 @@ static bool flip_social_parse_json_friends() {
         if(*(start + strlen(start) - 1) == ']') *(start + strlen(start) - 1) = '\0';
         if(*(start + strlen(start) - 1) == '"') *(start + strlen(start) - 1) = '\0';
 
-        strncpy(
+        snprintf(
             flip_social_friends->usernames[flip_social_friends->count],
-            start,
-            MAX_USER_LENGTH - 1);
-        flip_social_friends->usernames[flip_social_friends->count][MAX_USER_LENGTH - 1] =
-            '\0'; // Ensure null termination
+            MAX_USER_LENGTH,
+            "%s",
+            start);
         flip_social_friends->count++;
     }
 
     // Add submenu items for the friends
     if(!flip_social_update_friends()) {
         FURI_LOG_E(TAG, "Failed to update friends submenu");
+        furi_string_free(friend_data);
+        free(data_cstr);
         return false;
     }
 
@@ -144,7 +164,7 @@ static bool flip_social_parse_json_friends() {
     free(json_users);
     free(start);
     free(end);
-
+    furi_string_free(friend_data);
+    free(data_cstr);
     return true;
 }
-#endif // FLIP_SOCIAL_FRIENDS

+ 11 - 0
flip_social/friends/flip_social_friends.h

@@ -0,0 +1,11 @@
+#ifndef FLIP_SOCIAL_FRIENDS_H
+#define FLIP_SOCIAL_FRIENDS_H
+#include "flip_social.h"
+#include <callback/flip_social_callback.h>
+
+FlipSocialModel* flip_social_friends_alloc();
+void flip_social_free_friends();
+bool flip_social_get_friends();
+bool flip_social_update_friends();
+bool flip_social_parse_json_friends();
+#endif

+ 26 - 142
flip_social/jsmn.h → flip_social/jsmn/jsmn.c

@@ -3,113 +3,20 @@
  *
  * Copyright (c) 2010 Serge Zaitsev
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * [License text continues...]
  */
-#ifndef JSMN_H
-#define JSMN_H
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef JSMN_STATIC
-#define JSMN_API static
-#else
-#define JSMN_API extern
-#endif
-
-/**
-   * JSON type identifier. Basic types are:
-   * 	o Object
-   * 	o Array
-   * 	o String
-   * 	o Other primitive: number, boolean (true/false) or null
-   */
-typedef enum {
-    JSMN_UNDEFINED = 0,
-    JSMN_OBJECT = 1 << 0,
-    JSMN_ARRAY = 1 << 1,
-    JSMN_STRING = 1 << 2,
-    JSMN_PRIMITIVE = 1 << 3
-} jsmntype_t;
-
-enum jsmnerr {
-    /* Not enough tokens were provided */
-    JSMN_ERROR_NOMEM = -1,
-    /* Invalid character inside JSON string */
-    JSMN_ERROR_INVAL = -2,
-    /* The string is not a full JSON packet, more bytes expected */
-    JSMN_ERROR_PART = -3
-};
-
-/**
-   * JSON token description.
-   * type		type (object, array, string etc.)
-   * start	start position in JSON data string
-   * end		end position in JSON data string
-   */
-typedef struct jsmntok {
-    jsmntype_t type;
-    int start;
-    int end;
-    int size;
-#ifdef JSMN_PARENT_LINKS
-    int parent;
-#endif
-} jsmntok_t;
-
-/**
-   * JSON parser. Contains an array of token blocks available. Also stores
-   * the string being parsed now and current position in that string.
-   */
-typedef struct jsmn_parser {
-    unsigned int pos; /* offset in the JSON string */
-    unsigned int toknext; /* next token to allocate */
-    int toksuper; /* superior token node, e.g. parent object or array */
-} jsmn_parser;
 
-/**
-   * Create JSON parser over an array of tokens
-   */
-JSMN_API void jsmn_init(jsmn_parser* parser);
-
-/**
-   * Run JSON parser. It parses a JSON data string into and array of tokens, each
-   * describing
-   * a single JSON object.
-   */
-JSMN_API int jsmn_parse(
-    jsmn_parser* parser,
-    const char* js,
-    const size_t len,
-    jsmntok_t* tokens,
-    const unsigned int num_tokens);
+#include <jsmn/jsmn.h>
+#include <stdlib.h>
+#include <string.h>
 
-#ifndef JSMN_HEADER
 /**
-   * Allocates a fresh unused token from the token pool.
-   */
+ * Allocates a fresh unused token from the token pool.
+ */
 static jsmntok_t*
     jsmn_alloc_token(jsmn_parser* parser, jsmntok_t* tokens, const size_t num_tokens) {
     jsmntok_t* tok;
+
     if(parser->toknext >= num_tokens) {
         return NULL;
     }
@@ -123,8 +30,8 @@ static jsmntok_t*
 }
 
 /**
-   * Fills token type and boundaries.
-   */
+ * Fills token type and boundaries.
+ */
 static void
     jsmn_fill_token(jsmntok_t* token, const jsmntype_t type, const int start, const int end) {
     token->type = type;
@@ -134,8 +41,8 @@ static void
 }
 
 /**
-   * Fills next available token with JSON primitive.
-   */
+ * Fills next available token with JSON primitive.
+ */
 static int jsmn_parse_primitive(
     jsmn_parser* parser,
     const char* js,
@@ -195,8 +102,8 @@ found:
 }
 
 /**
-   * Fills next token with JSON string.
-   */
+ * Fills next token with JSON string.
+ */
 static int jsmn_parse_string(
     jsmn_parser* parser,
     const char* js,
@@ -272,9 +179,18 @@ static int jsmn_parse_string(
 }
 
 /**
-   * Parse JSON string and fill tokens.
-   */
-JSMN_API int jsmn_parse(
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser* parser) {
+    parser->pos = 0;
+    parser->toknext = 0;
+    parser->toksuper = -1;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int jsmn_parse(
     jsmn_parser* parser,
     const char* js,
     const size_t len,
@@ -464,36 +380,6 @@ JSMN_API int jsmn_parse(
     return count;
 }
 
-/**
-   * Creates a new parser based over a given buffer with an array of tokens
-   * available.
-   */
-JSMN_API void jsmn_init(jsmn_parser* parser) {
-    parser->pos = 0;
-    parser->toknext = 0;
-    parser->toksuper = -1;
-}
-
-#endif /* JSMN_HEADER */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* JSMN_H */
-
-#ifndef JB_JSMN_EDIT
-#define JB_JSMN_EDIT
-/* Added in by JBlanked on 2024-10-16 for use in Flipper Zero SDK*/
-
-#include <string.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdio.h>
-#include <string.h>
-#include <furi.h>
-
 // Helper function to create a JSON object
 char* jsmn(const char* key, const char* value) {
     int length = strlen(key) + strlen(value) + 8; // Calculate required length
@@ -514,7 +400,7 @@ int jsoneq(const char* json, jsmntok_t* tok, const char* s) {
     return -1;
 }
 
-// return the value of the key in the JSON data
+// Return the value of the key in the JSON data
 char* get_json_value(char* key, char* json_data, uint32_t max_tokens) {
     // Parse the JSON feed
     if(json_data != NULL) {
@@ -778,5 +664,3 @@ char** get_json_array_values(char* key, char* json_data, uint32_t max_tokens, in
     free(array_str);
     return values;
 }
-
-#endif /* JB_JSMN_EDIT */

+ 131 - 0
flip_social/jsmn/jsmn.h

@@ -0,0 +1,131 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2010 Serge Zaitsev
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * [License text continues...]
+ */
+
+#ifndef JSMN_H
+#define JSMN_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef JSMN_STATIC
+#define JSMN_API static
+#else
+#define JSMN_API extern
+#endif
+
+/**
+     * JSON type identifier. Basic types are:
+     * 	o Object
+     * 	o Array
+     * 	o String
+     * 	o Other primitive: number, boolean (true/false) or null
+     */
+typedef enum {
+    JSMN_UNDEFINED = 0,
+    JSMN_OBJECT = 1 << 0,
+    JSMN_ARRAY = 1 << 1,
+    JSMN_STRING = 1 << 2,
+    JSMN_PRIMITIVE = 1 << 3
+} jsmntype_t;
+
+enum jsmnerr {
+    /* Not enough tokens were provided */
+    JSMN_ERROR_NOMEM = -1,
+    /* Invalid character inside JSON string */
+    JSMN_ERROR_INVAL = -2,
+    /* The string is not a full JSON packet, more bytes expected */
+    JSMN_ERROR_PART = -3
+};
+
+/**
+     * JSON token description.
+     * type		type (object, array, string etc.)
+     * start	start position in JSON data string
+     * end		end position in JSON data string
+     */
+typedef struct {
+    jsmntype_t type;
+    int start;
+    int end;
+    int size;
+#ifdef JSMN_PARENT_LINKS
+    int parent;
+#endif
+} jsmntok_t;
+
+/**
+     * JSON parser. Contains an array of token blocks available. Also stores
+     * the string being parsed now and current position in that string.
+     */
+typedef struct {
+    unsigned int pos; /* offset in the JSON string */
+    unsigned int toknext; /* next token to allocate */
+    int toksuper; /* superior token node, e.g. parent object or array */
+} jsmn_parser;
+
+/**
+     * Create JSON parser over an array of tokens
+     */
+JSMN_API void jsmn_init(jsmn_parser* parser);
+
+/**
+     * Run JSON parser. It parses a JSON data string into and array of tokens, each
+     * describing a single JSON object.
+     */
+JSMN_API int jsmn_parse(
+    jsmn_parser* parser,
+    const char* js,
+    const size_t len,
+    jsmntok_t* tokens,
+    const unsigned int num_tokens);
+
+#ifndef JSMN_HEADER
+/* Implementation has been moved to jsmn.c */
+#endif /* JSMN_HEADER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* JSMN_H */
+
+/* Custom Helper Functions */
+#ifndef JB_JSMN_EDIT
+#define JB_JSMN_EDIT
+/* Added in by JBlanked on 2024-10-16 for use in Flipper Zero SDK*/
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <furi.h>
+
+// Helper function to create a JSON object
+char* jsmn(const char* key, const char* value);
+// Helper function to compare JSON keys
+int jsoneq(const char* json, jsmntok_t* tok, const char* s);
+
+// Return the value of the key in the JSON data
+char* get_json_value(char* key, char* json_data, uint32_t max_tokens);
+
+// Revised get_json_array_value function
+char* get_json_array_value(char* key, uint32_t index, char* json_data, uint32_t max_tokens);
+
+// Revised get_json_array_values function with correct token skipping
+char** get_json_array_values(char* key, char* json_data, uint32_t max_tokens, int* num_values);
+#endif /* JB_JSMN_EDIT */

+ 112 - 64
flip_social/flip_social_messages.h → flip_social/messages/flip_social_messages.c

@@ -1,7 +1,6 @@
-#ifndef FLIP_SOCIAL_MESSAGES_H
-#define FLIP_SOCIAL_MESSAGES_H
+#include "flip_social_messages.h"
 
-static FlipSocialModel2* flip_social_messages_alloc() {
+FlipSocialModel2* flip_social_messages_alloc() {
     // Allocate memory for each username only if not already allocated
     FlipSocialModel2* users = malloc(sizeof(FlipSocialModel2));
     if(users == NULL) {
@@ -20,7 +19,7 @@ static FlipSocialModel2* flip_social_messages_alloc() {
     return users;
 }
 
-static FlipSocialMessage* flip_social_user_messages_alloc() {
+FlipSocialMessage* flip_social_user_messages_alloc() {
     // Allocate memory for each username only if not already allocated
     FlipSocialMessage* messages = malloc(sizeof(FlipSocialMessage));
     if(messages == NULL) {
@@ -46,28 +45,34 @@ static FlipSocialMessage* flip_social_user_messages_alloc() {
     return messages;
 }
 
-static void flip_social_free_message_users() {
+void flip_social_free_message_users() {
     if(flip_social_message_users == NULL) {
         FURI_LOG_E(TAG, "Message users model is NULL");
         return;
     }
     for(int i = 0; i < flip_social_message_users->count; i++) {
-        free(flip_social_message_users->usernames[i]);
+        if(flip_social_message_users->usernames[i]) {
+            free(flip_social_message_users->usernames[i]);
+        }
     }
 }
 
-static void flip_social_free_messages() {
+void flip_social_free_messages() {
     if(flip_social_messages == NULL) {
         FURI_LOG_E(TAG, "Messages model is NULL");
         return;
     }
     for(int i = 0; i < flip_social_messages->count; i++) {
-        free(flip_social_messages->usernames[i]);
-        free(flip_social_messages->messages[i]);
+        if(flip_social_messages->usernames[i]) {
+            free(flip_social_messages->usernames[i]);
+        }
+        if(flip_social_messages->messages[i]) {
+            free(flip_social_messages->messages[i]);
+        }
     }
 }
 
-static bool flip_social_update_messages_submenu() {
+bool flip_social_update_messages_submenu() {
     if(app_instance->submenu_messages == NULL) {
         FURI_LOG_E(TAG, "Submenu is NULL");
         return false;
@@ -95,7 +100,7 @@ static bool flip_social_update_messages_submenu() {
     return true;
 }
 
-static bool flip_social_update_submenu_user_choices() {
+bool flip_social_update_submenu_user_choices() {
     if(app_instance->submenu_messages_user_choices == NULL) {
         FURI_LOG_E(TAG, "Submenu is NULL");
         return false;
@@ -118,21 +123,29 @@ static bool flip_social_update_submenu_user_choices() {
 }
 
 // Get all the users that have sent messages to the logged in user
-static bool flip_social_get_message_users() {
+bool flip_social_get_message_users() {
     if(app_instance->login_username_logged_out == NULL) {
         FURI_LOG_E(TAG, "Username is NULL");
         return false;
     }
     char command[128];
+    snprintf(
+        fhttp.file_path,
+        sizeof(fhttp.file_path),
+        STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/message_users.txt");
+
+    fhttp.save_received_data = true;
+    char* headers = jsmn("Content-Type", "application/json");
     snprintf(
         command,
         128,
         "https://www.flipsocial.net/api/messages/%s/get/list/",
         app_instance->login_username_logged_out);
-    bool success =
-        flipper_http_get_request_with_headers(command, "{\"Content-Type\":\"application/json\"}");
+    bool success = flipper_http_get_request_with_headers(command, headers);
+    free(headers);
     if(!success) {
         FURI_LOG_E(TAG, "Failed to send HTTP request for messages");
+        fhttp.state = ISSUE;
         return false;
     }
     fhttp.state = RECEIVING;
@@ -140,22 +153,30 @@ static bool flip_social_get_message_users() {
 }
 
 // Get all the messages between the logged in user and the selected user
-static bool flip_social_get_messages_with_user() {
+bool flip_social_get_messages_with_user() {
     if(app_instance->login_username_logged_out == NULL) {
         FURI_LOG_E(TAG, "Username is NULL");
         return false;
     }
     char command[128];
+    snprintf(
+        fhttp.file_path,
+        sizeof(fhttp.file_path),
+        STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social/messages.txt");
+
+    fhttp.save_received_data = true;
+    char* headers = jsmn("Content-Type", "application/json");
     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]);
-    bool success =
-        flipper_http_get_request_with_headers(command, "{\"Content-Type\":\"application/json\"}");
+    bool success = flipper_http_get_request_with_headers(command, headers);
+    free(headers);
     if(!success) {
         FURI_LOG_E(TAG, "Failed to send HTTP request for messages");
+        fhttp.state = ISSUE;
         return false;
     }
     fhttp.state = RECEIVING;
@@ -163,9 +184,17 @@ static bool flip_social_get_messages_with_user() {
 }
 
 // Parse the users that have sent messages to the logged-in user
-static bool flip_social_parse_json_message_users() {
-    if(fhttp.received_data == NULL) {
-        FURI_LOG_E(TAG, "No data received.");
+bool flip_social_parse_json_message_users() {
+    // load the received data from the saved file
+    FuriString* message_data = flipper_http_load_from_file(fhttp.file_path);
+    if(message_data == NULL) {
+        FURI_LOG_E(TAG, "Failed to load received data from file.");
+        return false;
+    }
+    char* data_cstr = (char*)furi_string_get_cstr(message_data);
+    if(data_cstr == NULL) {
+        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
+        furi_string_free(message_data);
         return false;
     }
 
@@ -173,11 +202,13 @@ static bool flip_social_parse_json_message_users() {
     flip_social_message_users = flip_social_messages_alloc();
     if(flip_social_message_users == NULL) {
         FURI_LOG_E(TAG, "Failed to allocate memory for message users.");
+        furi_string_free(message_data);
+        free(data_cstr);
         return false;
     }
 
     // Remove newlines
-    char* pos = fhttp.received_data;
+    char* pos = data_cstr;
     while((pos = strchr(pos, '\n')) != NULL) {
         *pos = ' ';
     }
@@ -186,9 +217,11 @@ static bool flip_social_parse_json_message_users() {
     flip_social_message_users->count = 0;
 
     // Extract the users array from the JSON
-    char* json_users = get_json_value("users", fhttp.received_data, MAX_TOKENS);
+    char* json_users = get_json_value("users", data_cstr, MAX_TOKENS);
     if(json_users == NULL) {
         FURI_LOG_E(TAG, "Failed to parse users array.");
+        furi_string_free(message_data);
+        free(data_cstr);
         return false;
     }
 
@@ -204,13 +237,11 @@ static bool flip_social_parse_json_message_users() {
         if(*(end - 1) == '"') *(end - 1) = '\0';
 
         // Copy username to pre-allocated memory
-        strncpy(
+        snprintf(
             flip_social_message_users->usernames[flip_social_message_users->count],
-            start,
-            MAX_USER_LENGTH - 1);
-        flip_social_message_users
-            ->usernames[flip_social_message_users->count][MAX_USER_LENGTH - 1] =
-            '\0'; // Ensure null termination
+            MAX_USER_LENGTH,
+            "%s",
+            start);
         flip_social_message_users->count++;
         start = end + 1;
     }
@@ -221,13 +252,11 @@ static bool flip_social_parse_json_message_users() {
         if(*(start + strlen(start) - 1) == ']') *(start + strlen(start) - 1) = '\0';
         if(*(start + strlen(start) - 1) == '"') *(start + strlen(start) - 1) = '\0';
 
-        strncpy(
+        snprintf(
             flip_social_message_users->usernames[flip_social_message_users->count],
-            start,
-            MAX_USER_LENGTH - 1);
-        flip_social_message_users
-            ->usernames[flip_social_message_users->count][MAX_USER_LENGTH - 1] =
-            '\0'; // Ensure null termination
+            MAX_USER_LENGTH,
+            "%s",
+            start);
         flip_social_message_users->count++;
     }
 
@@ -238,14 +267,23 @@ static bool flip_social_parse_json_message_users() {
     free(json_users);
     free(start);
     free(end);
-
+    furi_string_free(message_data);
+    free(data_cstr);
     return true;
 }
 
 // Parse the users that the logged in user can message
-static bool flip_social_parse_json_message_user_choices() {
-    if(fhttp.received_data == NULL) {
-        FURI_LOG_E(TAG, "No data received.");
+bool flip_social_parse_json_message_user_choices() {
+    // load the received data from the saved file
+    FuriString* user_data = flipper_http_load_from_file(fhttp.file_path);
+    if(user_data == NULL) {
+        FURI_LOG_E(TAG, "Failed to load received data from file.");
+        return false;
+    }
+    char* data_cstr = (char*)furi_string_get_cstr(user_data);
+    if(data_cstr == NULL) {
+        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
+        furi_string_free(user_data);
         return false;
     }
 
@@ -253,11 +291,13 @@ static bool flip_social_parse_json_message_user_choices() {
     flip_social_explore = flip_social_explore_alloc();
     if(flip_social_explore == NULL) {
         FURI_LOG_E(TAG, "Failed to allocate memory for explore usernames.");
+        furi_string_free(user_data);
+        free(data_cstr);
         return false;
     }
 
     // Remove newlines
-    char* pos = fhttp.received_data;
+    char* pos = data_cstr;
     while((pos = strchr(pos, '\n')) != NULL) {
         *pos = ' ';
     }
@@ -266,9 +306,11 @@ static bool flip_social_parse_json_message_user_choices() {
     flip_social_explore->count = 0;
 
     // Extract the users array from the JSON
-    char* json_users = get_json_value("users", fhttp.received_data, MAX_TOKENS);
+    char* json_users = get_json_value("users", data_cstr, MAX_TOKENS);
     if(json_users == NULL) {
         FURI_LOG_E(TAG, "Failed to parse users array.");
+        furi_string_free(user_data);
+        free(data_cstr);
         return false;
     }
 
@@ -283,12 +325,11 @@ static bool flip_social_parse_json_message_user_choices() {
         if(*(end - 1) == '"') *(end - 1) = '\0';
 
         // Copy username to pre-allocated memory
-        strncpy(
+        snprintf(
             flip_social_explore->usernames[flip_social_explore->count],
-            start,
-            MAX_USER_LENGTH - 1);
-        flip_social_explore->usernames[flip_social_explore->count][MAX_USER_LENGTH - 1] =
-            '\0'; // Ensure null termination
+            MAX_USER_LENGTH,
+            "%s",
+            start);
         flip_social_explore->count++;
         start = end + 1;
     }
@@ -299,12 +340,11 @@ static bool flip_social_parse_json_message_user_choices() {
         if(*(start + strlen(start) - 1) == ']') *(start + strlen(start) - 1) = '\0';
         if(*(start + strlen(start) - 1) == '"') *(start + strlen(start) - 1) = '\0';
 
-        strncpy(
+        snprintf(
             flip_social_explore->usernames[flip_social_explore->count],
-            start,
-            MAX_USER_LENGTH - 1);
-        flip_social_explore->usernames[flip_social_explore->count][MAX_USER_LENGTH - 1] =
-            '\0'; // Ensure null termination
+            MAX_USER_LENGTH,
+            "%s",
+            start);
         flip_social_explore->count++;
     }
 
@@ -315,14 +355,23 @@ static bool flip_social_parse_json_message_user_choices() {
     free(json_users);
     free(start);
     free(end);
-
+    furi_string_free(user_data);
+    free(data_cstr);
     return true;
 }
 
 // parse messages between the logged in user and the selected user
-static bool flip_social_parse_json_messages() {
-    if(fhttp.received_data == NULL) {
-        FURI_LOG_E(TAG, "No data received.");
+bool flip_social_parse_json_messages() {
+    // load the received data from the saved file
+    FuriString* message_data = flipper_http_load_from_file(fhttp.file_path);
+    if(message_data == NULL) {
+        FURI_LOG_E(TAG, "Failed to load received data from file.");
+        return false;
+    }
+    char* data_cstr = (char*)furi_string_get_cstr(message_data);
+    if(data_cstr == NULL) {
+        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
+        furi_string_free(message_data);
         return false;
     }
 
@@ -330,11 +379,13 @@ static bool flip_social_parse_json_messages() {
     flip_social_messages = flip_social_user_messages_alloc();
     if(!flip_social_messages) {
         FURI_LOG_E(TAG, "Failed to allocate memory for messages.");
+        furi_string_free(message_data);
+        free(data_cstr);
         return false;
     }
 
     // Remove newlines
-    char* pos = fhttp.received_data;
+    char* pos = data_cstr;
     while((pos = strchr(pos, '\n')) != NULL) {
         *pos = ' ';
     }
@@ -345,7 +396,7 @@ static bool flip_social_parse_json_messages() {
     // Iterate through the messages array
     for(int i = 0; i < MAX_MESSAGES; i++) {
         // Parse each item in the array
-        char* item = get_json_array_value("conversations", i, fhttp.received_data, MAX_TOKENS);
+        char* item = get_json_array_value("conversations", i, data_cstr, MAX_TOKENS);
         if(item == NULL) {
             break;
         }
@@ -360,19 +411,16 @@ static bool flip_social_parse_json_messages() {
             continue;
         }
 
-        // Store parsed values
-        strncpy(flip_social_messages->usernames[i], sender, MAX_USER_LENGTH - 1);
-        flip_social_messages->usernames[i][MAX_USER_LENGTH - 1] = '\0';
-        strncpy(flip_social_messages->messages[i], content, MAX_MESSAGE_LENGTH - 1);
-        flip_social_messages->messages[i][MAX_MESSAGE_LENGTH - 1] = '\0';
+        // Store parsed values in pre-allocated memory
+        snprintf(flip_social_messages->usernames[i], MAX_USER_LENGTH, "%s", sender);
+        snprintf(flip_social_messages->messages[i], MAX_MESSAGE_LENGTH, "%s", content);
         flip_social_messages->count++;
 
         free(item);
         free(sender);
         free(content);
     }
-
+    furi_string_free(message_data);
+    free(data_cstr);
     return true;
 }
-
-#endif // FLIP_SOCIAL_MESSAGES_H

+ 27 - 0
flip_social/messages/flip_social_messages.h

@@ -0,0 +1,27 @@
+#ifndef FLIP_SOCIAL_MESSAGES_H
+#define FLIP_SOCIAL_MESSAGES_H
+
+#include "flip_social.h"
+#include <callback/flip_social_callback.h>
+#include <explore/flip_social_explore.h>
+
+FlipSocialModel2* flip_social_messages_alloc();
+FlipSocialMessage* flip_social_user_messages_alloc();
+void flip_social_free_message_users();
+void flip_social_free_messages();
+bool flip_social_update_messages_submenu();
+bool flip_social_update_submenu_user_choices();
+// Get all the users that have sent messages to the logged in user
+bool flip_social_get_message_users();
+// Get all the messages between the logged in user and the selected user
+bool flip_social_get_messages_with_user();
+// Parse the users that have sent messages to the logged-in user
+bool flip_social_parse_json_message_users();
+
+// Parse the users that the logged in user can message
+bool flip_social_parse_json_message_user_choices();
+
+// parse messages between the logged in user and the selected user
+bool flip_social_parse_json_messages();
+
+#endif

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio