Skip to content

Commit

Permalink
Merge pull request #49 from lrtlt/feature/car-play-podcasts
Browse files Browse the repository at this point in the history
Feature/car play podcasts
  • Loading branch information
KestasVenslauskas authored May 2, 2024
2 parents 541a606 + f38f80d commit 1cda8d0
Show file tree
Hide file tree
Showing 35 changed files with 464 additions and 129 deletions.
9 changes: 3 additions & 6 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<React.PropsWithChildren<{}>> = ({children}) => {
Expand All @@ -34,17 +33,15 @@ const App: React.FC = () => {
useGoogleAnalyticsSetup();
useGemiusSetup();

const CarPlayProviderImpl = Platform.OS === 'ios' ? CarPlayProvider : CarPlayEmptyProvider;

return (
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
<SettingsProvider>
<ThemeProvider>
<AppBackground>
<PlayerProvider>
<CarPlayProviderImpl>
<CarPlayProvider>
<Navigation />
</CarPlayProviderImpl>
</CarPlayProvider>
</PlayerProvider>
</AppBackground>
</ThemeProvider>
Expand Down
6 changes: 6 additions & 0 deletions app/api/Endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,9 @@ 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}`;

export const carPlaylistCategoryGet = (id: number | string) =>
`https://www.lrt.lt/api/json/category?id=${id}`;
25 changes: 25 additions & 0 deletions app/api/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,31 @@ export type CarPlaylistItem = {
streamUrl: string;
};

export type CarPlayPodcastsResponse = {
total_found: number;
items: CarPlayPodcastItem[];
};

export type CarPlayPodcastItem = {
id: number;
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;
Expand Down
10 changes: 10 additions & 0 deletions app/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import {
articleGet,
articlesGetByTag,
audiotekaGet,
carPlaylistCategoryGet,
carPlaylistNewestGet,
carPlaylistPodcastsGet,
carPlaylistPopularGet,
carPlaylistRecommendedGet,
categoryGet,
Expand All @@ -25,6 +27,8 @@ import {get, put} from './HttpClient';
import {
ArticleContentResponse,
AudiotekaResponse,
CarPlayCategoryResponse,
CarPlayPodcastsResponse,
CarPlaylistItem,
CategoryArticlesResponse,
ChannelResponse,
Expand Down Expand Up @@ -103,3 +107,9 @@ export const fetchCarNewestPlaylist = () => get<CarPlaylistItem[]>(carPlaylistNe
export const fetchCarPopularPlaylist = () => get<CarPlaylistItem[]>(carPlaylistPopularGet());

export const fetchCarRecommendedPlaylist = () => get<CarPlaylistItem[]>(carPlaylistRecommendedGet());

export const fetchCarPodcasts = (count: number) =>
get<CarPlayPodcastsResponse>(carPlaylistPodcastsGet(count));

export const fetchCarCategoryPlaylist = (id: string | number) =>
get<CarPlayCategoryResponse>(carPlaylistCategoryGet(id));
8 changes: 7 additions & 1 deletion app/car/CarPlayContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -12,7 +19,6 @@ export type PlayListItem = {

export type CarPlayContextType = {
isConnected: boolean;
selectedCategory?: CarCategory;
};

const defaults: CarPlayContextType = {
Expand Down
18 changes: 0 additions & 18 deletions app/car/CarPlayEmptyProvider.tsx

This file was deleted.

8 changes: 7 additions & 1 deletion app/car/CarPlayProvider.tsx
Original file line number Diff line number Diff line change
@@ -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<PropsWithChildren<{}>> = (props) => {
const {isConnected} = useCarPlayController();
const {isConnected} =
Platform.OS === 'ios'
? useCarPlayController()
: {
isConnected: false,
};

const context: CarPlayContextType = useMemo(
() => ({
Expand Down
12 changes: 12 additions & 0 deletions app/car/category/createPlayCategoryTemplate.ts
Original file line number Diff line number Diff line change
@@ -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,
});
36 changes: 36 additions & 0 deletions app/car/category/useCarCategoryPlaylist.ts
Original file line number Diff line number Diff line change
@@ -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<CategoryListItem[]>([]);

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;
67 changes: 67 additions & 0 deletions app/car/category/useCarCategoryTemplate.ts
Original file line number Diff line number Diff line change
@@ -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<ListTemplate>();
const [selectedEpisode, setSelectedEpisode] = useState<CategoryListItem | undefined>(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;
38 changes: 38 additions & 0 deletions app/car/category/useCarPlayCategoryEpisodeStream.ts
Original file line number Diff line number Diff line change
@@ -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<PlayListItem>();

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;
2 changes: 2 additions & 0 deletions app/car/live/createPlayLiveTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down
2 changes: 2 additions & 0 deletions app/car/newest/createPlayNewestTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down
6 changes: 1 addition & 5 deletions app/car/newest/useCarNewestPlaylist.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -9,7 +6,6 @@ import {fetchCarNewestPlaylist} from '../../api';
const useCarPlayNewestPlaylist = (isConnected: boolean) => {
const [channels, setChannels] = useState<PlayListItem[]>([]);
const [lastLoadTime, setLastLoadTime] = useState<number>(0);
const channelsData = useSelector(selectHomeChannels, checkEqual);

const cancellablePromise = useCancellablePromise();

Expand All @@ -31,7 +27,7 @@ const useCarPlayNewestPlaylist = (isConnected: boolean) => {
}
}),
);
}, [channelsData, isConnected, lastLoadTime]);
}, [isConnected, lastLoadTime]);

return {
channels,
Expand Down
Binary file added app/car/nowPlaying/assets/backward-solid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/car/nowPlaying/assets/forward-solid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1cda8d0

Please sign in to comment.