From b06c799c0cf0845df5762ec431719aa09805288f Mon Sep 17 00:00:00 2001 From: Kestas Venslauskas Date: Mon, 29 Apr 2024 14:10:20 +0300 Subject: [PATCH 1/4] feat: car play podcasts template --- App.tsx | 9 +-- app/api/Endpoints.ts | 3 + app/api/Types.ts | 10 +++ app/api/index.ts | 5 ++ app/car/CarPlayContext.ts | 1 - app/car/CarPlayEmptyProvider.tsx | 18 ----- app/car/CarPlayProvider.tsx | 8 ++- app/car/newest/useCarNewestPlaylist.ts | 3 +- .../podcasts/createPlayPodcastsTemplate.ts | 12 ++++ app/car/podcasts/useCarPodcastsPlaylist.ts | 35 ++++++++++ app/car/podcasts/useCarPodcastsTemplate.ts | 68 +++++++++++++++++++ app/car/popular/useCarPopularPlaylist.ts | 3 +- .../recommended/useCarRecommendedPlaylist.ts | 3 +- app/car/root/createPlayRootTemplate.ts | 4 ++ app/car/root/useCarPlayRootTemplate.ts | 6 ++ 15 files changed, 156 insertions(+), 32 deletions(-) delete mode 100644 app/car/CarPlayEmptyProvider.tsx create mode 100644 app/car/podcasts/createPlayPodcastsTemplate.ts create mode 100644 app/car/podcasts/useCarPodcastsPlaylist.ts create mode 100644 app/car/podcasts/useCarPodcastsTemplate.ts diff --git a/App.tsx b/App.tsx index f29ff58..78dc4a2 100644 --- a/App.tsx +++ b/App.tsx @@ -10,12 +10,11 @@ import {initialWindowMetrics, SafeAreaProvider} from 'react-native-safe-area-con import {persistor, store} from './app/redux/store'; import useAppTrackingPermission from './app/util/useAppTrackingPermission'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; -import {Platform, StyleSheet} from 'react-native'; +import {StyleSheet} from 'react-native'; import PlayerProvider from './app/components/videoComponent/context/PlayerProvider'; import useNotificationsPermission from './app/util/useNotificationsPermission'; import useGoogleAnalyticsSetup from './app/util/useGoogleAnalyticsSetup'; import CarPlayProvider from './app/car/CarPlayProvider'; -import CarPlayEmptyProvider from './app/car/CarPlayEmptyProvider'; import ThemeProvider from './app/theme/ThemeProvider'; const ReduxProvider: React.FC> = ({children}) => { @@ -34,17 +33,15 @@ const App: React.FC = () => { useGoogleAnalyticsSetup(); useGemiusSetup(); - const CarPlayProviderImpl = Platform.OS === 'ios' ? CarPlayProvider : CarPlayEmptyProvider; - return ( - + - + diff --git a/app/api/Endpoints.ts b/app/api/Endpoints.ts index f3985ec..3505a38 100644 --- a/app/api/Endpoints.ts +++ b/app/api/Endpoints.ts @@ -169,3 +169,6 @@ export const carPlaylistNewestGet = () => 'https://www.lrt.lt/static/carplay/nau export const carPlaylistPopularGet = () => 'https://www.lrt.lt/static/carplay/pop.json'; export const carPlaylistRecommendedGet = () => 'https://www.lrt.lt/static/carplay/rekomenduoja.json'; + +export const carPlaylistPodcastsGet = (count: number) => + `https://www.lrt.lt/api/json/search/categories?type=audio&count=${count}`; diff --git a/app/api/Types.ts b/app/api/Types.ts index cf29ddc..c36a4ef 100644 --- a/app/api/Types.ts +++ b/app/api/Types.ts @@ -508,6 +508,16 @@ export type CarPlaylistItem = { streamUrl: string; }; +export type CarPlayPodcastsResponse = { + total_found: number; + items: CarPlayPodcastItem[]; +}; + +export type CarPlayPodcastItem = { + id: number; + title: string; +}; + export type ArticleContentDefault = { article_id: number; article_title: string; diff --git a/app/api/index.ts b/app/api/index.ts index 87c1107..62b7728 100644 --- a/app/api/index.ts +++ b/app/api/index.ts @@ -3,6 +3,7 @@ import { articlesGetByTag, audiotekaGet, carPlaylistNewestGet, + carPlaylistPodcastsGet, carPlaylistPopularGet, carPlaylistRecommendedGet, categoryGet, @@ -25,6 +26,7 @@ import {get, put} from './HttpClient'; import { ArticleContentResponse, AudiotekaResponse, + CarPlayPodcastsResponse, CarPlaylistItem, CategoryArticlesResponse, ChannelResponse, @@ -103,3 +105,6 @@ export const fetchCarNewestPlaylist = () => get(carPlaylistNe export const fetchCarPopularPlaylist = () => get(carPlaylistPopularGet()); export const fetchCarRecommendedPlaylist = () => get(carPlaylistRecommendedGet()); + +export const fetchCarPodcasts = (count: number) => + get(carPlaylistPodcastsGet(count)); diff --git a/app/car/CarPlayContext.ts b/app/car/CarPlayContext.ts index f226395..086331a 100644 --- a/app/car/CarPlayContext.ts +++ b/app/car/CarPlayContext.ts @@ -12,7 +12,6 @@ export type PlayListItem = { export type CarPlayContextType = { isConnected: boolean; - selectedCategory?: CarCategory; }; const defaults: CarPlayContextType = { diff --git a/app/car/CarPlayEmptyProvider.tsx b/app/car/CarPlayEmptyProvider.tsx deleted file mode 100644 index 074d2c8..0000000 --- a/app/car/CarPlayEmptyProvider.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, {PropsWithChildren, useMemo} from 'react'; -import {CarPlayContext, CarPlayContextType} from './CarPlayContext'; - -const CarPlayEmptyProvider: React.FC> = (props) => { - const context: CarPlayContextType = useMemo( - () => ({ - isConnected: false, - playItem: () => {}, - playlist: [], - setPlaylist: () => {}, - }), - [], - ); - - return {props.children}; -}; - -export default CarPlayEmptyProvider; diff --git a/app/car/CarPlayProvider.tsx b/app/car/CarPlayProvider.tsx index 2244fe3..9f8fb2d 100644 --- a/app/car/CarPlayProvider.tsx +++ b/app/car/CarPlayProvider.tsx @@ -1,9 +1,15 @@ import React, {PropsWithChildren, useMemo} from 'react'; import {CarPlayContext, CarPlayContextType} from './CarPlayContext'; +import {Platform} from 'react-native'; import useCarPlayController from './useCarPlayController'; const CarPlayProvider: React.FC> = (props) => { - const {isConnected} = useCarPlayController(); + const {isConnected} = + Platform.OS === 'ios' + ? useCarPlayController() + : { + isConnected: false, + }; const context: CarPlayContextType = useMemo( () => ({ diff --git a/app/car/newest/useCarNewestPlaylist.ts b/app/car/newest/useCarNewestPlaylist.ts index 171b621..d592776 100644 --- a/app/car/newest/useCarNewestPlaylist.ts +++ b/app/car/newest/useCarNewestPlaylist.ts @@ -9,7 +9,6 @@ import {fetchCarNewestPlaylist} from '../../api'; const useCarPlayNewestPlaylist = (isConnected: boolean) => { const [channels, setChannels] = useState([]); const [lastLoadTime, setLastLoadTime] = useState(0); - const channelsData = useSelector(selectHomeChannels, checkEqual); const cancellablePromise = useCancellablePromise(); @@ -31,7 +30,7 @@ const useCarPlayNewestPlaylist = (isConnected: boolean) => { } }), ); - }, [channelsData, isConnected, lastLoadTime]); + }, [isConnected, lastLoadTime]); return { channels, diff --git a/app/car/podcasts/createPlayPodcastsTemplate.ts b/app/car/podcasts/createPlayPodcastsTemplate.ts new file mode 100644 index 0000000..eaaeb1d --- /dev/null +++ b/app/car/podcasts/createPlayPodcastsTemplate.ts @@ -0,0 +1,12 @@ +import {ListTemplate} from 'react-native-carplay'; + +export const carPlayPodcastsTemplate = new ListTemplate({ + title: 'Radijo laidos', + id: 'lrt-list-template-podcasts', + sections: [ + { + items: [], + }, + ], + backButtonHidden: true, +}); diff --git a/app/car/podcasts/useCarPodcastsPlaylist.ts b/app/car/podcasts/useCarPodcastsPlaylist.ts new file mode 100644 index 0000000..8a71f07 --- /dev/null +++ b/app/car/podcasts/useCarPodcastsPlaylist.ts @@ -0,0 +1,35 @@ +import {useEffect, useState} from 'react'; +import useCancellablePromise from '../../hooks/useCancellablePromise'; +import {fetchCarPodcasts} from '../../api'; +import {CarPlayPodcastItem} from '../../api/Types'; + +const useCarPlayPodcastsPlaylist = (isConnected: boolean) => { + const [podcasts, setPodcasts] = useState([]); + const [lastLoadTime, setLastLoadTime] = useState(0); + + const cancellablePromise = useCancellablePromise(); + + useEffect(() => { + if (!isConnected) { + return; + } + cancellablePromise( + fetchCarPodcasts(1000).then((data) => { + if (data.items.length) { + data.items.forEach((item) => { + item.title = item.title.replace(/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g, ''); + }); + data.items.sort((a, b) => a.title.localeCompare(b.title)); + setPodcasts(data.items); + } + }), + ); + }, [isConnected, lastLoadTime]); + + return { + podcasts, + reload: () => setLastLoadTime(Date.now()), + }; +}; + +export default useCarPlayPodcastsPlaylist; diff --git a/app/car/podcasts/useCarPodcastsTemplate.ts b/app/car/podcasts/useCarPodcastsTemplate.ts new file mode 100644 index 0000000..ae12166 --- /dev/null +++ b/app/car/podcasts/useCarPodcastsTemplate.ts @@ -0,0 +1,68 @@ +import {useEffect, useState} from 'react'; +import {ListTemplate} from 'react-native-carplay'; +import {carPlayPodcastsTemplate} from './createPlayPodcastsTemplate'; +import useCarPlayPodcastsPlaylist from './useCarPodcastsPlaylist'; +import {ListSection} from 'react-native-carplay/lib/interfaces/ListSection'; + +const useCarPodcastsTemplate = (isConnected: boolean) => { + const [template] = useState(carPlayPodcastsTemplate); + + const {podcasts, reload} = useCarPlayPodcastsPlaylist(isConnected); + + useEffect(() => { + console.log('updating podcasts template'); + const sections = podcasts.reduce<{ + [key: string]: {title: string}[]; + }>((acc, item) => { + let firstLetter = item.title.charAt(0).toUpperCase(); + + if (firstLetter.match(/[0-9]/) !== null) { + firstLetter = '0-9'; + } + + if (!acc[firstLetter]) { + acc[firstLetter] = []; + } + acc[firstLetter].push(item); + return acc; + }, {}); + + const listSections: ListSection[] = Object.entries(sections).map(([letter, items]) => ({ + header: letter, + title: letter, + sectionIndexTitle: letter, + items: items.map((item) => ({ + text: item.title, + })), + })); + + template.updateSections(listSections); + + template.config.onItemSelect = async ({index}) => { + const podcast = podcasts[index]; + console.log('Podcast selected', podcast); + // CarPlay.pushTemplate(carPlayNowPlayingTemplate, true); + }; + return () => { + template.config.onItemSelect = undefined; + }; + }, [podcasts]); + + useEffect(() => { + if (template) { + template.config.onBarButtonPressed = async ({id}) => { + if (id === 'reload') { + console.log('reloading live template'); + reload(); + } + }; + return () => { + template.config.onBarButtonPressed = undefined; + }; + } + }, [template, reload]); + + return template; +}; + +export default useCarPodcastsTemplate; diff --git a/app/car/popular/useCarPopularPlaylist.ts b/app/car/popular/useCarPopularPlaylist.ts index 7e3f3d4..f2c9f81 100644 --- a/app/car/popular/useCarPopularPlaylist.ts +++ b/app/car/popular/useCarPopularPlaylist.ts @@ -9,7 +9,6 @@ import {fetchCarPopularPlaylist} from '../../api'; const useCarPlayPopularPlaylist = (isConnected: boolean) => { const [channels, setChannels] = useState([]); const [lastLoadTime, setLastLoadTime] = useState(0); - const channelsData = useSelector(selectHomeChannels, checkEqual); const cancellablePromise = useCancellablePromise(); @@ -31,7 +30,7 @@ const useCarPlayPopularPlaylist = (isConnected: boolean) => { } }), ); - }, [channelsData, isConnected, lastLoadTime]); + }, [isConnected, lastLoadTime]); return { channels, diff --git a/app/car/recommended/useCarRecommendedPlaylist.ts b/app/car/recommended/useCarRecommendedPlaylist.ts index d19be89..5e1925e 100644 --- a/app/car/recommended/useCarRecommendedPlaylist.ts +++ b/app/car/recommended/useCarRecommendedPlaylist.ts @@ -9,7 +9,6 @@ import {fetchCarRecommendedPlaylist} from '../../api'; const useCarPlayRecommendedPlaylist = (isConnected: boolean) => { const [channels, setChannels] = useState([]); const [lastLoadTime, setLastLoadTime] = useState(0); - const channelsData = useSelector(selectHomeChannels, checkEqual); const cancellablePromise = useCancellablePromise(); @@ -31,7 +30,7 @@ const useCarPlayRecommendedPlaylist = (isConnected: boolean) => { } }), ); - }, [channelsData, isConnected, lastLoadTime]); + }, [isConnected, lastLoadTime]); return { channels, diff --git a/app/car/root/createPlayRootTemplate.ts b/app/car/root/createPlayRootTemplate.ts index c5a00af..81decd4 100644 --- a/app/car/root/createPlayRootTemplate.ts +++ b/app/car/root/createPlayRootTemplate.ts @@ -4,6 +4,7 @@ export const CATEGORY_LIVE = 0; export const CATEGORY_RECOMMENDED = 1; export const CATEGORY_POPULAR = 2; export const CATEGORY_NEWEST = 3; +export const CATEGORY_PODCASTS = 4; export const carPlayRootTemplate = new ListTemplate({ title: 'LRT.lt', @@ -29,6 +30,9 @@ export const carPlayRootTemplate = new ListTemplate({ { text: 'Naujausi', }, + { + text: 'Radijo laidos (A-Z)', + }, ], }, ], diff --git a/app/car/root/useCarPlayRootTemplate.ts b/app/car/root/useCarPlayRootTemplate.ts index 752209e..ec30b9e 100644 --- a/app/car/root/useCarPlayRootTemplate.ts +++ b/app/car/root/useCarPlayRootTemplate.ts @@ -4,6 +4,7 @@ import useCarLiveTemplate from '../live/useCarLiveTemplate'; import { CATEGORY_LIVE, CATEGORY_NEWEST, + CATEGORY_PODCASTS, CATEGORY_POPULAR, CATEGORY_RECOMMENDED, carPlayRootTemplate, @@ -11,6 +12,7 @@ import { import useCarNewestTemplate from '../newest/useCarNewestTemplate'; import useCarPopularTemplate from '../popular/useCarPopularTemplate'; import useCarRecommendedTemplate from '../recommended/useCarRecommendedTemplate'; +import useCarPodcastsTemplate from '../podcasts/useCarPodcastsTemplate'; const useCarPlayRootTemplate = (isConnected: boolean) => { const [template] = useState(carPlayRootTemplate); @@ -19,6 +21,7 @@ const useCarPlayRootTemplate = (isConnected: boolean) => { const newestTemplate = useCarNewestTemplate(isConnected); const popularTemplate = useCarPopularTemplate(isConnected); const recommendedTemplate = useCarRecommendedTemplate(isConnected); + const podcastsTemplate = useCarPodcastsTemplate(isConnected); useEffect(() => { template.config.onItemSelect = async ({index}) => { @@ -36,6 +39,9 @@ const useCarPlayRootTemplate = (isConnected: boolean) => { case CATEGORY_RECOMMENDED: CarPlay.pushTemplate(recommendedTemplate, true); break; + case CATEGORY_PODCASTS: + CarPlay.pushTemplate(podcastsTemplate, true); + break; } }; From 14311aeead28632ae095c8e31352080dac24cb92 Mon Sep 17 00:00:00 2001 From: Kestas Venslauskas Date: Thu, 2 May 2024 10:21:36 +0300 Subject: [PATCH 2/4] feat: car play podcasts implemented --- app/api/Endpoints.ts | 3 + app/api/Types.ts | 15 ++++ app/api/index.ts | 5 ++ app/car/CarPlayContext.ts | 7 ++ .../category/createPlayCategoryTemplate.ts | 12 ++++ app/car/category/useCarCategoryPlaylist.ts | 36 ++++++++++ app/car/category/useCarCategoryTemplate.ts | 67 ++++++++++++++++++ .../useCarPlayCategoryEpisodeStream.ts | 38 ++++++++++ app/car/live/createPlayLiveTemplate.ts | 2 + app/car/newest/createPlayNewestTemplate.ts | 2 + app/car/nowPlaying/assets/backward-solid.png | Bin 0 -> 3873 bytes app/car/nowPlaying/assets/forward-solid.png | Bin 0 -> 3978 bytes .../nowPlaying/createNowPlayingTemplate.ts | 48 ++++++++----- .../podcasts/createPlayPodcastsTemplate.ts | 3 + app/car/podcasts/useCarPodcastsTemplate.ts | 18 ++++- app/car/popular/createPlayPopularTemplate.ts | 2 + .../createPlayRecommendedTemplate.ts | 3 + app/car/root/useCarPlayRootTemplate.ts | 61 +++++----------- .../videoComponent/context/PlayerContext.ts | 4 ++ .../videoComponent/context/PlayerProvider.tsx | 20 +++++- ios/lrtApp.xcodeproj/project.pbxproj | 12 ++++ 21 files changed, 290 insertions(+), 68 deletions(-) create mode 100644 app/car/category/createPlayCategoryTemplate.ts create mode 100644 app/car/category/useCarCategoryPlaylist.ts create mode 100644 app/car/category/useCarCategoryTemplate.ts create mode 100644 app/car/category/useCarPlayCategoryEpisodeStream.ts create mode 100644 app/car/nowPlaying/assets/backward-solid.png create mode 100644 app/car/nowPlaying/assets/forward-solid.png diff --git a/app/api/Endpoints.ts b/app/api/Endpoints.ts index 3505a38..37a4ac5 100644 --- a/app/api/Endpoints.ts +++ b/app/api/Endpoints.ts @@ -172,3 +172,6 @@ export const carPlaylistRecommendedGet = () => 'https://www.lrt.lt/static/carpla export const carPlaylistPodcastsGet = (count: number) => `https://www.lrt.lt/api/json/search/categories?type=audio&count=${count}`; + +export const carPlaylistCategoryGet = (id: number | string) => + `https://www.lrt.lt/api/json/category?id=${id}`; diff --git a/app/api/Types.ts b/app/api/Types.ts index c36a4ef..a21cea5 100644 --- a/app/api/Types.ts +++ b/app/api/Types.ts @@ -518,6 +518,21 @@ export type CarPlayPodcastItem = { title: string; }; +export type CarPlayCategoryResponse = { + articles: CarPlayCategoryItem[]; + category_info: any; + page: number; + next_page: number | null; +}; + +export type CarPlayCategoryItem = { + id: number; + title: string; + img_path_postfix: string; + img_path_prefix: string; + item_date: string; +}; + export type ArticleContentDefault = { article_id: number; article_title: string; diff --git a/app/api/index.ts b/app/api/index.ts index 62b7728..88d1078 100644 --- a/app/api/index.ts +++ b/app/api/index.ts @@ -2,6 +2,7 @@ import { articleGet, articlesGetByTag, audiotekaGet, + carPlaylistCategoryGet, carPlaylistNewestGet, carPlaylistPodcastsGet, carPlaylistPopularGet, @@ -26,6 +27,7 @@ import {get, put} from './HttpClient'; import { ArticleContentResponse, AudiotekaResponse, + CarPlayCategoryResponse, CarPlayPodcastsResponse, CarPlaylistItem, CategoryArticlesResponse, @@ -108,3 +110,6 @@ export const fetchCarRecommendedPlaylist = () => get(carPlayl export const fetchCarPodcasts = (count: number) => get(carPlaylistPodcastsGet(count)); + +export const fetchCarCategoryPlaylist = (id: string | number) => + get(carPlaylistCategoryGet(id)); diff --git a/app/car/CarPlayContext.ts b/app/car/CarPlayContext.ts index 086331a..ae087a1 100644 --- a/app/car/CarPlayContext.ts +++ b/app/car/CarPlayContext.ts @@ -2,6 +2,13 @@ import React from 'react'; export type CarCategory = 'live' | 'newest'; +export type CategoryListItem = { + articleId: number; + text: string; + detailText?: string; + imgUrl: string; +}; + export type PlayListItem = { id: string | number; text: string; diff --git a/app/car/category/createPlayCategoryTemplate.ts b/app/car/category/createPlayCategoryTemplate.ts new file mode 100644 index 0000000..bf9bc99 --- /dev/null +++ b/app/car/category/createPlayCategoryTemplate.ts @@ -0,0 +1,12 @@ +import {ListTemplate} from 'react-native-carplay'; + +export const carPlayCategoryTemplate = new ListTemplate({ + title: 'Laida', + id: 'lrt-list-template-Category', + sections: [ + { + items: [], + }, + ], + backButtonHidden: true, +}); diff --git a/app/car/category/useCarCategoryPlaylist.ts b/app/car/category/useCarCategoryPlaylist.ts new file mode 100644 index 0000000..6cbd3c8 --- /dev/null +++ b/app/car/category/useCarCategoryPlaylist.ts @@ -0,0 +1,36 @@ +import {useEffect, useState} from 'react'; +import {CategoryListItem} from '../CarPlayContext'; +import useCancellablePromise from '../../hooks/useCancellablePromise'; +import {fetchCarCategoryPlaylist} from '../../api'; +import {IMG_SIZE_XS, buildImageUri} from '../../util/ImageUtil'; + +const useCarPlayCategoryPlaylist = (categoryId?: number) => { + const [episodes, setEpisodes] = useState([]); + + const cancellablePromise = useCancellablePromise(); + + useEffect(() => { + if (!categoryId) { + return; + } + cancellablePromise( + fetchCarCategoryPlaylist(categoryId).then((data) => { + if (data?.articles) { + const episodes: CategoryListItem[] = data.articles.map((item) => ({ + articleId: item.id, + text: item.title, + detailText: item.item_date, + imgUrl: buildImageUri(IMG_SIZE_XS, item.img_path_prefix, item.img_path_postfix), + })); + setEpisodes(episodes); + } + }), + ); + }, [categoryId]); + + return { + episodes, + }; +}; + +export default useCarPlayCategoryPlaylist; diff --git a/app/car/category/useCarCategoryTemplate.ts b/app/car/category/useCarCategoryTemplate.ts new file mode 100644 index 0000000..23d5357 --- /dev/null +++ b/app/car/category/useCarCategoryTemplate.ts @@ -0,0 +1,67 @@ +import {useEffect, useState} from 'react'; +import {CarPlay, ListTemplate} from 'react-native-carplay'; +import {useMediaPlayer} from '../../components/videoComponent/context/useMediaPlayer'; +import useCarPlayCategoryPlaylist from './useCarCategoryPlaylist'; +import {CarPlayPodcastItem} from '../../api/Types'; +import {CategoryListItem} from '../CarPlayContext'; +import useCarPlayCategoryEpisodeStream from './useCarPlayCategoryEpisodeStream'; +import {MediaType} from '../../components/videoComponent/context/PlayerContext'; +import {carPlayNowPlayingTemplate} from '../nowPlaying/createNowPlayingTemplate'; + +const useCarCategoryTemplate = (podcast?: CarPlayPodcastItem) => { + const [template, setTemplate] = useState(); + const [selectedEpisode, setSelectedEpisode] = useState(undefined); + + const {episodes} = useCarPlayCategoryPlaylist(podcast?.id); + const {streamInfo} = useCarPlayCategoryEpisodeStream(selectedEpisode); + + const {setPlaylist} = useMediaPlayer(); + + useEffect(() => { + const t = new ListTemplate({ + title: podcast?.title, + id: 'lrt-list-template-podcast-' + podcast?.id, + sections: [ + { + items: [ + ...episodes.map((item) => ({ + text: item.text, + detailText: item.detailText, + // imgUrl: item.imgUrl as any, + })), + ], + }, + ], + backButtonHidden: true, + }); + t.config.onItemSelect = async ({index}) => { + console.log('onItemSelect', episodes[index]); + setSelectedEpisode(episodes[index]); + }; + setTemplate(t); + return () => { + t.config.onItemSelect = undefined; + }; + }, [episodes]); + + useEffect(() => { + if (!streamInfo) { + return; + } + setPlaylist([ + { + title: streamInfo.text, + startTime: 0, + poster: streamInfo.imgUrl, + uri: streamInfo.streamUrl, + mediaType: MediaType.AUDIO, + isLiveStream: false, + }, + ]); + CarPlay.pushTemplate(carPlayNowPlayingTemplate, true); + }, [streamInfo]); + + return template; +}; + +export default useCarCategoryTemplate; diff --git a/app/car/category/useCarPlayCategoryEpisodeStream.ts b/app/car/category/useCarPlayCategoryEpisodeStream.ts new file mode 100644 index 0000000..e0f656e --- /dev/null +++ b/app/car/category/useCarPlayCategoryEpisodeStream.ts @@ -0,0 +1,38 @@ +import {useEffect, useState} from 'react'; +import {CategoryListItem, PlayListItem} from '../CarPlayContext'; +import useCancellablePromise from '../../hooks/useCancellablePromise'; +import {fetchArticle} from '../../api'; +import {isMediaArticle} from '../../api/Types'; +import {IMG_SIZE_XS, buildArticleImageUri} from '../../util/ImageUtil'; + +const useCarPlayCategoryEpisodeStream = (episode?: CategoryListItem) => { + const [streamInfo, setStreamInfo] = useState(); + + const cancellablePromise = useCancellablePromise(); + + useEffect(() => { + if (!episode) { + return; + } + cancellablePromise( + fetchArticle(episode.articleId).then((data) => { + const article = data?.article; + if (isMediaArticle(article)) { + setStreamInfo({ + id: episode.articleId, + text: article.title, + detailText: article.date, + imgUrl: buildArticleImageUri(IMG_SIZE_XS, article?.main_photo?.path) as any, + streamUrl: article.stream_url, + }); + } + }), + ); + }, [episode]); + + return { + streamInfo, + }; +}; + +export default useCarPlayCategoryEpisodeStream; diff --git a/app/car/live/createPlayLiveTemplate.ts b/app/car/live/createPlayLiveTemplate.ts index 43fb91b..a864d7b 100644 --- a/app/car/live/createPlayLiveTemplate.ts +++ b/app/car/live/createPlayLiveTemplate.ts @@ -2,6 +2,8 @@ import {ListTemplate} from 'react-native-carplay'; export const carPlayLiveTemplate = new ListTemplate({ title: 'Tiesiogiai', + tabTitle: 'Tiesiogiai', + tabSystemImageName: 'play.square.fill', id: 'lrt-list-template-live', trailingNavigationBarButtons: [ { diff --git a/app/car/newest/createPlayNewestTemplate.ts b/app/car/newest/createPlayNewestTemplate.ts index 2bbffb4..40f9d63 100644 --- a/app/car/newest/createPlayNewestTemplate.ts +++ b/app/car/newest/createPlayNewestTemplate.ts @@ -2,6 +2,8 @@ import {ListTemplate} from 'react-native-carplay'; export const carPlayNewestTemplate = new ListTemplate({ title: 'Naujausi', + tabTitle: 'Naujausi', + tabSystemImageName: 'newspaper.fill', id: 'lrt-list-template-newest', trailingNavigationBarButtons: [ { diff --git a/app/car/nowPlaying/assets/backward-solid.png b/app/car/nowPlaying/assets/backward-solid.png new file mode 100644 index 0000000000000000000000000000000000000000..0b42fb3c0ceb61f60370c9dd4955f5ee35d2b6bd GIT binary patch literal 3873 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 void; + onPreviousPress: () => void; +}; + +export const createNowPlayingTemplate = ({onNextPress, onPreviousPress}: Props) => { + return new NowPlayingTemplate({ + id: 'lrt-now-playing-template', + onButtonPressed: (button) => { + if (button.id === 'next') { + onNextPress(); + } else if (button.id === 'previous') { + onPreviousPress(); + } + }, + albumArtistButtonEnabled: true, + buttons: [ + { + id: 'previous', + type: 'image', + image: require('./assets/backward-solid.png'), + }, + { + id: 'next', + type: 'image', + image: require('./assets/forward-solid.png'), + }, + ], + }); +}; export const carPlayNowPlayingTemplate = new NowPlayingTemplate({ id: 'lrt-now-playing-template', - // buttons: [ - // { - // id: 'play', - // type: 'image', - // image: { - // uri: 'https://i.imgur.com/SOzyBsf.jpeg', - // }, - // }, - // { - // id: 'test', - // type: 'more', - // }, - // ], - onButtonPressed: ({id, templateId}) => { - console.log('Button pressed', id, templateId); - }, - - tabTitle: 'Dabar grojama', albumArtistButtonEnabled: true, }); diff --git a/app/car/podcasts/createPlayPodcastsTemplate.ts b/app/car/podcasts/createPlayPodcastsTemplate.ts index eaaeb1d..1086ba1 100644 --- a/app/car/podcasts/createPlayPodcastsTemplate.ts +++ b/app/car/podcasts/createPlayPodcastsTemplate.ts @@ -2,6 +2,9 @@ import {ListTemplate} from 'react-native-carplay'; export const carPlayPodcastsTemplate = new ListTemplate({ title: 'Radijo laidos', + tabTitle: 'Laidos', + // tabSystemImageName: 'folder.fill', + tabSystemImageName: 'circle.grid.3x3.fill', id: 'lrt-list-template-podcasts', sections: [ { diff --git a/app/car/podcasts/useCarPodcastsTemplate.ts b/app/car/podcasts/useCarPodcastsTemplate.ts index ae12166..406f75d 100644 --- a/app/car/podcasts/useCarPodcastsTemplate.ts +++ b/app/car/podcasts/useCarPodcastsTemplate.ts @@ -1,14 +1,19 @@ import {useEffect, useState} from 'react'; -import {ListTemplate} from 'react-native-carplay'; +import {CarPlay, ListTemplate} from 'react-native-carplay'; import {carPlayPodcastsTemplate} from './createPlayPodcastsTemplate'; import useCarPlayPodcastsPlaylist from './useCarPodcastsPlaylist'; import {ListSection} from 'react-native-carplay/lib/interfaces/ListSection'; +import {CarPlayPodcastItem} from '../../api/Types'; +import useCarCategoryTemplate from '../category/useCarCategoryTemplate'; const useCarPodcastsTemplate = (isConnected: boolean) => { const [template] = useState(carPlayPodcastsTemplate); + const [selectedPodcast, setSelectedPodcast] = useState(undefined); const {podcasts, reload} = useCarPlayPodcastsPlaylist(isConnected); + const categoryTemplate = useCarCategoryTemplate(selectedPodcast); + useEffect(() => { console.log('updating podcasts template'); const sections = podcasts.reduce<{ @@ -30,7 +35,7 @@ const useCarPodcastsTemplate = (isConnected: boolean) => { const listSections: ListSection[] = Object.entries(sections).map(([letter, items]) => ({ header: letter, title: letter, - sectionIndexTitle: letter, + sectionIndexTitle: letter.charAt(0).toUpperCase(), items: items.map((item) => ({ text: item.title, })), @@ -41,7 +46,7 @@ const useCarPodcastsTemplate = (isConnected: boolean) => { template.config.onItemSelect = async ({index}) => { const podcast = podcasts[index]; console.log('Podcast selected', podcast); - // CarPlay.pushTemplate(carPlayNowPlayingTemplate, true); + setSelectedPodcast(podcast); }; return () => { template.config.onItemSelect = undefined; @@ -62,6 +67,13 @@ const useCarPodcastsTemplate = (isConnected: boolean) => { } }, [template, reload]); + useEffect(() => { + console.log('pushing category template', categoryTemplate); + if (categoryTemplate) { + CarPlay.pushTemplate(categoryTemplate, true); + } + }, [categoryTemplate]); + return template; }; diff --git a/app/car/popular/createPlayPopularTemplate.ts b/app/car/popular/createPlayPopularTemplate.ts index a03bc25..b19fc6f 100644 --- a/app/car/popular/createPlayPopularTemplate.ts +++ b/app/car/popular/createPlayPopularTemplate.ts @@ -2,6 +2,8 @@ import {ListTemplate} from 'react-native-carplay'; export const carPlayPopularTemplate = new ListTemplate({ title: 'Populiariausi', + tabTitle: 'Populiariausi', + tabSystemImageName: 'star.fill', id: 'lrt-list-template-popular', trailingNavigationBarButtons: [ { diff --git a/app/car/recommended/createPlayRecommendedTemplate.ts b/app/car/recommended/createPlayRecommendedTemplate.ts index f236042..69988c6 100644 --- a/app/car/recommended/createPlayRecommendedTemplate.ts +++ b/app/car/recommended/createPlayRecommendedTemplate.ts @@ -2,6 +2,9 @@ import {ListTemplate} from 'react-native-carplay'; export const carPlayRecommendedTemplate = new ListTemplate({ title: 'Rekomenduojame', + tabTitle: 'Siƫlome', + // tabSystemImageName: 'square.grid.3x3.fill', + tabSystemImageName: 'star.fill', id: 'lrt-list-template-Recommended', trailingNavigationBarButtons: [ { diff --git a/app/car/root/useCarPlayRootTemplate.ts b/app/car/root/useCarPlayRootTemplate.ts index ec30b9e..b935f16 100644 --- a/app/car/root/useCarPlayRootTemplate.ts +++ b/app/car/root/useCarPlayRootTemplate.ts @@ -1,54 +1,25 @@ -import {useEffect, useState} from 'react'; -import {CarPlay, ListTemplate} from 'react-native-carplay'; +import {useState} from 'react'; +import {TabBarTemplate} from 'react-native-carplay'; import useCarLiveTemplate from '../live/useCarLiveTemplate'; -import { - CATEGORY_LIVE, - CATEGORY_NEWEST, - CATEGORY_PODCASTS, - CATEGORY_POPULAR, - CATEGORY_RECOMMENDED, - carPlayRootTemplate, -} from './createPlayRootTemplate'; + import useCarNewestTemplate from '../newest/useCarNewestTemplate'; -import useCarPopularTemplate from '../popular/useCarPopularTemplate'; import useCarRecommendedTemplate from '../recommended/useCarRecommendedTemplate'; import useCarPodcastsTemplate from '../podcasts/useCarPodcastsTemplate'; const useCarPlayRootTemplate = (isConnected: boolean) => { - const [template] = useState(carPlayRootTemplate); - - const liveTemplate = useCarLiveTemplate(isConnected); - const newestTemplate = useCarNewestTemplate(isConnected); - const popularTemplate = useCarPopularTemplate(isConnected); - const recommendedTemplate = useCarRecommendedTemplate(isConnected); - const podcastsTemplate = useCarPodcastsTemplate(isConnected); - - useEffect(() => { - template.config.onItemSelect = async ({index}) => { - console.log('Root template onItemSelect', index); - switch (index) { - case CATEGORY_LIVE: - CarPlay.pushTemplate(liveTemplate, true); - break; - case CATEGORY_POPULAR: - CarPlay.pushTemplate(popularTemplate, true); - break; - case CATEGORY_NEWEST: - CarPlay.pushTemplate(newestTemplate, true); - break; - case CATEGORY_RECOMMENDED: - CarPlay.pushTemplate(recommendedTemplate, true); - break; - case CATEGORY_PODCASTS: - CarPlay.pushTemplate(podcastsTemplate, true); - break; - } - }; - - return () => { - template.config.onItemSelect = undefined; - }; - }, []); + const [template] = useState( + new TabBarTemplate({ + title: 'LRT', + id: 'root-tab-bar', + templates: [ + useCarRecommendedTemplate(isConnected), + useCarLiveTemplate(isConnected), + useCarNewestTemplate(isConnected), + useCarPodcastsTemplate(isConnected), + ], + onTemplateSelect: (_template, _params) => {}, + }), + ); return template; }; diff --git a/app/components/videoComponent/context/PlayerContext.ts b/app/components/videoComponent/context/PlayerContext.ts index 8db5f2d..43aefe5 100644 --- a/app/components/videoComponent/context/PlayerContext.ts +++ b/app/components/videoComponent/context/PlayerContext.ts @@ -17,6 +17,8 @@ export type MediaBaseData = { export type PlayerContextType = { setPlaylist: (data: MediaBaseData[], current?: number) => void; + playNext: () => void; + playPrevious: () => void; close: () => void; }; @@ -26,5 +28,7 @@ const noOp = (): any => { export const PlayerContext = React.createContext({ setPlaylist: noOp, + playNext: noOp, + playPrevious: noOp, close: noOp, }); diff --git a/app/components/videoComponent/context/PlayerProvider.tsx b/app/components/videoComponent/context/PlayerProvider.tsx index 092a721..f620e8e 100644 --- a/app/components/videoComponent/context/PlayerProvider.tsx +++ b/app/components/videoComponent/context/PlayerProvider.tsx @@ -29,6 +29,7 @@ const PlayerProvider: React.FC> = (props) => { }, [uuid]); const playNext = useCallback(() => { + console.log('playNext', playlist.length); if (playlist.length === 0) { return; } @@ -42,7 +43,20 @@ const PlayerProvider: React.FC> = (props) => { } else { setCurrentMedia(playlist[nextIndex]); } - }, [uuid, playlist, currentMedia]); + }, [playlist, currentMedia]); + + const playPrevious = useCallback(() => { + console.log('playPrevious', playlist.length); + if (playlist.length === 0) { + return; + } + const nextIndex = playlist.findIndex((item) => item === currentMedia) - 1; + if (nextIndex < 0) { + setCurrentMedia(playlist[playlist.length - 1]); + } else { + setCurrentMedia(playlist[nextIndex]); + } + }, [playlist, currentMedia]); const renderMiniPlayer = useCallback(() => { if (!currentMedia) { @@ -116,9 +130,11 @@ const PlayerProvider: React.FC> = (props) => { setCurrentMedia(undefined); } }, + playNext: playNext, + playPrevious: playPrevious, close: handleClose, }), - [setPlaylist, handleClose, playlist], + [setPlaylist, handleClose, playNext, playPrevious, playlist], ); return ( diff --git a/ios/lrtApp.xcodeproj/project.pbxproj b/ios/lrtApp.xcodeproj/project.pbxproj index 56ff0b0..2966882 100644 --- a/ios/lrtApp.xcodeproj/project.pbxproj +++ b/ios/lrtApp.xcodeproj/project.pbxproj @@ -1002,6 +1002,12 @@ " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; @@ -1076,6 +1082,12 @@ " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; From b86eb30bf781a52de4ab9299a52324121be2b236 Mon Sep 17 00:00:00 2001 From: Kestas Venslauskas Date: Thu, 2 May 2024 10:28:02 +0300 Subject: [PATCH 3/4] chore: sonar fixes --- app/car/newest/useCarNewestPlaylist.ts | 3 --- app/car/popular/useCarPopularPlaylist.ts | 3 --- app/car/recommended/useCarRecommendedPlaylist.ts | 3 --- .../embeded/embedComponents/EmbedHTML.tsx | 7 ++++--- .../safeWebView/SafeAutoHeightWebView.tsx | 2 +- .../main/tabScreen/usePopularArticlesProvider.ts | 16 +++------------- 6 files changed, 8 insertions(+), 26 deletions(-) diff --git a/app/car/newest/useCarNewestPlaylist.ts b/app/car/newest/useCarNewestPlaylist.ts index d592776..4abdf90 100644 --- a/app/car/newest/useCarNewestPlaylist.ts +++ b/app/car/newest/useCarNewestPlaylist.ts @@ -1,6 +1,3 @@ -import {useSelector} from 'react-redux'; -import {selectHomeChannels} from '../../redux/selectors'; -import {checkEqual} from '../../util/LodashEqualityCheck'; import {useEffect, useState} from 'react'; import {PlayListItem} from '../CarPlayContext'; import useCancellablePromise from '../../hooks/useCancellablePromise'; diff --git a/app/car/popular/useCarPopularPlaylist.ts b/app/car/popular/useCarPopularPlaylist.ts index f2c9f81..f5cdae0 100644 --- a/app/car/popular/useCarPopularPlaylist.ts +++ b/app/car/popular/useCarPopularPlaylist.ts @@ -1,6 +1,3 @@ -import {useSelector} from 'react-redux'; -import {selectHomeChannels} from '../../redux/selectors'; -import {checkEqual} from '../../util/LodashEqualityCheck'; import {useEffect, useState} from 'react'; import {PlayListItem} from '../CarPlayContext'; import useCancellablePromise from '../../hooks/useCancellablePromise'; diff --git a/app/car/recommended/useCarRecommendedPlaylist.ts b/app/car/recommended/useCarRecommendedPlaylist.ts index 5e1925e..d0973d0 100644 --- a/app/car/recommended/useCarRecommendedPlaylist.ts +++ b/app/car/recommended/useCarRecommendedPlaylist.ts @@ -1,6 +1,3 @@ -import {useSelector} from 'react-redux'; -import {selectHomeChannels} from '../../redux/selectors'; -import {checkEqual} from '../../util/LodashEqualityCheck'; import {useEffect, useState} from 'react'; import {PlayListItem} from '../CarPlayContext'; import useCancellablePromise from '../../hooks/useCancellablePromise'; diff --git a/app/components/articleParagraphs/embeded/embedComponents/EmbedHTML.tsx b/app/components/articleParagraphs/embeded/embedComponents/EmbedHTML.tsx index f51d3b5..bd95caf 100644 --- a/app/components/articleParagraphs/embeded/embedComponents/EmbedHTML.tsx +++ b/app/components/articleParagraphs/embeded/embedComponents/EmbedHTML.tsx @@ -1,6 +1,7 @@ -import React, {useCallback, useState} from 'react'; -import {View, useWindowDimensions} from 'react-native'; -import {WebViewSource} from 'react-native-webview/lib/WebViewTypes'; +import React, {useState} from 'react'; +import {View} from 'react-native'; +import {WebViewSource} from 'react-native-webview/src/WebViewTypes'; + import {ArticleEmbedHTMLType} from '../../../../api/Types'; import SafeAutoHeightWebView from '../../../safeWebView/SafeAutoHeightWebView'; diff --git a/app/components/safeWebView/SafeAutoHeightWebView.tsx b/app/components/safeWebView/SafeAutoHeightWebView.tsx index 4a56908..6e56b39 100644 --- a/app/components/safeWebView/SafeAutoHeightWebView.tsx +++ b/app/components/safeWebView/SafeAutoHeightWebView.tsx @@ -4,7 +4,7 @@ import React, {forwardRef, useCallback, useRef} from 'react'; import {LayoutChangeEvent, Linking} from 'react-native'; import {StyleSheet, View} from 'react-native'; import WebView, {AutoHeightWebViewProps} from 'react-native-autoheight-webview'; -import {ShouldStartLoadRequest} from 'react-native-webview/lib/WebViewTypes'; +import {type ShouldStartLoadRequest} from 'react-native-webview/src/WebViewTypes'; import {MainStackParamList} from '../../navigation/MainStack'; import {useTheme} from '../../Theme'; diff --git a/app/screens/main/tabScreen/usePopularArticlesProvider.ts b/app/screens/main/tabScreen/usePopularArticlesProvider.ts index 28d8c2f..f6efee6 100644 --- a/app/screens/main/tabScreen/usePopularArticlesProvider.ts +++ b/app/screens/main/tabScreen/usePopularArticlesProvider.ts @@ -1,5 +1,6 @@ import {useCallback} from 'react'; import {useDispatch, useSelector} from 'react-redux'; + import {ARTICLES_PER_PAGE_COUNT} from '../../../constants'; import {fetchPopular, refreshPopular} from '../../../redux/actions'; import {selectPopularArticlesScreenState} from '../../../redux/selectors'; @@ -12,19 +13,8 @@ const usePopularArticlesProvider: ArticleScreenAdapter = () => { const dispatch = useDispatch(); const loadNextPage = useCallback(() => { - if (state.lastArticle) { - dispatch( - fetchPopular( - page + 1, - ARTICLES_PER_PAGE_COUNT, - state.lastArticle.item_date, - String(state.lastArticle.id), - ), - ); - } else { - dispatch(fetchPopular(page + 1, ARTICLES_PER_PAGE_COUNT)); - } - }, [dispatch, page, state.lastArticle]); + dispatch(fetchPopular(page + 1, ARTICLES_PER_PAGE_COUNT)); + }, [dispatch, page]); const refresh = useCallback(() => { dispatch(refreshPopular(ARTICLES_PER_PAGE_COUNT)); From f38f80d86c8294212cd2e2510cad7b388de353c1 Mon Sep 17 00:00:00 2001 From: Kestas Venslauskas Date: Thu, 2 May 2024 10:57:27 +0300 Subject: [PATCH 4/4] fix: do not use navigation theme --- .../scrollingChannels/channel/Channel.tsx | 4 ++-- .../blocks/SlugArticlesBlock/SlugArticlesBlock.tsx | 12 ++++++------ app/theme/ThemeProvider.tsx | 13 ++++++++++--- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/components/scrollingChannels/channel/Channel.tsx b/app/components/scrollingChannels/channel/Channel.tsx index e1f60e3..3ddfb03 100644 --- a/app/components/scrollingChannels/channel/Channel.tsx +++ b/app/components/scrollingChannels/channel/Channel.tsx @@ -9,8 +9,8 @@ import TouchableDebounce from '../../touchableDebounce/TouchableDebounce'; import TextComponent from '../../text/Text'; import {channelColors, themeLight, useTheme} from '../../../Theme'; import {isLiveChannel, LiveChannel, TVChannel} from '../../../api/Types'; -import {ThemeProvider} from '@react-navigation/native'; import {IconPlay} from '../../svg'; +import ThemeProvider from '../../../theme/ThemeProvider'; interface Props { data: TVChannel | LiveChannel; @@ -55,7 +55,7 @@ const Channel: React.FC> = ({data, onPress}) => { - + {getIconForChannel(data.channel, {height: 28})} diff --git a/app/screens/main/tabScreen/home/blocks/SlugArticlesBlock/SlugArticlesBlock.tsx b/app/screens/main/tabScreen/home/blocks/SlugArticlesBlock/SlugArticlesBlock.tsx index c1b7218..93fbce4 100644 --- a/app/screens/main/tabScreen/home/blocks/SlugArticlesBlock/SlugArticlesBlock.tsx +++ b/app/screens/main/tabScreen/home/blocks/SlugArticlesBlock/SlugArticlesBlock.tsx @@ -1,5 +1,4 @@ import {useNavigation} from '@react-navigation/core'; -import {ThemeProvider} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import React, {useCallback, useMemo} from 'react'; import {StyleSheet, View} from 'react-native'; @@ -12,6 +11,7 @@ import {themeDark, themeLight, useTheme} from '../../../../../../Theme'; import {formatArticles} from '../../../../../../util/articleFormatters'; import {buildArticleImageUri, IMG_SIZE_L} from '../../../../../../util/ImageUtil'; import FastImage from 'react-native-fast-image'; +import ThemeProvider from '../../../../../../theme/ThemeProvider'; interface SlugArticlesBlockProps { block: HomeBlockSlug; @@ -24,10 +24,10 @@ const SlugArticlesBlock: React.FC = ({block}) => { const navigation = useNavigation>(); const theme = useTheme(); - const formattedArticles = useMemo(() => formatArticles(template_id, articles_list), [ - articles_list, - template_id, - ]); + const formattedArticles = useMemo( + () => formatArticles(template_id, articles_list), + [articles_list, template_id], + ); const articlePressHandler = useCallback( (article: Article) => { @@ -81,7 +81,7 @@ const SlugArticlesBlock: React.FC = ({block}) => { onPress={onHeaderPressHandler} color={template_id === 9 ? themeDark.colors.text : undefined} /> - {articleList} + {articleList} ); diff --git a/app/theme/ThemeProvider.tsx b/app/theme/ThemeProvider.tsx index a0cdde3..df08540 100644 --- a/app/theme/ThemeProvider.tsx +++ b/app/theme/ThemeProvider.tsx @@ -3,14 +3,21 @@ import {AppTheme, themeDark, themeLight} from '../Theme'; import {useSettings} from '../settings/useSettings'; import {ThemeContext} from './ThemeContext'; -const ThemeProvider: React.FC> = (props) => { +type Props = { + forceTheme?: AppTheme; +}; + +const ThemeProvider: React.FC> = ({children, forceTheme}) => { const settings = useSettings(); console.log('SETTINGS', settings); const {isDarkMode} = settings; - const context: AppTheme = useMemo(() => (isDarkMode ? themeDark : themeLight), [isDarkMode]); + const context: AppTheme = useMemo( + () => (forceTheme ? forceTheme : isDarkMode ? themeDark : themeLight), + [isDarkMode, forceTheme], + ); - return {props.children}; + return {children}; }; export default ThemeProvider;