Browse Source

FlipSocial - v0.4 (Direct Messaging)

jblanked 1 năm trước cách đây
mục cha
commit
4a4607ef2d
20 tập tin đã thay đổi với 1284 bổ sung302 xóa
  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

BIN
.DS_Store


+ 4 - 0
.gitignore

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

+ 36 - 23
README.md

@@ -1,8 +1,12 @@
 # FlipSocial
 # 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
 ## 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
 - WiFi Access Point
 
 
 ## Features
 ## Features
@@ -13,21 +17,21 @@ The first social media app for Flipper Zero. Connect with other users directly o
 - Customizable Pre-Saves
 - Customizable Pre-Saves
 - Explore (NEW)
 - Explore (NEW)
 - Friends (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.
 **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.
 **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.
 **Explore:** Discover other users and add them as friends.
 
 
 **Friends:** View and remove friends.
 **Friends:** View and remove friends.
 
 
-
+**Direct Messaging:** Send direct messages to other Flipper users and view your conversations.
 
 
 ## Roadmap
 ## Roadmap
 **v0.2**
 **v0.2**
@@ -39,35 +43,44 @@ The first social media app for Flipper Zero. Connect with other users directly o
 
 
 **v0.4**
 **v0.4**
 - Direct Messaging
 - Direct Messaging
-- Privacy Settings
 
 
 **v0.5**
 **v0.5**
-- Improved Explore Page
+- Improved memory allocation
+- Improved Feed Page (Flip count, report, block)
 
 
 **v0.6**
 **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
 ## 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
 ## 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.
 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."
 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.
 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_feed.h"
 #include "flip_social_explore.h"
 #include "flip_social_explore.h"
 #include "flip_social_friends.h"
 #include "flip_social_friends.h"
+#include "flip_social_messages.h"
 #include <flip_social_callback.h> // Include the callback functions
 #include <flip_social_callback.h> // Include the callback functions
 #include <flip_social_i.h>        // Include the initialization functions
 #include <flip_social_i.h>        // Include the initialization functions
 #include <flip_social_free.h>     // Include the cleanup functions
 #include <flip_social_free.h>     // Include the cleanup functions

+ 1 - 1
application.fam

@@ -9,6 +9,6 @@ App(
     fap_icon_assets="assets",
     fap_icon_assets="assets",
     fap_author="jblanked",
     fap_author="jblanked",
     fap_weburl="https://github.com/jblanked/FlipSocial",
     fap_weburl="https://github.com/jblanked/FlipSocial",
-    fap_version="0.3",
+    fap_version="0.4",
     fap_description="Social media platform for the Flipper Zero.",
     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
 ## 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
 ## 0.2
-- Stability patch.
+- Stability improvements.
 
 
 ## 0.1
 ## 0.1
 - Initial release.
 - Initial release.

+ 36 - 23
assets/README.md

@@ -1,8 +1,12 @@
 # FlipSocial
 # 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
 ## 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
 - WiFi Access Point
 
 
 ## Features
 ## Features
@@ -13,21 +17,21 @@ The first social media app for Flipper Zero. Connect with other users directly o
 - Customizable Pre-Saves
 - Customizable Pre-Saves
 - Explore (NEW)
 - Explore (NEW)
 - Friends (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.
 **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.
 **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.
 **Explore:** Discover other users and add them as friends.
 
 
 **Friends:** View and remove friends.
 **Friends:** View and remove friends.
 
 
-
+**Direct Messaging:** Send direct messages to other Flipper users and view your conversations.
 
 
 ## Roadmap
 ## Roadmap
 **v0.2**
 **v0.2**
@@ -39,35 +43,44 @@ The first social media app for Flipper Zero. Connect with other users directly o
 
 
 **v0.4**
 **v0.4**
 - Direct Messaging
 - Direct Messaging
-- Privacy Settings
 
 
 **v0.5**
 **v0.5**
-- Improved Explore Page
+- Improved memory allocation
+- Improved Feed Page (Flip count, report, block)
 
 
 **v0.6**
 **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
 ## 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
 ## 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.
 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."
 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.
 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 <uart_text_input.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
+#include <jsmn.h>
 
 
 #define EASY_TAG "EasyFlipper"
 #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;
     flip_social_dialog_stop = true;
     last_explore_response = "";
     last_explore_response = "";
     flip_social_dialog_shown = false;
     flip_social_dialog_shown = false;
-    flip_social_explore.index = 0;
+    app_instance->flip_social_explore.index = 0;
     action = ActionNone;
     action = ActionNone;
     return FlipSocialViewLoggedInExploreSubmenu;
     return FlipSocialViewLoggedInExploreSubmenu;
 }
 }
@@ -133,10 +133,33 @@ static uint32_t flip_social_callback_to_friends_logged_in(void *context)
     flip_social_dialog_stop = true;
     flip_social_dialog_stop = true;
     last_explore_response = "";
     last_explore_response = "";
     flip_social_dialog_shown = false;
     flip_social_dialog_shown = false;
-    flip_social_friends.index = 0;
+    app_instance->flip_social_friends.index = 0;
     action = ActionNone;
     action = ActionNone;
     return FlipSocialViewLoggedInFriendsSubmenu;
     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
  * @brief Navigation callback for exiting the application
  * @param context The context - unused
  * @param context The context - unused
@@ -189,44 +212,29 @@ static void flip_social_callback_submenu_choices(void *context, uint32_t index)
     case FlipSocialSubmenuLoggedInIndexProfile:
     case FlipSocialSubmenuLoggedInIndexProfile:
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProfile);
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProfile);
         break;
         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;
         break;
     case FlipSocialSubmenuExploreIndex:
     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;
         break;
     case FlipSocialSubmenuLoggedInIndexCompose:
     case FlipSocialSubmenuLoggedInIndexCompose:
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInCompose);
         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)
         // Handle the pre-saved message selection (has a max of 25 items)
         if (index >= FlipSocialSubemnuComposeIndexStartIndex && index < FlipSocialSubemnuComposeIndexStartIndex + MAX_PRE_SAVED_MESSAGES)
         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);
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProcessCompose);
         }
         }
 
 
         // Handle the explore selection
         // Handle the explore selection
         else if (index >= FlipSocialSubmenuExploreIndexStartIndex && index < FlipSocialSubmenuExploreIndexStartIndex + MAX_EXPLORE_USERS)
         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);
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInExploreProccess);
         }
         }
 
 
         // handle the friends selection
         // handle the friends selection
         else if (index >= FlipSocialSubmenuLoggedInIndexFriendsStart && index < FlipSocialSubmenuLoggedInIndexFriendsStart + MAX_FRIENDS)
         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);
             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
         else
         {
         {
             FURI_LOG_E(TAG, "Unknown submenu index");
             FURI_LOG_E(TAG, "Unknown submenu index");
         }
         }
+
         break;
         break;
     }
     }
 }
 }
@@ -808,14 +834,15 @@ static void flip_social_logged_in_profile_change_password_updated(void *context)
     // send post request to change password
     // send post request to change password
     char payload[256];
     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);
     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, "Failed to send post request to change password");
         FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
         FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
+        free(headers);
         return;
         return;
     }
     }
-
+    free(headers);
     // Save the settings
     // Save the settings
     save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->is_logged_in);
     save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->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
 #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_shown = false;
 bool flip_social_dialog_stop = false;
 bool flip_social_dialog_stop = false;
 char *last_explore_response = "";
 char *last_explore_response = "";
-static void flip_social_update_friends();
+static bool flip_social_update_friends();
 
 
 bool flip_social_board_is_active(Canvas *canvas)
 bool flip_social_board_is_active(Canvas *canvas)
 {
 {
@@ -166,7 +166,7 @@ static void flip_social_callback_draw_compose(Canvas *canvas, void *model)
         return;
         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)
     if (!flip_social_dialog_shown)
     {
     {
@@ -247,9 +247,9 @@ static void flip_social_callback_draw_compose(Canvas *canvas, void *model)
     case ActionPrev:
     case ActionPrev:
         // delete message
         // delete message
         // remove the message from app_instance->pre_saved_messages
         // 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];
             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);
         furi_pubsub_unsubscribe(app_instance->input_event_queue, app_instance->input_event);
         flip_social_dialog_shown = false;
         flip_social_dialog_shown = false;
         flip_social_dialog_stop = 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;
             action = ActionNone;
             view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInSubmenu);
             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)
     switch (action)
     {
     {
     case ActionNone:
     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;
         break;
     case ActionNext:
     case ActionNext:
         canvas_clear(canvas);
         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;
         action = ActionNone;
         break;
         break;
     case ActionPrev:
     case ActionPrev:
         canvas_clear(canvas);
         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;
         action = ActionNone;
         break;
         break;
     case ActionFlip:
     case ActionFlip:
         canvas_clear(canvas);
         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;
         action = ActionNone;
         // send post request to flip the message
         // send post request to flip the message
         if (app_instance->login_username_logged_in == NULL)
         if (app_instance->login_username_logged_in == NULL)
@@ -382,13 +387,13 @@ static void flip_social_callback_draw_feed(Canvas *canvas, void *model)
             return;
             return;
         }
         }
         char payload[256];
         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);
         flipper_http_post_request_with_headers("https://www.flipsocial.net/api/feed/flip/", "{\"Content-Type\":\"application/json\"}", payload);
         break;
         break;
     case ActionBack:
     case ActionBack:
         canvas_clear(canvas);
         canvas_clear(canvas);
         flip_social_dialog_stop = true;
         flip_social_dialog_stop = true;
-        flip_social_feed.index = 0;
+        app_instance->flip_social_feed.index = 0;
         action = ActionNone;
         action = ActionNone;
         break;
         break;
     default:
     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_queue = furi_record_open(RECORD_INPUT_EVENTS);
         app_instance->input_event = furi_pubsub_subscribe(app_instance->input_event_queue, on_input, NULL);
         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
     // handle action
     switch (action)
     switch (action)
@@ -698,19 +703,19 @@ static void flip_social_callback_draw_explore(Canvas *canvas, void *model)
     case ActionNext:
     case ActionNext:
         // add friend
         // add friend
         char add_payload[128];
         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);
         flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/add-friend/", "{\"Content-Type\":\"application/json\"}", add_payload);
         canvas_clear(canvas);
         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;
         action = ActionNone;
         break;
         break;
     case ActionPrev:
     case ActionPrev:
         // remove friend
         // remove friend
         char remove_payload[128];
         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);
         flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/remove-friend/", "{\"Content-Type\":\"application/json\"}", remove_payload);
         canvas_clear(canvas);
         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;
         action = ActionNone;
         break;
         break;
     case ActionBack:
     case ActionBack:
@@ -718,7 +723,7 @@ static void flip_social_callback_draw_explore(Canvas *canvas, void *model)
         flip_social_dialog_stop = true;
         flip_social_dialog_stop = true;
         last_explore_response = "";
         last_explore_response = "";
         flip_social_dialog_shown = false;
         flip_social_dialog_shown = false;
-        flip_social_explore.index = 0;
+        app_instance->flip_social_explore.index = 0;
         action = ActionNone;
         action = ActionNone;
         break;
         break;
     default:
     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_queue = furi_record_open(RECORD_INPUT_EVENTS);
         app_instance->input_event = furi_pubsub_subscribe(app_instance->input_event_queue, on_input, NULL);
         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
     // handle action
     switch (action)
     switch (action)
@@ -763,35 +768,41 @@ static void flip_social_callback_draw_friends(Canvas *canvas, void *model)
     case ActionNext:
     case ActionNext:
         // add friend
         // add friend
         char add_payload[128];
         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))
         if (flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/add-friend/", "{\"Content-Type\":\"application/json\"}", add_payload))
         {
         {
             canvas_clear(canvas);
             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
             // 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;
         action = ActionNone;
         break;
         break;
     case ActionPrev:
     case ActionPrev:
         // remove friend
         // remove friend
         char remove_payload[128];
         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))
         if (flipper_http_post_request_with_headers("https://www.flipsocial.net/api/user/remove-friend/", "{\"Content-Type\":\"application/json\"}", remove_payload))
         {
         {
             canvas_clear(canvas);
             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
             // 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;
         action = ActionNone;
         break;
         break;
@@ -800,7 +811,7 @@ static void flip_social_callback_draw_friends(Canvas *canvas, void *model)
         flip_social_dialog_stop = true;
         flip_social_dialog_stop = true;
         last_explore_response = "";
         last_explore_response = "";
         flip_social_dialog_shown = false;
         flip_social_dialog_shown = false;
-        flip_social_friends.index = 0;
+        app_instance->flip_social_friends.index = 0;
         action = ActionNone;
         action = ActionNone;
         break;
         break;
     default:
     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
 #endif // FLIP_SOCIAL_DRAW_H

+ 90 - 50
flip_social_e.h

@@ -6,16 +6,19 @@
 #include <storage/storage.h>
 #include <storage/storage.h>
 #include <flipper_http.h>
 #include <flipper_http.h>
 #include <input/input.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 TAG "FlipSocial"
 
 
 #define MAX_PRE_SAVED_MESSAGES 25 // Maximum number of pre-saved messages
 #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_MESSAGE_LENGTH 100    // Maximum length of a message in the feed
 #define MAX_EXPLORE_USERS 50      // Maximum number of users to explore
 #define MAX_EXPLORE_USERS 50      // Maximum number of users to explore
+#define MAX_USER_LENGTH 32        // Maximum length of a username
 #define MAX_FRIENDS 50            // Maximum number of friends
 #define MAX_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_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
 // Define the submenu items for our Hello World application
 typedef enum
 typedef enum
@@ -26,18 +29,25 @@ typedef enum
     FlipSocialSubmenuLoggedOutIndexWifiSettings, // click to go to the wifi settings screen
     FlipSocialSubmenuLoggedOutIndexWifiSettings, // click to go to the wifi settings screen
     //
     //
     FlipSocialSubmenuLoggedInIndexProfile,  // click to go to the profile screen
     FlipSocialSubmenuLoggedInIndexProfile,  // click to go to the profile screen
+    FlipSocialSubmenuExploreIndex,          // click to go to the explore
     FlipSocialSubmenuLoggedInIndexFeed,     // click to go to the feed screen
     FlipSocialSubmenuLoggedInIndexFeed,     // click to go to the feed screen
+    FlipSocialSubmenuLoggedInIndexMessages, // click to go to the messages screen
     FlipSocialSubmenuLoggedInIndexCompose,  // click to go to the compose screen
     FlipSocialSubmenuLoggedInIndexCompose,  // click to go to the compose screen
     FlipSocialSubmenuLoggedInIndexSettings, // click to go to the settings screen
     FlipSocialSubmenuLoggedInIndexSettings, // click to go to the settings screen
     FlipSocialSubmenuLoggedInSignOutButton, // click to sign out
     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;
 } FlipSocialSubmenuIndex;
 
 
 typedef enum
 typedef enum
@@ -49,7 +59,7 @@ typedef enum
     ActionFlip,
     ActionFlip,
 } Action;
 } Action;
 
 
-Action action = ActionNone;
+static Action action = ActionNone;
 
 
 // Define the ScriptPlaylist structure
 // Define the ScriptPlaylist structure
 typedef struct
 typedef struct
@@ -58,6 +68,38 @@ typedef struct
     size_t count;
     size_t count;
 } PreSavedPlaylist;
 } 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
 // Define views for our Hello World application
 typedef enum
 typedef enum
 {
 {
@@ -83,9 +125,13 @@ typedef enum
     FlipSocialViewLoggedInCompose,  // The compose screen
     FlipSocialViewLoggedInCompose,  // The compose screen
     FlipSocialViewLoggedInSettings, // The settings 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
     FlipSocialViewLoggedInSettingsAbout,             // The about screen
     FlipSocialViewLoggedInSettingsWifi,              // The wifi settings screen
     FlipSocialViewLoggedInSettingsWifi,              // The wifi settings screen
     FlipSocialViewLoggedInWifiSettingsSSIDInput,     // Text input screen for SSID input on wifi 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
     FlipSocialViewLoggedInExploreProccess, // The view after clicking on a user in the explore screen
     FlipSocialViewLoggedInFriendsSubmenu,  // The view after clicking the friends button on the profile 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
     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;
 } FlipSocialView;
 
 
 // Define the application structure
 // Define the application structure
 typedef struct
 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_login;    // The screen displayed after clicking login
     View *view_process_register; // The screen displayed after clicking register
     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_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_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_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_wifi_settings; // The wifi settings menu
     VariableItemList *variable_item_list_logged_out_login;         // The login 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_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_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_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_ssid;     // Reference to the ssid configuration item
     VariableItem *variable_item_logged_out_wifi_settings_password; // Reference to the password 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
     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
     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;
 } FlipSocialApp;
 
 
 // include strndup (otherwise NULL pointer dereference)
 // include strndup (otherwise NULL pointer dereference)
@@ -236,36 +306,6 @@ char *strndup(const char *s, size_t n)
 }
 }
 
 
 static FlipSocialApp *app_instance = NULL;
 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_logged_in_compose_pre_save_updated(void *context);
 static void flip_social_callback_submenu_choices(void *context, uint32_t index);
 static void flip_social_callback_submenu_choices(void *context, uint32_t index);
 #endif
 #endif

+ 55 - 47
flip_social_explore.h

@@ -1,12 +1,43 @@
 #ifndef FLIP_SOCIAL_EXPLORE_H
 #ifndef FLIP_SOCIAL_EXPLORE_H
 #define 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
 // for now we're just listing the current users
 // as the feed is upgraded, then we can port more to the explore view
 // 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
     // 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)
     if (!success)
     {
     {
         FURI_LOG_E(TAG, "Failed to send HTTP request for explore");
         FURI_LOG_E(TAG, "Failed to send HTTP request for explore");
@@ -16,7 +47,7 @@ bool flip_social_get_explore()
     return true;
     return true;
 }
 }
 
 
-bool flip_social_parse_json_explore()
+static bool flip_social_parse_json_explore()
 {
 {
     if (fhttp.received_data == NULL)
     if (fhttp.received_data == NULL)
     {
     {
@@ -24,6 +55,13 @@ bool flip_social_parse_json_explore()
         return false;
         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
     // Remove newlines
     char *pos = fhttp.received_data;
     char *pos = fhttp.received_data;
     while ((pos = strchr(pos, '\n')) != NULL)
     while ((pos = strchr(pos, '\n')) != NULL)
@@ -32,7 +70,7 @@ bool flip_social_parse_json_explore()
     }
     }
 
 
     // Initialize explore count
     // Initialize explore count
-    flip_social_explore.count = 0;
+    app_instance->flip_social_explore.count = 0;
 
 
     // Extract the users array from the JSON
     // Extract the users array from the JSON
     char *json_users = get_json_value("users", fhttp.received_data, MAX_TOKENS);
     char *json_users = get_json_value("users", fhttp.received_data, MAX_TOKENS);
@@ -45,74 +83,44 @@ bool flip_social_parse_json_explore()
     // Manual tokenization for comma-separated values
     // Manual tokenization for comma-separated values
     char *start = json_users + 1; // Skip the opening bracket
     char *start = json_users + 1; // Skip the opening bracket
     char *end;
     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
         *end = '\0'; // Null-terminate the current token
 
 
-        // Remove the quotes
+        // Remove quotes
         if (*start == '"')
         if (*start == '"')
-        {
             start++;
             start++;
-        }
         if (*(end - 1) == '"')
         if (*(end - 1) == '"')
-        {
             *(end - 1) = '\0';
             *(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
     // Handle the last token
-    if (*start != '\0')
+    if (*start != '\0' && app_instance->flip_social_explore.count < MAX_EXPLORE_USERS)
     {
     {
-        // Remove the quotes
         if (*start == '"')
         if (*start == '"')
-        {
             start++;
             start++;
-        }
-        // Skip the closing bracket
         if (*(start + strlen(start) - 1) == ']')
         if (*(start + strlen(start) - 1) == ']')
-        {
             *(start + strlen(start) - 1) = '\0';
             *(start + strlen(start) - 1) = '\0';
-        }
-        // Remove the quotes
         if (*(start + strlen(start) - 1) == '"')
         if (*(start + strlen(start) - 1) == '"')
-        {
             *(start + strlen(start) - 1) = '\0';
             *(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
     // Add submenu items for the users
     submenu_reset(app_instance->submenu_explore);
     submenu_reset(app_instance->submenu_explore);
     submenu_set_header(app_instance->submenu_explore, "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
     // Free the json_users

+ 109 - 12
flip_social_feed.h

@@ -1,7 +1,93 @@
 #ifndef FLIP_SOCIAL_FEED_H
 #ifndef FLIP_SOCIAL_FEED_H
 #define 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
     // Get the feed from the server
     if (app_instance->login_username_logged_out == NULL)
     if (app_instance->login_username_logged_out == NULL)
@@ -9,9 +95,9 @@ bool flip_social_get_feed()
         FURI_LOG_E(TAG, "Username is NULL");
         FURI_LOG_E(TAG, "Username is NULL");
         return false;
         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)
     if (!success)
     {
     {
         FURI_LOG_E(TAG, "Failed to send HTTP request for feed");
         FURI_LOG_E(TAG, "Failed to send HTTP request for feed");
@@ -20,7 +106,7 @@ bool flip_social_get_feed()
     fhttp.state = RECEIVING;
     fhttp.state = RECEIVING;
     return true;
     return true;
 }
 }
-bool flip_social_parse_json_feed()
+static bool flip_social_parse_json_feed()
 {
 {
     if (fhttp.received_data == NULL)
     if (fhttp.received_data == NULL)
     {
     {
@@ -28,6 +114,12 @@ bool flip_social_parse_json_feed()
         return false;
         return false;
     }
     }
 
 
+    // Allocate memory for each feed item if not already allocated
+    if (!flip_social_feed_alloc())
+    {
+        return false;
+    }
+
     // Remove newlines
     // Remove newlines
     char *pos = fhttp.received_data;
     char *pos = fhttp.received_data;
     while ((pos = strchr(pos, '\n')) != NULL)
     while ((pos = strchr(pos, '\n')) != NULL)
@@ -36,7 +128,7 @@ bool flip_social_parse_json_feed()
     }
     }
 
 
     // Initialize feed count
     // Initialize feed count
-    flip_social_feed.count = 0;
+    app_instance->flip_social_feed.count = 0;
 
 
     // Iterate through the feed array
     // Iterate through the feed array
     for (int i = 0; i < MAX_FEED_ITEMS; i++)
     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.");
             FURI_LOG_E(TAG, "Failed to parse item fields.");
             free(item);
             free(item);
+            free(username);
+            free(message);
+            free(flipped);
+            free(id);
             continue;
             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);
         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);
         view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInFriendsSubmenu);
         submenu_free(app->submenu_friends);
         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)
     // Free Variable Item List(s)
     if (app->variable_item_list_logged_out_wifi_settings)
     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);
         view_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInWifiSettingsPasswordInput);
         uart_text_input_free(app->text_input_logged_in_wifi_settings_password);
         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)
     // Free Widget(s)
     if (app->widget_logged_out_about)
     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_dispatcher_remove_view(app->view_dispatcher, FlipSocialViewLoggedInFriendsProcess);
         view_free(app->view_process_friends);
         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)
     if (app->view_dispatcher)
         view_dispatcher_free(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);
         free(app->login_username_logged_in);
     if (app->login_username_logged_in_temp_buffer)
     if (app->login_username_logged_in_temp_buffer)
         free(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)
     if (app->input_event && app->input_event_queue)
         furi_pubsub_unsubscribe(app->input_event_queue, app->input_event);
         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(fhttp.received_data);
 
 
     // free playlist and explore page
     // 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
     // DeInit UART
     flipper_http_deinit();
     flipper_http_deinit();

+ 66 - 49
flip_social_friends.h

@@ -1,6 +1,37 @@
 #ifndef FLIP_SOCIAL_FRIENDS
 #ifndef FLIP_SOCIAL_FRIENDS
 #define 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
 // for now we're just listing the current users
 // as the feed is upgraded, then we can port more to the friends view
 // as the feed is upgraded, then we can port more to the friends view
 static bool flip_social_get_friends()
 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
     // will return true unless the devboard is not connected
     char url[100];
     char url[100];
     snprintf(url, 100, "https://www.flipsocial.net/api/user/friends/%s/", app_instance->login_username_logged_in);
     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)
     if (!success)
     {
     {
         FURI_LOG_E(TAG, "Failed to send HTTP request for friends");
         FURI_LOG_E(TAG, "Failed to send HTTP request for friends");
@@ -18,15 +49,21 @@ static bool flip_social_get_friends()
     return true;
     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
     // Add submenu items for the users
     submenu_reset(app_instance->submenu_friends);
     submenu_reset(app_instance->submenu_friends);
     submenu_set_header(app_instance->submenu_friends, "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()
 static bool flip_social_parse_json_friends()
@@ -37,6 +74,13 @@ static bool flip_social_parse_json_friends()
         return false;
         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
     // Remove newlines
     char *pos = fhttp.received_data;
     char *pos = fhttp.received_data;
     while ((pos = strchr(pos, '\n')) != NULL)
     while ((pos = strchr(pos, '\n')) != NULL)
@@ -45,7 +89,7 @@ static bool flip_social_parse_json_friends()
     }
     }
 
 
     // Initialize friends count
     // Initialize friends count
-    flip_social_friends.count = 0;
+    app_instance->flip_social_friends.count = 0;
 
 
     // Extract the users array from the JSON
     // Extract the users array from the JSON
     char *json_users = get_json_value("friends", fhttp.received_data, MAX_TOKENS);
     char *json_users = get_json_value("friends", fhttp.received_data, MAX_TOKENS);
@@ -58,75 +102,48 @@ static bool flip_social_parse_json_friends()
     // Manual tokenization for comma-separated values
     // Manual tokenization for comma-separated values
     char *start = json_users + 1; // Skip the opening bracket
     char *start = json_users + 1; // Skip the opening bracket
     char *end;
     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
         *end = '\0'; // Null-terminate the current token
 
 
-        // Remove the quotes
+        // Remove quotes
         if (*start == '"')
         if (*start == '"')
-        {
             start++;
             start++;
-        }
         if (*(end - 1) == '"')
         if (*(end - 1) == '"')
-        {
             *(end - 1) = '\0';
             *(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
     // Handle the last token
-    if (*start != '\0')
+    if (*start != '\0' && app_instance->flip_social_friends.count < MAX_FRIENDS)
     {
     {
-        // Remove the quotes
         if (*start == '"')
         if (*start == '"')
-        {
             start++;
             start++;
-        }
-        // Skip the closing bracket
         if (*(start + strlen(start) - 1) == ']')
         if (*(start + strlen(start) - 1) == ']')
-        {
             *(start + strlen(start) - 1) = '\0';
             *(start + strlen(start) - 1) = '\0';
-        }
-        // Remove the quotes
         if (*(start + strlen(start) - 1) == '"')
         if (*(start + strlen(start) - 1) == '"')
-        {
             *(start + strlen(start) - 1) = '\0';
             *(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 the json_users
     free(json_users);
     free(json_users);
 
 
     return true;
     return true;
 }
 }
-
 #endif // FLIP_SOCIAL_FRIENDS
 #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
     // 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->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))
     if (!easy_flipper_set_buffer(&app->wifi_ssid_logged_out_temp_buffer, app->wifi_ssid_logged_out_temp_buffer_size))
     {
     {
         return NULL;
         return NULL;
@@ -144,13 +145,30 @@ static FlipSocialApp *flip_social_app_alloc()
     {
     {
         return NULL;
         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)
     // 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;
         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;
         return NULL;
     }
     }
@@ -166,6 +184,14 @@ static FlipSocialApp *flip_social_app_alloc()
     {
     {
         return NULL;
         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, "Login", FlipSocialSubmenuLoggedOutIndexLogin, flip_social_callback_submenu_choices, app);
     submenu_add_item(app->submenu_logged_out, "Register", FlipSocialSubmenuLoggedOutIndexRegister, 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, "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, "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, "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, "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, "Settings", FlipSocialSubmenuLoggedInIndexSettings, flip_social_callback_submenu_choices, app);
     submenu_add_item(app->submenu_logged_in, "Sign Out", FlipSocialSubmenuLoggedInSignOutButton, 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;
         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)
     // 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))
     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;
         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)
     // 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))
     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
     // load the playlist
     if (load_playlist(&app->pre_saved_messages))
     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++)
         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);
             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");
         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->is_logged_in = "false";
         }
         }
         app_instance = app;
         app_instance = app;
@@ -486,6 +527,35 @@ static FlipSocialApp *flip_social_app_alloc()
         //
         //
 
 
         app_instance = app;
         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)
         if (app->is_logged_in != NULL && strcmp(app->is_logged_in, "true") == 0)
         {
         {
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInSubmenu);
             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;
         return;
     }
     }
     // Create the directory for saving settings
     // 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");
     snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social");
 
 
     // Create the directory
     // 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_close(file);
     storage_file_free(file);
     storage_file_free(file);
     furi_record_close(RECORD_STORAGE);
     furi_record_close(RECORD_STORAGE);
@@ -156,7 +154,7 @@ static void save_settings(
     const char *is_logged_in)
     const char *is_logged_in)
 {
 {
     // Create the directory for saving settings
     // 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");
     snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_social");
 
 
     // Create the directory
     // 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[]);
 bool flipper_http_save_received_data(size_t bytes_received, const char line_buffer[]);
 static char *trim(const char *str);
 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
 // State variable to track the UART state
 typedef enum
 typedef enum
@@ -173,7 +177,7 @@ static int32_t flipper_http_worker(void *context)
             break;
             break;
         if (events & WorkerEvtRxDone)
         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++)
             for (size_t i = 0; i < len; i++)
             {
             {
                 char c = fhttp.rx_buf[i];
                 char c = fhttp.rx_buf[i];
@@ -1155,4 +1159,39 @@ char *trim(const char *str)
     return trimmed_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
 #endif // FLIPPER_HTTP_H

+ 15 - 0
jsmn.h

@@ -535,8 +535,23 @@ extern "C"
 #include <stdint.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdio.h>
+#include <stdio.h>
+#include <string.h>
 #include <furi.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
 // Helper function to compare JSON keys
 int jsoneq(const char *json, jsmntok_t *tok, const char *s)
 int jsoneq(const char *json, jsmntok_t *tok, const char *s)
 {
 {