jblanked 1 год назад
Родитель
Сommit
93f57d319c

+ 24 - 8
alloc/alloc.c

@@ -350,6 +350,30 @@ bool pre_saved_messages_alloc(void)
     return true;
 }
 
+bool alloc_submenu(uint32_t view_id)
+{
+    if (!app_instance)
+    {
+        return false;
+    }
+    if (!app_instance->submenu)
+    {
+        switch (view_id)
+        {
+        case FlipSocialViewLoggedInSettings:
+            if (!easy_flipper_set_submenu(&app_instance->submenu, FlipSocialViewSubmenu, "Settings", flip_social_callback_to_submenu_logged_in, &app_instance->view_dispatcher))
+            {
+                return false;
+            }
+            submenu_reset(app_instance->submenu);
+            submenu_add_item(app_instance->submenu, "About", FlipSocialSubmenuLoggedInIndexAbout, flip_social_callback_submenu_choices, app_instance);
+            submenu_add_item(app_instance->submenu, "WiFi", FlipSocialSubmenuLoggedInIndexWifiSettings, flip_social_callback_submenu_choices, app_instance);
+            break;
+        }
+    }
+    return true;
+}
+
 bool alloc_variable_item_list(uint32_t view_id)
 {
     if (!app_instance)
@@ -403,14 +427,6 @@ bool alloc_variable_item_list(uint32_t view_id)
         if (app_instance->change_bio_logged_in)
             variable_item_set_current_value_text(app_instance->variable_item_logged_in_profile_change_bio, app_instance->change_bio_logged_in);
         return true;
-    case FlipSocialViewLoggedInSettings:
-        if (!easy_flipper_set_variable_item_list(&app_instance->variable_item_list, FlipSocialViewVariableItemList, flip_social_text_input_logged_in_settings_item_selected, flip_social_callback_to_submenu_logged_in, &app_instance->view_dispatcher, app_instance))
-        {
-            return false;
-        }
-        app_instance->variable_item_logged_in_settings_about = variable_item_list_add(app_instance->variable_item_list, "About", 0, NULL, app_instance);
-        app_instance->variable_item_logged_in_settings_wifi = variable_item_list_add(app_instance->variable_item_list, "WiFi", 0, NULL, app_instance);
-        return true;
     case FlipSocialViewLoggedInSettingsWifi:
         if (!easy_flipper_set_variable_item_list(&app_instance->variable_item_list, FlipSocialViewVariableItemList, flip_social_text_input_logged_in_wifi_settings_item_selected, flip_social_callback_to_settings_logged_in, &app_instance->view_dispatcher, app_instance))
         {

+ 2 - 1
alloc/alloc.h

@@ -9,4 +9,5 @@ bool feed_dialog_alloc();
 bool alloc_text_input(uint32_t view_id);
 bool about_widget_alloc(bool is_logged_in);
 bool pre_saved_messages_alloc(void);
-bool alloc_variable_item_list(uint32_t view_id);
+bool alloc_variable_item_list(uint32_t view_id);
+bool alloc_submenu(uint32_t view_id);

+ 17 - 1
alloc/free.c

@@ -1,5 +1,5 @@
 #include <alloc/free.h>
-void free_all(bool should_free_variable_item_list)
+void free_all(bool should_free_variable_item_list, bool should_free_submenu)
 {
     free_text_input();
     flip_social_free_friends();
@@ -16,6 +16,8 @@ void free_all(bool should_free_variable_item_list)
     free_about_widget(false);
     if (should_free_variable_item_list)
         free_variable_item_list();
+    if (should_free_submenu)
+        free_submenu();
 }
 void free_text_input()
 {
@@ -133,4 +135,18 @@ void free_variable_item_list(void)
         app_instance->variable_item_list = NULL;
         view_dispatcher_remove_view(app_instance->view_dispatcher, FlipSocialViewVariableItemList);
     }
+}
+
+void free_submenu(void)
+{
+    if (!app_instance)
+    {
+        return;
+    }
+    if (app_instance->submenu)
+    {
+        submenu_free(app_instance->submenu);
+        app_instance->submenu = NULL;
+        view_dispatcher_remove_view(app_instance->view_dispatcher, FlipSocialViewSubmenu);
+    }
 }

+ 3 - 2
alloc/free.h

@@ -1,7 +1,7 @@
 #pragma once
 #include <flip_social.h>
 #include <callback/flip_social_callback.h>
-void free_all(bool should_free_variable_item_list);
+void free_all(bool should_free_variable_item_list, bool should_free_submenu);
 void free_text_input();
 void flip_social_free_explore_dialog();
 void flip_social_free_friends_dialog();
@@ -12,4 +12,5 @@ void free_about_widget(bool is_logged_in);
 void free_pre_saved_messages(void);
 void flip_social_free_friends(void);
 void flip_feed_info_free(void);
-void free_variable_item_list(void);
+void free_variable_item_list(void);
+void free_submenu(void);

+ 48 - 70
callback/flip_social_callback.c

@@ -306,7 +306,7 @@ uint32_t flip_social_callback_to_wifi_settings_logged_in(void *context)
 uint32_t flip_social_callback_to_settings_logged_in(void *context)
 {
     UNUSED(context);
-    return FlipSocialViewVariableItemList;
+    return FlipSocialViewSubmenu;
 }
 
 /**
@@ -393,7 +393,7 @@ uint32_t flip_social_callback_exit_app(void *context)
 {
     // Exit the application
     UNUSED(context);
-    free_all(true);
+    free_all(true, true);
     return VIEW_NONE;
 }
 
@@ -500,7 +500,7 @@ void messages_dialog_callback(DialogExResult result, void *context)
     }
     else if (result == DialogExResultCenter) // new message
     {
-        free_all(true);
+        free_text_input();
         if (!alloc_text_input(FlipSocialViewLoggedInMessagesNewMessageInput))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input");
@@ -760,8 +760,8 @@ static bool flip_social_parse_user_info()
         FURI_LOG_E(TAG, "App instance is NULL");
         return false;
     }
-    char *bio = get_json_value("bio", fhttp.last_response, 32);
-    char *friends = get_json_value("friends", fhttp.last_response, 32);
+    char *bio = get_json_value("bio", fhttp.last_response);
+    char *friends = get_json_value("friends", fhttp.last_response);
     bool parse_success = false;
     if (bio && friends)
     {
@@ -800,7 +800,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
     case FlipSocialSubmenuLoggedOutIndexLogin:
         flip_social_sent_login_request = false;
         flip_social_login_success = false;
-        free_all(true);
+        free_all(true, true);
         if (!alloc_variable_item_list(FlipSocialViewLoggedOutLogin))
         {
             FURI_LOG_E(TAG, "Failed to allocate variable item list");
@@ -811,7 +811,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
     case FlipSocialSubmenuLoggedOutIndexRegister:
         flip_social_sent_register_request = false;
         flip_social_register_success = false;
-        free_all(true);
+        free_all(true, true);
         if (!alloc_variable_item_list(FlipSocialViewLoggedOutRegister))
         {
             FURI_LOG_E(TAG, "Failed to allocate variable item list");
@@ -827,7 +827,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutAbout);
         break;
     case FlipSocialSubmenuLoggedOutIndexWifiSettings:
-        free_all(false);
+        free_all(false, false);
         if (!alloc_variable_item_list(FlipSocialViewLoggedOutWifiSettings))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input");
@@ -836,7 +836,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewVariableItemList);
         break;
     case FlipSocialSubmenuLoggedInIndexProfile:
-        free_all(true);
+        free_all(true, true);
         if (!alloc_variable_item_list(FlipSocialViewLoggedInProfile))
         {
             FURI_LOG_E(TAG, "Failed to allocate variable item list");
@@ -845,7 +845,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewVariableItemList);
         break;
     case FlipSocialSubmenuLoggedInIndexMessages:
-        free_all(true);
+        free_all(true, true);
         flipper_http_loading_task(
             flip_social_get_message_users,         // get the message users
             flip_social_parse_json_message_users,  // parse the message users
@@ -864,7 +864,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewTextInput);
         break;
     case FlipSocialSubmenuLoggedInIndexFeed:
-        free_all(true);
+        free_all(true, true);
         if (!flip_social_load_initial_feed(true))
         {
             FURI_LOG_E(TAG, "Failed to load the initial feed");
@@ -872,7 +872,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         }
         break;
     case FlipSocialSubmenuExploreIndex:
-        free_all(true);
+        free_all(true, true);
         if (!alloc_text_input(FlipSocialViewLoggedInExploreInput))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input");
@@ -881,7 +881,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewTextInput);
         break;
     case FlipSocialSubmenuLoggedInIndexCompose:
-        free_all(true);
+        free_all(true, true);
         if (!pre_saved_messages_alloc())
         {
             FURI_LOG_E(TAG, "Failed to allocate pre-saved messages");
@@ -890,8 +890,26 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInCompose);
         break;
     case FlipSocialSubmenuLoggedInIndexSettings:
-        free_all(true);
-        if (!alloc_variable_item_list(FlipSocialViewLoggedInSettings))
+        free_all(true, true);
+        if (!alloc_submenu(FlipSocialViewLoggedInSettings))
+        {
+            FURI_LOG_E(TAG, "Failed to allocate submenu");
+            return;
+        }
+        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewSubmenu);
+        break;
+    case FlipSocialSubmenuLoggedInIndexAbout:
+        free_all(true, false);
+        if (!about_widget_alloc(true))
+        {
+            FURI_LOG_E(TAG, "Failed to allocate about widget");
+            return;
+        }
+        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInSettingsAbout);
+        break;
+    case FlipSocialSubmenuLoggedInIndexWifiSettings:
+        free_all(true, false);
+        if (!alloc_variable_item_list(FlipSocialViewLoggedInSettingsWifi))
         {
             FURI_LOG_E(TAG, "Failed to allocate variable item list");
             return;
@@ -899,7 +917,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewVariableItemList);
         break;
     case FlipSocialSubmenuLoggedInSignOutButton:
-        free_all(true);
+        free_all(true, true);
         app->is_logged_in = "false";
 
         save_settings(app->wifi_ssid_logged_out, app->wifi_password_logged_out, app->login_username_logged_out, app->login_username_logged_in, app->login_password_logged_out, app->change_password_logged_in, app->change_bio_logged_in, app->is_logged_in);
@@ -1338,7 +1356,7 @@ void flip_social_text_input_logged_out_login_item_selected(void *context, uint32
     {
     case 0: // Input Username
         // view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutLoginUsernameInput);
-        free_all(false);
+        free_all(false, true);
         if (!alloc_text_input(FlipSocialViewLoggedOutLoginUsernameInput))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input");
@@ -1348,7 +1366,7 @@ void flip_social_text_input_logged_out_login_item_selected(void *context, uint32
         break;
     case 1: // Input Password
         // view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutLoginPasswordInput);
-        free_all(false);
+        free_all(false, true);
         if (!alloc_text_input(FlipSocialViewLoggedOutLoginPasswordInput))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input");
@@ -1472,7 +1490,7 @@ void flip_social_text_input_logged_out_register_item_selected(void *context, uin
     {
     case 0: // Input Username
         // view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutRegisterUsernameInput);
-        free_all(false);
+        free_all(false, true);
         if (!alloc_text_input(FlipSocialViewLoggedOutRegisterUsernameInput))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input");
@@ -1482,7 +1500,7 @@ void flip_social_text_input_logged_out_register_item_selected(void *context, uin
         break;
     case 1: // Input Password
         // view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutRegisterPasswordInput);
-        free_all(false);
+        free_all(false, true);
         if (!alloc_text_input(FlipSocialViewLoggedOutRegisterPasswordInput))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input");
@@ -1492,7 +1510,7 @@ void flip_social_text_input_logged_out_register_item_selected(void *context, uin
         break;
     case 2: // Input Password 2
         // view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutRegisterPassword2Input);
-        free_all(false);
+        free_all(false, true);
         if (!alloc_text_input(FlipSocialViewLoggedOutRegisterPassword2Input))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input");
@@ -1633,7 +1651,7 @@ void flip_social_text_input_logged_in_wifi_settings_item_selected(void *context,
     {
     case 0: // Input SSID
         // view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInWifiSettingsSSIDInput);
-        free_all(false);
+        free_all(false, false);
         if (!alloc_text_input(FlipSocialViewLoggedInWifiSettingsSSIDInput))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input for SSID");
@@ -1643,7 +1661,7 @@ void flip_social_text_input_logged_in_wifi_settings_item_selected(void *context,
         break;
     case 1: // Input Password
         // view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInWifiSettingsPasswordInput);
-        free_all(false);
+        free_all(false, false);
         if (!alloc_text_input(FlipSocialViewLoggedInWifiSettingsPasswordInput))
         {
             FURI_LOG_E(TAG, "Failed to allocate text input for Password");
@@ -1766,7 +1784,8 @@ void flip_social_logged_in_profile_change_password_updated(void *context)
     // Save the settings
     save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in);
 
-    view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProfile);
+    // instead of going to a view, just show a success message
+    easy_flipper_dialog("Success", "Password updated successfully\n\n\nPress BACK to return :D");
 }
 
 void flip_social_logged_in_profile_change_bio_updated(void *context)
@@ -1801,12 +1820,14 @@ void flip_social_logged_in_profile_change_bio_updated(void *context)
             FURI_LOG_E(TAG, "Failed to send post request to change bio");
             FURI_LOG_E(TAG, "Make sure the Flipper is connected to the Wifi Dev Board");
         }
+        furi_delay_ms(500);
         flipper_http_deinit();
     }
     // Save the settings
-    save_settings(app_instance->wifi_ssid_logged_out, app_instance->wifi_password_logged_out, app_instance->login_username_logged_out, app_instance->login_username_logged_in, app_instance->login_password_logged_out, app_instance->change_password_logged_in, app_instance->change_bio_logged_in, app_instance->is_logged_in);
+    save_settings(app->wifi_ssid_logged_out, app->wifi_password_logged_out, app->login_username_logged_out, app->login_username_logged_in, app->login_password_logged_out, app->change_password_logged_in, app->change_bio_logged_in, app->is_logged_in);
 
-    view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInProfile);
+    // instead of going to a view, just show a success message
+    easy_flipper_dialog("Success", "Bio updated successfully\n\n\nPress BACK to return :D");
 }
 
 static bool flip_social_fetch_friends(DataLoaderModel *model)
@@ -1879,8 +1900,7 @@ void flip_social_text_input_logged_in_profile_item_selected(void *context, uint3
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewTextInput);
         break;
     case 3: // Friends
-
-        free_all(false);
+        free_all(false, true);
         if (!app->submenu_friends)
         {
             if (!easy_flipper_set_submenu(&app->submenu_friends, FlipSocialViewLoggedInFriendsSubmenu, "Friends", flip_social_callback_to_profile_logged_in, &app->view_dispatcher))
@@ -1902,48 +1922,6 @@ void flip_social_text_input_logged_in_profile_item_selected(void *context, uint3
     }
 }
 
-/**
- * @brief Callback when a user selects a menu item in the settings (logged in) screen.
- * @param context The context - FlipSocialApp object.
- * @param index The index of the selected item.
- * @return void
- */
-void flip_social_text_input_logged_in_settings_item_selected(void *context, uint32_t index)
-{
-    FlipSocialApp *app = (FlipSocialApp *)context;
-    if (!app)
-    {
-        FURI_LOG_E(TAG, "FlipSocialApp is NULL");
-        return;
-    }
-    switch (index)
-    {
-    case 0: // About
-        free_all(false);
-        if (!about_widget_alloc(true))
-        {
-            return;
-        }
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInSettingsAbout);
-        break;
-    case 1: // Wifi
-            // CHANGE THIS TO JUST WIFI SETTINGS (REMOVE ABOUT)
-        // the view before is a variable item list too, so we should switch to another view first
-        // then switch to the wifi settings view
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewWidgetResult);
-        free_all(true); // free everything, including the previous variable item list
-        if (!alloc_variable_item_list(FlipSocialViewLoggedInSettingsWifi))
-        {
-            FURI_LOG_E(TAG, "Failed to allocate variable item list for wifi settings");
-            return;
-        }
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewVariableItemList);
-        break;
-    default:
-        break;
-    }
-}
-
 /**
  * @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.

+ 1 - 0
easy_flipper/easy_flipper.h

@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <jsmn/jsmn.h>
+#include <jsmn/jsmn_furi.h>
 
 #define EASY_TAG "EasyFlipper"
 

+ 3 - 13
explore/flip_social_explore.c

@@ -114,21 +114,12 @@ bool flip_social_parse_json_explore()
 
     flipper_http_deinit();
 
-    char *data_cstr = (char *)furi_string_get_cstr(user_data);
-    if (data_cstr == NULL)
-    {
-        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
-        furi_string_free(user_data);
-        return false;
-    }
-
     // Allocate memory for each username only if not already allocated
     flip_social_explore = flip_social_explore_alloc();
     if (flip_social_explore == NULL)
     {
         FURI_LOG_E(TAG, "Failed to allocate memory for explore usernames.");
         furi_string_free(user_data);
-        free(data_cstr);
         return false;
     }
 
@@ -141,17 +132,16 @@ bool flip_social_parse_json_explore()
     // Parse the JSON array of usernames
     for (size_t i = 0; i < MAX_EXPLORE_USERS; i++)
     {
-        char *username = get_json_array_value("users", i, data_cstr, 64); // currently its 53 tokens (with max explore users at 50)
+        FuriString *username = get_json_array_value_furi("users", i, user_data); // currently its 53 tokens (with max explore users at 50)
         if (username == NULL)
         {
             break;
         }
-        snprintf(flip_social_explore->usernames[i], MAX_USER_LENGTH, "%s", username);
+        snprintf(flip_social_explore->usernames[i], MAX_USER_LENGTH, "%s", furi_string_get_cstr(username));
         submenu_add_item(app_instance->submenu_explore, flip_social_explore->usernames[i], FlipSocialSubmenuExploreIndexStartIndex + i, flip_social_callback_submenu_choices, app_instance);
         flip_social_explore->count++;
-        free(username);
+        furi_string_free(username);
     }
-    free(data_cstr);
     furi_string_free(user_data);
     return flip_social_explore->count > 0;
 }

+ 47 - 73
feed/flip_social_feed.c

@@ -47,13 +47,6 @@ FlipSocialFeedMini *flip_social_parse_json_feed()
         return NULL;
     }
     flipper_http_deinit();
-    char *data_cstr = (char *)furi_string_get_cstr(feed_data);
-    if (data_cstr == NULL)
-    {
-        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
-        furi_string_free(feed_data);
-        return NULL;
-    }
 
     FlipSocialFeedMini *feed_info = (FlipSocialFeedMini *)malloc(sizeof(FlipSocialFeedMini));
     if (!feed_info)
@@ -62,65 +55,58 @@ FlipSocialFeedMini *flip_social_parse_json_feed()
         return NULL;
     }
 
-    // Remove newlines
-    char *pos = data_cstr;
-    while ((pos = strchr(pos, '\n')) != NULL)
-    {
-        *pos = ' ';
-    }
-
     int feed_count = 0;
 
     // Iterate through the feed array
     for (int i = 0; i < MAX_FEED_ITEMS; i++)
     {
         // Parse each item in the array
-        char *item = get_json_array_value("feed", i, data_cstr, MAX_TOKENS);
+        FuriString *item = get_json_array_value_furi("feed", i, feed_data);
         if (item == NULL)
         {
             break;
         }
 
         // Extract individual fields from the JSON object
-        char *username = get_json_value("username", item, 40);
-        char *message = get_json_value("message", item, 40);
-        char *flipped = get_json_value("flipped", item, 40);
-        char *flips = get_json_value("flip_count", item, 40);
-        char *id = get_json_value("id", item, 40);
+        FuriString *username = get_json_value_furi("username", item);
+        FuriString *message = get_json_value_furi("message", item);
+        FuriString *flipped = get_json_value_furi("flipped", item);
+        FuriString *flips = get_json_value_furi("flip_count", item);
+        FuriString *id = get_json_value_furi("id", item);
 
         if (username == NULL || message == NULL || flipped == NULL || id == NULL)
         {
             FURI_LOG_E(TAG, "Failed to parse item fields.");
-            free(item);
-            free(username);
-            free(message);
-            free(flipped);
-            free(flips);
-            free(id);
+            furi_string_free(item);
+            furi_string_free(username);
+            furi_string_free(message);
+            furi_string_free(flipped);
+            furi_string_free(flips);
+            furi_string_free(id);
             continue;
         }
 
-        if (!flip_social_save_post(id, item))
+        if (!flip_social_save_post((char *)furi_string_get_cstr(id), (char *)furi_string_get_cstr(item)))
         {
             FURI_LOG_E(TAG, "Failed to save post.");
-            free(item);
-            free(username);
-            free(message);
-            free(flipped);
-            free(flips);
-            free(id);
+            furi_string_free(item);
+            furi_string_free(username);
+            furi_string_free(message);
+            furi_string_free(flipped);
+            furi_string_free(flips);
+            furi_string_free(id);
             continue;
         }
         feed_count++;
-        feed_info->ids[i] = atoi(id);
+        feed_info->ids[i] = atoi(furi_string_get_cstr(id));
 
-        // Free allocated memory
-        free(item);
-        free(username);
-        free(message);
-        free(flipped);
-        free(flips);
-        free(id);
+        // Furi_string_free allocated memory
+        furi_string_free(item);
+        furi_string_free(username);
+        furi_string_free(message);
+        furi_string_free(flipped);
+        furi_string_free(flips);
+        furi_string_free(id);
     }
 
     // Store the number of feed items
@@ -128,7 +114,6 @@ FlipSocialFeedMini *flip_social_parse_json_feed()
     feed_info->index = 0;
 
     furi_string_free(feed_data);
-    free(data_cstr);
     return feed_info;
 }
 
@@ -144,13 +129,6 @@ bool flip_social_load_feed_post(int post_id)
         FURI_LOG_E(TAG, "Failed to load received data from file.");
         return false;
     }
-    char *data_cstr = (char *)furi_string_get_cstr(feed_data);
-    if (data_cstr == NULL)
-    {
-        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
-        furi_string_free(feed_data);
-        return false;
-    }
 
     // Parse the feed post
     if (!flip_feed_item)
@@ -160,49 +138,45 @@ bool flip_social_load_feed_post(int post_id)
         {
             FURI_LOG_E(TAG, "Failed to allocate memory for feed post.");
             furi_string_free(feed_data);
-            free(data_cstr);
             return false;
         }
     }
 
     // Extract individual fields from the JSON object
-    char *username = get_json_value("username", data_cstr, 16);
-    char *message = get_json_value("message", data_cstr, 16);
-    char *flipped = get_json_value("flipped", data_cstr, 16);
-    char *flips = get_json_value("flip_count", data_cstr, 16);
-    char *id = get_json_value("id", data_cstr, 16);
+    FuriString *username = get_json_value_furi("username", feed_data);
+    FuriString *message = get_json_value_furi("message", feed_data);
+    FuriString *flipped = get_json_value_furi("flipped", feed_data);
+    FuriString *flips = get_json_value_furi("flip_count", feed_data);
+    FuriString *id = get_json_value_furi("id", feed_data);
 
     if (username == NULL || message == NULL || flipped == NULL || id == NULL)
     {
         FURI_LOG_E(TAG, "Failed to parse item fields.");
-        free(username);
-        free(message);
-        free(flipped);
-        free(flips);
-        free(id);
-        free(data_cstr);
+        furi_string_free(username);
+        furi_string_free(message);
+        furi_string_free(flipped);
+        furi_string_free(flips);
+        furi_string_free(id);
         furi_string_free(feed_data);
         return false;
     }
 
     // Safely copy strings with bounds checking
-    snprintf(flip_feed_item->username, MAX_USER_LENGTH, "%s", username);
-    snprintf(flip_feed_item->message, MAX_MESSAGE_LENGTH, "%s", message);
+    snprintf(flip_feed_item->username, MAX_USER_LENGTH, "%s", furi_string_get_cstr(username));
+    snprintf(flip_feed_item->message, MAX_MESSAGE_LENGTH, "%s", furi_string_get_cstr(message));
 
     // Store boolean and integer values
-    flip_feed_item->is_flipped = strstr(flipped, "true") != NULL;
-    flip_feed_item->id = atoi(id);
-    flip_feed_item->flips = atoi(flips);
+    flip_feed_item->is_flipped = strstr(furi_string_get_cstr(flipped), "true") != NULL;
+    flip_feed_item->id = atoi(furi_string_get_cstr(id));
+    flip_feed_item->flips = atoi(furi_string_get_cstr(flips));
 
     // Free allocated memory
-    free(username);
-    free(message);
-    free(flipped);
-    free(flips);
-    free(id);
-
+    furi_string_free(username);
+    furi_string_free(message);
+    furi_string_free(flipped);
+    furi_string_free(flips);
+    furi_string_free(id);
     furi_string_free(feed_data);
-    free(data_cstr);
 
     return true;
 }

+ 14 - 5
flip_social.h

@@ -16,7 +16,6 @@
 #define MAX_EXPLORE_USERS 50      // Maximum number of users to explore
 #define MAX_USER_LENGTH 32        // Maximum length of a username
 #define MAX_FRIENDS 50            // Maximum number of friends
-#define MAX_TOKENS 576            // Adjust based on expected JSON tokens
 #define MAX_FEED_ITEMS 50         // Maximum number of feed items
 #define MAX_LINE_LENGTH 30
 #define MAX_MESSAGE_USERS 40 // Maximum number of users to display in the submenu
@@ -28,11 +27,15 @@
 // Define the submenu items for our Hello World application
 typedef enum
 {
-    FlipSocialSubmenuLoggedOutIndexLogin,        // click to go to the login screen
-    FlipSocialSubmenuLoggedOutIndexRegister,     // click to go to the register screen
+    FlipSocialSubmenuLoggedOutIndexLogin,    // click to go to the login screen
+    FlipSocialSubmenuLoggedOutIndexRegister, // click to go to the register screen
+    //
     FlipSocialSubmenuLoggedOutIndexAbout,        // click to go to the about screen
     FlipSocialSubmenuLoggedOutIndexWifiSettings, // click to go to the wifi settings screen
     //
+    FlipSocialSubmenuLoggedInIndexAbout,        // click to go to the about screen
+    FlipSocialSubmenuLoggedInIndexWifiSettings, // click to go to the wifi settings screen
+    //
     FlipSocialSubmenuLoggedInIndexProfile,  // click to go to the profile screen
     FlipSocialSubmenuExploreIndex,          // click to go to the explore
     FlipSocialSubmenuLoggedInIndexFeed,     // click to go to the feed screen
@@ -161,6 +164,8 @@ typedef enum
     //
     FlipSocialViewTextInput, // The text input screen
     FlipSocialViewVariableItemList,
+    //
+    FlipSocialViewSubmenu,
 } FlipSocialView;
 
 // Define the application structure
@@ -177,8 +182,12 @@ typedef struct
     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)
+    Submenu *submenu_logged_in_settings;    // The application submenu (settings)
+    //
+    Submenu *submenu;
+    //
+    Widget *widget_logged_out_about; // The about screen (logged out)
+    Widget *widget_logged_in_about;  // The about screen (logged in)
 
     VariableItemList *variable_item_list; // The main menu
 

+ 3 - 3
friends/flip_social_friends.c

@@ -92,17 +92,17 @@ bool flip_social_parse_json_friends()
     // Extract the users array from the JSON
     for (int i = 0; i < MAX_FRIENDS; i++)
     {
-        char *friend = get_json_array_value("friends", i, (char *)furi_string_get_cstr(friend_data), 256);
+        FuriString *friend = get_json_array_value_furi("friends", i, friend_data);
         if (friend == NULL)
         {
             FURI_LOG_E(TAG, "Failed to parse friend %d.", i);
             furi_string_free(friend_data);
             break;
         }
-        snprintf(flip_social_friends->usernames[i], MAX_USER_LENGTH, "%s", friend);
+        snprintf(flip_social_friends->usernames[i], MAX_USER_LENGTH, "%s", furi_string_get_cstr(friend));
         submenu_add_item(app_instance->submenu_friends, flip_social_friends->usernames[i], FlipSocialSubmenuLoggedInIndexFriendsStart + i, flip_social_callback_submenu_choices, app_instance);
         flip_social_friends->count++;
-        free(friend);
+        furi_string_free(friend);
     }
     furi_string_free(friend_data);
     return true;

+ 82 - 45
jsmn/jsmn.c

@@ -7,8 +7,6 @@
  */
 
 #include <jsmn/jsmn.h>
-#include <stdlib.h>
-#include <string.h>
 
 /**
  * Allocates a fresh unused token from the token pool.
@@ -448,14 +446,14 @@ int jsoneq(const char *json, jsmntok_t *tok, const char *s)
 }
 
 // Return the value of the key in the JSON data
-char *get_json_value(char *key, char *json_data, uint32_t max_tokens)
+char *get_json_value(char *key, const char *json_data)
 {
     // Parse the JSON feed
     if (json_data != NULL)
     {
         jsmn_parser parser;
         jsmn_init(&parser);
-
+        uint32_t max_tokens = json_token_count(json_data);
         // Allocate tokens array on the heap
         jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens);
         if (tokens == NULL)
@@ -514,22 +512,67 @@ char *get_json_value(char *key, char *json_data, uint32_t max_tokens)
     return NULL; // Return NULL if something goes wrong
 }
 
-// Revised get_json_array_value function
-char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t max_tokens)
+// Helper function to skip a token and all its descendants.
+// Returns the index of the next token after skipping this one.
+// On error or out of bounds, returns -1.
+static int skip_token(const jsmntok_t *tokens, int start, int total)
 {
-    // Retrieve the array string for the given key
-    char *array_str = get_json_value(key, json_data, max_tokens);
+    if (start < 0 || start >= total)
+        return -1;
+
+    int i = start;
+    if (tokens[i].type == JSMN_OBJECT)
+    {
+        // For an object: size is number of key-value pairs
+        int pairs = tokens[i].size;
+        i++; // move to first key-value pair
+        for (int p = 0; p < pairs; p++)
+        {
+            // skip key (primitive/string)
+            i++;
+            if (i >= total)
+                return -1;
+            // skip value (which could be object/array and must be skipped recursively)
+            i = skip_token(tokens, i, total);
+            if (i == -1)
+                return -1;
+        }
+        return i; // i is now just past the object
+    }
+    else if (tokens[i].type == JSMN_ARRAY)
+    {
+        // For an array: size is number of elements
+        int elems = tokens[i].size;
+        i++; // move to first element
+        for (int e = 0; e < elems; e++)
+        {
+            i = skip_token(tokens, i, total);
+            if (i == -1)
+                return -1;
+        }
+        return i; // i is now just past the array
+    }
+    else
+    {
+        // Primitive or string token, just skip it
+        return i + 1;
+    }
+}
+
+// Revised get_json_array_value
+char *get_json_array_value(char *key, uint32_t index, const char *json_data)
+{
+    // Always extract the full array each time from the original json_data
+    char *array_str = get_json_value(key, json_data);
     if (array_str == NULL)
     {
         FURI_LOG_E("JSMM.H", "Failed to get array for key: %s", key);
         return NULL;
     }
+    uint32_t max_tokens = json_token_count(array_str);
 
-    // Initialize the JSON parser
     jsmn_parser parser;
     jsmn_init(&parser);
-
-    // Allocate memory for JSON tokens
     jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * max_tokens);
     if (tokens == NULL)
     {
@@ -538,7 +581,6 @@ char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t
         return NULL;
     }
 
-    // Parse the JSON array
     int ret = jsmn_parse(&parser, array_str, strlen(array_str), tokens, max_tokens);
     if (ret < 0)
     {
@@ -548,7 +590,6 @@ char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t
         return NULL;
     }
 
-    // Ensure the root element is an array
     if (ret < 1 || tokens[0].type != JSMN_ARRAY)
     {
         FURI_LOG_E("JSMM.H", "Value for key '%s' is not an array.", key);
@@ -557,50 +598,33 @@ char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t
         return NULL;
     }
 
-    // Check if the index is within bounds
     if (index >= (uint32_t)tokens[0].size)
     {
-        FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %d.", (unsigned long)index, tokens[0].size);
+        FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %u.", index, tokens[0].size);
         free(tokens);
         free(array_str);
         return NULL;
     }
 
-    // Locate the token corresponding to the desired array element
-    int current_token = 1; // Start after the array token
+    // Find the index-th element: start from token[1], which is the first element
+    int elem_token = 1;
     for (uint32_t i = 0; i < index; i++)
     {
-        if (tokens[current_token].type == JSMN_OBJECT)
-        {
-            // For objects, skip all key-value pairs
-            current_token += 1 + 2 * tokens[current_token].size;
-        }
-        else if (tokens[current_token].type == JSMN_ARRAY)
-        {
-            // For nested arrays, skip all elements
-            current_token += 1 + tokens[current_token].size;
-        }
-        else
+        elem_token = skip_token(tokens, elem_token, ret);
+        if (elem_token == -1 || elem_token >= ret)
         {
-            // For primitive types, simply move to the next token
-            current_token += 1;
-        }
-
-        // Safety check to prevent out-of-bounds
-        if (current_token >= ret)
-        {
-            FURI_LOG_E("JSMM.H", "Unexpected end of tokens while traversing array.");
+            FURI_LOG_E("JSMM.H", "Error skipping tokens to reach element %lu.", i);
             free(tokens);
             free(array_str);
             return NULL;
         }
     }
 
-    // Extract the array element
-    jsmntok_t element = tokens[current_token];
+    // Now elem_token should point to the token of the requested element
+    jsmntok_t element = tokens[elem_token];
     int length = element.end - element.start;
     char *value = malloc(length + 1);
-    if (value == NULL)
+    if (!value)
     {
         FURI_LOG_E("JSMM.H", "Failed to allocate memory for array element.");
         free(tokens);
@@ -608,11 +632,9 @@ char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t
         return NULL;
     }
 
-    // Copy the element value to a new string
     strncpy(value, array_str + element.start, length);
-    value[length] = '\0'; // Null-terminate the string
+    value[length] = '\0';
 
-    // Clean up
     free(tokens);
     free(array_str);
 
@@ -620,16 +642,16 @@ char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t
 }
 
 // Revised get_json_array_values function with correct token skipping
-char **get_json_array_values(char *key, char *json_data, uint32_t max_tokens, int *num_values)
+char **get_json_array_values(char *key, char *json_data, int *num_values)
 {
     // Retrieve the array string for the given key
-    char *array_str = get_json_value(key, json_data, max_tokens);
+    char *array_str = get_json_value(key, json_data);
     if (array_str == NULL)
     {
         FURI_LOG_E("JSMM.H", "Failed to get array for key: %s", key);
         return NULL;
     }
-
+    uint32_t max_tokens = json_token_count(array_str);
     // Initialize the JSON parser
     jsmn_parser parser;
     jsmn_init(&parser);
@@ -745,3 +767,18 @@ char **get_json_array_values(char *key, char *json_data, uint32_t max_tokens, in
     free(array_str);
     return values;
 }
+
+int json_token_count(const char *json)
+{
+    if (json == NULL)
+    {
+        return JSMN_ERROR_INVAL;
+    }
+
+    jsmn_parser parser;
+    jsmn_init(&parser);
+
+    // Pass NULL for tokens and 0 for num_tokens to get the token count only
+    int ret = jsmn_parse(&parser, json, strlen(json), NULL, 0);
+    return ret; // If ret >= 0, it represents the number of tokens needed.
+}

+ 6 - 58
jsmn/jsmn.h

@@ -17,6 +17,7 @@
 #define JSMN_H
 
 #include <stddef.h>
+#include <jsmn/jsmn_h.h>
 
 #ifdef __cplusplus
 extern "C"
@@ -28,61 +29,6 @@ extern "C"
 #else
 #define JSMN_API extern
 #endif
-
-    /**
-     * JSON type identifier. Basic types are:
-     * 	o Object
-     * 	o Array
-     * 	o String
-     * 	o Other primitive: number, boolean (true/false) or null
-     */
-    typedef enum
-    {
-        JSMN_UNDEFINED = 0,
-        JSMN_OBJECT = 1 << 0,
-        JSMN_ARRAY = 1 << 1,
-        JSMN_STRING = 1 << 2,
-        JSMN_PRIMITIVE = 1 << 3
-    } jsmntype_t;
-
-    enum jsmnerr
-    {
-        /* Not enough tokens were provided */
-        JSMN_ERROR_NOMEM = -1,
-        /* Invalid character inside JSON string */
-        JSMN_ERROR_INVAL = -2,
-        /* The string is not a full JSON packet, more bytes expected */
-        JSMN_ERROR_PART = -3
-    };
-
-    /**
-     * JSON token description.
-     * type		type (object, array, string etc.)
-     * start	start position in JSON data string
-     * end		end position in JSON data string
-     */
-    typedef struct
-    {
-        jsmntype_t type;
-        int start;
-        int end;
-        int size;
-#ifdef JSMN_PARENT_LINKS
-        int parent;
-#endif
-    } jsmntok_t;
-
-    /**
-     * JSON parser. Contains an array of token blocks available. Also stores
-     * the string being parsed now and current position in that string.
-     */
-    typedef struct
-    {
-        unsigned int pos;     /* offset in the JSON string */
-        unsigned int toknext; /* next token to allocate */
-        int toksuper;         /* superior token node, e.g. parent object or array */
-    } jsmn_parser;
-
     /**
      * Create JSON parser over an array of tokens
      */
@@ -122,11 +68,13 @@ char *jsmn(const char *key, const char *value);
 int jsoneq(const char *json, jsmntok_t *tok, const char *s);
 
 // Return the value of the key in the JSON data
-char *get_json_value(char *key, char *json_data, uint32_t max_tokens);
+char *get_json_value(char *key, const char *json_data);
 
 // Revised get_json_array_value function
-char *get_json_array_value(char *key, uint32_t index, char *json_data, uint32_t max_tokens);
+char *get_json_array_value(char *key, uint32_t index, const char *json_data);
 
 // Revised get_json_array_values function with correct token skipping
-char **get_json_array_values(char *key, char *json_data, uint32_t max_tokens, int *num_values);
+char **get_json_array_values(char *key, char *json_data, int *num_values);
+
+int json_token_count(const char *json);
 #endif /* JB_JSMN_EDIT */

+ 720 - 0
jsmn/jsmn_furi.c

@@ -0,0 +1,720 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2010 Serge Zaitsev
+ *
+ * [License text continues...]
+ */
+
+#include <jsmn/jsmn_furi.h>
+
+// Forward declarations of helper functions
+static int jsoneq_furi(const FuriString *json, jsmntok_t *tok, const FuriString *s);
+static int skip_token(const jsmntok_t *tokens, int start, int total);
+
+/**
+ * Allocates a fresh unused token from the token pool.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
+                                   const size_t num_tokens)
+{
+    if (parser->toknext >= num_tokens)
+    {
+        return NULL;
+    }
+    jsmntok_t *tok = &tokens[parser->toknext++];
+    tok->start = tok->end = -1;
+    tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+    tok->parent = -1;
+#endif
+    return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
+                            const int start, const int end)
+{
+    token->type = type;
+    token->start = start;
+    token->end = end;
+    token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ * Now uses FuriString to access characters.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const FuriString *js,
+                                jsmntok_t *tokens, const size_t num_tokens)
+{
+    size_t len = furi_string_size(js);
+    int start = parser->pos;
+
+    for (; parser->pos < len; parser->pos++)
+    {
+        char c = furi_string_get_char(js, parser->pos);
+        switch (c)
+        {
+#ifndef JSMN_STRICT
+        case ':':
+#endif
+        case '\t':
+        case '\r':
+        case '\n':
+        case ' ':
+        case ',':
+        case ']':
+        case '}':
+            goto found;
+        default:
+            break;
+        }
+        if (c < 32 || c >= 127)
+        {
+            parser->pos = start;
+            return JSMN_ERROR_INVAL;
+        }
+    }
+
+#ifdef JSMN_STRICT
+    // In strict mode primitive must be followed by a comma/object/array
+    parser->pos = start;
+    return JSMN_ERROR_PART;
+#endif
+
+found:
+    if (tokens == NULL)
+    {
+        parser->pos--;
+        return 0;
+    }
+    jsmntok_t *token = jsmn_alloc_token(parser, tokens, num_tokens);
+    if (token == NULL)
+    {
+        parser->pos = start;
+        return JSMN_ERROR_NOMEM;
+    }
+    jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+    token->parent = parser->toksuper;
+#endif
+    parser->pos--;
+    return 0;
+}
+
+/**
+ * Fills next token with JSON string.
+ * Now uses FuriString to access characters.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const FuriString *js,
+                             jsmntok_t *tokens, const size_t num_tokens)
+{
+    size_t len = furi_string_size(js);
+    int start = parser->pos;
+    parser->pos++;
+
+    for (; parser->pos < len; parser->pos++)
+    {
+        char c = furi_string_get_char(js, parser->pos);
+        if (c == '\"')
+        {
+            if (tokens == NULL)
+            {
+                return 0;
+            }
+            jsmntok_t *token = jsmn_alloc_token(parser, tokens, num_tokens);
+            if (token == NULL)
+            {
+                parser->pos = start;
+                return JSMN_ERROR_NOMEM;
+            }
+            jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+            token->parent = parser->toksuper;
+#endif
+            return 0;
+        }
+
+        if (c == '\\' && (parser->pos + 1) < len)
+        {
+            parser->pos++;
+            char esc = furi_string_get_char(js, parser->pos);
+            switch (esc)
+            {
+            case '\"':
+            case '/':
+            case '\\':
+            case 'b':
+            case 'f':
+            case 'r':
+            case 'n':
+            case 't':
+                break;
+            case 'u':
+            {
+                parser->pos++;
+                for (int i = 0; i < 4 && parser->pos < len; i++)
+                {
+                    char hex = furi_string_get_char(js, parser->pos);
+                    if (!((hex >= '0' && hex <= '9') ||
+                          (hex >= 'A' && hex <= 'F') ||
+                          (hex >= 'a' && hex <= 'f')))
+                    {
+                        parser->pos = start;
+                        return JSMN_ERROR_INVAL;
+                    }
+                    parser->pos++;
+                }
+                parser->pos--;
+                break;
+            }
+            default:
+                parser->pos = start;
+                return JSMN_ERROR_INVAL;
+            }
+        }
+    }
+    parser->pos = start;
+    return JSMN_ERROR_PART;
+}
+
+/**
+ * Create JSON parser
+ */
+void jsmn_init_furi(jsmn_parser *parser)
+{
+    parser->pos = 0;
+    parser->toknext = 0;
+    parser->toksuper = -1;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ * Now uses FuriString for the input JSON.
+ */
+int jsmn_parse_furi(jsmn_parser *parser, const FuriString *js,
+                    jsmntok_t *tokens, const unsigned int num_tokens)
+{
+    size_t len = furi_string_size(js);
+    int r;
+    int i;
+    int count = parser->toknext;
+
+    for (; parser->pos < len; parser->pos++)
+    {
+        char c = furi_string_get_char(js, parser->pos);
+        jsmntype_t type;
+
+        switch (c)
+        {
+        case '{':
+        case '[':
+        {
+            count++;
+            if (tokens == NULL)
+            {
+                break;
+            }
+            jsmntok_t *token = jsmn_alloc_token(parser, tokens, num_tokens);
+            if (token == NULL)
+                return JSMN_ERROR_NOMEM;
+            if (parser->toksuper != -1)
+            {
+                jsmntok_t *t = &tokens[parser->toksuper];
+#ifdef JSMN_STRICT
+                if (t->type == JSMN_OBJECT)
+                    return JSMN_ERROR_INVAL;
+#endif
+                t->size++;
+#ifdef JSMN_PARENT_LINKS
+                token->parent = parser->toksuper;
+#endif
+            }
+            token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+            token->start = parser->pos;
+            parser->toksuper = parser->toknext - 1;
+            break;
+        }
+        case '}':
+        case ']':
+            if (tokens == NULL)
+            {
+                break;
+            }
+            type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+            if (parser->toknext < 1)
+            {
+                return JSMN_ERROR_INVAL;
+            }
+            {
+                jsmntok_t *token = &tokens[parser->toknext - 1];
+                for (;;)
+                {
+                    if (token->start != -1 && token->end == -1)
+                    {
+                        if (token->type != type)
+                            return JSMN_ERROR_INVAL;
+                        token->end = parser->pos + 1;
+                        parser->toksuper = token->parent;
+                        break;
+                    }
+                    if (token->parent == -1)
+                    {
+                        if (token->type != type || parser->toksuper == -1)
+                        {
+                            return JSMN_ERROR_INVAL;
+                        }
+                        break;
+                    }
+                    token = &tokens[token->parent];
+                }
+            }
+#else
+            {
+                jsmntok_t *token;
+                for (i = parser->toknext - 1; i >= 0; i--)
+                {
+                    token = &tokens[i];
+                    if (token->start != -1 && token->end == -1)
+                    {
+                        if (token->type != type)
+                            return JSMN_ERROR_INVAL;
+                        parser->toksuper = -1;
+                        token->end = parser->pos + 1;
+                        break;
+                    }
+                }
+                if (i == -1)
+                    return JSMN_ERROR_INVAL;
+                for (; i >= 0; i--)
+                {
+                    token = &tokens[i];
+                    if (token->start != -1 && token->end == -1)
+                    {
+                        parser->toksuper = i;
+                        break;
+                    }
+                }
+            }
+#endif
+            break;
+        case '\"':
+            r = jsmn_parse_string(parser, js, tokens, num_tokens);
+            if (r < 0)
+                return r;
+            count++;
+            if (parser->toksuper != -1 && tokens != NULL)
+            {
+                tokens[parser->toksuper].size++;
+            }
+            break;
+        case '\t':
+        case '\r':
+        case '\n':
+        case ' ':
+            // Whitespace - ignore
+            break;
+        case ':':
+            parser->toksuper = parser->toknext - 1;
+            break;
+        case ',':
+            if (tokens != NULL && parser->toksuper != -1 &&
+                tokens[parser->toksuper].type != JSMN_ARRAY &&
+                tokens[parser->toksuper].type != JSMN_OBJECT)
+            {
+#ifdef JSMN_PARENT_LINKS
+                parser->toksuper = tokens[parser->toksuper].parent;
+#else
+                for (i = parser->toknext - 1; i >= 0; i--)
+                {
+                    if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT)
+                    {
+                        if (tokens[i].start != -1 && tokens[i].end == -1)
+                        {
+                            parser->toksuper = i;
+                            break;
+                        }
+                    }
+                }
+#endif
+            }
+            break;
+#ifdef JSMN_STRICT
+        case '-':
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case 't':
+        case 'f':
+        case 'n':
+            if (tokens != NULL && parser->toksuper != -1)
+            {
+                const jsmntok_t *t = &tokens[parser->toksuper];
+                if (t->type == JSMN_OBJECT ||
+                    (t->type == JSMN_STRING && t->size != 0))
+                {
+                    return JSMN_ERROR_INVAL;
+                }
+            }
+#else
+        default:
+#endif
+            r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
+            if (r < 0)
+                return r;
+            count++;
+            if (parser->toksuper != -1 && tokens != NULL)
+            {
+                tokens[parser->toksuper].size++;
+            }
+            break;
+#ifdef JSMN_STRICT
+        default:
+            return JSMN_ERROR_INVAL;
+#endif
+        }
+    }
+
+    if (tokens != NULL)
+    {
+        for (i = parser->toknext - 1; i >= 0; i--)
+        {
+            if (tokens[i].start != -1 && tokens[i].end == -1)
+            {
+                return JSMN_ERROR_PART;
+            }
+        }
+    }
+
+    return count;
+}
+
+// The rest of your code (e.g., get_json_value_furi, get_json_array_value_furi, etc.)
+// remains unchanged and can still rely on these updated parsing functions.
+
+// Helper function to create a JSON object: {"key":"value"}
+FuriString *jsmn_create_object(const FuriString *key, const FuriString *value)
+{
+    FuriString *result = furi_string_alloc();
+    furi_string_printf(result, "{\"%s\":\"%s\"}",
+                       furi_string_get_cstr(key),
+                       furi_string_get_cstr(value));
+    return result; // Caller responsible for furi_string_free
+}
+
+// Helper function to compare JSON keys
+static int jsoneq_furi(const FuriString *json, jsmntok_t *tok, const FuriString *s)
+{
+    size_t s_len = furi_string_size(s);
+    size_t tok_len = tok->end - tok->start;
+
+    if (tok->type != JSMN_STRING)
+        return -1;
+    if (s_len != tok_len)
+        return -1;
+
+    FuriString *sub = furi_string_alloc_set(json);
+    furi_string_mid(sub, tok->start, tok_len);
+
+    int res = furi_string_cmp(sub, s);
+    furi_string_free(sub);
+
+    return (res == 0) ? 0 : -1;
+}
+
+// Skip a token and its descendants
+static int skip_token(const jsmntok_t *tokens, int start, int total)
+{
+    if (start < 0 || start >= total)
+        return -1;
+
+    int i = start;
+    if (tokens[i].type == JSMN_OBJECT)
+    {
+        int pairs = tokens[i].size;
+        i++;
+        for (int p = 0; p < pairs; p++)
+        {
+            i++; // skip key
+            if (i >= total)
+                return -1;
+            i = skip_token(tokens, i, total); // skip value
+            if (i == -1)
+                return -1;
+        }
+        return i;
+    }
+    else if (tokens[i].type == JSMN_ARRAY)
+    {
+        int elems = tokens[i].size;
+        i++;
+        for (int e = 0; e < elems; e++)
+        {
+            i = skip_token(tokens, i, total);
+            if (i == -1)
+                return -1;
+        }
+        return i;
+    }
+    else
+    {
+        return i + 1;
+    }
+}
+
+/**
+ * Parse JSON and return the value associated with a given char* key.
+ */
+FuriString *get_json_value_furi(const char *key, const FuriString *json_data)
+{
+    if (json_data == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "JSON data is NULL");
+        return NULL;
+    }
+    uint32_t max_tokens = json_token_count_furi(json_data);
+    // Create a temporary FuriString from key
+    FuriString *key_str = furi_string_alloc();
+    furi_string_cat_str(key_str, key);
+
+    jsmn_parser parser;
+    jsmn_init_furi(&parser);
+
+    jsmntok_t *tokens = (jsmntok_t *)malloc(sizeof(jsmntok_t) * max_tokens);
+    if (tokens == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
+        furi_string_free(key_str);
+        return NULL;
+    }
+
+    int ret = jsmn_parse_furi(&parser, json_data, tokens, max_tokens);
+    if (ret < 0)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to parse JSON: %d", ret);
+        free(tokens);
+        furi_string_free(key_str);
+        return NULL;
+    }
+
+    if (ret < 1 || tokens[0].type != JSMN_OBJECT)
+    {
+        FURI_LOG_E("JSMM.H", "Root element is not an object.");
+        free(tokens);
+        furi_string_free(key_str);
+        return NULL;
+    }
+
+    for (int i = 1; i < ret; i++)
+    {
+        if (jsoneq_furi(json_data, &tokens[i], key_str) == 0)
+        {
+            int length = tokens[i + 1].end - tokens[i + 1].start;
+            FuriString *value = furi_string_alloc_set(json_data);
+            furi_string_mid(value, tokens[i + 1].start, length);
+            free(tokens);
+            furi_string_free(key_str);
+            return value;
+        }
+    }
+
+    free(tokens);
+    furi_string_free(key_str);
+    FURI_LOG_E("JSMM.H", "Failed to find the key in the JSON.");
+    return NULL;
+}
+
+/**
+ * Return the value at a given index in a JSON array for a given char* key.
+ */
+FuriString *get_json_array_value_furi(const char *key, uint32_t index, const FuriString *json_data)
+{
+    FuriString *array_str = get_json_value_furi(key, json_data);
+    if (array_str == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to get array for key");
+        return NULL;
+    }
+    uint32_t max_tokens = json_token_count_furi(array_str);
+    jsmn_parser parser;
+    jsmn_init_furi(&parser);
+
+    jsmntok_t *tokens = (jsmntok_t *)malloc(sizeof(jsmntok_t) * max_tokens);
+    if (tokens == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
+        furi_string_free(array_str);
+        return NULL;
+    }
+
+    int ret = jsmn_parse_furi(&parser, array_str, tokens, max_tokens);
+    if (ret < 0)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to parse JSON array: %d", ret);
+        free(tokens);
+        furi_string_free(array_str);
+        return NULL;
+    }
+
+    if (ret < 1 || tokens[0].type != JSMN_ARRAY)
+    {
+        FURI_LOG_E("JSMM.H", "Value for key is not an array.");
+        free(tokens);
+        furi_string_free(array_str);
+        return NULL;
+    }
+
+    if (index >= (uint32_t)tokens[0].size)
+    {
+        FURI_LOG_E("JSMM.H", "Index %lu out of bounds for array with size %u.", index, tokens[0].size);
+        free(tokens);
+        furi_string_free(array_str);
+        return NULL;
+    }
+
+    int elem_token = 1;
+    for (uint32_t i = 0; i < index; i++)
+    {
+        elem_token = skip_token(tokens, elem_token, ret);
+        if (elem_token == -1 || elem_token >= ret)
+        {
+            FURI_LOG_E("JSMM.H", "Error skipping tokens to reach element %lu.", i);
+            free(tokens);
+            furi_string_free(array_str);
+            return NULL;
+        }
+    }
+
+    jsmntok_t element = tokens[elem_token];
+    int length = element.end - element.start;
+
+    FuriString *value = furi_string_alloc_set(array_str);
+    furi_string_mid(value, element.start, length);
+
+    free(tokens);
+    furi_string_free(array_str);
+
+    return value;
+}
+
+/**
+ * Extract all object values from a JSON array associated with a given char* key.
+ */
+FuriString **get_json_array_values_furi(const char *key, const FuriString *json_data, int *num_values)
+{
+    *num_values = 0;
+    // Convert key to FuriString and call get_json_value_furi
+    FuriString *array_str = get_json_value_furi(key, json_data);
+    if (array_str == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to get array for key");
+        return NULL;
+    }
+
+    uint32_t max_tokens = json_token_count_furi(array_str);
+    jsmn_parser parser;
+    jsmn_init_furi(&parser);
+
+    jsmntok_t *tokens = (jsmntok_t *)malloc(sizeof(jsmntok_t) * max_tokens);
+    if (tokens == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to allocate memory for JSON tokens.");
+        furi_string_free(array_str);
+        return NULL;
+    }
+
+    int ret = jsmn_parse_furi(&parser, array_str, tokens, max_tokens);
+    if (ret < 0)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to parse JSON array: %d", ret);
+        free(tokens);
+        furi_string_free(array_str);
+        return NULL;
+    }
+
+    if (tokens[0].type != JSMN_ARRAY)
+    {
+        FURI_LOG_E("JSMM.H", "Value for key is not an array.");
+        free(tokens);
+        furi_string_free(array_str);
+        return NULL;
+    }
+
+    int array_size = tokens[0].size;
+    FuriString **values = (FuriString **)malloc(array_size * sizeof(FuriString *));
+    if (values == NULL)
+    {
+        FURI_LOG_E("JSMM.H", "Failed to allocate memory for array of values.");
+        free(tokens);
+        furi_string_free(array_str);
+        return NULL;
+    }
+
+    int actual_num_values = 0;
+    int current_token = 1;
+    for (int i = 0; i < array_size; i++)
+    {
+        if (current_token >= ret)
+        {
+            FURI_LOG_E("JSMM.H", "Unexpected end of tokens while traversing array.");
+            break;
+        }
+
+        jsmntok_t element = tokens[current_token];
+
+        int length = element.end - element.start;
+        FuriString *value = furi_string_alloc_set(array_str);
+        furi_string_mid(value, element.start, length);
+
+        values[actual_num_values] = value;
+        actual_num_values++;
+
+        // Skip this element and its descendants
+        current_token = skip_token(tokens, current_token, ret);
+        if (current_token == -1)
+        {
+            FURI_LOG_E("JSMM.H", "Error skipping tokens after element %d.", i);
+            break;
+        }
+    }
+
+    *num_values = actual_num_values;
+    if (actual_num_values < array_size)
+    {
+        FuriString **reduced_values = (FuriString **)realloc(values, actual_num_values * sizeof(FuriString *));
+        if (reduced_values != NULL)
+        {
+            values = reduced_values;
+        }
+    }
+
+    free(tokens);
+    furi_string_free(array_str);
+    return values;
+}
+
+uint32_t json_token_count_furi(const FuriString *json)
+{
+    if (json == NULL)
+    {
+        return JSMN_ERROR_INVAL;
+    }
+
+    jsmn_parser parser;
+    jsmn_init_furi(&parser);
+
+    // Pass NULL for tokens and 0 for num_tokens to get the token count only
+    int ret = jsmn_parse_furi(&parser, json, NULL, 0);
+    return ret; // If ret >= 0, it represents the number of tokens needed.
+}

+ 74 - 0
jsmn/jsmn_furi.h

@@ -0,0 +1,74 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2010 Serge Zaitsev
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * [License text continues...]
+ */
+
+#ifndef JSMN_FURI_H
+#define JSMN_FURI_H
+
+#include <jsmn/jsmn_h.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef JSMN_STATIC
+#define JSMN_API static
+#else
+#define JSMN_API extern
+#endif
+
+    JSMN_API void jsmn_init_furi(jsmn_parser *parser);
+    JSMN_API int jsmn_parse_furi(jsmn_parser *parser, const FuriString *js,
+                                 jsmntok_t *tokens, const unsigned int num_tokens);
+
+#ifndef JSMN_HEADER
+/* Implementation in jsmn_furi.c */
+#endif /* JSMN_HEADER */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* JSMN_FURI_H */
+
+#ifndef JB_JSMN_FURI_EDIT
+#define JB_JSMN_FURI_EDIT
+
+// Helper function to create a JSON object
+FuriString *jsmn_create_object(const FuriString *key, const FuriString *value);
+
+// Updated signatures to accept const char* key
+FuriString *get_json_value_furi(const char *key, const FuriString *json_data);
+FuriString *get_json_array_value_furi(const char *key, uint32_t index, const FuriString *json_data);
+FuriString **get_json_array_values_furi(const char *key, const FuriString *json_data, int *num_values);
+
+uint32_t json_token_count_furi(const FuriString *json);
+/* Example usage:
+char *json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
+FuriString *json_data = char_to_furi_string(json);
+if (!json_data)
+{
+    FURI_LOG_E(TAG, "Failed to allocate FuriString");
+    return -1;
+}
+FuriString *value = get_json_value_furi("key1", json_data, json_token_count_furi(json_data));
+if (value)
+{
+    FURI_LOG_I(TAG, "Value: %s", furi_string_get_cstr(value));
+    furi_string_free(value);
+}
+furi_string_free(json_data);
+*/
+#endif /* JB_JSMN_EDIT */

+ 14 - 0
jsmn/jsmn_h.c

@@ -0,0 +1,14 @@
+#include <jsmn/jsmn_h.h>
+FuriString *char_to_furi_string(const char *str)
+{
+    FuriString *furi_str = furi_string_alloc();
+    if (!furi_str)
+    {
+        return NULL;
+    }
+    for (size_t i = 0; i < strlen(str); i++)
+    {
+        furi_string_push_back(furi_str, str[i]);
+    }
+    return furi_str;
+}

+ 41 - 0
jsmn/jsmn_h.h

@@ -0,0 +1,41 @@
+#pragma once
+#include <furi.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+typedef enum
+{
+    JSMN_UNDEFINED = 0,
+    JSMN_OBJECT = 1 << 0,
+    JSMN_ARRAY = 1 << 1,
+    JSMN_STRING = 1 << 2,
+    JSMN_PRIMITIVE = 1 << 3
+} jsmntype_t;
+
+enum jsmnerr
+{
+    JSMN_ERROR_NOMEM = -1,
+    JSMN_ERROR_INVAL = -2,
+    JSMN_ERROR_PART = -3
+};
+
+typedef struct
+{
+    jsmntype_t type;
+    int start;
+    int end;
+    int size;
+#ifdef JSMN_PARENT_LINKS
+    int parent;
+#endif
+} jsmntok_t;
+
+typedef struct
+{
+    unsigned int pos;     /* offset in the JSON string */
+    unsigned int toknext; /* next token to allocate */
+    int toksuper;         /* superior token node, e.g. parent object or array */
+} jsmn_parser;
+
+FuriString *char_to_furi_string(const char *str);

+ 11 - 21
messages/flip_social_messages.c

@@ -203,7 +203,7 @@ bool flip_social_parse_json_message_users()
     flip_social_message_users->count = 0;
 
     // Extract the users array from the JSON
-    char *json_users = get_json_value("users", data_cstr, 64);
+    char *json_users = get_json_value("users", data_cstr);
     if (json_users == NULL)
     {
         FURI_LOG_E(TAG, "Failed to parse users array.");
@@ -292,7 +292,7 @@ bool flip_social_parse_json_message_user_choices()
     flip_social_explore->count = 0;
 
     // Extract the users array from the JSON
-    char *json_users = get_json_value("users", data_cstr, 64);
+    char *json_users = get_json_value("users", data_cstr);
     if (json_users == NULL)
     {
         FURI_LOG_E(TAG, "Failed to parse users array.");
@@ -357,13 +357,6 @@ bool flip_social_parse_json_messages()
         return false;
     }
     flipper_http_deinit();
-    char *data_cstr = (char *)furi_string_get_cstr(message_data);
-    if (data_cstr == NULL)
-    {
-        FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
-        furi_string_free(message_data);
-        return false;
-    }
 
     // Allocate memory for each message only if not already allocated
     flip_social_messages = flip_social_user_messages_alloc();
@@ -371,7 +364,6 @@ bool flip_social_parse_json_messages()
     {
         FURI_LOG_E(TAG, "Failed to allocate memory for messages.");
         furi_string_free(message_data);
-        free(data_cstr);
         return false;
     }
 
@@ -382,40 +374,38 @@ bool flip_social_parse_json_messages()
     for (int i = 0; i < MAX_MESSAGES; i++)
     {
         // Parse each item in the array
-        char *item = get_json_array_value("conversations", i, data_cstr, 64);
+        FuriString *item = get_json_array_value_furi("conversations", i, message_data);
         if (item == NULL)
         {
             break;
         }
 
         // Extract individual fields from the JSON object
-        char *sender = get_json_value("sender", item, 8);
-        char *content = get_json_value("content", item, 8);
+        FuriString *sender = get_json_value_furi("sender", item);
+        FuriString *content = get_json_value_furi("content", item);
 
         if (sender == NULL || content == NULL)
         {
             FURI_LOG_E(TAG, "Failed to parse item fields.");
-            free(item);
+            furi_string_free(item);
             continue;
         }
 
         // Store parsed values in pre-allocated memory
-        snprintf(flip_social_messages->usernames[i], MAX_USER_LENGTH, "%s", sender);
-        snprintf(flip_social_messages->messages[i], MAX_MESSAGE_LENGTH, "%s", content);
+        snprintf(flip_social_messages->usernames[i], MAX_USER_LENGTH, "%s", furi_string_get_cstr(sender));
+        snprintf(flip_social_messages->messages[i], MAX_MESSAGE_LENGTH, "%s", furi_string_get_cstr(content));
         flip_social_messages->count++;
 
-        free(item);
-        free(sender);
-        free(content);
+        furi_string_free(item);
+        furi_string_free(sender);
+        furi_string_free(content);
     }
     if (!messages_dialog_alloc(true))
     {
         FURI_LOG_E(TAG, "Failed to allocate and set messages dialog.");
         furi_string_free(message_data);
-        free(data_cstr);
         return false;
     }
     furi_string_free(message_data);
-    free(data_cstr);
     return true;
 }