diff --git a/CHANGELOG.md b/CHANGELOG.md index 833a1453..71be97e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- A home screen editor allowing to enable/disable feeds, and change their order. + ## [0.18.1] - 2024-01-02 ### Fixed diff --git a/docs/playlet-web-api.yml b/docs/playlet-web-api.yml index ba081132..87ccc08d 100644 --- a/docs/playlet-web-api.yml +++ b/docs/playlet-web-api.yml @@ -153,7 +153,7 @@ paths: properties: index: type: integer - playlistIndex + playlistIndex: type: integer items: type: array @@ -178,7 +178,7 @@ paths: properties: index: type: integer - playlistIndex + playlistIndex: type: integer items: type: array @@ -227,6 +227,41 @@ paths: description: No Content "400": description: Bad Request + /api/home-layout: + get: + summary: Get home layout + description: Get the current home layout. This is the layout of the home screen, based on user preferences. + operationId: getHomeLayout + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: string + title: + type: string + feedSources: + type: array + items: + type: object + properties: + apiType: + type: string + endpoint: + type: string + id: + type: string + queryParams: + type: object + additionalProperties: true + title: + type: string /api/playlet-lib-urls: get: summary: Get Playlet lib URLs diff --git a/playlet-lib/src/components/Screens/HomeScreen/HomeScreen.bs b/playlet-lib/src/components/Screens/HomeScreen/HomeScreen.bs index 7016fc03..1d7eb135 100644 --- a/playlet-lib/src/components/Screens/HomeScreen/HomeScreen.bs +++ b/playlet-lib/src/components/Screens/HomeScreen/HomeScreen.bs @@ -1,3 +1,4 @@ +import "HomeScreenUtils.bs" import "pkg:/components/ContextMenu/ContextMenuUtils.bs" import "pkg:/components/Navigation/Navigation.bs" import "pkg:/source/utils/FocusManagement.bs" @@ -14,7 +15,15 @@ function OnNodeReady() SetNavigation(invalid, "left", m.navBar) m.rowList@.BindNode() - m.rowList.feeds = ParseJson(ReadAsciiFile(m.top.feedFile)) + + ' TODO:P1 home screen should only be refreshed when the user navigates to it + ' (similar to bookmarks screen) + m.preferences.observeFieldScoped("misc.home_screen_layout", FuncName(OnHomeLayoutChange)) + OnHomeLayoutChange() +end function + +function OnHomeLayoutChange() as void + m.rowList.feeds = HomeScreenUtils.GetFeed(m.top.feedFile, m.preferences) end function function OnFocusChange() as void diff --git a/playlet-lib/src/components/Screens/HomeScreen/HomeScreen.xml b/playlet-lib/src/components/Screens/HomeScreen/HomeScreen.xml index 1bc3da92..65778c7b 100644 --- a/playlet-lib/src/components/Screens/HomeScreen/HomeScreen.xml +++ b/playlet-lib/src/components/Screens/HomeScreen/HomeScreen.xml @@ -2,6 +2,7 @@ + diff --git a/playlet-lib/src/components/Screens/HomeScreen/HomeScreenUtils.bs b/playlet-lib/src/components/Screens/HomeScreen/HomeScreenUtils.bs new file mode 100644 index 00000000..41edfc97 --- /dev/null +++ b/playlet-lib/src/components/Screens/HomeScreen/HomeScreenUtils.bs @@ -0,0 +1,28 @@ +import "pkg:/source/utils/Types.bs" + +namespace HomeScreenUtils + + function GetFeed(feedFileName as string, preferences as object) as object + feed = ParseJson(ReadAsciiFile(feedFileName)) + + homeLayout = preferences["misc.home_screen_layout"] + if not IsArray(homeLayout) or homeLayout.Count() = 0 + return feed + end if + + feedItems = {} + for each item in feed + feedItems[item["id"]] = item + end for + + filteredFeed = [] + for each item in homeLayout + if item.enabled = true + filteredFeed.push(feedItems[item.id]) + end if + end for + + return filteredFeed + end function + +end namespace diff --git a/playlet-lib/src/components/Screens/SettingsScreen/HomeScreenEditor/EditHomeScreenControl.bs b/playlet-lib/src/components/Screens/SettingsScreen/HomeScreenEditor/EditHomeScreenControl.bs new file mode 100644 index 00000000..b4c98019 --- /dev/null +++ b/playlet-lib/src/components/Screens/SettingsScreen/HomeScreenEditor/EditHomeScreenControl.bs @@ -0,0 +1,58 @@ +import "pkg:/components/parts/AutoBind/OnNodeReadyNoOp.bs" +import "pkg:/source/utils/FocusManagement.bs" +import "pkg:/source/utils/Types.bs" + +function Init() + m.top.focusable = true + m.top.itemSpacings = [8] + + m.button = m.top.findNode("button") + m.button.observeField("buttonSelected", FuncName(OpenHomeScreenEditor)) +end function + +function OnFocusChange() as void + if not m.top.focus + return + end if + + NodeSetFocus(m.button, true) +end function + +function BindPreference(preferences as object, key as string) + if m.preferences <> invalid and m.key <> invalid + m.preferences.unobserveFieldScoped(m.key) + end if + + m.preferences = preferences + m.key = key + + if preferences <> invalid and key <> invalid + preferences.observeFieldScoped(key, FuncName(OnPreferenceChange)) + OnPreferenceChange() + end if +end function + +function OpenHomeScreenEditor() + editor = CreateObject("roSGNode", "HomeScreenEditor") + m.appController@.PushScreen(editor) + editor@.BindNode() + editor.value = m.top.value + editor.observeField("save", FuncName(OnSaveHomeScreenEditor)) +end function + +function OnSaveHomeScreenEditor(event as object) + editor = event.GetRoSGNode() + m.top.value = editor.value +end function + +function OnPreferenceChange() + m.top.value = m.preferences[m.key] +end function + +function OnValueChange() as void + if m.preferences = invalid or m.key = invalid + return + end if + + m.preferences[m.key] = m.top.value +end function diff --git a/playlet-lib/src/components/Screens/SettingsScreen/HomeScreenEditor/EditHomeScreenControl.xml b/playlet-lib/src/components/Screens/SettingsScreen/HomeScreenEditor/EditHomeScreenControl.xml new file mode 100644 index 00000000..30f0b728 --- /dev/null +++ b/playlet-lib/src/components/Screens/SettingsScreen/HomeScreenEditor/EditHomeScreenControl.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/playlet-web/src/lib/Screens/Settings/SettingsNode.svelte b/playlet-web/src/lib/Screens/Settings/SettingsNode.svelte index b2f6012c..e2a1eb6c 100644 --- a/playlet-web/src/lib/Screens/Settings/SettingsNode.svelte +++ b/playlet-web/src/lib/Screens/Settings/SettingsNode.svelte @@ -1,6 +1,7 @@ diff --git a/playlet-web/src/lib/Stores.ts b/playlet-web/src/lib/Stores.ts index 035f51fa..cda0be34 100644 --- a/playlet-web/src/lib/Stores.ts +++ b/playlet-web/src/lib/Stores.ts @@ -15,6 +15,8 @@ export const userPreferencesStore = writable({} as any); export const invidiousVideoApiStore = writable({} as any); +export const homeLayoutStore = writable([] as any); + export const homeLayoutFileStore = writable([] as any); export const bookmarksStore = writable([] as any); diff --git a/playlet-web/src/lib/VideoFeed/VideoListRow.svelte b/playlet-web/src/lib/VideoFeed/VideoListRow.svelte index 389cbe7d..d6a02824 100644 --- a/playlet-web/src/lib/VideoFeed/VideoListRow.svelte +++ b/playlet-web/src/lib/VideoFeed/VideoListRow.svelte @@ -7,7 +7,7 @@ // TODO:P1 figure out why some uncached feeds (e.g. channels/ucid/videos) get hit twice export let feed: any = undefined; - export let videos = []; + let videos = []; enum FeedLoadState { None, @@ -28,6 +28,8 @@ let scrollStart = 0; let scrollEnd = 0; + let loadDataTask = undefined; + // w-80|w-60 p-2: 320px|240px + 16px padding on each side const videoItemWidth = 320 + 16 * 2; const channelItemWidth = 240 + 16 * 2; @@ -42,6 +44,18 @@ { threshold: [0] } ); + $: { + if (feed) { + feedSourcesIndex = 0; + feedLoadState = FeedLoadState.None; + videos = []; + itemWidths = []; + scrollStart = 0; + scrollEnd = 0; + loadDataTask = undefined; + } + } + $: { if ( carouselElement && @@ -112,8 +126,6 @@ loadDataTask = undefined; } - let loadDataTask; - async function loadData() { feedLoadState = FeedLoadState.Loading; let totalFetchedItems = 0;