Преглед изворни кода

FlipSocial - v0.4 (Direct Messaging)

jblanked пре 1 година
родитељ
комит
4a4607ef2d
20 измењених фајлова са 1284 додато и 302 уклоњено
  1. BIN
      .DS_Store
  2. 4 0
      .gitignore
  3. 36 23
      README.md
  4. 1 0
      app.c
  5. 1 1
      application.fam
  6. 10 4
      assets/CHANGELOG.md
  7. 36 23
      assets/README.md
  8. 1 0
      easy_flipper.h
  9. 184 36
      flip_social_callback.h
  10. 153 35
      flip_social_draw.h
  11. 90 50
      flip_social_e.h
  12. 55 47
      flip_social_explore.h
  13. 109 12
      flip_social_feed.h
  14. 38 0
      flip_social_free.h
  15. 66 49
      flip_social_friends.h
  16. 87 17
      flip_social_i.h
  17. 356 0
      flip_social_messages.h
  18. 2 4
      flip_social_storage.h
  19. 40 1
      flipper_http.h
  20. 15 0
      jsmn.h

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+
+.vscode/compile_commands.json
+.DS_Store
+.DS_Store

+ 36 - 23
README.md

@@ -1,8 +1,12 @@
 # FlipSocial
-The first social media app for Flipper Zero. Connect with other users directly on your device through WiFi. The highlight of this app is customizable pre-saves, which, as explained below, aim to solve the dissatisfaction 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/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP
+The first social media app for Flipper Zero. Connect with other users directly on your device through WiFi.
+
+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)
 
 ## Requirements
-- WiFi Dev Board for Flipper Zero with FlipperHTTP Flash: https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP
+- WiFi Dev Board for Flipper Zero with FlipperHTTP Flash: [FlipperHTTP](https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP)
 - WiFi Access Point
 
 ## Features
@@ -13,21 +17,21 @@ The first social media app for Flipper Zero. Connect with other users directly o
 - Customizable Pre-Saves
 - Explore (NEW)
 - Friends (NEW)
-- Direct Messaging (coming soon)
+- Direct Messaging (NEW)
 
 **Login/Logout:** Log in to your account to view and post on the Feed. You can also change your password and log out when needed.
 
 **Registration:** Create an account with just a username and password—no email or personal information required or collected.
 
-**Feed:** View up to 128 of the latest posts, create your own posts, and "Flip" a post—FlipSocial’s version of liking or favoriting a post.
+**Feed:** View up to 50 of the latest posts, create your own posts, and "Flip" a post—FlipSocial’s version of liking or favoriting a post.
 
-**Customizable Pre-Saves**: The biggest challenge with a social media app on the Flipper Zero is using only the directional pad for input. As a solution, I implemented a pre-saved text system. The pre-saves are stored in a pre_saved_messages.txt file on your SD card. You can edit the pre-saves by opening qFlipper, downloading the file from the /apps_data/flip_social/ folder, adding your pre-saves (separated by new lines), and then copying it back to your SD card. You can also create the pre-saves directly within the app.
+**Customizable Pre-Saves:** The biggest challenge with a social media app on the Flipper Zero is using only the directional pad for input. To address this, I implemented a pre-saved text system. The pre-saves are stored in a pre_saved_messages.txt file on your SD card. You can edit the pre-saves by opening qFlipper, downloading the file from the /apps_data/flip_social/ folder, adding your pre-saves (separated by new lines), and then copying it back to your SD card. You can also create pre-saves directly within the app.
 
 **Explore:** Discover other users and add them as friends.
 
 **Friends:** View and remove friends.
 
-
+**Direct Messaging:** Send direct messages to other Flipper users and view your conversations.
 
 ## Roadmap
 **v0.2**
@@ -39,35 +43,44 @@ The first social media app for Flipper Zero. Connect with other users directly o
 
 **v0.4**
 - Direct Messaging
-- Privacy Settings
 
 **v0.5**
-- Improved Explore Page
+- Improved memory allocation
+- Improved Feed Page (Flip count, report, block)
 
 **v0.6**
-- Improved Feed Page
+- Improved User Profile (Bio, friend count, block)
+
+**v0.7**
+- Improved Explore Page
+
+**v0.8**
+- Create and Post Pixel Art
+
+**v1.0**
+- Official Release
 
 ## Contribution
-This is a big task, and I welcome all contributors, especially developers who are into animations and graphics. Fork the repository, create a pull request, and I will review your edits.
+This is a big project, and I welcome all contributors, especially developers interested in animations and graphics. Fork the repository, create a pull request, and I will review your edits.
 
 ## Known Bugs
-1. When clicking any button other than the BACK button in the Feed view, post creation view, or friends view, the app doesn't respond to inputs.
-- Solution: Restart your Flipper device.
+1. When clicking any button other than the BACK button in the Feed view, post creation view, messages view, or the friends view, the app doesn't respond to inputs.
+- **Solution:** Restart your Flipper device.
    
 2. When trying to log in, the app shows "Awaiting response..." and nothing happens for more than 30 seconds.
-- Solution 1: Update your WiFi credentials. Sometimes you just need to hit Save again on either the SSID or password.
-- Solution 2: Ensure your WiFi Devboard is plugged in, then restart the app.
-- Solution 3: Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
+- **Solution 1:** Update your WiFi credentials. Sometimes, you just need to hit Save again on either the SSID or password.
+- **Solution 2:** Ensure your WiFi Devboard is plugged in, then restart the app.
+- **Solution 3:** Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
    
 3. When accessing the Feed, I keep getting the message "Either the feed didn’t load or there was a server error."
-- Solution 1: Update your WiFi credentials. Sometimes you just need to hit Save again on either the SSID or password.
-- Solution 2: Ensure your WiFi Devboard is plugged in, then restart the app.
-- Solution 3: Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
+- **Solution 1:** Update your WiFi credentials. Sometimes, you just need to hit Save again on either the SSID or password.
+- **Solution 2:** Ensure your WiFi Devboard is plugged in, then restart the app.
+- **Solution 3:** Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
    
 4. The Feed is empty.
-- Solution 1: Update your WiFi credentials. Sometimes you just need to hit Save again on either the SSID or password.
-- Solution 2: Ensure your WiFi Devboard is plugged in, then restart the app.
-- Solution 3: Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
+- **Solution 1:** Update your WiFi credentials. Sometimes, you just need to hit Save again on either the SSID or password.
+- **Solution 2:** Ensure your WiFi Devboard is plugged in, then restart the app.
+- **Solution 3:** Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
 
-5. Out of memory after visiting the feed and post views back-t-back.
-- Solution: Restart your Flipper device.
+5. Out of memory when starting the app or after visiting the feed and post views back-to-back.
+- **Solution:** Restart your Flipper device.

+ 1 - 0
app.c

@@ -7,6 +7,7 @@
 #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

+ 1 - 1
application.fam

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

+ 10 - 4
assets/CHANGELOG.md

@@ -1,10 +1,16 @@
+## 0.4
+- Added direct messaging.
+- Updated app flow for a smoother user experience.
+- Reduced recent feed posts from 128 to 50.
+- Improved memory allocation.
+
 ## 0.3
-- Added an Explore page: Discover other users and add them as friends.
-- Added a Friends page: View and remove friends.
-- Improved error handling.
+- Added an Explore page to discover and add other users as friends.
+- Added a Friends page to view and remove friends.
+- Enhanced error handling.
 
 ## 0.2
-- Stability patch.
+- Stability improvements.
 
 ## 0.1
 - Initial release.

+ 36 - 23
assets/README.md

@@ -1,8 +1,12 @@
 # FlipSocial
-The first social media app for Flipper Zero. Connect with other users directly on your device through WiFi. The highlight of this app is customizable pre-saves, which, as explained below, aim to solve the dissatisfaction 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/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP
+The first social media app for Flipper Zero. Connect with other users directly on your device through WiFi.
+
+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)
 
 ## Requirements
-- WiFi Dev Board for Flipper Zero with FlipperHTTP Flash: https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP
+- WiFi Dev Board for Flipper Zero with FlipperHTTP Flash: [FlipperHTTP](https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP)
 - WiFi Access Point
 
 ## Features
@@ -13,21 +17,21 @@ The first social media app for Flipper Zero. Connect with other users directly o
 - Customizable Pre-Saves
 - Explore (NEW)
 - Friends (NEW)
-- Direct Messaging (coming soon)
+- Direct Messaging (NEW)
 
 **Login/Logout:** Log in to your account to view and post on the Feed. You can also change your password and log out when needed.
 
 **Registration:** Create an account with just a username and password—no email or personal information required or collected.
 
-**Feed:** View up to 128 of the latest posts, create your own posts, and "Flip" a post—FlipSocial’s version of liking or favoriting a post.
+**Feed:** View up to 50 of the latest posts, create your own posts, and "Flip" a post—FlipSocial’s version of liking or favoriting a post.
 
-**Customizable Pre-Saves**: The biggest challenge with a social media app on the Flipper Zero is using only the directional pad for input. As a solution, I implemented a pre-saved text system. The pre-saves are stored in a pre_saved_messages.txt file on your SD card. You can edit the pre-saves by opening qFlipper, downloading the file from the /apps_data/flip_social/ folder, adding your pre-saves (separated by new lines), and then copying it back to your SD card. You can also create the pre-saves directly within the app.
+**Customizable Pre-Saves:** The biggest challenge with a social media app on the Flipper Zero is using only the directional pad for input. To address this, I implemented a pre-saved text system. The pre-saves are stored in a pre_saved_messages.txt file on your SD card. You can edit the pre-saves by opening qFlipper, downloading the file from the /apps_data/flip_social/ folder, adding your pre-saves (separated by new lines), and then copying it back to your SD card. You can also create pre-saves directly within the app.
 
 **Explore:** Discover other users and add them as friends.
 
 **Friends:** View and remove friends.
 
-
+**Direct Messaging:** Send direct messages to other Flipper users and view your conversations.
 
 ## Roadmap
 **v0.2**
@@ -39,35 +43,44 @@ The first social media app for Flipper Zero. Connect with other users directly o
 
 **v0.4**
 - Direct Messaging
-- Privacy Settings
 
 **v0.5**
-- Improved Explore Page
+- Improved memory allocation
+- Improved Feed Page (Flip count, report, block)
 
 **v0.6**
-- Improved Feed Page
+- Improved User Profile (Bio, friend count, block)
+
+**v0.7**
+- Improved Explore Page
+
+**v0.8**
+- Create and Post Pixel Art
+
+**v1.0**
+- Official Release
 
 ## Contribution
-This is a big task, and I welcome all contributors, especially developers who are into animations and graphics. Fork the repository, create a pull request, and I will review your edits.
+This is a big project, and I welcome all contributors, especially developers interested in animations and graphics. Fork the repository, create a pull request, and I will review your edits.
 
 ## Known Bugs
-1. When clicking any button other than the BACK button in the Feed view, post creation view, or friends view, the app doesn't respond to inputs.
-- Solution: Restart your Flipper device.
+1. When clicking any button other than the BACK button in the Feed view, post creation view, messages view, or the friends view, the app doesn't respond to inputs.
+- **Solution:** Restart your Flipper device.
    
 2. When trying to log in, the app shows "Awaiting response..." and nothing happens for more than 30 seconds.
-- Solution 1: Update your WiFi credentials. Sometimes you just need to hit Save again on either the SSID or password.
-- Solution 2: Ensure your WiFi Devboard is plugged in, then restart the app.
-- Solution 3: Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
+- **Solution 1:** Update your WiFi credentials. Sometimes, you just need to hit Save again on either the SSID or password.
+- **Solution 2:** Ensure your WiFi Devboard is plugged in, then restart the app.
+- **Solution 3:** Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
    
 3. When accessing the Feed, I keep getting the message "Either the feed didn’t load or there was a server error."
-- Solution 1: Update your WiFi credentials. Sometimes you just need to hit Save again on either the SSID or password.
-- Solution 2: Ensure your WiFi Devboard is plugged in, then restart the app.
-- Solution 3: Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
+- **Solution 1:** Update your WiFi credentials. Sometimes, you just need to hit Save again on either the SSID or password.
+- **Solution 2:** Ensure your WiFi Devboard is plugged in, then restart the app.
+- **Solution 3:** Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
    
 4. The Feed is empty.
-- Solution 1: Update your WiFi credentials. Sometimes you just need to hit Save again on either the SSID or password.
-- Solution 2: Ensure your WiFi Devboard is plugged in, then restart the app.
-- Solution 3: Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
+- **Solution 1:** Update your WiFi credentials. Sometimes, you just need to hit Save again on either the SSID or password.
+- **Solution 2:** Ensure your WiFi Devboard is plugged in, then restart the app.
+- **Solution 3:** Ensure your WiFi Devboard is plugged in, then restart your Flipper device.
 
-5. Out of memory after visiting the feed and post views back-t-back.
-- Solution: Restart your Flipper device.
+5. Out of memory when starting the app or after visiting the feed and post views back-to-back.
+- **Solution:** Restart your Flipper device.

+ 1 - 0
easy_flipper.h

@@ -20,6 +20,7 @@
 #include <uart_text_input.h>
 #include <stdio.h>
 #include <string.h>
+#include <jsmn.h>
 
 #define EASY_TAG "EasyFlipper"
 

+ 184 - 36
flip_social_callback.h

@@ -117,7 +117,7 @@ static uint32_t flip_social_callback_to_explore_logged_in(void *context)
     flip_social_dialog_stop = true;
     last_explore_response = "";
     flip_social_dialog_shown = false;
-    flip_social_explore.index = 0;
+    app_instance->flip_social_explore.index = 0;
     action = ActionNone;
     return FlipSocialViewLoggedInExploreSubmenu;
 }
@@ -133,10 +133,33 @@ static uint32_t flip_social_callback_to_friends_logged_in(void *context)
     flip_social_dialog_stop = true;
     last_explore_response = "";
     flip_social_dialog_shown = false;
-    flip_social_friends.index = 0;
+    app_instance->flip_social_friends.index = 0;
     action = ActionNone;
     return FlipSocialViewLoggedInFriendsSubmenu;
 }
+
+/**
+ * @brief Navigation callback to bring the user back to the Messages submenu
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInMessagesSubmenu)
+ */
+static uint32_t flip_social_callback_to_messages_logged_in(void *context)
+{
+    UNUSED(context);
+    return FlipSocialViewLoggedInMessagesSubmenu;
+}
+
+/**
+ * @brief Navigation callback to bring the user back to the User Choices screen
+ * @param context The context - unused
+ * @return next view id (FlipSocialViewLoggedInMessagesUserChoices)
+ */
+static uint32_t flip_social_callback_to_messages_user_choices(void *context)
+{
+    UNUSED(context);
+    return FlipSocialViewLoggedInMessagesUserChoices;
+}
+
 /**
  * @brief Navigation callback for exiting the application
  * @param context The context - unused
@@ -189,44 +212,29 @@ static void flip_social_callback_submenu_choices(void *context, uint32_t index)
     case FlipSocialSubmenuLoggedInIndexProfile:
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProfile);
         break;
-    case FlipSocialSubmenuLoggedInIndexFeed:
-        if (flip_social_get_feed()) // start the async feed request
+    case FlipSocialSubmenuLoggedInIndexMessages:
+        if (flipper_http_process_response_async(flip_social_get_message_users, flip_social_parse_json_message_users))
         {
-            furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesSubmenu);
         }
-        while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0)
+        break;
+    case FlipSocialSubmenuLoggedInIndexMessagesNewMessage:
+        if (flipper_http_process_response_async(flip_social_get_explore, flip_social_parse_json_message_user_choices))
         {
-            // Wait for the feed to be received
-            furi_delay_ms(100);
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesUserChoices);
         }
-        furi_timer_stop(fhttp.get_timeout_timer);
-
-        if (!flip_social_parse_json_feed()) // parse the JSON before switching to the feed (synchonous)
+        break;
+    case FlipSocialSubmenuLoggedInIndexFeed:
+        if (flipper_http_process_response_async(flip_social_get_feed, flip_social_parse_json_feed))
         {
-            FURI_LOG_E(TAG, "Failed to parse the JSON feed. Using the temporary feed.");
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInFeed);
         }
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInFeed);
         break;
     case FlipSocialSubmenuExploreIndex:
-        // process explore page the same way as the feed page, then switch to the explore page
-        if (flip_social_get_explore()) // start the async explore request
+        if (flipper_http_process_response_async(flip_social_get_explore, flip_social_parse_json_explore))
         {
-            furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInExploreSubmenu);
         }
-        while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0)
-        {
-            // Wait for the explore to be received
-            furi_delay_ms(100);
-        }
-        furi_timer_stop(fhttp.get_timeout_timer);
-        if (!flip_social_parse_json_explore()) // parse the JSON before switching to the explore (synchonous)
-        {
-            FURI_LOG_E(TAG, "Failed to parse the JSON explore...");
-            return; // just return for now, no temporary explore yet
-            // show a popup message saving wifi is disconnected
-        }
-        furi_timer_stop(fhttp.get_timeout_timer);
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInExploreSubmenu);
         break;
     case FlipSocialSubmenuLoggedInIndexCompose:
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInCompose);
@@ -248,27 +256,45 @@ static void flip_social_callback_submenu_choices(void *context, uint32_t index)
         // 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->flip_social_feed.index = index - FlipSocialSubemnuComposeIndexStartIndex;
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProcessCompose);
         }
 
         // Handle the explore selection
         else if (index >= FlipSocialSubmenuExploreIndexStartIndex && index < FlipSocialSubmenuExploreIndexStartIndex + MAX_EXPLORE_USERS)
         {
-            flip_social_explore.index = index - FlipSocialSubmenuExploreIndexStartIndex;
+            app->flip_social_explore.index = index - FlipSocialSubmenuExploreIndexStartIndex;
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInExploreProccess);
         }
 
         // handle the friends selection
         else if (index >= FlipSocialSubmenuLoggedInIndexFriendsStart && index < FlipSocialSubmenuLoggedInIndexFriendsStart + MAX_FRIENDS)
         {
-            flip_social_friends.index = index - FlipSocialSubmenuLoggedInIndexFriendsStart;
+            app->flip_social_friends.index = index - FlipSocialSubmenuLoggedInIndexFriendsStart;
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInFriendsProcess);
         }
+
+        // handle the messages selection
+        else if (index >= FlipSocialSubmenuLoggedInIndexMessagesUsersStart && index < FlipSocialSubmenuLoggedInIndexMessagesUsersStart + MAX_MESSAGE_USERS)
+        {
+            app->flip_social_message_users.index = index - FlipSocialSubmenuLoggedInIndexMessagesUsersStart;
+            if (flipper_http_process_response_async(flip_social_get_messages_with_user, flip_social_parse_json_messages))
+            {
+                view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesProcess);
+            }
+        }
+
+        // handle the messages user choices selection
+        else if (index >= FlipSocialSubmenuLoggedInIndexMessagesUserChoicesIndexStart && index < FlipSocialSubmenuLoggedInIndexMessagesUserChoicesIndexStart + MAX_EXPLORE_USERS)
+        {
+            app->flip_social_explore.index = index - FlipSocialSubmenuLoggedInIndexMessagesUserChoicesIndexStart;
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesNewMessageUserChoicesInput);
+        }
         else
         {
             FURI_LOG_E(TAG, "Unknown submenu index");
         }
+
         break;
     }
 }
@@ -808,14 +834,15 @@ static void flip_social_logged_in_profile_change_password_updated(void *context)
     // send post request to change password
     char payload[256];
     snprintf(payload, sizeof(payload), "{\"username\":\"%s\",\"old_password\":\"%s\",\"new_password\":\"%s\"}", app->login_username_logged_out, old_password, app->change_password_logged_in);
-
-    if (!flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/change-password/", "Content-Type: application/json", payload))
+    char *headers = jsmn("Content-Type", "application/json");
+    if (!flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/change-password/", headers, payload))
     {
         FURI_LOG_E(TAG, "Failed to send post request to change password");
         FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
+        free(headers);
         return;
     }
-
+    free(headers);
     // Save the settings
     save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in);
 
@@ -898,4 +925,125 @@ static void flip_social_text_input_logged_in_settings_item_selected(void *contex
     }
 }
 
+/**
+ * @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
+ */
+static 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");
+        return;
+    }
+
+    // check if the message is empty
+    if (app->message_user_choice_logged_in_temp_buffer_size == 0)
+    {
+        FURI_LOG_E(TAG, "Message is empty");
+        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesNewMessageUserChoicesInput);
+        return;
+    }
+
+    // Store the entered message
+    strncpy(app->message_user_choice_logged_in, app->message_user_choice_logged_in_temp_buffer, app->message_user_choice_logged_in_temp_buffer_size);
+
+    // Ensure null-termination
+    app->message_user_choice_logged_in[app->message_user_choice_logged_in_temp_buffer_size - 1] = '\0';
+
+    // send post request to send message
+    char url[128];
+    char payload[256];
+    snprintf(url, sizeof(url), "https://www.flipsocial.net/api/messages/%s/post/", app->login_username_logged_in);
+    snprintf(payload, sizeof(payload), "{\"receiver\":\"%s\",\"content\":\"%s\"}", app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.index], app->message_user_choice_logged_in);
+    char *headers = jsmn("Content-Type", "application/json");
+
+    if (flipper_http_post_request_with_headers(url, headers, payload)) // start the async request
+    {
+        furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
+        fhttp.state = RECEIVING;
+        free(headers);
+    }
+    else
+    {
+        FURI_LOG_E(TAG, "Failed to send post request to send message");
+        FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
+        free(headers);
+        return;
+    }
+    while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0)
+    {
+        // Wait for the request to be received
+        furi_delay_ms(100);
+    }
+    furi_timer_stop(fhttp.get_timeout_timer);
+
+    // add user to the message list
+    strncpy(app_instance->flip_social_message_users.usernames[app_instance->flip_social_message_users.count], app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.index], strlen(app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.index]));
+    app_instance->flip_social_message_users.count++;
+
+    // redraw submenu
+    flip_social_update_messages_submenu();
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesSubmenu);
+}
+
+/**
+ * @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
+ */
+static void flip_social_logged_in_messages_new_message_updated(void *context)
+{
+    FlipSocialApp *app = (FlipSocialApp *)context;
+    if (!app)
+    {
+        FURI_LOG_E(TAG, "FlipSocialApp is NULL");
+        return;
+    }
+
+    // check if the message is empty
+    if (app->messages_new_message_logged_in_temp_buffer_size == 0)
+    {
+        FURI_LOG_E(TAG, "Message is empty");
+        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesNewMessageInput);
+        return;
+    }
+
+    // Store the entered message
+    strncpy(app->messages_new_message_logged_in, app->messages_new_message_logged_in_temp_buffer, app->messages_new_message_logged_in_temp_buffer_size);
+
+    // Ensure null-termination
+    app->messages_new_message_logged_in[app->messages_new_message_logged_in_temp_buffer_size - 1] = '\0';
+
+    // send post request to send message
+    char url[128];
+    char payload[256];
+    snprintf(url, sizeof(url), "https://www.flipsocial.net/api/messages/%s/post/", app->login_username_logged_in);
+    snprintf(payload, sizeof(payload), "{\"receiver\":\"%s\",\"content\":\"%s\"}", app_instance->flip_social_message_users.usernames[app_instance->flip_social_message_users.index], app->messages_new_message_logged_in);
+    char *headers = jsmn("Content-Type", "application/json");
+
+    if (flipper_http_post_request_with_headers(url, headers, payload)) // start the async request
+    {
+        furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
+        fhttp.state = RECEIVING;
+        free(headers);
+    }
+    else
+    {
+        FURI_LOG_E(TAG, "Failed to send post request to send message");
+        FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
+        free(headers);
+        return;
+    }
+    while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0)
+    {
+        // Wait for the request to be received
+        furi_delay_ms(100);
+    }
+    furi_timer_stop(fhttp.get_timeout_timer);
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesSubmenu);
+}
+
 #endif // FLIP_SOCIAL_CALLBACK_H

+ 153 - 35
flip_social_draw.h

@@ -8,7 +8,7 @@ bool flip_social_register_success = false;
 bool flip_social_dialog_shown = false;
 bool flip_social_dialog_stop = false;
 char *last_explore_response = "";
-static void flip_social_update_friends();
+static bool flip_social_update_friends();
 
 bool flip_social_board_is_active(Canvas *canvas)
 {
@@ -166,7 +166,7 @@ static void flip_social_callback_draw_compose(Canvas *canvas, void *model)
         return;
     }
 
-    char *message = app_instance->pre_saved_messages.messages[flip_social_feed.index];
+    char *message = app_instance->pre_saved_messages.messages[app_instance->flip_social_feed.index];
 
     if (!flip_social_dialog_shown)
     {
@@ -247,9 +247,9 @@ static void flip_social_callback_draw_compose(Canvas *canvas, void *model)
     case ActionPrev:
         // delete message
         // remove the message from app_instance->pre_saved_messages
-        app_instance->pre_saved_messages.messages[flip_social_feed.index] = NULL;
+        app_instance->pre_saved_messages.messages[app_instance->flip_social_feed.index] = NULL;
 
-        for (uint32_t i = flip_social_feed.index; i < app_instance->pre_saved_messages.count - 1; i++)
+        for (uint32_t i = app_instance->flip_social_feed.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];
         }
@@ -280,7 +280,12 @@ static void flip_social_callback_draw_compose(Canvas *canvas, void *model)
         furi_pubsub_unsubscribe(app_instance->input_event_queue, app_instance->input_event);
         flip_social_dialog_shown = false;
         flip_social_dialog_stop = false;
-        if (action == ActionBack || action == ActionNext)
+        if (action == ActionNext)
+        {
+            action = ActionNone;
+            view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInFeed);
+        }
+        else if (action == ActionBack)
         {
             action = ActionNone;
             view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInSubmenu);
@@ -350,30 +355,30 @@ static void flip_social_callback_draw_feed(Canvas *canvas, void *model)
     switch (action)
     {
     case ActionNone:
-        flip_social_canvas_draw_message(canvas, flip_social_feed.usernames[flip_social_feed.index], flip_social_feed.messages[flip_social_feed.index], flip_social_feed.is_flipped[flip_social_feed.index], flip_social_feed.index > 0, flip_social_feed.index < flip_social_feed.count - 1);
+        flip_social_canvas_draw_message(canvas, app_instance->flip_social_feed.usernames[app_instance->flip_social_feed.index], app_instance->flip_social_feed.messages[app_instance->flip_social_feed.index], app_instance->flip_social_feed.is_flipped[app_instance->flip_social_feed.index], app_instance->flip_social_feed.index > 0, app_instance->flip_social_feed.index < app_instance->flip_social_feed.count - 1);
         break;
     case ActionNext:
         canvas_clear(canvas);
-        if (flip_social_feed.index < flip_social_feed.count - 1)
+        if (app_instance->flip_social_feed.index < app_instance->flip_social_feed.count - 1)
         {
-            flip_social_feed.index++;
+            app_instance->flip_social_feed.index++;
         }
-        flip_social_canvas_draw_message(canvas, flip_social_feed.usernames[flip_social_feed.index], flip_social_feed.messages[flip_social_feed.index], flip_social_feed.is_flipped[flip_social_feed.index], flip_social_feed.index > 0, flip_social_feed.index < flip_social_feed.count - 1);
+        flip_social_canvas_draw_message(canvas, app_instance->flip_social_feed.usernames[app_instance->flip_social_feed.index], app_instance->flip_social_feed.messages[app_instance->flip_social_feed.index], app_instance->flip_social_feed.is_flipped[app_instance->flip_social_feed.index], app_instance->flip_social_feed.index > 0, app_instance->flip_social_feed.index < app_instance->flip_social_feed.count - 1);
         action = ActionNone;
         break;
     case ActionPrev:
         canvas_clear(canvas);
-        if (flip_social_feed.index > 0)
+        if (app_instance->flip_social_feed.index > 0)
         {
-            flip_social_feed.index--;
+            app_instance->flip_social_feed.index--;
         }
-        flip_social_canvas_draw_message(canvas, flip_social_feed.usernames[flip_social_feed.index], flip_social_feed.messages[flip_social_feed.index], flip_social_feed.is_flipped[flip_social_feed.index], flip_social_feed.index > 0, flip_social_feed.index < flip_social_feed.count - 1);
+        flip_social_canvas_draw_message(canvas, app_instance->flip_social_feed.usernames[app_instance->flip_social_feed.index], app_instance->flip_social_feed.messages[app_instance->flip_social_feed.index], app_instance->flip_social_feed.is_flipped[app_instance->flip_social_feed.index], app_instance->flip_social_feed.index > 0, app_instance->flip_social_feed.index < app_instance->flip_social_feed.count - 1);
         action = ActionNone;
         break;
     case ActionFlip:
         canvas_clear(canvas);
-        flip_social_feed.is_flipped[flip_social_feed.index] = !flip_social_feed.is_flipped[flip_social_feed.index];
-        flip_social_canvas_draw_message(canvas, flip_social_feed.usernames[flip_social_feed.index], flip_social_feed.messages[flip_social_feed.index], flip_social_feed.is_flipped[flip_social_feed.index], flip_social_feed.index > 0, flip_social_feed.index < flip_social_feed.count - 1);
+        app_instance->flip_social_feed.is_flipped[app_instance->flip_social_feed.index] = !app_instance->flip_social_feed.is_flipped[app_instance->flip_social_feed.index];
+        flip_social_canvas_draw_message(canvas, app_instance->flip_social_feed.usernames[app_instance->flip_social_feed.index], app_instance->flip_social_feed.messages[app_instance->flip_social_feed.index], app_instance->flip_social_feed.is_flipped[app_instance->flip_social_feed.index], app_instance->flip_social_feed.index > 0, app_instance->flip_social_feed.index < app_instance->flip_social_feed.count - 1);
         action = ActionNone;
         // send post request to flip the message
         if (app_instance->login_username_logged_in == NULL)
@@ -382,13 +387,13 @@ static void flip_social_callback_draw_feed(Canvas *canvas, void *model)
             return;
         }
         char payload[256];
-        snprintf(payload, sizeof(payload), "{\"username\":\"%s\",\"post_id\":\"%lu\"}", app_instance->login_username_logged_in, flip_social_feed.ids[flip_social_feed.index]);
+        snprintf(payload, sizeof(payload), "{\"username\":\"%s\",\"post_id\":\"%lu\"}", app_instance->login_username_logged_in, app_instance->flip_social_feed.ids[app_instance->flip_social_feed.index]);
         flipper_http_post_request_with_headers("https://www.flipsocial.net/api/feed/flip/", "{\"Content-Type\":\"application/json\"}", payload);
         break;
     case ActionBack:
         canvas_clear(canvas);
         flip_social_dialog_stop = true;
-        flip_social_feed.index = 0;
+        app_instance->flip_social_feed.index = 0;
         action = ActionNone;
         break;
     default:
@@ -690,7 +695,7 @@ static void flip_social_callback_draw_explore(Canvas *canvas, void *model)
         app_instance->input_event_queue = furi_record_open(RECORD_INPUT_EVENTS);
         app_instance->input_event = furi_pubsub_subscribe(app_instance->input_event_queue, on_input, NULL);
     }
-    flip_social_canvas_draw_explore(canvas, flip_social_explore.usernames[flip_social_explore.index], last_explore_response);
+    flip_social_canvas_draw_explore(canvas, app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.index], last_explore_response);
 
     // handle action
     switch (action)
@@ -698,19 +703,19 @@ static void flip_social_callback_draw_explore(Canvas *canvas, void *model)
     case ActionNext:
         // add friend
         char add_payload[128];
-        snprintf(add_payload, sizeof(add_payload), "{\"username\":\"%s\",\"friend\":\"%s\"}", app_instance->login_username_logged_in, flip_social_explore.usernames[flip_social_explore.index]);
+        snprintf(add_payload, sizeof(add_payload), "{\"username\":\"%s\",\"friend\":\"%s\"}", app_instance->login_username_logged_in, app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.index]);
         flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/add-friend/", "{\"Content-Type\":\"application/json\"}", add_payload);
         canvas_clear(canvas);
-        flip_social_canvas_draw_explore(canvas, flip_social_explore.usernames[flip_social_explore.index], "Added!");
+        flip_social_canvas_draw_explore(canvas, app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.index], "Added!");
         action = ActionNone;
         break;
     case ActionPrev:
         // remove friend
         char remove_payload[128];
-        snprintf(remove_payload, sizeof(remove_payload), "{\"username\":\"%s\",\"friend\":\"%s\"}", app_instance->login_username_logged_in, flip_social_explore.usernames[flip_social_explore.index]);
+        snprintf(remove_payload, sizeof(remove_payload), "{\"username\":\"%s\",\"friend\":\"%s\"}", app_instance->login_username_logged_in, app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.index]);
         flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/remove-friend/", "{\"Content-Type\":\"application/json\"}", remove_payload);
         canvas_clear(canvas);
-        flip_social_canvas_draw_explore(canvas, flip_social_explore.usernames[flip_social_explore.index], "Removed!");
+        flip_social_canvas_draw_explore(canvas, app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.index], "Removed!");
         action = ActionNone;
         break;
     case ActionBack:
@@ -718,7 +723,7 @@ static void flip_social_callback_draw_explore(Canvas *canvas, void *model)
         flip_social_dialog_stop = true;
         last_explore_response = "";
         flip_social_dialog_shown = false;
-        flip_social_explore.index = 0;
+        app_instance->flip_social_explore.index = 0;
         action = ActionNone;
         break;
     default:
@@ -755,7 +760,7 @@ static void flip_social_callback_draw_friends(Canvas *canvas, void *model)
         app_instance->input_event_queue = furi_record_open(RECORD_INPUT_EVENTS);
         app_instance->input_event = furi_pubsub_subscribe(app_instance->input_event_queue, on_input, NULL);
     }
-    flip_social_canvas_draw_explore(canvas, flip_social_friends.usernames[flip_social_friends.index], last_explore_response);
+    flip_social_canvas_draw_explore(canvas, app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.index], last_explore_response);
 
     // handle action
     switch (action)
@@ -763,35 +768,41 @@ static void flip_social_callback_draw_friends(Canvas *canvas, void *model)
     case ActionNext:
         // add friend
         char add_payload[128];
-        snprintf(add_payload, sizeof(add_payload), "{\"username\":\"%s\",\"friend\":\"%s\"}", app_instance->login_username_logged_in, flip_social_friends.usernames[flip_social_friends.index]);
+        snprintf(add_payload, sizeof(add_payload), "{\"username\":\"%s\",\"friend\":\"%s\"}", app_instance->login_username_logged_in, app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.index]);
         if (flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/add-friend/", "{\"Content-Type\":\"application/json\"}", add_payload))
         {
             canvas_clear(canvas);
-            flip_social_canvas_draw_explore(canvas, flip_social_friends.usernames[flip_social_friends.index], "Added!");
+            flip_social_canvas_draw_explore(canvas, app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.index], "Added!");
 
             // add the friend to the friends list
-            flip_social_friends.usernames[flip_social_friends.count] = flip_social_friends.usernames[flip_social_friends.index];
-            flip_social_friends.count++;
-            flip_social_update_friends();
+            app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.count] = app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.index];
+            app_instance->flip_social_friends.count++;
+            if (!flip_social_update_friends())
+            {
+                FURI_LOG_E(TAG, "Failed to update friends");
+            }
         }
         action = ActionNone;
         break;
     case ActionPrev:
         // remove friend
         char remove_payload[128];
-        snprintf(remove_payload, sizeof(remove_payload), "{\"username\":\"%s\",\"friend\":\"%s\"}", app_instance->login_username_logged_in, flip_social_friends.usernames[flip_social_friends.index]);
+        snprintf(remove_payload, sizeof(remove_payload), "{\"username\":\"%s\",\"friend\":\"%s\"}", app_instance->login_username_logged_in, app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.index]);
         if (flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/remove-friend/", "{\"Content-Type\":\"application/json\"}", remove_payload))
         {
             canvas_clear(canvas);
-            flip_social_canvas_draw_explore(canvas, flip_social_friends.usernames[flip_social_friends.index], "Removed!");
+            flip_social_canvas_draw_explore(canvas, app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.index], "Removed!");
 
             // remove the friend from the friends list
-            for (uint32_t i = flip_social_friends.index; i < flip_social_friends.count - 1; i++)
+            for (int i = app_instance->flip_social_friends.index; i < app_instance->flip_social_friends.count - 1; i++)
+            {
+                app_instance->flip_social_friends.usernames[i] = app_instance->flip_social_friends.usernames[i + 1];
+            }
+            app_instance->flip_social_friends.count--;
+            if (!flip_social_update_friends())
             {
-                flip_social_friends.usernames[i] = flip_social_friends.usernames[i + 1];
+                FURI_LOG_E(TAG, "Failed to update friends");
             }
-            flip_social_friends.count--;
-            flip_social_update_friends();
         }
         action = ActionNone;
         break;
@@ -800,7 +811,7 @@ static void flip_social_callback_draw_friends(Canvas *canvas, void *model)
         flip_social_dialog_stop = true;
         last_explore_response = "";
         flip_social_dialog_shown = false;
-        flip_social_friends.index = 0;
+        app_instance->flip_social_friends.index = 0;
         action = ActionNone;
         break;
     default:
@@ -816,4 +827,111 @@ static void flip_social_callback_draw_friends(Canvas *canvas, void *model)
     }
 }
 
+static void flip_social_canvas_draw_user_message(Canvas *canvas, char *user_username, char *user_message, bool show_prev, bool show_next)
+{
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, user_username);
+    canvas_set_font(canvas, FontSecondary);
+
+    draw_user_message(canvas, user_message, 12);
+
+    canvas_set_font(canvas, FontSecondary);
+    if (show_prev)
+    {
+        canvas_draw_icon(canvas, 0, 53, &I_ButtonLeft_4x7);
+        canvas_draw_str_aligned(canvas, 9, 54, AlignLeft, AlignTop, "Prev");
+    }
+
+    canvas_draw_icon(canvas, 47, 53, &I_ButtonOK_7x7);
+    canvas_draw_str_aligned(canvas, 56, 54, AlignLeft, AlignTop, "Create");
+
+    if (show_next)
+    {
+        canvas_draw_icon(canvas, 98, 53, &I_ButtonRight_4x7);
+        canvas_draw_str_aligned(canvas, 107, 54, AlignLeft, AlignTop, "Next");
+    }
+}
+
+// Callback function to handle the messages dialog
+static void flip_social_callback_draw_messages(Canvas *canvas, void *model)
+{
+    UNUSED(model);
+    if (!canvas)
+    {
+        FURI_LOG_E(TAG, "Canvas is NULL");
+        return;
+    }
+    if (!app_instance)
+    {
+        FURI_LOG_E(TAG, "FlipSocialApp is NULL");
+        return;
+    }
+
+    if (!flip_social_dialog_shown)
+    {
+        flip_social_dialog_shown = true;
+        app_instance->input_event_queue = furi_record_open(RECORD_INPUT_EVENTS);
+        app_instance->input_event = furi_pubsub_subscribe(app_instance->input_event_queue, on_input, NULL);
+    }
+
+    // handle action
+    switch (action)
+    {
+    case ActionNone:
+        flip_social_canvas_draw_user_message(canvas, app_instance->flip_social_messages.usernames[app_instance->flip_social_messages.index], app_instance->flip_social_messages.messages[app_instance->flip_social_messages.index], app_instance->flip_social_messages.index > 0, app_instance->flip_social_messages.index < app_instance->flip_social_messages.count - 1);
+        action = ActionNone;
+        break;
+    case ActionNext:
+        // view next message (if any)
+        canvas_clear(canvas);
+        if (app_instance->flip_social_messages.index < app_instance->flip_social_messages.count - 1)
+        {
+            app_instance->flip_social_messages.index++;
+        }
+        flip_social_canvas_draw_user_message(canvas, app_instance->flip_social_messages.usernames[app_instance->flip_social_messages.index], app_instance->flip_social_messages.messages[app_instance->flip_social_messages.index], app_instance->flip_social_messages.index > 0, app_instance->flip_social_messages.index < app_instance->flip_social_messages.count - 1);
+        action = ActionNone;
+        break;
+    case ActionPrev:
+        // view previous message (if any)
+        canvas_clear(canvas);
+        if (app_instance->flip_social_messages.index > 0)
+        {
+            app_instance->flip_social_messages.index--;
+        }
+        flip_social_canvas_draw_user_message(canvas, app_instance->flip_social_messages.usernames[app_instance->flip_social_messages.index], app_instance->flip_social_messages.messages[app_instance->flip_social_messages.index], app_instance->flip_social_messages.index > 0, app_instance->flip_social_messages.index < app_instance->flip_social_messages.count - 1);
+        action = ActionNone;
+        break;
+    case ActionBack:
+        //  go back to the previous view
+        flip_social_dialog_stop = true;
+        action = ActionNone;
+        break;
+    case ActionFlip:
+        // go to the input view
+        flip_social_dialog_stop = true;
+        break;
+    default:
+        action = ActionNone;
+        break;
+    }
+
+    if (flip_social_dialog_stop)
+    {
+        furi_pubsub_unsubscribe(app_instance->input_event_queue, app_instance->input_event);
+        flip_social_dialog_shown = false;
+        flip_social_dialog_stop = false;
+        if (action == ActionFlip)
+        {
+            action = ActionNone;
+            view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInMessagesNewMessageInput);
+        }
+        else
+        {
+            action = ActionNone;
+            view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInMessagesSubmenu);
+        }
+    }
+}
+
 #endif // FLIP_SOCIAL_DRAW_H

+ 90 - 50
flip_social_e.h

@@ -6,16 +6,19 @@
 #include <storage/storage.h>
 #include <flipper_http.h>
 #include <input/input.h>
-#include <flip_social_icons.h> // add <(YOUR-APP)_icons.h> so the compiler treats the .pngs are icons
+#include <flip_social_icons.h>
 #define TAG "FlipSocial"
 
 #define MAX_PRE_SAVED_MESSAGES 25 // Maximum number of pre-saved messages
 #define MAX_MESSAGE_LENGTH 100    // Maximum length of a message in the feed
 #define MAX_EXPLORE_USERS 50      // Maximum number of users to explore
+#define MAX_USER_LENGTH 32        // Maximum length of a username
 #define MAX_FRIENDS 50            // Maximum number of friends
-#define MAX_TOKENS 512            // Adjust based on expected JSON tokens
-#define MAX_FEED_ITEMS 128
+#define MAX_TOKENS 500            // Adjust based on expected JSON tokens
+#define MAX_FEED_ITEMS 50         // Maximum number of feed items
 #define MAX_LINE_LENGTH 30
+#define MAX_MESSAGE_USERS 20 // Maximum number of users to display in the submenu
+#define MAX_MESSAGES 20      // Maximum number of meesages between each user
 
 // Define the submenu items for our Hello World application
 typedef enum
@@ -26,18 +29,25 @@ typedef enum
     FlipSocialSubmenuLoggedOutIndexWifiSettings, // click to go to the wifi settings screen
     //
     FlipSocialSubmenuLoggedInIndexProfile,  // click to go to the profile screen
+    FlipSocialSubmenuExploreIndex,          // click to go to the explore
     FlipSocialSubmenuLoggedInIndexFeed,     // click to go to the feed screen
+    FlipSocialSubmenuLoggedInIndexMessages, // click to go to the messages screen
     FlipSocialSubmenuLoggedInIndexCompose,  // click to go to the compose screen
     FlipSocialSubmenuLoggedInIndexSettings, // click to go to the settings screen
     FlipSocialSubmenuLoggedInSignOutButton, // click to sign out
     //
-    FlipSocialSubmenuComposeIndexAddPreSave,      // click to add a pre-saved message
-    FlipSocialSubemnuComposeIndexStartIndex = 99, // starting index for the first pre saved message
+    FlipSocialSubmenuLoggedInIndexMessagesNewMessage, // click to add a new message
     //
-    FlipSocialSubmenuExploreIndex = 150,           // click to go to the explore
-    FlipSocialSubmenuExploreIndexStartIndex = 151, // starting index for the users to explore
+    FlipSocialSubmenuComposeIndexAddPreSave,       // click to add a pre-saved message
+    FlipSocialSubemnuComposeIndexStartIndex = 100, // starting index for the first pre saved message
     //
-    FlipSocialSubmenuLoggedInIndexFriendsStart = 1000, // starting index for the friends
+    FlipSocialSubmenuExploreIndexStartIndex = 150, // starting index for the users to explore
+    //
+    FlipSocialSubmenuLoggedInIndexFriendsStart = 200, // starting index for the friends
+    //
+    FlipSocialSubmenuLoggedInIndexMessagesUsersStart = 250, // starting index for the messages
+    //
+    FlipSocialSubmenuLoggedInIndexMessagesUserChoicesIndexStart = 300, // click to select a user to message
 } FlipSocialSubmenuIndex;
 
 typedef enum
@@ -49,7 +59,7 @@ typedef enum
     ActionFlip,
 } Action;
 
-Action action = ActionNone;
+static Action action = ActionNone;
 
 // Define the ScriptPlaylist structure
 typedef struct
@@ -58,6 +68,38 @@ typedef struct
     size_t count;
 } PreSavedPlaylist;
 
+typedef struct
+{
+    char *usernames[MAX_FEED_ITEMS];
+    char *messages[MAX_FEED_ITEMS];
+    bool is_flipped[MAX_FEED_ITEMS];
+    uint32_t ids[MAX_FEED_ITEMS];
+    size_t count;
+    size_t index;
+} FlipSocialFeed;
+
+typedef struct
+{
+    char *usernames[MAX_EXPLORE_USERS];
+    int count;
+    int index;
+} FlipSocialModel;
+
+typedef struct
+{
+    char *usernames[MAX_MESSAGE_USERS];
+    int count;
+    int index;
+} FlipSocialModel2;
+
+typedef struct
+{
+    char *usernames[MAX_MESSAGES];
+    char *messages[MAX_MESSAGES];
+    int count;
+    int index;
+} FlipSocialMessage;
+
 // Define views for our Hello World application
 typedef enum
 {
@@ -83,9 +125,13 @@ typedef enum
     FlipSocialViewLoggedInCompose,  // The compose screen
     FlipSocialViewLoggedInSettings, // The settings screen
     //
-    FlipSocialViewLoggedInChangePasswordInput,       // Text input screen for password input on change password screen
-    FlipSocialViewLoggedInComposeAddPreSaveInput,    // Text input screen for add text input on compose screen
-    FlipSocialViewLoggedInMessagesNewMessageInput,   // Text input screen for new message input on messages screen
+    FlipSocialViewLoggedInChangePasswordInput,    // Text input screen for password input on change password screen
+    FlipSocialViewLoggedInComposeAddPreSaveInput, // Text input screen for add text input on compose screen
+    //
+    FlipSocialViewLoggedInMessagesNewMessageInput,            // Text input screen for new message input on messages screen
+    FlipSocialViewLoggedInMessagesNewMessageUserChoicesInput, // Text input screen for new message input on messages screen
+    FlipSocialViewLoggedInMessagesUserChoices,                // the view after clicking [New Message] - select a user to message, then direct to input view
+    //
     FlipSocialViewLoggedInSettingsAbout,             // The about screen
     FlipSocialViewLoggedInSettingsWifi,              // The wifi settings screen
     FlipSocialViewLoggedInWifiSettingsSSIDInput,     // Text input screen for SSID input on wifi screen
@@ -98,19 +144,23 @@ typedef enum
     FlipSocialViewLoggedInExploreProccess, // The view after clicking on a user in the explore screen
     FlipSocialViewLoggedInFriendsSubmenu,  // The view after clicking the friends button on the profile screen
     FlipSocialViewLoggedInFriendsProcess,  // The view after clicking on a friend in the friends screen
+    FlipSocialViewLoggedInMessagesSubmenu, // The view after clicking the messages button on the profile screen
+    FlipSocialViewLoggedInMessagesProcess, // The view after clicking on a user in the messages screen
 } FlipSocialView;
 
 // Define the application structure
 typedef struct
 {
-    ViewDispatcher *view_dispatcher; // Switches between our views
-    Submenu *submenu_logged_out;     // The application submenu (logged out)
-    Submenu *submenu_logged_in;      // The application submenu (logged in)
-    Submenu *submenu_compose;        // The application submenu (compose)
-    Submenu *submenu_explore;        // The application submenu (explore)
-    Submenu *submenu_friends;        // The application submenu (friends)
-    Widget *widget_logged_out_about; // The about screen (logged out)
-    Widget *widget_logged_in_about;  // The about screen (logged in)
+    ViewDispatcher *view_dispatcher;        // Switches between our views
+    Submenu *submenu_logged_out;            // The application submenu (logged out)
+    Submenu *submenu_logged_in;             // The application submenu (logged in)
+    Submenu *submenu_compose;               // The application submenu (compose)
+    Submenu *submenu_explore;               // The application submenu (explore)
+    Submenu *submenu_friends;               // The application submenu (friends)
+    Submenu *submenu_messages;              // The application submenu (messages)
+    Submenu *submenu_messages_user_choices; // The application submenu (messages user choices)
+    Widget *widget_logged_out_about;        // The about screen (logged out)
+    Widget *widget_logged_in_about;         // The about screen (logged in)
 
     View *view_process_login;    // The screen displayed after clicking login
     View *view_process_register; // The screen displayed after clicking register
@@ -118,6 +168,7 @@ typedef struct
     View *view_process_compose;  // Dialog for the compose screen (delete or send)
     View *view_process_explore;  // Dialog for the explore screen (view user profile - add or delete friend)
     View *view_process_friends;  // Dialog for the friends screen (view user profile - add or delete friend)
+    View *view_process_messages; // Dialog for the messages screen (next, previous, send message)
 
     VariableItemList *variable_item_list_logged_out_wifi_settings; // The wifi settings menu
     VariableItemList *variable_item_list_logged_out_login;         // The login menu
@@ -139,6 +190,9 @@ typedef struct
     UART_TextInput *text_input_logged_in_compose_pre_save_input; // Text input for pre save input on compose screen
     UART_TextInput *text_input_logged_in_wifi_settings_ssid;     // Text input for ssid input on wifi settings screen
     UART_TextInput *text_input_logged_in_wifi_settings_password; // Text input for password input on wifi settings screen
+    //
+    UART_TextInput *text_input_logged_in_messages_new_message;              // Text input for new message input on messages screen
+    UART_TextInput *text_input_logged_in_messages_new_message_user_choices; //
 
     VariableItem *variable_item_logged_out_wifi_settings_ssid;     // Reference to the ssid configuration item
     VariableItem *variable_item_logged_out_wifi_settings_password; // Reference to the password configuration item
@@ -216,6 +270,22 @@ typedef struct
     char *wifi_password_logged_in_temp_buffer;         // Temporary buffer for wifi_password text input
     uint32_t wifi_password_logged_in_temp_buffer_size; // Size of the wifi_password temporary buffer
 
+    //
+    char *messages_new_message_logged_in;                     // Store the entered new message
+    char *messages_new_message_logged_in_temp_buffer;         // Temporary buffer for new message text input
+    uint32_t messages_new_message_logged_in_temp_buffer_size; // Size of the new message temporary buffer
+
+    char *message_user_choice_logged_in;                     // Store the entered message to send to the selected user
+    char *message_user_choice_logged_in_temp_buffer;         // Temporary buffer for message to send to the selected user
+    uint32_t message_user_choice_logged_in_temp_buffer_size; // Size of the message to send to the selected user temporary buffer
+
+    //
+    FlipSocialFeed flip_social_feed;            // Store the feed
+    FlipSocialModel flip_social_friends;        // Store the friends
+    FlipSocialModel2 flip_social_message_users; // Store the users that have sent messages to the logged in user
+    FlipSocialModel flip_social_explore;        // Store the users to explore
+    FlipSocialMessage flip_social_messages;     // Store the messages between the logged in user and the selected user
+
 } FlipSocialApp;
 
 // include strndup (otherwise NULL pointer dereference)
@@ -236,36 +306,6 @@ char *strndup(const char *s, size_t n)
 }
 
 static FlipSocialApp *app_instance = NULL;
-
-typedef struct
-{
-    char *usernames[MAX_FEED_ITEMS];
-    char *messages[MAX_FEED_ITEMS];
-    bool is_flipped[MAX_FEED_ITEMS];
-    uint32_t ids[MAX_FEED_ITEMS];
-    size_t count;
-    size_t index;
-} FlipSocialFeed;
-
-typedef struct
-{
-    char *usernames[MAX_EXPLORE_USERS];
-    size_t count;
-    size_t index;
-} FlipSocialModel;
-
-static FlipSocialModel flip_social_explore;
-static FlipSocialModel flip_social_friends;
-
-// temporary FlipSocialFeed object
-static FlipSocialFeed flip_social_feed = {
-    .usernames = {"JBlanked", "FlipperKing", "FlipperQueen"},
-    .messages = {"Welcome. This is a temp message. Either the feed didn't load or there was a server error.", "I am the Chosen Flipper.", "No one can flip like me."},
-    .is_flipped = {false, false, true},
-    .ids = {0, 1, 2},
-    .count = 3,
-    .index = 0};
-
 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

+ 55 - 47
flip_social_explore.h

@@ -1,12 +1,43 @@
 #ifndef FLIP_SOCIAL_EXPLORE_H
 #define FLIP_SOCIAL_EXPLORE_H
 
+static bool flip_social_explore_alloc()
+{
+    // Allocate memory for each username only if not already allocated
+    for (size_t i = 0; i < MAX_EXPLORE_USERS; i++)
+    {
+        if (app_instance->flip_social_explore.usernames[i] == NULL)
+        {
+            app_instance->flip_social_explore.usernames[i] = malloc(MAX_USER_LENGTH);
+            if (app_instance->flip_social_explore.usernames[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for username %zu", i);
+                return false; // Return false on memory allocation failure
+            }
+        }
+    }
+    return true;
+}
+
+static void flip_social_free_explore()
+{
+    if (!app_instance)
+    {
+        FURI_LOG_E(TAG, "App instance is NULL");
+        return;
+    }
+    for (int i = 0; i < app_instance->flip_social_explore.count; i++)
+    {
+        free(app_instance->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
-bool flip_social_get_explore()
+static bool flip_social_get_explore()
 {
     // will return true unless the devboard is not connected
-    bool success = flipper_http_get_request_with_headers("https://www.flipsocial.net/api/user/users/", "{\"Content-Type\":\"application/json\"}");
+    bool success = flipper_http_get_request_with_headers("https://www.flipsocial.net/api/user/users/", jsmn("Content-Type", "application/json"));
     if (!success)
     {
         FURI_LOG_E(TAG, "Failed to send HTTP request for explore");
@@ -16,7 +47,7 @@ bool flip_social_get_explore()
     return true;
 }
 
-bool flip_social_parse_json_explore()
+static bool flip_social_parse_json_explore()
 {
     if (fhttp.received_data == NULL)
     {
@@ -24,6 +55,13 @@ bool flip_social_parse_json_explore()
         return false;
     }
 
+    // Allocate memory for each username only if not already allocated
+    if (!flip_social_explore_alloc())
+    {
+        FURI_LOG_E(TAG, "Failed to allocate memory for explore usernames.");
+        return false;
+    }
+
     // Remove newlines
     char *pos = fhttp.received_data;
     while ((pos = strchr(pos, '\n')) != NULL)
@@ -32,7 +70,7 @@ bool flip_social_parse_json_explore()
     }
 
     // Initialize explore count
-    flip_social_explore.count = 0;
+    app_instance->flip_social_explore.count = 0;
 
     // Extract the users array from the JSON
     char *json_users = get_json_value("users", fhttp.received_data, MAX_TOKENS);
@@ -45,74 +83,44 @@ bool flip_social_parse_json_explore()
     // Manual tokenization for comma-separated values
     char *start = json_users + 1; // Skip the opening bracket
     char *end;
-    while ((end = strchr(start, ',')) != NULL)
+    while ((end = strchr(start, ',')) != NULL && app_instance->flip_social_explore.count < MAX_EXPLORE_USERS)
     {
         *end = '\0'; // Null-terminate the current token
 
-        // Remove the quotes
+        // Remove quotes
         if (*start == '"')
-        {
             start++;
-        }
         if (*(end - 1) == '"')
-        {
             *(end - 1) = '\0';
-        }
 
-        // Allocate memory for the username
-        size_t length = strlen(start) + 1;
-        flip_social_explore.usernames[flip_social_explore.count] = malloc(length);
-        if (flip_social_explore.usernames[flip_social_explore.count] == NULL)
-        {
-            FURI_LOG_E(TAG, "Memory allocation failed.");
-            return false;
-        }
-
-        // Copy the username to the allocated memory
-        strncpy(flip_social_explore.usernames[flip_social_explore.count], start, length);
-        flip_social_explore.count++;
-        start = end + 1; // Move to the next token
+        // Copy username to pre-allocated memory
+        strncpy(app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.count], start, MAX_USER_LENGTH - 1);
+        app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.count][MAX_USER_LENGTH - 1] = '\0'; // Ensure null termination
+        app_instance->flip_social_explore.count++;
+        start = end + 1;
     }
 
     // Handle the last token
-    if (*start != '\0')
+    if (*start != '\0' && app_instance->flip_social_explore.count < MAX_EXPLORE_USERS)
     {
-        // Remove the quotes
         if (*start == '"')
-        {
             start++;
-        }
-        // Skip the closing bracket
         if (*(start + strlen(start) - 1) == ']')
-        {
             *(start + strlen(start) - 1) = '\0';
-        }
-        // Remove the quotes
         if (*(start + strlen(start) - 1) == '"')
-        {
             *(start + strlen(start) - 1) = '\0';
-        }
-
-        // Allocate memory for the username
-        size_t length = strlen(start) + 1;
-        flip_social_explore.usernames[flip_social_explore.count] = malloc(length);
-        if (flip_social_explore.usernames[flip_social_explore.count] == NULL)
-        {
-            FURI_LOG_E(TAG, "Memory allocation failed.");
-            return false;
-        }
 
-        // Copy the username to the allocated memory
-        strncpy(flip_social_explore.usernames[flip_social_explore.count], start, length);
-        flip_social_explore.count++;
+        strncpy(app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.count], start, MAX_USER_LENGTH - 1);
+        app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.count][MAX_USER_LENGTH - 1] = '\0'; // Ensure null termination
+        app_instance->flip_social_explore.count++;
     }
 
     // Add submenu items for the users
     submenu_reset(app_instance->submenu_explore);
     submenu_set_header(app_instance->submenu_explore, "Explore");
-    for (uint32_t i = 0; i < flip_social_explore.count; i++)
+    for (int i = 0; i < app_instance->flip_social_explore.count; i++)
     {
-        submenu_add_item(app_instance->submenu_explore, flip_social_explore.usernames[i], FlipSocialSubmenuExploreIndexStartIndex + i, flip_social_callback_submenu_choices, app_instance);
+        submenu_add_item(app_instance->submenu_explore, app_instance->flip_social_explore.usernames[i], FlipSocialSubmenuExploreIndexStartIndex + i, flip_social_callback_submenu_choices, app_instance);
     }
 
     // Free the json_users

+ 109 - 12
flip_social_feed.h

@@ -1,7 +1,93 @@
 #ifndef FLIP_SOCIAL_FEED_H
 #define FLIP_SOCIAL_FEED_H
 
-bool flip_social_get_feed()
+// Set failure FlipSocialFeed object
+static bool flip_social_temp_feed()
+{
+    if (!app_instance)
+    {
+        FURI_LOG_E(TAG, "App instance is NULL");
+        return false;
+    }
+    for (int i = 0; i < 3; i++)
+    {
+        if (app_instance->flip_social_feed.usernames[i] == NULL)
+        {
+            app_instance->flip_social_feed.usernames[i] = malloc(MAX_USER_LENGTH);
+            if (app_instance->flip_social_feed.usernames[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for username %zu", i);
+                return false;
+            }
+        }
+        if (app_instance->flip_social_feed.messages[i] == NULL)
+        {
+            app_instance->flip_social_feed.messages[i] = malloc(MAX_MESSAGE_LENGTH);
+            if (app_instance->flip_social_feed.messages[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for message %zu", i);
+                return false;
+            }
+        }
+    }
+    app_instance->flip_social_feed.usernames[0] = "JBlanked";
+    app_instance->flip_social_feed.usernames[1] = "FlipperKing";
+    app_instance->flip_social_feed.usernames[2] = "FlipperQueen";
+    //
+    app_instance->flip_social_feed.messages[0] = "Welcome. This is a temp message. Either the feed didn't load or there was a server error.";
+    app_instance->flip_social_feed.messages[1] = "I am the Chosen Flipper.";
+    app_instance->flip_social_feed.messages[2] = "No one can flip like me.";
+    //
+    app_instance->flip_social_feed.is_flipped[0] = false;
+    app_instance->flip_social_feed.is_flipped[1] = false;
+    app_instance->flip_social_feed.is_flipped[2] = true;
+    //
+    app_instance->flip_social_feed.ids[0] = 0;
+    app_instance->flip_social_feed.ids[1] = 1;
+    app_instance->flip_social_feed.ids[2] = 2;
+    //
+    app_instance->flip_social_feed.count = 3;
+    app_instance->flip_social_feed.index = 0;
+
+    return true;
+}
+
+// Allocate memory for each feed item if not already allocated
+static bool flip_social_feed_alloc()
+{
+    for (size_t i = 0; i < MAX_FEED_ITEMS; i++)
+    {
+        if (app_instance->flip_social_feed.usernames[i] == NULL)
+        {
+            app_instance->flip_social_feed.usernames[i] = malloc(MAX_USER_LENGTH);
+            if (app_instance->flip_social_feed.usernames[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for username %zu", i);
+                return false;
+            }
+        }
+        if (app_instance->flip_social_feed.messages[i] == NULL)
+        {
+            app_instance->flip_social_feed.messages[i] = malloc(MAX_MESSAGE_LENGTH);
+            if (app_instance->flip_social_feed.messages[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for message %zu", i);
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static void flip_social_free_feed()
+{
+    for (uint32_t i = 0; i < app_instance->flip_social_feed.count; i++)
+    {
+        free(app_instance->flip_social_feed.usernames[i]);
+    }
+}
+
+static bool flip_social_get_feed()
 {
     // Get the feed from the server
     if (app_instance->login_username_logged_out == NULL)
@@ -9,9 +95,9 @@ bool flip_social_get_feed()
         FURI_LOG_E(TAG, "Username is NULL");
         return false;
     }
-    char command[256];
-    snprintf(command, 128, "https://www.flipsocial.net/api/feed/20/%s/", app_instance->login_username_logged_out);
-    bool success = flipper_http_get_request_with_headers(command, "{\"Content-Type\":\"application/json\"}");
+    char command[128];
+    snprintf(command, 128, "https://www.flipsocial.net/api/feed/50/%s/", app_instance->login_username_logged_out);
+    bool success = flipper_http_get_request_with_headers(command, jsmn("Content-Type", "application/json"));
     if (!success)
     {
         FURI_LOG_E(TAG, "Failed to send HTTP request for feed");
@@ -20,7 +106,7 @@ bool flip_social_get_feed()
     fhttp.state = RECEIVING;
     return true;
 }
-bool flip_social_parse_json_feed()
+static bool flip_social_parse_json_feed()
 {
     if (fhttp.received_data == NULL)
     {
@@ -28,6 +114,12 @@ bool flip_social_parse_json_feed()
         return false;
     }
 
+    // Allocate memory for each feed item if not already allocated
+    if (!flip_social_feed_alloc())
+    {
+        return false;
+    }
+
     // Remove newlines
     char *pos = fhttp.received_data;
     while ((pos = strchr(pos, '\n')) != NULL)
@@ -36,7 +128,7 @@ bool flip_social_parse_json_feed()
     }
 
     // Initialize feed count
-    flip_social_feed.count = 0;
+    app_instance->flip_social_feed.count = 0;
 
     // Iterate through the feed array
     for (int i = 0; i < MAX_FEED_ITEMS; i++)
@@ -58,16 +150,21 @@ bool flip_social_parse_json_feed()
         {
             FURI_LOG_E(TAG, "Failed to parse item fields.");
             free(item);
+            free(username);
+            free(message);
+            free(flipped);
+            free(id);
             continue;
         }
 
-        // Store parsed values
-        flip_social_feed.usernames[i] = username;
-        flip_social_feed.messages[i] = message;
-        flip_social_feed.is_flipped[i] = strstr(flipped, "true") != NULL;
-        flip_social_feed.ids[i] = atoi(id);
-        flip_social_feed.count++;
+        // Copy parsed values into allocated memory
+        app_instance->flip_social_feed.usernames[i] = username;
+        app_instance->flip_social_feed.messages[i] = message;
+        app_instance->flip_social_feed.is_flipped[i] = strstr(flipped, "true") != NULL;
+        app_instance->flip_social_feed.ids[i] = atoi(id);
+        app_instance->flip_social_feed.count++;
 
+        // Free temporary JSON value
         free(item);
     }
 

+ 38 - 0
flip_social_free.h

@@ -53,6 +53,16 @@ static void flip_social_app_free(FlipSocialApp *app)
         view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInFriendsSubmenu);
         submenu_free(app->submenu_friends);
     }
+    if (app->submenu_messages)
+    {
+        view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesSubmenu);
+        submenu_free(app->submenu_messages);
+    }
+    if (app->submenu_messages_user_choices)
+    {
+        view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesUserChoices);
+        submenu_free(app->submenu_messages_user_choices);
+    }
 
     // Free Variable Item List(s)
     if (app->variable_item_list_logged_out_wifi_settings)
@@ -142,6 +152,16 @@ static void flip_social_app_free(FlipSocialApp *app)
         view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInWifiSettingsPasswordInput);
         uart_text_input_free(app->text_input_logged_in_wifi_settings_password);
     }
+    if (app->text_input_logged_in_messages_new_message)
+    {
+        view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesNewMessageInput);
+        uart_text_input_free(app->text_input_logged_in_messages_new_message);
+    }
+    if (app->text_input_logged_in_messages_new_message_user_choices)
+    {
+        view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesNewMessageUserChoicesInput);
+        uart_text_input_free(app->text_input_logged_in_messages_new_message_user_choices);
+    }
 
     // Free Widget(s)
     if (app->widget_logged_out_about)
@@ -186,6 +206,11 @@ static void flip_social_app_free(FlipSocialApp *app)
         view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInFriendsProcess);
         view_free(app->view_process_friends);
     }
+    if (app->view_process_messages)
+    {
+        view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInMessagesProcess);
+        view_free(app->view_process_messages);
+    }
 
     if (app->view_dispatcher)
         view_dispatcher_free(app->view_dispatcher);
@@ -241,6 +266,14 @@ static void flip_social_app_free(FlipSocialApp *app)
         free(app->login_username_logged_in);
     if (app->login_username_logged_in_temp_buffer)
         free(app->login_username_logged_in_temp_buffer);
+    if (app->messages_new_message_logged_in)
+        free(app->messages_new_message_logged_in);
+    if (app->messages_new_message_logged_in_temp_buffer)
+        free(app->messages_new_message_logged_in_temp_buffer);
+    if (app->message_user_choice_logged_in)
+        free(app->message_user_choice_logged_in);
+    if (app->message_user_choice_logged_in_temp_buffer)
+        free(app->message_user_choice_logged_in_temp_buffer);
 
     if (app->input_event && app->input_event_queue)
         furi_pubsub_unsubscribe(app->input_event_queue, app->input_event);
@@ -250,6 +283,11 @@ static void flip_social_app_free(FlipSocialApp *app)
         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();

+ 66 - 49
flip_social_friends.h

@@ -1,6 +1,37 @@
 #ifndef FLIP_SOCIAL_FRIENDS
 #define FLIP_SOCIAL_FRIENDS
 
+static bool flip_social_friends_alloc()
+{
+    // Allocate memory for each username only if not already allocated
+    for (size_t i = 0; i < MAX_FRIENDS; i++)
+    {
+        if (app_instance->flip_social_friends.usernames[i] == NULL)
+        {
+            app_instance->flip_social_friends.usernames[i] = malloc(MAX_USER_LENGTH);
+            if (app_instance->flip_social_friends.usernames[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for username %zu", i);
+                return false; // Return false on memory allocation failure
+            }
+        }
+    }
+    return true;
+}
+
+static void flip_social_free_friends()
+{
+    if (!app_instance)
+    {
+        FURI_LOG_E(TAG, "App instance is NULL");
+        return;
+    }
+    for (int i = 0; i < app_instance->flip_social_friends.count; i++)
+    {
+        free(app_instance->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()
@@ -8,7 +39,7 @@ static bool flip_social_get_friends()
     // will return true unless the devboard is not connected
     char url[100];
     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, "{\"Content-Type\":\"application/json\"}");
+    bool success = flipper_http_get_request_with_headers(url, jsmn("Content-Type", "application/json"));
     if (!success)
     {
         FURI_LOG_E(TAG, "Failed to send HTTP request for friends");
@@ -18,15 +49,21 @@ static bool flip_social_get_friends()
     return true;
 }
 
-static void flip_social_update_friends()
+static bool flip_social_update_friends()
 {
+    if (!app_instance->submenu_friends)
+    {
+        FURI_LOG_E(TAG, "Friends submenu is NULL");
+        return false;
+    }
     // Add submenu items for the users
     submenu_reset(app_instance->submenu_friends);
     submenu_set_header(app_instance->submenu_friends, "Friends");
-    for (uint32_t i = 0; i < flip_social_friends.count; i++)
+    for (int i = 0; i < app_instance->flip_social_friends.count; i++)
     {
-        submenu_add_item(app_instance->submenu_friends, flip_social_friends.usernames[i], FlipSocialSubmenuLoggedInIndexFriendsStart + i, flip_social_callback_submenu_choices, app_instance);
+        submenu_add_item(app_instance->submenu_friends, app_instance->flip_social_friends.usernames[i], FlipSocialSubmenuLoggedInIndexFriendsStart + i, flip_social_callback_submenu_choices, app_instance);
     }
+    return true;
 }
 
 static bool flip_social_parse_json_friends()
@@ -37,6 +74,13 @@ static bool flip_social_parse_json_friends()
         return false;
     }
 
+    // Allocate memory for each username only if not already allocated
+    if (!flip_social_friends_alloc())
+    {
+        FURI_LOG_E(TAG, "Failed to allocate memory for friends usernames.");
+        return false;
+    }
+
     // Remove newlines
     char *pos = fhttp.received_data;
     while ((pos = strchr(pos, '\n')) != NULL)
@@ -45,7 +89,7 @@ static bool flip_social_parse_json_friends()
     }
 
     // Initialize friends count
-    flip_social_friends.count = 0;
+    app_instance->flip_social_friends.count = 0;
 
     // Extract the users array from the JSON
     char *json_users = get_json_value("friends", fhttp.received_data, MAX_TOKENS);
@@ -58,75 +102,48 @@ static bool flip_social_parse_json_friends()
     // Manual tokenization for comma-separated values
     char *start = json_users + 1; // Skip the opening bracket
     char *end;
-    while ((end = strchr(start, ',')) != NULL)
+    while ((end = strchr(start, ',')) != NULL && app_instance->flip_social_friends.count < MAX_FRIENDS)
     {
         *end = '\0'; // Null-terminate the current token
 
-        // Remove the quotes
+        // Remove quotes
         if (*start == '"')
-        {
             start++;
-        }
         if (*(end - 1) == '"')
-        {
             *(end - 1) = '\0';
-        }
 
-        // Allocate memory for the username
-        size_t length = strlen(start) + 1;
-        flip_social_friends.usernames[flip_social_friends.count] = malloc(length);
-        if (flip_social_friends.usernames[flip_social_friends.count] == NULL)
-        {
-            FURI_LOG_E(TAG, "Memory allocation failed.");
-            return false;
-        }
-
-        // Copy the username to the allocated memory
-        strncpy(flip_social_friends.usernames[flip_social_friends.count], start, length);
-        flip_social_friends.count++;
-        start = end + 1; // Move to the next token
+        // Copy username to pre-allocated memory
+        strncpy(app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.count], start, MAX_USER_LENGTH - 1);
+        app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.count][MAX_USER_LENGTH - 1] = '\0'; // Ensure null termination
+        app_instance->flip_social_friends.count++;
+        start = end + 1;
     }
 
     // Handle the last token
-    if (*start != '\0')
+    if (*start != '\0' && app_instance->flip_social_friends.count < MAX_FRIENDS)
     {
-        // Remove the quotes
         if (*start == '"')
-        {
             start++;
-        }
-        // Skip the closing bracket
         if (*(start + strlen(start) - 1) == ']')
-        {
             *(start + strlen(start) - 1) = '\0';
-        }
-        // Remove the quotes
         if (*(start + strlen(start) - 1) == '"')
-        {
             *(start + strlen(start) - 1) = '\0';
-        }
-
-        // Allocate memory for the username
-        size_t length = strlen(start) + 1;
-        flip_social_friends.usernames[flip_social_friends.count] = malloc(length);
-        if (flip_social_friends.usernames[flip_social_friends.count] == NULL)
-        {
-            FURI_LOG_E(TAG, "Memory allocation failed.");
-            return false;
-        }
 
-        // Copy the username to the allocated memory
-        strncpy(flip_social_friends.usernames[flip_social_friends.count], start, length);
-        flip_social_friends.count++;
+        strncpy(app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.count], start, MAX_USER_LENGTH - 1);
+        app_instance->flip_social_friends.usernames[app_instance->flip_social_friends.count][MAX_USER_LENGTH - 1] = '\0'; // Ensure null termination
+        app_instance->flip_social_friends.count++;
     }
 
-    // Add submenu items for the users
-    flip_social_update_friends();
+    // Add submenu items for the friends
+    if (!flip_social_update_friends())
+    {
+        FURI_LOG_E(TAG, "Failed to update friends submenu");
+        return false;
+    }
 
     // Free the json_users
     free(json_users);
 
     return true;
 }
-
 #endif // FLIP_SOCIAL_FRIENDS

+ 87 - 17
flip_social_i.h

@@ -29,20 +29,21 @@ static FlipSocialApp *flip_social_app_alloc()
     }
 
     // Allocate the text input buffers
-    app->wifi_ssid_logged_out_temp_buffer_size = 64;
-    app->wifi_password_logged_out_temp_buffer_size = 64;
-    app->login_username_logged_out_temp_buffer_size = 64;
-    app->login_password_logged_out_temp_buffer_size = 64;
-    app->register_username_logged_out_temp_buffer_size = 64;
-    app->register_password_logged_out_temp_buffer_size = 64;
-    app->register_password_2_logged_out_temp_buffer_size = 64;
-    app->change_password_logged_in_temp_buffer_size = 64;
-    app->compose_pre_save_logged_in_temp_buffer_size = 64;
-    app->wifi_ssid_logged_in_temp_buffer_size = 64;
-    app->wifi_password_logged_in_temp_buffer_size = 64;
+    app->wifi_ssid_logged_out_temp_buffer_size = MAX_USER_LENGTH;
+    app->wifi_password_logged_out_temp_buffer_size = MAX_USER_LENGTH;
+    app->login_username_logged_out_temp_buffer_size = MAX_USER_LENGTH;
+    app->login_password_logged_out_temp_buffer_size = MAX_USER_LENGTH;
+    app->register_username_logged_out_temp_buffer_size = MAX_USER_LENGTH;
+    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->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 = 64;
-
+    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;
     if (!easy_flipper_set_buffer(&app->wifi_ssid_logged_out_temp_buffer, app->wifi_ssid_logged_out_temp_buffer_size))
     {
         return NULL;
@@ -144,13 +145,30 @@ static FlipSocialApp *flip_social_app_alloc()
     {
         return NULL;
     }
+    //
+    if (!easy_flipper_set_buffer(&app->messages_new_message_logged_in, app->messages_new_message_logged_in_temp_buffer_size))
+    {
+        return NULL;
+    }
+    if (!easy_flipper_set_buffer(&app->messages_new_message_logged_in_temp_buffer, app->messages_new_message_logged_in_temp_buffer_size))
+    {
+        return NULL;
+    }
+    if (!easy_flipper_set_buffer(&app->message_user_choice_logged_in, app->message_user_choice_logged_in_temp_buffer_size))
+    {
+        return NULL;
+    }
+    if (!easy_flipper_set_buffer(&app->message_user_choice_logged_in_temp_buffer, 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.3", flip_social_callback_exit_app, &app->view_dispatcher))
+    if (!easy_flipper_set_submenu(&app->submenu_logged_out, FlipSocialViewLoggedOutSubmenu, "FlipSocial v0.4", flip_social_callback_exit_app, &app->view_dispatcher))
     {
         return NULL;
     }
-    if (!easy_flipper_set_submenu(&app->submenu_logged_in, FlipSocialViewLoggedInSubmenu, "FlipSocial v0.3", flip_social_callback_exit_app, &app->view_dispatcher))
+    if (!easy_flipper_set_submenu(&app->submenu_logged_in, FlipSocialViewLoggedInSubmenu, "FlipSocial v0.4", flip_social_callback_exit_app, &app->view_dispatcher))
     {
         return NULL;
     }
@@ -166,6 +184,14 @@ static FlipSocialApp *flip_social_app_alloc()
     {
         return NULL;
     }
+    if (!easy_flipper_set_submenu(&app->submenu_messages, FlipSocialViewLoggedInMessagesSubmenu, "Messages", flip_social_callback_to_submenu_logged_in, &app->view_dispatcher))
+    {
+        return NULL;
+    }
+    if (!easy_flipper_set_submenu(&app->submenu_messages_user_choices, FlipSocialViewLoggedInMessagesUserChoices, "Users", flip_social_callback_to_messages_logged_in, &app->view_dispatcher))
+    {
+        return NULL;
+    }
 
     submenu_add_item(app->submenu_logged_out, "Login", FlipSocialSubmenuLoggedOutIndexLogin, flip_social_callback_submenu_choices, app);
     submenu_add_item(app->submenu_logged_out, "Register", FlipSocialSubmenuLoggedOutIndexRegister, flip_social_callback_submenu_choices, app);
@@ -175,6 +201,7 @@ static FlipSocialApp *flip_social_app_alloc()
     submenu_add_item(app->submenu_logged_in, "Explore", FlipSocialSubmenuExploreIndex, flip_social_callback_submenu_choices, app);
     submenu_add_item(app->submenu_logged_in, "Feed", FlipSocialSubmenuLoggedInIndexFeed, flip_social_callback_submenu_choices, app);
     submenu_add_item(app->submenu_logged_in, "Post", FlipSocialSubmenuLoggedInIndexCompose, flip_social_callback_submenu_choices, app);
+    submenu_add_item(app->submenu_logged_in, "Messages", FlipSocialSubmenuLoggedInIndexMessages, flip_social_callback_submenu_choices, app);
     submenu_add_item(app->submenu_logged_in, "Profile", FlipSocialSubmenuLoggedInIndexProfile, flip_social_callback_submenu_choices, app);
     submenu_add_item(app->submenu_logged_in, "Settings", FlipSocialSubmenuLoggedInIndexSettings, flip_social_callback_submenu_choices, app);
     submenu_add_item(app->submenu_logged_in, "Sign Out", FlipSocialSubmenuLoggedInSignOutButton, flip_social_callback_submenu_choices, app);
@@ -207,6 +234,10 @@ static FlipSocialApp *flip_social_app_alloc()
     {
         return NULL;
     }
+    if (!easy_flipper_set_view(&app->view_process_messages, FlipSocialViewLoggedInMessagesProcess, flip_social_callback_draw_messages, NULL, flip_social_callback_to_messages_logged_in, &app->view_dispatcher, app))
+    {
+        return NULL;
+    }
 
     // Setup Variable Item List(s)
     if (!easy_flipper_set_variable_item_list(&app->variable_item_list_logged_out_wifi_settings, FlipSocialViewLoggedOutWifiSettings, flip_social_text_input_logged_out_wifi_settings_item_selected, flip_social_callback_to_submenu_logged_out, &app->view_dispatcher, app))
@@ -302,6 +333,15 @@ static FlipSocialApp *flip_social_app_alloc()
     {
         return NULL;
     }
+    //
+    if (!easy_flipper_set_uart_text_input(&app->text_input_logged_in_messages_new_message, FlipSocialViewLoggedInMessagesNewMessageInput, "Enter Message", app->messages_new_message_logged_in_temp_buffer, app->messages_new_message_logged_in_temp_buffer_size, flip_social_logged_in_messages_new_message_updated, flip_social_callback_to_messages_logged_in, &app->view_dispatcher, app))
+    {
+        return NULL;
+    }
+    if (!easy_flipper_set_uart_text_input(&app->text_input_logged_in_messages_new_message_user_choices, FlipSocialViewLoggedInMessagesNewMessageUserChoicesInput, "Enter Message", app->message_user_choice_logged_in_temp_buffer, app->message_user_choice_logged_in_temp_buffer_size, flip_social_logged_in_messages_user_choice_message_updated, flip_social_callback_to_messages_user_choices, &app->view_dispatcher, app))
+    {
+        return NULL;
+    }
 
     // Setup About(s)
     if (!easy_flipper_set_widget(&app->widget_logged_out_about, FlipSocialViewLoggedOutAbout, "Welcome to FlipSocial\n---\nThe social media app for\nFlipper Zero, created by\nJBlanked.\n---\nPress BACK to return.", flip_social_callback_to_submenu_logged_out, &app->view_dispatcher))
@@ -316,7 +356,7 @@ static FlipSocialApp *flip_social_app_alloc()
     // load the playlist
     if (load_playlist(&app->pre_saved_messages))
     {
-        // Update the submenu
+        // Update the playlist submenu
         for (uint32_t i = 0; i < app->pre_saved_messages.count; i++)
         {
             submenu_add_item(app->submenu_compose, app->pre_saved_messages.messages[i], FlipSocialSubemnuComposeIndexStartIndex + i, flip_social_callback_submenu_choices, app);
@@ -342,8 +382,9 @@ static FlipSocialApp *flip_social_app_alloc()
     {
         FURI_LOG_E(TAG, "Failed to load settings");
 
-        if (app->is_logged_in != NULL)
+        if (app->is_logged_in == NULL)
         {
+            app->is_logged_in = (char *)malloc(app->is_logged_in_size);
             app->is_logged_in = "false";
         }
         app_instance = app;
@@ -486,6 +527,35 @@ static FlipSocialApp *flip_social_app_alloc()
         //
 
         app_instance = app;
+
+        // Initialize structs
+        if (!flip_social_feed_alloc())
+        {
+            return NULL;
+        }
+        if (!flip_social_friends_alloc())
+        {
+            return NULL;
+        }
+        if (!flip_social_explore_alloc())
+        {
+            return NULL;
+        }
+        if (!flip_social_messages_alloc())
+        {
+            return NULL;
+        }
+        if (!flip_social_user_messages_alloc())
+        {
+            return NULL;
+        }
+
+        // Set failure FlipSocialFeed object
+        if (!flip_social_temp_feed())
+        {
+            return NULL;
+        }
+
         if (app->is_logged_in != NULL && strcmp(app->is_logged_in, "true") == 0)
         {
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInSubmenu);

+ 356 - 0
flip_social_messages.h

@@ -0,0 +1,356 @@
+#ifndef FLIP_SOCIAL_MESSAGES_H
+#define FLIP_SOCIAL_MESSAGES_H
+
+static bool flip_social_messages_alloc()
+{
+    // Allocate memory for each username only if not already allocated
+    for (size_t i = 0; i < MAX_MESSAGE_USERS; i++)
+    {
+        if (app_instance->flip_social_message_users.usernames[i] == NULL)
+        {
+            app_instance->flip_social_message_users.usernames[i] = malloc(MAX_USER_LENGTH);
+            if (app_instance->flip_social_message_users.usernames[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for username %zu", i);
+                return false; // Return false on memory allocation failure
+            }
+        }
+    }
+    return true;
+}
+
+static bool flip_social_user_messages_alloc()
+{
+    // Allocate memory for each username only if not already allocated
+    for (size_t i = 0; i < MAX_MESSAGE_USERS; i++)
+    {
+        if (app_instance->flip_social_messages.usernames[i] == NULL)
+        {
+            app_instance->flip_social_messages.usernames[i] = malloc(MAX_USER_LENGTH);
+            if (app_instance->flip_social_messages.usernames[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for username %zu", i);
+                return false; // Return false on memory allocation failure
+            }
+        }
+        if (app_instance->flip_social_messages.messages[i] == NULL)
+        {
+            app_instance->flip_social_messages.messages[i] = malloc(MAX_MESSAGE_LENGTH);
+            if (app_instance->flip_social_messages.messages[i] == NULL)
+            {
+                FURI_LOG_E(TAG, "Failed to allocate memory for message %zu", i);
+                return false; // Return false on memory allocation failure
+            }
+        }
+    }
+    return true;
+}
+
+static void flip_social_free_message_users()
+{
+    if (!app_instance)
+    {
+        FURI_LOG_E(TAG, "App instance is NULL");
+        return;
+    }
+    for (int i = 0; i < app_instance->flip_social_message_users.count; i++)
+    {
+        free(app_instance->flip_social_message_users.usernames[i]);
+    }
+}
+
+static void flip_social_free_messages()
+{
+    if (!app_instance)
+    {
+        FURI_LOG_E(TAG, "App instance is NULL");
+        return;
+    }
+    for (int i = 0; i < app_instance->flip_social_messages.count; i++)
+    {
+        free(app_instance->flip_social_messages.usernames[i]);
+        free(app_instance->flip_social_messages.messages[i]);
+    }
+}
+
+static bool flip_social_update_messages_submenu()
+{
+    if (app_instance->submenu_messages == NULL)
+    {
+        FURI_LOG_E(TAG, "Submenu is NULL");
+        return false;
+    }
+    submenu_reset(app_instance->submenu_messages);
+    submenu_set_header(app_instance->submenu_messages, "Messages");
+    submenu_add_item(app_instance->submenu_messages, "[New Message]", FlipSocialSubmenuLoggedInIndexMessagesNewMessage, flip_social_callback_submenu_choices, app_instance);
+    for (int i = 0; i < app_instance->flip_social_message_users.count; i++)
+    {
+        submenu_add_item(app_instance->submenu_messages, app_instance->flip_social_message_users.usernames[i], FlipSocialSubmenuLoggedInIndexMessagesUsersStart + i, flip_social_callback_submenu_choices, app_instance);
+    }
+    return true;
+}
+
+static bool flip_social_update_submenu_user_choices()
+{
+    if (app_instance->submenu_messages_user_choices == NULL)
+    {
+        FURI_LOG_E(TAG, "Submenu is NULL");
+        return false;
+    }
+    submenu_reset(app_instance->submenu_messages_user_choices);
+    submenu_set_header(app_instance->submenu_messages_user_choices, "Users");
+    for (int i = 0; i < app_instance->flip_social_explore.count; i++)
+    {
+        submenu_add_item(app_instance->submenu_messages_user_choices, app_instance->flip_social_explore.usernames[i], FlipSocialSubmenuLoggedInIndexMessagesUserChoicesIndexStart + i, flip_social_callback_submenu_choices, app_instance);
+    }
+    return true;
+}
+
+// Get all the users that have sent messages to the logged in user
+static 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(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\"}");
+    if (!success)
+    {
+        FURI_LOG_E(TAG, "Failed to send HTTP request for messages");
+        return false;
+    }
+    fhttp.state = RECEIVING;
+    return true;
+}
+
+// Get all the messages between the logged in user and the selected user
+static 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(command, 128, "https://www.flipsocial.net/api/messages/%s/get/%s/", app_instance->login_username_logged_out, app_instance->flip_social_message_users.usernames[app_instance->flip_social_message_users.index]);
+    bool success = flipper_http_get_request_with_headers(command, "{\"Content-Type\":\"application/json\"}");
+    if (!success)
+    {
+        FURI_LOG_E(TAG, "Failed to send HTTP request for messages");
+        return false;
+    }
+    fhttp.state = RECEIVING;
+    return true;
+}
+
+// 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.");
+        return false;
+    }
+
+    // Allocate memory for each username only if not already allocated
+    if (!flip_social_messages_alloc())
+    {
+        FURI_LOG_E(TAG, "Failed to allocate memory for message users.");
+        return false;
+    }
+
+    // Remove newlines
+    char *pos = fhttp.received_data;
+    while ((pos = strchr(pos, '\n')) != NULL)
+    {
+        *pos = ' ';
+    }
+
+    // Initialize message users count
+    app_instance->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);
+    if (json_users == NULL)
+    {
+        FURI_LOG_E(TAG, "Failed to parse users array.");
+        return false;
+    }
+
+    // Manual tokenization for comma-separated values
+    char *start = json_users + 1; // Skip the opening bracket
+    char *end;
+    while ((end = strchr(start, ',')) != NULL && app_instance->flip_social_message_users.count < MAX_MESSAGE_USERS)
+    {
+        *end = '\0'; // Null-terminate the current token
+
+        // Remove quotes
+        if (*start == '"')
+            start++;
+        if (*(end - 1) == '"')
+            *(end - 1) = '\0';
+
+        // Copy username to pre-allocated memory
+        strncpy(app_instance->flip_social_message_users.usernames[app_instance->flip_social_message_users.count], start, MAX_USER_LENGTH - 1);
+        app_instance->flip_social_message_users.usernames[app_instance->flip_social_message_users.count][MAX_USER_LENGTH - 1] = '\0'; // Ensure null termination
+        app_instance->flip_social_message_users.count++;
+        start = end + 1;
+    }
+
+    // Handle the last token
+    if (*start != '\0' && app_instance->flip_social_message_users.count < MAX_MESSAGE_USERS)
+    {
+        if (*start == '"')
+            start++;
+        if (*(start + strlen(start) - 1) == ']')
+            *(start + strlen(start) - 1) = '\0';
+        if (*(start + strlen(start) - 1) == '"')
+            *(start + strlen(start) - 1) = '\0';
+
+        strncpy(app_instance->flip_social_message_users.usernames[app_instance->flip_social_message_users.count], start, MAX_USER_LENGTH - 1);
+        app_instance->flip_social_message_users.usernames[app_instance->flip_social_message_users.count][MAX_USER_LENGTH - 1] = '\0'; // Ensure null termination
+        app_instance->flip_social_message_users.count++;
+    }
+
+    // Add submenu items for the users
+    flip_social_update_messages_submenu();
+
+    // Free the JSON data
+    free(json_users);
+
+    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.");
+        return false;
+    }
+
+    // Allocate memory for each username only if not already allocated
+    flip_social_explore_alloc();
+
+    // Remove newlines
+    char *pos = fhttp.received_data;
+    while ((pos = strchr(pos, '\n')) != NULL)
+    {
+        *pos = ' ';
+    }
+
+    // Initialize explore count
+    app_instance->flip_social_explore.count = 0;
+
+    // Extract the users array from the JSON
+    char *json_users = get_json_value("users", fhttp.received_data, MAX_TOKENS);
+    if (json_users == NULL)
+    {
+        FURI_LOG_E(TAG, "Failed to parse users array.");
+        return false;
+    }
+
+    // Manual tokenization for comma-separated values
+    char *start = json_users + 1; // Skip the opening bracket
+    char *end;
+    while ((end = strchr(start, ',')) != NULL && app_instance->flip_social_explore.count < MAX_EXPLORE_USERS)
+    {
+        *end = '\0'; // Null-terminate the current token
+
+        // Remove quotes
+        if (*start == '"')
+            start++;
+        if (*(end - 1) == '"')
+            *(end - 1) = '\0';
+
+        // Copy username to pre-allocated memory
+        strncpy(app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.count], start, MAX_USER_LENGTH - 1);
+        app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.count][MAX_USER_LENGTH - 1] = '\0'; // Ensure null termination
+        app_instance->flip_social_explore.count++;
+        start = end + 1;
+    }
+
+    // Handle the last token
+    if (*start != '\0' && app_instance->flip_social_explore.count < MAX_EXPLORE_USERS)
+    {
+        if (*start == '"')
+            start++;
+        if (*(start + strlen(start) - 1) == ']')
+            *(start + strlen(start) - 1) = '\0';
+        if (*(start + strlen(start) - 1) == '"')
+            *(start + strlen(start) - 1) = '\0';
+
+        strncpy(app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.count], start, MAX_USER_LENGTH - 1);
+        app_instance->flip_social_explore.usernames[app_instance->flip_social_explore.count][MAX_USER_LENGTH - 1] = '\0'; // Ensure null termination
+        app_instance->flip_social_explore.count++;
+    }
+
+    // Add submenu items for the users
+    flip_social_update_submenu_user_choices();
+
+    // Free the JSON data
+    free(json_users);
+
+    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.");
+        return false;
+    }
+
+    // Remove newlines
+    char *pos = fhttp.received_data;
+    while ((pos = strchr(pos, '\n')) != NULL)
+    {
+        *pos = ' ';
+    }
+
+    // Initialize messages count
+    app_instance->flip_social_messages.count = 0;
+
+    // example response:
+
+    // {'conversations': [{'sender': 'Zett', 'content': 'Hello JBlanked'}, {'sender': 'Zett', 'content': 'Received bro.'}, {'sender': 'JBlanked', 'content': 'Yoo testing'}]}
+
+    // 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);
+        if (item == NULL)
+        {
+            break;
+        }
+
+        // Extract individual fields from the JSON object
+        char *sender = get_json_value("sender", item, MAX_TOKENS);
+        char *content = get_json_value("content", item, MAX_TOKENS);
+
+        if (sender == NULL || content == NULL)
+        {
+            FURI_LOG_E(TAG, "Failed to parse item fields.");
+            free(item);
+            continue;
+        }
+
+        // Store parsed values
+        app_instance->flip_social_messages.usernames[i] = sender;
+        app_instance->flip_social_messages.messages[i] = content;
+        app_instance->flip_social_messages.count++;
+
+        free(item);
+    }
+
+    return true;
+}
+
+#endif // FLIP_SOCIAL_MESSAGES_H

+ 2 - 4
flip_social_storage.h

@@ -17,7 +17,7 @@ void save_playlist(const PreSavedPlaylist *playlist)
         return;
     }
     // Create the directory for saving settings
-    char directory_path[256];
+    char directory_path[128];
     snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social");
 
     // Create the directory
@@ -50,8 +50,6 @@ void save_playlist(const PreSavedPlaylist *playlist)
         }
     }
 
-    FURI_LOG_I(TAG, "Playlist saved: playlist_count=%zu", playlist->count);
-
     storage_file_close(file);
     storage_file_free(file);
     furi_record_close(RECORD_STORAGE);
@@ -156,7 +154,7 @@ static void save_settings(
     const char *is_logged_in)
 {
     // Create the directory for saving settings
-    char directory_path[256];
+    char directory_path[128];
     snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social");
 
     // Create the directory

+ 40 - 1
flipper_http.h

@@ -42,6 +42,10 @@ bool flipper_http_delete_request_with_headers(const char *url, const char *heade
 //---
 bool flipper_http_save_received_data(size_t bytes_received, const char line_buffer[]);
 static char *trim(const char *str);
+//
+bool flipper_http_process_response_async(
+    bool (*http_request)(void),
+    bool (*parse_json)(void));
 
 // State variable to track the UART state
 typedef enum
@@ -173,7 +177,7 @@ static int32_t flipper_http_worker(void *context)
             break;
         if (events & WorkerEvtRxDone)
         {
-            size_t len = furi_stream_buffer_receive(fhttp.flipper_http_stream, fhttp.rx_buf, RX_BUF_SIZE, 0);
+            size_t len = furi_stream_buffer_receive(fhttp.flipper_http_stream, fhttp.rx_buf, RX_BUF_SIZE + 1, 0);
             for (size_t i = 0; i < len; i++)
             {
                 char c = fhttp.rx_buf[i];
@@ -1155,4 +1159,39 @@ char *trim(const char *str)
     return trimmed_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))
+{
+    if (http_request()) // start the async request
+    {
+        furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
+        fhttp.state = RECEIVING;
+    }
+    else
+    {
+        FURI_LOG_E(HTTP_TAG, "Failed to send request");
+        return false;
+    }
+    while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0)
+    {
+        // Wait for the request to be received
+        furi_delay_ms(100);
+    }
+    furi_timer_stop(fhttp.get_timeout_timer);
+    if (!parse_json()) // parse the JSON before switching to the view (synchonous)
+    {
+        FURI_LOG_E(HTTP_TAG, "Failed to parse the JSON...");
+        return false;
+    }
+    furi_timer_stop(fhttp.get_timeout_timer);
+    return true;
+}
+
 #endif // FLIPPER_HTTP_H

+ 15 - 0
jsmn.h

@@ -535,8 +535,23 @@ extern "C"
 #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
+  char *result = (char *)malloc(length * sizeof(char)); // Allocate memory
+  if (result == NULL)
+  {
+    return NULL; // Handle memory allocation failure
+  }
+  snprintf(result, length, "{\"%s\":\"%s\"}", key, value);
+  return result; // Caller is responsible for freeing this memory
+}
+
 // Helper function to compare JSON keys
 int jsoneq(const char *json, jsmntok_t *tok, const char *s)
 {