Ver código fonte

final v1.0 edits

jblanked 1 ano atrás
pai
commit
d587fa5aac

+ 2 - 4
README.md

@@ -3,11 +3,9 @@ The first social media app for Flipper Zero. Connect with other users directly o
 
 The highlight of this app is customizable pre-saves, which, as explained below, aim to address the challenges of typing with the directional pad.
 
-FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in the WebCrawler app: https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP
-
 ## Requirements
 - WiFi Developer Board, Raspberry Pi, or ESP32 Device with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP
-- WiFi Access Point
+- 2.4 Ghz WiFi Access Point
 
 
 ## Features
@@ -24,7 +22,7 @@ FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in
 
 **Registration:** Create an account with just a username and password—no email or personal information required or collected.
 
-**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.
+**Feed:** View 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. 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.
 

+ 95 - 13
alloc/alloc.c

@@ -202,7 +202,7 @@ void on_input(const void *event, void *ctx)
 
 #define MAX_LINES 6
 #define LINE_HEIGHT 8
-#define MAX_LINE_WIDTH_PX 128 // Adjust this to your display width
+#define MAX_LINE_WIDTH_PX 128
 #define TEMP_BUF_SIZE 128
 
 static void draw_user_message(Canvas *canvas, const char *user_message, int x, int y)
@@ -331,8 +331,6 @@ static void draw_user_message(Canvas *canvas, const char *user_message, int x, i
 
 static void flip_social_feed_draw_callback(Canvas *canvas, void *model)
 {
-    UNUSED(model);
-    canvas_clear(canvas);
     UNUSED(model);
     canvas_clear(canvas);
     canvas_set_font_custom(canvas, FONT_SIZE_LARGE);
@@ -387,6 +385,44 @@ static bool flip_social_feed_input_callback(InputEvent *event, void *context)
     }
     else if (event->type == InputTypePress && event->key == InputKeyRight) // Next message
     {
+        // if next message is the last message, then use flip_social_load_initial_feed
+        if (flip_feed_info->index == flip_feed_info->count - 1)
+        {
+            char series_index[16];
+            load_char("series_index", series_index, sizeof(series_index));
+            flip_feed_info->series_index = atoi(series_index) + 1;
+            char new_series_index[16];
+            snprintf(new_series_index, sizeof(new_series_index), "%d", flip_feed_info->series_index);
+            FURI_LOG_I(TAG, "New series index: %s", new_series_index);
+            save_char("series_index", new_series_index);
+
+            if (!flip_social_load_initial_feed(true, flip_feed_info->series_index))
+            {
+                FURI_LOG_E(TAG, "Failed to load initial feed");
+                fhttp.state = ISSUE;
+                return false;
+            }
+            // switch view, free dialog, re-alloc dialog, switch back to dialog
+            view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewWidgetResult);
+            flip_social_free_feed_view();
+            // load feed item
+            if (!flip_social_load_feed_post(flip_feed_info->ids[flip_feed_info->index]))
+            {
+                FURI_LOG_E(TAG, "Failed to load nexy feed post");
+                fhttp.state = ISSUE;
+                return false;
+            }
+            if (feed_view_alloc())
+            {
+                view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewLoggedInFeed);
+            }
+            else
+            {
+                FURI_LOG_E(TAG, "Failed to allocate feed dialog");
+                fhttp.state = ISSUE;
+                return false;
+            }
+        }
         if (flip_feed_info->index < flip_feed_info->count - 1)
         {
             flip_feed_info->index++;
@@ -423,7 +459,8 @@ static bool flip_social_feed_input_callback(InputEvent *event, void *context)
         else
         {
             // decrease the flip count
-            flip_feed_item->flips--;
+            if (flip_feed_item->flips > 0)
+                flip_feed_item->flips--;
         }
         // change the flip status
         flip_feed_item->is_flipped = !flip_feed_item->is_flipped;
@@ -444,15 +481,17 @@ static bool flip_social_feed_input_callback(InputEvent *event, void *context)
         if (flipper_http_post_request_with_headers("https://www.flipsocial.net/api/feed/flip/", auth_headers, payload))
         {
             // save feed item
-            char new_save[256];
-            snprintf(new_save, sizeof(new_save), "{\"id\":%u,\"username\":\"%s\",\"message\":\"%s\",\"flip_count\":%u,\"flipped\":%s}",
-                     flip_feed_item->id, flip_feed_item->username, flip_feed_item->message, flip_feed_item->flips, flip_feed_item->is_flipped ? "true" : "false");
-            // if (!flip_social_save_post((char *)flip_feed_item->id, new_save))
-            // {
-            //     FURI_LOG_E(TAG, "Failed to save the feed post");
-            //     fhttp.state = ISSUE;
-            //     return false;
-            // }
+            char new_save[512];
+            snprintf(new_save, sizeof(new_save), "{\"id\":%u,\"username\":\"%s\",\"message\":\"%s\",\"flip_count\":%u,\"flipped\":%s,\"date_created\":\"%s\"}",
+                     flip_feed_item->id, flip_feed_item->username, flip_feed_item->message, flip_feed_item->flips, flip_feed_item->is_flipped ? "true" : "false", flip_feed_item->date_created);
+            char id[16];
+            snprintf(id, sizeof(id), "%u", flip_feed_item->id);
+            if (!flip_social_save_post(id, new_save))
+            {
+                FURI_LOG_E(TAG, "Failed to save the feed post");
+                flipper_http_deinit();
+                return false;
+            }
         }
         // switch view, free dialog, re-alloc dialog, switch back to dialog
         view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipSocialViewWidgetResult);
@@ -667,6 +706,7 @@ bool alloc_submenu(uint32_t view_id)
             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);
+            submenu_add_item(app_instance->submenu, "User", FlipSocialSubmenuLoggedInIndexUserSettings, flip_social_callback_submenu_choices, app_instance);
             break;
         case FlipSocialViewLoggedInCompose:
             if (!easy_flipper_set_submenu(&app_instance->submenu, FlipSocialViewSubmenu, "Create A Post", flip_social_callback_to_submenu_logged_in, &app_instance->view_dispatcher))
@@ -724,6 +764,22 @@ bool alloc_submenu(uint32_t view_id)
     }
     return true;
 }
+static void flip_social_feed_type_change(VariableItem *item)
+{
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, flip_social_feed_type[index]);
+
+    // save the feed type
+    save_char("user_feed_type", strstr(flip_social_feed_type[index], "Global") ? "global" : "friends");
+}
+static void flip_social_notification_type_change(VariableItem *item)
+{
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, flip_social_notification_type[index]);
+
+    // save the notification type
+    save_char("user_notifications", strstr(flip_social_notification_type[index], "ON") ? "on" : "off");
+}
 
 bool alloc_variable_item_list(uint32_t view_id)
 {
@@ -790,6 +846,32 @@ bool alloc_variable_item_list(uint32_t view_id)
             if (app_instance->wifi_ssid_logged_in)
                 variable_item_set_current_value_text(app_instance->variable_item_logged_in_wifi_settings_ssid, app_instance->wifi_ssid_logged_in);
             return true;
+        case FlipSocialViewLoggedInSettingsUser:
+            if (!easy_flipper_set_variable_item_list(&app_instance->variable_item_list, FlipSocialViewVariableItemList, flip_social_logged_in_user_settings_item_selected, flip_social_callback_to_settings_logged_in, &app_instance->view_dispatcher, app_instance))
+            {
+                return false;
+            }
+            app_instance->variable_item_logged_in_user_settings_feed_type = variable_item_list_add(app_instance->variable_item_list, "Feed Type", 2, flip_social_feed_type_change, app_instance);
+            app_instance->variable_item_logged_in_user_settings_notifications = variable_item_list_add(app_instance->variable_item_list, "Notifications", 2, flip_social_notification_type_change, app_instance);
+            variable_item_set_current_value_text(app_instance->variable_item_logged_in_user_settings_feed_type, flip_social_feed_type[flip_social_feed_type_index]);
+            variable_item_set_current_value_index(app_instance->variable_item_logged_in_user_settings_feed_type, flip_social_feed_type_index);
+            variable_item_set_current_value_text(app_instance->variable_item_logged_in_user_settings_notifications, flip_social_notification_type[flip_social_notification_type_index]);
+            variable_item_set_current_value_index(app_instance->variable_item_logged_in_user_settings_notifications, flip_social_notification_type_index);
+            char user_feed_type[32];
+            char user_notifications[32];
+            if (load_char("user_feed_type", user_feed_type, sizeof(user_feed_type)))
+            {
+                flip_social_feed_type_index = strstr(user_feed_type, "friends") ? 1 : 0;
+                variable_item_set_current_value_text(app_instance->variable_item_logged_in_user_settings_feed_type, flip_social_feed_type[flip_social_feed_type_index]);
+                variable_item_set_current_value_index(app_instance->variable_item_logged_in_user_settings_feed_type, flip_social_feed_type_index);
+            }
+            if (load_char("user_notifications", user_notifications, sizeof(user_notifications)))
+            {
+                flip_social_notification_type_index = strstr(user_notifications, "on") ? 1 : 0;
+                variable_item_set_current_value_text(app_instance->variable_item_logged_in_user_settings_notifications, flip_social_notification_type[flip_social_notification_type_index]);
+                variable_item_set_current_value_index(app_instance->variable_item_logged_in_user_settings_notifications, flip_social_notification_type_index);
+            }
+            return true;
         default:
             return false;
         }

+ 2 - 0
alloc/flip_social_alloc.c

@@ -381,10 +381,12 @@ FlipSocialApp *flip_social_app_alloc()
 
         if (app->is_logged_in != NULL && strcmp(app->is_logged_in, "true") == 0)
         {
+            save_char("is_logged_in", "true");
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedInSubmenu);
         }
         else
         {
+            save_char("is_logged_in", "false");
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewLoggedOutSubmenu);
         }
     }

+ 6 - 5
app.c

@@ -62,15 +62,16 @@ int32_t main_flip_social(void *p)
     {
         char is_connected[5];
         char is_logged_in[5];
+        char is_notifications[5];
         load_char("is_connected", is_connected, 5);
         load_char("is_logged_in", is_logged_in, 5);
+        load_char("user_notifications", is_notifications, 5);
 
-        if (strcmp(is_connected, "true") == 0)
+        if (strcmp(is_connected, "true") == 0 &&
+            strcmp(is_notifications, "on") == 0 &&
+            strcmp(is_logged_in, "true") == 0)
         {
-            if (strcmp(is_logged_in, "true") == 0)
-            {
-                flip_social_home_notification();
-            }
+            flip_social_home_notification();
         }
     }
 

+ 8 - 6
assets/CHANGELOG.md

@@ -1,11 +1,13 @@
 ## 1.0 - Official Release
-- Final memory improvements
-- Updated the New Message option in the Messages view to allow the user to search for users to send a message to.
-- Updated the display of the Feed to use a custom view and custom font, and show how long ago the post was created
-- Organized the files saved/utilized in the apps_data folder
-- Fixed bugs in the Direct Messaging View
-- Fixed bugs in the Pre-Save View
+- Final memory improvements.
+- Updated the New Message option in the Messages view to allow users to search for recipients to send messages to.
+- Updated the display of the Feed to use a custom view and custom font, and to show how long ago each post was created.
+- Organized the files saved/utilized in the apps_data folder.
+- Fixed bugs in the Direct Messaging View.
+- Fixed bugs in the Pre-Save View.
 - Added home announcements and notifications.
+- Added User Settings (Notifications and Feed Type). With the Feed Type option, users can choose between a private feed, which only consists of posts from their friends, and a global feed, which consists of posts from all Flippers. The Notifications settings allow users to turn home notifications on and off.
+- Updated the Feed to be "endless." Once you reach the end of the feed, previous posts will load in sets of 20.
 
 ## 0.8 - New Features
 - Added support for RPC_KEYBOARD (thanks to Derek Jamison).

+ 2 - 4
assets/README.md

@@ -3,11 +3,9 @@ The first social media app for Flipper Zero. Connect with other users directly o
 
 The highlight of this app is customizable pre-saves, which, as explained below, aim to address the challenges of typing with the directional pad.
 
-FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in the WebCrawler app: https://github.com/jblanked/WebCrawler-FlipperZero/tree/main/assets/FlipperHTTP
-
 ## Requirements
 - WiFi Developer Board, Raspberry Pi, or ESP32 Device with FlipperHTTP Flash: https://github.com/jblanked/FlipperHTTP
-- WiFi Access Point
+- 2.4 Ghz WiFi Access Point
 
 
 ## Features
@@ -24,7 +22,7 @@ FlipSocial uses the FlipperHTTP flash for the WiFi Devboard, first introduced in
 
 **Registration:** Create an account with just a username and password—no email or personal information required or collected.
 
-**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.
+**Feed:** View 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. 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.
 

BIN
assets/flip-social-main-menu.png


+ 110 - 40
callback/flip_social_callback.c

@@ -602,7 +602,7 @@ static void compose_dialog_callback(DialogExResult result, void *context)
         {
             furi_delay_ms(100);
         }
-        if (flip_social_load_initial_feed(false))
+        if (flip_social_load_initial_feed(false, 1))
         {
             flip_social_free_compose_dialog();
         }
@@ -870,7 +870,7 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         break;
     case FlipSocialSubmenuLoggedInIndexFeed:
         free_all(true, true);
-        if (!flip_social_load_initial_feed(true))
+        if (!flip_social_load_initial_feed(true, 1))
         {
             FURI_LOG_E(TAG, "Failed to load the initial feed");
             return;
@@ -921,6 +921,15 @@ void flip_social_callback_submenu_choices(void *context, uint32_t index)
         }
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewVariableItemList);
         break;
+    case FlipSocialSubmenuLoggedInIndexUserSettings:
+        free_all(true, false);
+        if (!alloc_variable_item_list(FlipSocialViewLoggedInSettingsUser))
+        {
+            FURI_LOG_E(TAG, "Failed to allocate variable item list");
+            return;
+        }
+        view_dispatcher_switch_to_view(app->view_dispatcher, FlipSocialViewVariableItemList);
+        break;
     case FlipSocialSubmenuLoggedInSignOutButton:
         free_all(true, true);
         app->is_logged_in = "false";
@@ -1677,6 +1686,23 @@ void flip_social_text_input_logged_in_wifi_settings_item_selected(void *context,
     }
 }
 
+void flip_social_logged_in_user_settings_item_selected(void *context, uint32_t index)
+{
+    FlipSocialApp *app = (FlipSocialApp *)context;
+    if (!app)
+    {
+        FURI_LOG_E(TAG, "FlipSocialApp is NULL");
+        return;
+    }
+
+    // Switch to the appropriate view
+    switch (index)
+    {
+    case 0: // Feed Type
+        break;
+    }
+}
+
 /**
  * @brief Text input callback for when the user finishes entering their message on the compose (logged in) screen for Add Text
  * @param context The context - FlipSocialApp object.
@@ -2597,7 +2623,8 @@ static bool flip_social_parse_home_notification()
         return false;
     }
     flipper_http_deinit();
-    // check if announcement and analytics key exists
+
+    // Check if announcement and analytics key exists
     FuriString *announcement_json = get_json_value_furi("announcement", notification);
     FuriString *analytics_json = get_json_value_furi("analytics", notification);
     if (announcement_json == NULL || analytics_json == NULL)
@@ -2611,8 +2638,11 @@ static bool flip_social_parse_home_notification()
         {
             furi_string_free(analytics_json);
         }
+        furi_string_free(notification);
         return false;
     }
+
+    // Extract values from JSON
     FuriString *announcement_value = get_json_value_furi("content", announcement_json);
     FuriString *announcement_time = get_json_value_furi("date_created", announcement_json);
     FuriString *analytics_value = get_json_value_furi("count", analytics_json);
@@ -2636,55 +2666,94 @@ static bool flip_social_parse_home_notification()
         {
             furi_string_free(analytics_time);
         }
+        furi_string_free(announcement_json);
+        furi_string_free(analytics_json);
+        furi_string_free(notification);
         return false;
     }
-    // load previous announcement and analytics times to see if there is a new announcement or analytics
-    char past_analytics_time[32];
-    char past_announcement_time[32];
-    if (load_char("analytics_time", past_analytics_time, sizeof(past_analytics_time)) && load_char("announcement_time", past_announcement_time, sizeof(past_announcement_time)))
+
+    // Load previous announcement and analytics times
+    char past_analytics_time[32] = {0};
+    char past_announcement_time[32] = {0};
+    bool analytics_time_loaded = load_char("analytics_time", past_analytics_time, sizeof(past_analytics_time));
+    bool announcement_time_loaded = load_char("announcement_time", past_announcement_time, sizeof(past_announcement_time));
+
+    bool new_announcement = false;
+    bool new_analytics = false;
+
+    // Check for new announcement
+    if (!announcement_time_loaded || strcmp(furi_string_get_cstr(announcement_time), past_announcement_time) != 0)
     {
-        // check if the announcement or analytics time has changed
-        if (strcmp(furi_string_get_cstr(announcement_time), past_announcement_time) == 0 && strcmp(furi_string_get_cstr(analytics_time), past_analytics_time) == 0)
-        {
-            FURI_LOG_D(TAG, "No new announcement or analytics");
-            furi_string_free(announcement_value);
-            furi_string_free(announcement_time);
-            furi_string_free(analytics_value);
-            furi_string_free(analytics_time);
-            furi_string_free(announcement_json);
-            furi_string_free(analytics_json);
-            furi_string_free(notification);
-            return true;
-        }
+        new_announcement = true;
+    }
+
+    // Check for new analytics
+    if (!analytics_time_loaded || strcmp(furi_string_get_cstr(analytics_time), past_analytics_time) != 0)
+    {
+        new_analytics = true;
+    }
+
+    // If no new announcement and no new analytics, exit early
+    if (!new_announcement && !new_analytics)
+    {
+        FURI_LOG_D(TAG, "No new announcement or analytics");
+        furi_string_free(announcement_value);
+        furi_string_free(announcement_time);
+        furi_string_free(analytics_value);
+        furi_string_free(analytics_time);
+        furi_string_free(announcement_json);
+        furi_string_free(analytics_json);
+        furi_string_free(notification);
+        return true;
+    }
+
+    // Save the new announcement and analytics times if they are new
+    if (new_announcement)
+    {
+        save_char("announcement_time", furi_string_get_cstr(announcement_time));
     }
-    // save the new announcement and analytics time
-    save_char("analytics_time", furi_string_get_cstr(analytics_time));
-    save_char("announcement_time", furi_string_get_cstr(announcement_time));
-    // show the announcement and analytics
-    char analytics_text[128];
-    // if previous analytics posts is not empty, then subtract the current posts from the previous psots and add it to analytics_text
-    if (atoi(furi_string_get_cstr(analytics_value)) > 0)
+
+    if (new_analytics)
     {
-        char past_analytics_value[32];
-        if (load_char("analytics_value", past_analytics_value, sizeof(past_analytics_value)))
+        save_char("analytics_time", furi_string_get_cstr(analytics_time));
+    }
+
+    // Prepare and show dialogs based on what is new
+    if (new_announcement)
+    {
+        easy_flipper_dialog("Announcement", (char *)furi_string_get_cstr(announcement_value));
+    }
+
+    if (new_analytics)
+    {
+        char analytics_text[128] = {0};
+        // Determine the new posts count
+        if (atoi(furi_string_get_cstr(analytics_value)) > 0)
         {
-            int past_posts = atoi(past_analytics_value);
-            int current_posts = atoi(furi_string_get_cstr(analytics_value));
-            int new_posts = current_posts - past_posts;
-            snprintf(analytics_text, sizeof(analytics_text), "%s feed posts\n%d new posts", furi_string_get_cstr(analytics_value), new_posts);
+            char past_analytics_value[32] = {0};
+            int new_posts = 0;
+            if (load_char("analytics_value", past_analytics_value, sizeof(past_analytics_value)))
+            {
+                int past_posts = atoi(past_analytics_value);
+                int current_posts = atoi(furi_string_get_cstr(analytics_value));
+                new_posts = current_posts - past_posts;
+                snprintf(analytics_text, sizeof(analytics_text), "%d new posts", new_posts);
+            }
+            else
+            {
+                snprintf(analytics_text, sizeof(analytics_text), "%s feed posts", furi_string_get_cstr(analytics_value));
+            }
+            save_char("analytics_value", furi_string_get_cstr(analytics_value));
         }
         else
         {
             snprintf(analytics_text, sizeof(analytics_text), "%s feed posts", furi_string_get_cstr(analytics_value));
         }
-        save_char("analytics_value", furi_string_get_cstr(analytics_value));
-    }
-    else
-    {
-        snprintf(analytics_text, sizeof(analytics_text), "%s feed posts", furi_string_get_cstr(analytics_value));
+
+        easy_flipper_dialog("Notifications", analytics_text);
     }
-    easy_flipper_dialog("Announcement", (char *)furi_string_get_cstr(announcement_value));
-    easy_flipper_dialog("Analytics", analytics_text);
+
+    // Free allocated resources
     furi_string_free(announcement_value);
     furi_string_free(announcement_time);
     furi_string_free(analytics_value);
@@ -2692,6 +2761,7 @@ static bool flip_social_parse_home_notification()
     furi_string_free(announcement_json);
     furi_string_free(analytics_json);
     furi_string_free(notification);
+
     return true;
 }
 

+ 1 - 0
callback/flip_social_callback.h

@@ -254,6 +254,7 @@ void flip_social_logged_in_messages_new_message_updated(void *context);
  * @return void
  */
 void flip_social_text_input_logged_out_register_item_selected(void *context, uint32_t index);
+void flip_social_logged_in_user_settings_item_selected(void *context, uint32_t index);
 void flip_social_logged_in_explore_updated(void *context);
 void flip_social_logged_in_message_users_updated(void *context);
 

+ 26 - 12
feed/flip_social_feed.c

@@ -1,6 +1,6 @@
 #include "flip_social_feed.h"
 
-bool flip_social_get_feed(bool alloc_http)
+bool flip_social_get_feed(bool alloc_http, int series_index)
 {
     if (!app_instance)
     {
@@ -26,7 +26,14 @@ bool flip_social_get_feed(bool alloc_http)
     fhttp.save_received_data = true;
     auth_headers_alloc();
     char command[96];
-    snprintf(command, 96, "https://www.flipsocial.net/api/feed/%d/%s/max/", MAX_FEED_ITEMS, app_instance->login_username_logged_out);
+    if (strstr(flip_social_feed_type[flip_social_feed_type_index], "Global"))
+    {
+        snprintf(command, 96, "https://www.flipsocial.net/api/feed/%d/%s/%d/max/series/", MAX_FEED_ITEMS, app_instance->login_username_logged_out, series_index);
+    }
+    else
+    {
+        snprintf(command, 96, "https://www.flipsocial.net/api/feed/%d/%s/%d/max/friends/series/", MAX_FEED_ITEMS, app_instance->login_username_logged_out, series_index);
+    }
     if (!flipper_http_get_request_with_headers(command, auth_headers))
     {
         FURI_LOG_E(TAG, "Failed to send HTTP request for feed");
@@ -114,15 +121,22 @@ bool flip_social_load_feed_post(int post_id)
     }
 
     // Parse the feed post
-    if (!flip_feed_item)
+    if (flip_feed_item)
     {
-        flip_feed_item = (FlipSocialFeedItem *)malloc(sizeof(FlipSocialFeedItem));
-        if (flip_feed_item == NULL)
-        {
-            FURI_LOG_E(TAG, "Failed to allocate memory for feed post.");
-            furi_string_free(feed_data);
-            return false;
-        }
+        free(flip_feed_item);
+    }
+    else
+    {
+        // first time
+        save_char("series_index", "1");
+    }
+
+    flip_feed_item = (FlipSocialFeedItem *)malloc(sizeof(FlipSocialFeedItem));
+    if (flip_feed_item == NULL)
+    {
+        FURI_LOG_E(TAG, "Failed to allocate memory for feed post.");
+        furi_string_free(feed_data);
+        return false;
     }
 
     // Extract individual fields from the JSON object
@@ -163,7 +177,7 @@ bool flip_social_load_feed_post(int post_id)
     return true;
 }
 
-bool flip_social_load_initial_feed(bool alloc_http)
+bool flip_social_load_initial_feed(bool alloc_http, int series_index)
 {
     if (fhttp.state == INACTIVE)
     {
@@ -180,7 +194,7 @@ bool flip_social_load_initial_feed(bool alloc_http)
     }
     view_dispatcher_add_view(app_instance->view_dispatcher, loading_view_id, loading_get_view(loading));
     view_dispatcher_switch_to_view(app_instance->view_dispatcher, loading_view_id);
-    if (flip_social_get_feed(alloc_http)) // start the async request
+    if (flip_social_get_feed(alloc_http, series_index)) // start the async request
     {
         furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
         fhttp.state = RECEIVING;

+ 2 - 2
feed/flip_social_feed.h

@@ -4,8 +4,8 @@
 #include <callback/flip_social_callback.h>
 #include <flip_storage/flip_social_storage.h>
 
-bool flip_social_get_feed(bool alloc_http);
+bool flip_social_get_feed(bool alloc_http, int series_index);
 bool flip_social_load_feed_post(int post_id);
-bool flip_social_load_initial_feed(bool alloc_http);
+bool flip_social_load_initial_feed(bool alloc_http, int series_index);
 FlipSocialFeedMini *flip_social_parse_json_feed();
 #endif

+ 4 - 0
flip_social.c

@@ -18,6 +18,10 @@ bool flip_social_send_message = false;
 char *selected_message = NULL;
 
 char auth_headers[256] = {0};
+char *flip_social_feed_type[] = {"Global", "Friends"};
+uint8_t flip_social_feed_type_index = 0;
+char *flip_social_notification_type[] = {"OFF", "ON"};
+uint8_t flip_social_notification_type_index = 0;
 
 void flip_social_loader_free_model(View *view);
 

+ 17 - 6
flip_social.h

@@ -17,7 +17,7 @@
 #define MAX_EXPLORE_USERS 50      // Maximum number of users to explore
 #define MAX_USER_LENGTH 32        // Maximum length of a username
 #define MAX_FRIENDS 50            // Maximum number of friends
-#define MAX_FEED_ITEMS 30         // Maximum number of feed items
+#define MAX_FEED_ITEMS 20         // Maximum number of feed items
 #define MAX_LINE_LENGTH 27
 #define MAX_MESSAGE_USERS 40 // Maximum number of users to display in the submenu
 #define MAX_MESSAGES 20      // Maximum number of meesages between each user
@@ -33,6 +33,7 @@ typedef enum
     //
     FlipSocialSubmenuLoggedOutIndexAbout,        // click to go to the about screen
     FlipSocialSubmenuLoggedOutIndexWifiSettings, // click to go to the wifi settings screen
+    FlipSocialSubmenuLoggedInIndexUserSettings,  // click to go to the user settings screen
     //
     FlipSocialSubmenuLoggedInIndexAbout,        // click to go to the about screen
     FlipSocialSubmenuLoggedInIndexWifiSettings, // click to go to the wifi settings screen
@@ -83,6 +84,7 @@ typedef struct
     int ids[MAX_FEED_ITEMS];
     size_t count;
     size_t index;
+    int series_index;
 } FlipSocialFeedMini;
 
 typedef struct
@@ -144,6 +146,7 @@ typedef enum
     //
     FlipSocialViewLoggedInSettingsAbout,             // The about screen
     FlipSocialViewLoggedInSettingsWifi,              // The wifi settings screen
+    FlipSocialViewLoggedInSettingsUser,              // The user settings screen
     FlipSocialViewLoggedInWifiSettingsSSIDInput,     // Text input screen for SSID input on wifi screen
     FlipSocialViewLoggedInWifiSettingsPasswordInput, // Text input screen for Password input on wifi screen
     //
@@ -162,7 +165,6 @@ typedef enum
     FlipSocialViewFriendsDialog,  // The dialog for the friends screen
     FlipSocialViewMessagesDialog, // The dialog for the messages screen
     FlipSocialViewComposeDialog,  // The dialog for the compose screen
-    FlipSocialViewFeedDialog,     // The dialog for the feed screen
     //
     FlipSocialViewTextInput, // The text input screen
     FlipSocialViewVariableItemList,
@@ -203,10 +205,13 @@ typedef struct
     VariableItem *variable_item_logged_in_profile_change_password; // Reference to the change password configuration item
     VariableItem *variable_item_logged_in_profile_change_bio;      // Reference to the change bio configuration item
     //
-    VariableItem *variable_item_logged_in_settings_about;         // Reference to the about configuration item
-    VariableItem *variable_item_logged_in_settings_wifi;          // Reference to the wifi settings configuration item
-    VariableItem *variable_item_logged_in_wifi_settings_ssid;     // Reference to the ssid configuration item
-    VariableItem *variable_item_logged_in_wifi_settings_password; // Reference to the password configuration item
+    VariableItem *variable_item_logged_in_settings_about;              // Reference to the about configuration item
+    VariableItem *variable_item_logged_in_settings_wifi;               // Reference to the wifi settings configuration item
+    VariableItem *variable_item_logged_in_settings_user;               // Reference to the user settings configuration item
+    VariableItem *variable_item_logged_in_user_settings_feed_type;     // Reference to the feed type configuration item
+    VariableItem *variable_item_logged_in_user_settings_notifications; // Reference to the notifications configuration item
+    VariableItem *variable_item_logged_in_wifi_settings_ssid;          // Reference to the ssid configuration item
+    VariableItem *variable_item_logged_in_wifi_settings_password;      // Reference to the password configuration item
     //
     VariableItem *variable_item_logged_in_profile_friends; // Reference to the friends configuration item
     //
@@ -314,4 +319,10 @@ extern bool flip_social_dialog_stop;
 extern bool flip_social_send_message;
 extern char *selected_message;
 extern char auth_headers[256];
+//
+extern char *flip_social_feed_type[];
+extern uint8_t flip_social_feed_type_index;
+//
+extern char *flip_social_notification_type[];
+extern uint8_t flip_social_notification_type_index;
 #endif

+ 5 - 0
flip_storage/flip_social_storage.c

@@ -379,6 +379,11 @@ bool load_settings(
 
 bool flip_social_save_post(const char *post_id, const char *json_feed_data)
 {
+    if (!post_id || !json_feed_data)
+    {
+        FURI_LOG_E(TAG, "Post ID or JSON feed data is NULL");
+        return false;
+    }
     Storage *storage = furi_record_open(RECORD_STORAGE);
     File *file = storage_file_alloc(storage);