Login
1 branch 0 tags
Ben (Desktop/Arch) Wifi/Podcasts on the ESP32!!!! 90acdde 1 month ago 77 Commits
moon / src / screens / podcasts_screen.c
#include "podcasts_screen.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../podcast_xml.h"
#include "../storage.h"
#include "../topbar.h"

#define PODCASTS_OPML "/Podcasts/podcasts.xml"

static void cancel_event_cb(lv_event_t* e) {
	(void)e;
	navigate_back(SCREEN_HOME);
}

static void sync_click_cb(lv_event_t* e) {
	if (lv_event_get_code(e) != LV_EVENT_CLICKED) {
		return;
	}
	navigate_to(SCREEN_PODCAST_SYNC);
}

static void feed_click_cb(lv_event_t* e) {
	if (lv_event_get_code(e) != LV_EVENT_CLICKED) {
		return;
	}

	int index = (int)(intptr_t)lv_event_get_user_data(e);

	// Re-parse OPML to get feed info
	podcast_feed_t* feeds = malloc(PODCAST_MAX_FEEDS * sizeof(podcast_feed_t));
	if (!feeds) {
		return;
	}

	int count = podcast_parse_opml(PODCASTS_OPML, feeds, PODCAST_MAX_FEEDS);
	if (index < 0 || index >= count) {
		free(feeds);
		return;
	}

	// Sanitize title to get directory name (same logic as sync)
	char dirname[65];
	size_t len = 0;
	for (const char* p = feeds[index].title; *p && len < 64; p++) {
		char c = *p;
		if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
		    (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-') {
			dirname[len++] = c;
		} else if (len > 0 && dirname[len - 1] != '_') {
			dirname[len++] = '_';
		}
	}
	while (len > 0 && dirname[len - 1] == '_') {
		len--;
	}
	if (len == 0) {
		dirname[0] = '_';
		len = 1;
	}
	dirname[len] = '\0';

	snprintf(app_state.podcast_dir, sizeof(app_state.podcast_dir),
	         "/Podcasts/%s", dirname);

	free(feeds);
	navigate_to(SCREEN_PODCAST_FEED);
}

static void back_event_cb(lv_event_t* e) {
	if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
		cancel_event_cb(e);
	}
}

ui_state_t setup_podcasts_screen(lv_obj_t* parent) {
	podcasts_screen_state_t* state = calloc(1, sizeof(podcasts_screen_state_t));

	state->list = bvs_list_create(parent);
	topbar_set_title("Podcasts");

	lv_obj_t* focus_btn = NULL;

	// Sync button
	lv_obj_t* sync_btn = bvs_list_add_button(state->list, NULL, "Sync now");
	lv_obj_add_event_cb(sync_btn, sync_click_cb, LV_EVENT_CLICKED, NULL);
	lv_obj_add_event_cb(sync_btn, cancel_event_cb, LV_EVENT_CANCEL, NULL);
	lv_group_add_obj(lv_group_get_default(), sync_btn);
	focus_btn = sync_btn;

	// Parse OPML and list feeds
	podcast_feed_t* feeds = malloc(PODCAST_MAX_FEEDS * sizeof(podcast_feed_t));
	int count =
	    feeds ? podcast_parse_opml(PODCASTS_OPML, feeds, PODCAST_MAX_FEEDS) : 0;

	if (count <= 0) {
		lv_obj_t* msg =
		    bvs_list_add_button(state->list, NULL, "No podcasts.xml");
		(void)msg;
	} else {
		for (int i = 0; i < count; i++) {
			lv_obj_t* btn =
			    bvs_list_add_button(state->list, NULL, feeds[i].title);
			lv_obj_add_event_cb(btn, feed_click_cb, LV_EVENT_CLICKED,
			                    (void*)(intptr_t)i);
			lv_obj_add_event_cb(btn, cancel_event_cb, LV_EVENT_CANCEL, NULL);
			lv_group_add_obj(lv_group_get_default(), btn);
		}
	}
	free(feeds);

	// Back button
	lv_obj_t* back_btn = bvs_list_add_button(state->list, NULL, "Back");
	lv_obj_add_event_cb(back_btn, back_event_cb, LV_EVENT_CLICKED, NULL);
	lv_obj_add_event_cb(back_btn, cancel_event_cb, LV_EVENT_CANCEL, NULL);
	lv_group_add_obj(lv_group_get_default(), back_btn);

	if (focus_btn) {
		lv_group_focus_obj(focus_btn);
	} else {
		lv_group_focus_obj(back_btn);
	}

	return (ui_state_t){.type = SCREEN_PODCASTS, .podcasts = state};
}

void free_podcasts_screen(podcasts_screen_state_t* state) {
	if (!state) {
		return;
	}
	free(state);
}

void update_podcasts_screen(podcasts_screen_state_t* state) {
	(void)state;
}