search page added

This commit is contained in:
polwex 2024-12-10 23:25:33 +07:00
parent eafdfe3f2b
commit c33e446352
16 changed files with 1254 additions and 41 deletions

View File

@ -5,6 +5,7 @@
* @format * @format
*/ */
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import React from 'react'; import React from 'react';
import type {PropsWithChildren} from 'react'; import type {PropsWithChildren} from 'react';
import { import {
@ -25,6 +26,14 @@ import {
ReloadInstructions, ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen'; } from 'react-native/Libraries/NewAppScreen';
import McIcon from 'react-native-vector-icons/MaterialCommunityIcons';
import Fa6 from 'react-native-vector-icons/FontAwesome6';
import {NavigationContainer} from '@react-navigation/native';
// pages
import SearchPage from './pages/Search.tsx';
// <pages
type SectionProps = PropsWithChildren<{ type SectionProps = PropsWithChildren<{
title: string; title: string;
}>; }>;
@ -63,38 +72,51 @@ function App(): React.JSX.Element {
}; };
return ( return (
<SafeAreaView style={backgroundStyle}> <NavigationContainer>
<StatusBar <SearchPage />
barStyle={isDarkMode ? 'light-content' : 'dark-content'} </NavigationContainer>
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<Header />
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Section title="Step One">
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
screen and then come back to see your edits.
</Section>
<Section title="See Your Changes">
<ReloadInstructions />
</Section>
<Section title="Debug">
<DebugInstructions />
</Section>
<Section title="Learn More">
Read the docs to discover what to do next:
</Section>
<LearnMoreLinks />
</View>
</ScrollView>
</SafeAreaView>
); );
} }
// function App(): React.JSX.Element {
// const isDarkMode = useColorScheme() === 'dark';
// const backgroundStyle = {
// backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
// };
// return (
// <SafeAreaView style={backgroundStyle}>
// <StatusBar
// barStyle={isDarkMode ? 'light-content' : 'dark-content'}
// backgroundColor={backgroundStyle.backgroundColor}
// />
// <ScrollView
// contentInsetAdjustmentBehavior="automatic"
// style={backgroundStyle}>
// <Header />
// <View
// style={{
// backgroundColor: isDarkMode ? Colors.black : Colors.white,
// }}>
// <Section title="Step One">
// Edit <Text style={styles.highlight}>App.tsx</Text> to change this
// screen and then come back to see your edits.
// </Section>
// <Section title="See Your Changes">
// <ReloadInstructions />
// </Section>
// <Section title="Debug">
// <DebugInstructions />
// </Section>
// <Section title="Learn More">
// Read the docs to discover what to do next:
// </Section>
// <LearnMoreLinks />
// </View>
// </ScrollView>
// </SafeAreaView>
// );
// }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
sectionContainer: { sectionContainer: {
@ -116,3 +138,51 @@ const styles = StyleSheet.create({
}); });
export default App; export default App;
function TabNav() {
const Footer = createBottomTabNavigator();
return (
<Footer.Navigator>
<Footer.Screen
name="Home"
component={Home}
options={{
tabBarIcon: ({focused, color, size}) => {
if (focused) return <McIcon name="home" size={30} />;
else return <McIcon name="home-outline" size={30} />;
},
}}
/>
<Footer.Screen
name="Search"
component={Search}
options={{
tabBarIcon: ({focused, color, size}) => {
if (focused) return <McIcon name="search-web" size={30} />;
else return <McIcon name="search-web" size={30} />;
},
}}
/>
<Footer.Screen
name="Library"
component={Library}
options={{
tabBarIcon: ({focused, color, size}) => {
if (focused) return <McIcon name="rss-box" size={30} />;
else return <McIcon name="rss" size={30} />;
},
}}
/>
</Footer.Navigator>
);
}
function Home() {
return <Text>Hi</Text>;
}
function Search() {
return <Text>Search</Text>;
}
function Library() {
return <Text>Library</Text>;
}

View File

49
dialpods/logic/store.ts Normal file
View File

@ -0,0 +1,49 @@
import {create} from 'zustand';
import {createJSONStorage, persist} from 'zustand/middleware';
import {useShallow} from 'zustand/shallow';
import type {Podcasts} from './types/types';
interface UIState {
subs: Podcasts;
}
type ProcessState = {subs: Podcasts};
type WsMessage =
| {kind: 'error'; data: string}
| {
kind: 'state';
data: ProcessState;
};
// | {
// kind: 'post';
// data: Post['data'];
// stream_name: string;
// post_id: string;
// }
// | {
// kind: 'image';
// data: {
// image: string;
// uri: string;
// };
// }
// | {
// kind: 'profile';
// data: CuratorProfileInfo;
// };
const storeInner = create<UIState>()(
// persist(
(set, get) => ({
subs: {},
}),
// {
// name: 'dial_pods_store',
// storage: createJSONStorage(() => sessionStorage),
// },
// ),
);
const useUIStore = <T extends (state: UIState) => any>(
selector: T,
): ReturnType<T> => storeInner(useShallow(selector));
export default useUIStore;

View File

@ -0,0 +1,62 @@
export type AsyncRes<T> = Promise<Result<T>>;
export type Result<T> = {ok: T} | {error: string};
export type Podcasts = Record<string, Podcast>;
export type Ack = null;
export type Podcast = SearchResult;
export type SearchResult = {image: string; name: string; url: string};
export type YoutubeChannel = {
channel: {
author: string;
authorUrl: string;
channelId: string;
published: string;
title: string;
};
entries: YoutubeItem[];
};
export type YoutubeItem = {
id: string;
videoId: string;
title: string;
published: string;
updated: string;
link: string;
thumbnail: string;
description: string;
views: number;
rating: {
count: number;
average: number;
};
};
export type RSSFeed = {
podcast: {
title: string;
description: string;
link: string;
copyright: string;
language: string;
lastBuildDate?: string;
author: string;
type: string;
image: string;
// categories: Array<{ text: string; subcategories: string[] }>;
// podcastGuid: string;
};
episodes: RSSFeedItem[];
};
// TODO deal with CData encoding
export type RSSFeedItem = {
title: string;
link: string;
guid: {value: string; isPermaLink: boolean};
pubDate: string;
// author: string;
description: string;
// content: string;
enclosure: {url: string; length: number; type: string};
// podcastInfo: { season: string; episode: string };
};

336
dialpods/logic/utils.ts Normal file
View File

@ -0,0 +1,336 @@
import {Result, RSSFeed, SearchResult, YoutubeChannel} from './types/types';
import DOMparser from 'react-native-html-parser';
export async function parseHTMLRes(htmls: string) {
const parser = new DOMparser.DOMParser();
const html = parser.parseFromString(htmls, 'text/html');
return html;
}
export function parseYoutubeChannel(
head: HTMLHeadElement,
): Result<SearchResult> {
const linkEl = head.querySelector('link[type="application/rss+xml"]');
if (!linkEl) return {error: `not found`};
const url = linkEl!.getAttribute('href');
if (!url) return {error: `not found`};
const og = head.querySelectorAll('meta');
let image = '';
let name = '';
for (const meta of og) {
if (image && name) break;
const n = meta.getAttribute('property');
if (n === 'og:image') image = meta.getAttribute('content')!;
if (n === 'og:title') name = meta.getAttribute('content')!;
}
if (image && name && url) return {ok: {image, name, url}};
else return {error: 'not found'};
}
export async function parseXMLRes(s: string) {
const parser = new DOMparser.DOMParser();
const xml = parser.parseFromString(s, 'text/xml');
return xml;
}
export function parseRSSFeed(url: string, doc: Document): Result<SearchResult> {
const chan = doc.getElementsByTagName('channel')[0];
const title = chan.getElementsByTagName('title')[0];
const image = chan.getElementsByTagName('image')[0];
const imageURL = image.getElementsByTagName('url')[0];
try {
return {
ok: {
name: title.textContent!,
image: imageURL.textContent!,
url,
},
};
} catch (_) {
return {error: 'not found'};
}
}
const namespaces: any = {
itunes: 'http://www.itunes.com/dtds/podcast-1.0.dtd',
podcast: 'https://podcastindex.org/namespace/1.0',
content: 'http://purl.org/rss/1.0/modules/content/',
dc: 'http://purl.org/dc/elements/1.1/',
atom: 'http://www.w3.org/2005/Atom',
podaccess: 'https://access.acast.com/schema/1.0',
media: 'http://search.yahoo.com/mrss/',
};
export function parseRSSFull(url: string, doc: Document): Result<RSSFeed> {
try {
// Helper function to get namespaced elements
const getNS = (element: Element, namespace: string, tagName: string) =>
element.getElementsByTagNameNS(namespaces[namespace], tagName)[0];
// Parse channel (podcast) information
const channel = doc.getElementsByTagName('channel')[0];
const podcastInfo = {
title: channel.getElementsByTagName('title')[0].textContent || '',
description:
channel.getElementsByTagName('description')[0].textContent || '',
link: channel.getElementsByTagName('link')[0].textContent || '',
copyright: channel.getElementsByTagName('copyright')[0].textContent || '',
language: channel.getElementsByTagName('language')[0].textContent || '',
//
//
//
// lastBuildDate:
// channel.getElementsByTagName('lastBuildDate')[0],
// ),
// iTunes specific fields
author: getNS(channel, 'itunes', 'author').textContent || '',
type: getNS(channel, 'itunes', 'type').textContent || '',
// owner: {
// name:
// getNS(channel, 'itunes', 'owner')?.getElementsByTagNameNS(
// namespaces.itunes,
// 'name',
// )[0],
// ),
// email:
// getNS(channel, 'itunes', 'owner')?.getElementsByTagNameNS(
// namespaces.itunes,
// 'email',
// )[0],
// ),
// },
image:
channel.getElementsByTagName('image')[0]?.getElementsByTagName('url')[0]
?.textContent || '',
// image: {
// url: channel
// .getElementsByTagName('image')[0]
// ?.getElementsByTagName('url')[0]?.textContent,
// itunesUrl: getNS(channel, 'itunes', 'image')?.getAttribute('href'),
// },
// Categories
categories: Array.from(
channel.getElementsByTagNameNS(namespaces.itunes, 'category'),
).map(category => ({
text: category.getAttribute('text') || '',
subcategories: Array.from(
category.getElementsByTagNameNS(namespaces.itunes, 'category'),
).map(sub => sub.getAttribute('text') || ''),
})),
// Podcast namespace specific fields
// podcastGuid: getNS(channel, 'podcast', 'guid')),
// hosts: Array.from(
// channel.getElementsByTagNameNS(namespaces.podcast, 'person'),
// )
// .filter((person) => person.getAttribute('role') === 'Host')
// .map((person) => ({
// name: person.textContent,
// role: person.getAttribute('role'),
// img: person.getAttribute('img'),
// href: person.getAttribute('href'),
// })),
};
// Parse episodes
const episodes = Array.from(doc.getElementsByTagName('item')).map(item => {
// Get episode images
const itunesImage = getNS(item, 'itunes', 'image');
return {
title: item.getElementsByTagName('title')[0].textContent || '',
link: item.getElementsByTagName('link')[0].textContent || '',
guid: {
value: item.getElementsByTagName('guid')[0].textContent || '',
isPermaLink:
item
.getElementsByTagName('guid')[0]
?.getAttribute('isPermaLink') === 'true',
},
pubDate: item.getElementsByTagName('pubDate')[0].textContent || '',
// author: item.getElementsByTagName('author')[0],
// Episode content
description:
item.getElementsByTagName('description')[0].textContent || '',
// content:
// getNS(item, 'content', 'encoded'),
// )?.trim(),
// Media information
enclosure: {
url: item.getElementsByTagName('enclosure')[0]?.getAttribute('url')!,
length: parseInt(
item.getElementsByTagName('enclosure')[0]?.getAttribute('length')!,
),
type: item
.getElementsByTagName('enclosure')[0]
?.getAttribute('type')!,
},
// iTunes specific episode fields
// itunesInfo: {
// episode: getNS(item, 'itunes', 'episode')),
// season: getNS(item, 'itunes', 'season')),
// duration: getNS(item, 'itunes', 'duration')),
// explicit: getNS(item, 'itunes', 'explicit')),
// episodeType:
// getNS(item, 'itunes', 'episodeType'),
// ),
// image: itunesImage
// ? itunesImage.getAttribute('href')
// : null,
// },
// Podcast namespace fields
// podcastInfo: {
// season: getNS(item, 'podcast', 'season')),
// episode: getNS(item, 'podcast', 'episode')),
// hosts: Array.from(
// item.getElementsByTagNameNS(
// namespaces.podcast,
// 'person',
// ),
// )
// .filter(
// (person) => person.getAttribute('role') === 'Host',
// )
// .map((person) => ({
// name: person.textContent,
// role: person.getAttribute('role'),
// img: person.getAttribute('img'),
// href: person.getAttribute('href'),
// })),
// },
};
});
return {
ok: {
podcast: podcastInfo,
episodes,
},
};
} catch (e) {
return {error: 'error parsing feed'};
}
}
export function parseYTFeed(doc: Document): Result<YoutubeChannel> {
// Create parser and parse XML string
// Define namespaces used in the document
const namespaces = {
media: 'http://search.yahoo.com/mrss/',
yt: 'http://www.youtube.com/xml/schemas/2015',
atom: 'http://www.w3.org/2005/Atom',
};
// Helper function to safely get text content
const getTextContent = (element: Element) => element.textContent!;
// Parse each entry
const entries = Array.from(doc.getElementsByTagName('entry')).map(entry => {
// Get media:group element
const mediaGroup = entry.getElementsByTagNameNS(
namespaces.media,
'group',
)[0];
// Get statistics and rating from media:community
const mediaCommunity = mediaGroup.getElementsByTagNameNS(
namespaces.media,
'community',
)[0];
const statistics = mediaCommunity.getElementsByTagNameNS(
namespaces.media,
'statistics',
)[0];
const starRating = mediaCommunity.getElementsByTagNameNS(
namespaces.media,
'starRating',
)[0];
return {
id: getTextContent(entry.getElementsByTagName('id')[0]),
videoId: getTextContent(
entry.getElementsByTagNameNS(namespaces.yt, 'videoId')[0],
),
title: getTextContent(entry.getElementsByTagName('title')[0]),
published: getTextContent(entry.getElementsByTagName('published')[0]),
updated: getTextContent(entry.getElementsByTagName('updated')[0]),
link: entry.getElementsByTagName('link')[0]?.getAttribute('href')!,
// Media group information
thumbnail: mediaGroup
.getElementsByTagNameNS(namespaces.media, 'thumbnail')[0]
?.getAttribute('url')!,
description: getTextContent(
mediaGroup.getElementsByTagNameNS(namespaces.media, 'description')[0],
),
// Statistics
views: parseInt(statistics?.getAttribute('views') || '0'),
rating: {
count: parseInt(starRating?.getAttribute('count') || '0'),
average: parseFloat(starRating?.getAttribute('average') || '0'),
},
};
});
// Get channel information
const channelInfo = {
title: getTextContent(doc.getElementsByTagName('title')[0]),
channelId: getTextContent(
doc.getElementsByTagNameNS(namespaces.yt, 'channelId')[0],
),
published: getTextContent(doc.getElementsByTagName('published')[0]),
author: getTextContent(doc.getElementsByTagName('name')[0]),
authorUrl: getTextContent(doc.getElementsByTagName('uri')[0]),
};
return {
ok: {
channel: channelInfo,
entries: entries,
},
};
}
export function abbreviate(s: string, length: number): string {
if (s.length <= length) return s;
else return s.substring(0, length) + '...';
}
export function date_diff(date: Date) {
const now = new Date().getTime();
const s = now - date.getTime();
if (s < 60) {
return 'now';
} else if (s < 3600) {
return `${Math.ceil(s / 60)}minutes ago`;
} else if (s < 86400) {
return `${Math.ceil(s / 60 / 60)}hours ago`;
} else if (s < 32140800) {
return date.toLocaleString('default', {
month: 'long',
day: 'numeric',
});
} else {
return date.toLocaleString('default', {
month: 'long',
day: 'numeric',
year: 'numeric',
});
}
}
export function durationString(secs: number): string {
const hours = Math.floor(secs / 3600);
const minutes = Math.floor((secs % 3600) / 60);
const seconds = Math.floor(secs % 60);
return hours > 0
? `${hours}:${minutes.toString().padStart(2, '0')}:${seconds
.toString()
.padStart(2, '0')}`
: `${minutes}:${seconds.toString().padStart(2, '0')}`;
}

View File

@ -8,8 +8,18 @@
"name": "dialpods", "name": "dialpods",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@react-navigation/bottom-tabs": "^7.1.3",
"@react-navigation/native": "^7.0.13",
"@react-navigation/native-stack": "^7.1.14",
"react": "18.3.1", "react": "18.3.1",
"react-native": "0.76.4" "react-native": "0.76.4",
"react-native-fast-image": "^8.6.3",
"react-native-html-parser": "^0.1.0",
"react-native-safe-area-context": "^5.0.0",
"react-native-screens": "^4.3.0",
"react-native-url-polyfill": "^2.0.0",
"react-native-vector-icons": "^10.2.0",
"zustand": "^5.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",
@ -23,6 +33,7 @@
"@react-native/metro-config": "0.76.4", "@react-native/metro-config": "0.76.4",
"@react-native/typescript-config": "0.76.4", "@react-native/typescript-config": "0.76.4",
"@types/react": "^18.2.6", "@types/react": "^18.2.6",
"@types/react-native-vector-icons": "^6.4.18",
"@types/react-test-renderer": "^18.0.0", "@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.6.3", "babel-jest": "^29.6.3",
"eslint": "^8.19.0", "eslint": "^8.19.0",
@ -3411,6 +3422,111 @@
} }
} }
}, },
"node_modules/@react-navigation/bottom-tabs": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.1.3.tgz",
"integrity": "sha512-cK5zE7OpZAgZLpFBnoH9AhZbSZfH9Qavdi3kIRd2vpQDtCfnnG5bQ2eM2u/IKHDLdI50Mhsf+srqYJgG2VcmVQ==",
"license": "MIT",
"dependencies": {
"@react-navigation/elements": "^2.2.4",
"color": "^4.2.3"
},
"peerDependencies": {
"@react-navigation/native": "^7.0.13",
"react": ">= 18.2.0",
"react-native": "*",
"react-native-safe-area-context": ">= 4.0.0",
"react-native-screens": ">= 4.0.0"
}
},
"node_modules/@react-navigation/core": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.3.0.tgz",
"integrity": "sha512-mfUPRdFCuHkaC+uU5iczqevn0PCTKzf6ApxFwgG9E8DfAVbAT7/piZEzFye2inaIRkipBwyNW40h+mEvYqE1og==",
"license": "MIT",
"dependencies": {
"@react-navigation/routers": "^7.1.1",
"escape-string-regexp": "^4.0.0",
"nanoid": "3.3.7",
"query-string": "^7.1.3",
"react-is": "^18.2.0",
"use-latest-callback": "^0.2.1",
"use-sync-external-store": "^1.2.2"
},
"peerDependencies": {
"react": ">= 18.2.0"
}
},
"node_modules/@react-navigation/core/node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"license": "MIT"
},
"node_modules/@react-navigation/elements": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.2.4.tgz",
"integrity": "sha512-/H6Gu/Hn2E/pBQTkZEMbP5SDi7C2q96PrHGvsDJiFtxFgOJusA3+ygUguqTeTP402s/5KvJm47g0UloCMiECwA==",
"license": "MIT",
"dependencies": {
"color": "^4.2.3"
},
"peerDependencies": {
"@react-native-masked-view/masked-view": ">= 0.2.0",
"@react-navigation/native": "^7.0.13",
"react": ">= 18.2.0",
"react-native": "*",
"react-native-safe-area-context": ">= 4.0.0"
},
"peerDependenciesMeta": {
"@react-native-masked-view/masked-view": {
"optional": true
}
}
},
"node_modules/@react-navigation/native": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.0.13.tgz",
"integrity": "sha512-HLoMyp453qIDGjG72cJ2xLeGHHpP4PQve5gQvSn3o/6r2+DAmDuIcd/jXTMJGCHd2LeR9LfuqIvpiIlihg1iBg==",
"license": "MIT",
"dependencies": {
"@react-navigation/core": "^7.3.0",
"escape-string-regexp": "^4.0.0",
"fast-deep-equal": "^3.1.3",
"nanoid": "3.3.7",
"use-latest-callback": "^0.2.1"
},
"peerDependencies": {
"react": ">= 18.2.0",
"react-native": "*"
}
},
"node_modules/@react-navigation/native-stack": {
"version": "7.1.14",
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.1.14.tgz",
"integrity": "sha512-MH3iktneSL8JSttcgJBIb6zmDpaurbtMSQcYwCcsGoyq4fJ2pBIwJaViSa0KrNkMsWAMZEOY9O72rf1umu7VKw==",
"license": "MIT",
"dependencies": {
"@react-navigation/elements": "^2.2.4",
"warn-once": "^0.1.1"
},
"peerDependencies": {
"@react-navigation/native": "^7.0.13",
"react": ">= 18.2.0",
"react-native": "*",
"react-native-safe-area-context": ">= 4.0.0",
"react-native-screens": ">= 4.0.0"
}
},
"node_modules/@react-navigation/routers": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.1.1.tgz",
"integrity": "sha512-OycWRj95p+/zENl9HU6tvvT6IUuxgVJirgsA0W9rQn3RC+9Hb0UVYA0+8avdt+WpMoWdrvwTxTXneB5mjYzHrw==",
"license": "MIT",
"dependencies": {
"nanoid": "3.3.7"
}
},
"node_modules/@sideway/address": { "node_modules/@sideway/address": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
@ -3576,6 +3692,27 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-native": {
"version": "0.70.19",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.19.tgz",
"integrity": "sha512-c6WbyCgWTBgKKMESj/8b4w+zWcZSsCforson7UdXtXMecG3MxCinYi6ihhrHVPyUrVzORsvEzK8zg32z4pK6Sg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-native-vector-icons": {
"version": "6.4.18",
"resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.18.tgz",
"integrity": "sha512-YGlNWb+k5laTBHd7+uZowB9DpIK3SXUneZqAiKQaj1jnJCZM0x71GDim5JCTMi4IFkhc9m8H/Gm28T5BjyivUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*",
"@types/react-native": "^0.70"
}
},
"node_modules/@types/react-test-renderer": { "node_modules/@types/react-test-renderer": {
"version": "18.3.1", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.1.tgz", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.1.tgz",
@ -4477,7 +4614,6 @@
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"devOptional": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -4829,6 +4965,19 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1",
"color-string": "^1.9.0"
},
"engines": {
"node": ">=12.5.0"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -4847,6 +4996,16 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"license": "MIT",
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"node_modules/colorette": { "node_modules/colorette": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
@ -5165,6 +5324,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/decode-uri-component": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"license": "MIT",
"engines": {
"node": ">=0.10"
}
},
"node_modules/dedent": { "node_modules/dedent": {
"version": "1.5.3", "version": "1.5.3",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
@ -6318,7 +6486,6 @@
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-glob": { "node_modules/fast-glob": {
@ -6431,6 +6598,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/filter-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@ -7074,7 +7250,6 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"devOptional": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -9826,6 +10001,24 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/natural-compare": { "node_modules/natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@ -9997,7 +10190,6 @@
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -10553,7 +10745,6 @@
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
@ -10565,14 +10756,12 @@
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -10595,6 +10784,24 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/query-string": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
"license": "MIT",
"dependencies": {
"decode-uri-component": "^0.2.2",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/queue": { "node_modules/queue": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
@ -10677,6 +10884,18 @@
} }
} }
}, },
"node_modules/react-freeze": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz",
"integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"peerDependencies": {
"react": ">=17.0.0"
}
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@ -10745,6 +10964,126 @@
} }
} }
}, },
"node_modules/react-native-fast-image": {
"version": "8.6.3",
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz",
"integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==",
"license": "(MIT AND Apache-2.0)",
"peerDependencies": {
"react": "^17 || ^18",
"react-native": ">=0.60.0"
}
},
"node_modules/react-native-html-parser": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/react-native-html-parser/-/react-native-html-parser-0.1.0.tgz",
"integrity": "sha512-G5d0pk7TA9YgjVJhLRf2zMOOf1HXd8ItM9JYsTS2pXoEfSRfMfRU5iTj03LGwvHU2NT9kc18NDJeLgZY6+Bbvg==",
"engines": {
"node": ">=0.1"
}
},
"node_modules/react-native-safe-area-context": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.0.0.tgz",
"integrity": "sha512-4K4TvEbRsTDYuSSJZfMNKuJNn1+qgrSkOBwRoreiHcuqy1egrHpkhPhoN1Zg1+b3BxcVXlKXtMIf4eVaG/DPJw==",
"license": "MIT",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-screens": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.3.0.tgz",
"integrity": "sha512-G0u8BPgu2vcRZoQTlRpBXKa0ElQSDvDBlRe6ncWwCeBmd5Uqa2I3tQ6Vn6trIE6+yneW/nD4p5wihEHlAWZPEw==",
"license": "MIT",
"dependencies": {
"react-freeze": "^1.0.0",
"warn-once": "^0.1.0"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-url-polyfill": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz",
"integrity": "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==",
"license": "MIT",
"dependencies": {
"whatwg-url-without-unicode": "8.0.0-3"
},
"peerDependencies": {
"react-native": "*"
}
},
"node_modules/react-native-vector-icons": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.2.0.tgz",
"integrity": "sha512-n5HGcxUuVaTf9QJPs/W22xQpC2Z9u0nb0KgLPnVltP8vdUvOp6+R26gF55kilP/fV4eL4vsAHUqUjewppJMBOQ==",
"license": "MIT",
"dependencies": {
"prop-types": "^15.7.2",
"yargs": "^16.1.1"
},
"bin": {
"fa-upgrade.sh": "bin/fa-upgrade.sh",
"fa5-upgrade": "bin/fa5-upgrade.sh",
"fa6-upgrade": "bin/fa6-upgrade.sh",
"generate-icon": "bin/generate-icon.js"
}
},
"node_modules/react-native-vector-icons/node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"node_modules/react-native-vector-icons/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/react-native-vector-icons/node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"license": "MIT",
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/react-native-vector-icons/node_modules/yargs-parser": {
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/react-native/node_modules/ansi-styles": { "node_modules/react-native/node_modules/ansi-styles": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
@ -11454,6 +11793,21 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.3.1"
}
},
"node_modules/simple-swizzle/node_modules/is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"license": "MIT"
},
"node_modules/sisteransi": { "node_modules/sisteransi": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@ -11535,6 +11889,15 @@
"source-map": "^0.6.0" "source-map": "^0.6.0"
} }
}, },
"node_modules/split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/sprintf-js": { "node_modules/sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -11598,6 +11961,15 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/string_decoder": { "node_modules/string_decoder": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@ -12337,6 +12709,24 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/use-latest-callback": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.3.tgz",
"integrity": "sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/use-sync-external-store": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -12392,6 +12782,12 @@
"makeerror": "1.0.12" "makeerror": "1.0.12"
} }
}, },
"node_modules/warn-once": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz",
"integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==",
"license": "MIT"
},
"node_modules/wcwidth": { "node_modules/wcwidth": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
@ -12424,6 +12820,29 @@
"webidl-conversions": "^3.0.0" "webidl-conversions": "^3.0.0"
} }
}, },
"node_modules/whatwg-url-without-unicode": {
"version": "8.0.0-3",
"resolved": "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz",
"integrity": "sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==",
"license": "MIT",
"dependencies": {
"buffer": "^5.4.3",
"punycode": "^2.1.1",
"webidl-conversions": "^5.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/whatwg-url-without-unicode/node_modules/webidl-conversions": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
"integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=8"
}
},
"node_modules/which": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@ -12676,6 +13095,35 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/zustand": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.2.tgz",
"integrity": "sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
"@types/react": ">=18.0.0",
"immer": ">=9.0.6",
"react": ">=18.0.0",
"use-sync-external-store": ">=1.2.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
},
"use-sync-external-store": {
"optional": true
}
}
} }
} }
} }

View File

@ -10,8 +10,18 @@
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"@react-navigation/bottom-tabs": "^7.1.3",
"@react-navigation/native": "^7.0.13",
"@react-navigation/native-stack": "^7.1.14",
"react": "18.3.1", "react": "18.3.1",
"react-native": "0.76.4" "react-native": "0.76.4",
"react-native-fast-image": "^8.6.3",
"react-native-html-parser": "^0.1.0",
"react-native-safe-area-context": "^5.0.0",
"react-native-screens": "^4.3.0",
"react-native-url-polyfill": "^2.0.0",
"react-native-vector-icons": "^10.2.0",
"zustand": "^5.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",
@ -25,6 +35,7 @@
"@react-native/metro-config": "0.76.4", "@react-native/metro-config": "0.76.4",
"@react-native/typescript-config": "0.76.4", "@react-native/typescript-config": "0.76.4",
"@types/react": "^18.2.6", "@types/react": "^18.2.6",
"@types/react-native-vector-icons": "^6.4.18",
"@types/react-test-renderer": "^18.0.0", "@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.6.3", "babel-jest": "^29.6.3",
"eslint": "^8.19.0", "eslint": "^8.19.0",

0
dialpods/pages/Feed.tsx Normal file
View File

0
dialpods/pages/Home.tsx Normal file
View File

View File

View File

211
dialpods/pages/Search.tsx Normal file
View File

@ -0,0 +1,211 @@
import React, {useEffect, useState} from 'react';
import {
View,
Text,
TextInput,
Image,
TouchableOpacity,
StyleSheet,
ActivityIndicator,
ScrollView,
} from 'react-native';
// import { corsProxy, saveFeed } from '../logic/api';
import {
parseHTMLRes,
parseRSSFeed,
parseXMLRes,
parseYoutubeChannel,
} from '../logic/utils';
import useUIStore from '../logic/store';
import type {SearchResult} from '../logic/types/types';
import {Button} from 'react-native';
function SearchScreen() {
const [loading, setLoading] = useState(false);
const [input, setInput] = useState('https://www.youtube.com/@bookclubradio');
const [results, setResults] = useState<SearchResult[]>([]);
console.log('search page loaded');
function handleButton() {
// handleURL(url);
console.log('button pressed', input);
const url = new URL(input);
console.log('got url', url);
handleYouTube(input);
}
// useEffect(() => {
// console.log('hi!!', input);
// if (!input) setResults([]);
// if (input.length > 2)
// try {
// } catch (_) {
// handleSearch(input);
// }
// }, [input]);
async function handleURL(url: URL) {
console.log('handling url');
// if (url.hostname.endsWith('youtube.com')) handleYouTube(url);
// else handleRSS(url);
}
async function handleYouTube(url: string) {
console.log('handling youtube', url);
// if (!url.search) {
// const res = await corsProxy(url);
console.log('fetching', url);
const res = await fetch(url);
console.log('youtube response', res);
const txt = await res.text();
console.log('youtube text', txt);
const doc = await parseHTMLRes(txt);
console.log('doc', doc);
console.log('head', doc.head);
const mparsed = parseYoutubeChannel(doc.head);
console.log(mparsed, 'mparsed');
// if ('error' in mparsed) handleError(url.toString(), mparsed.error);
// else setResults(r => [...r, mparsed.ok]);
// }
}
async function handleRSS(url: URL) {
// const res = await corsProxy(url);
// const res = await fetch(url);
// if (!('ok' in res)) return handleError(url.toString(), 'error fetching');
// const s = res.ok;
// if (!('HTML' in s)) return handleError('', '');
// const doc = await parseXMLRes(s.HTML);
// try {
// const mparsed = parseRSSFeed(url.toString(), doc);
// if ('error' in mparsed) handleError(url.toString(), mparsed.error);
// else setResults(r => [...r, mparsed.ok]);
// } catch (e) {
// console.log('wtf happened', e);
// }
}
async function handleSearch(input: string) {
setLoading(true);
console.log('searching', input);
}
function handleError(source: string, error: string) {
const alert = {message: `${error}: ${source}`, timeout: 2000};
// setAlert(alert);
}
function close() {
// resync();
setResults([]);
setInput('');
}
return (
<View style={styles.container}>
<View style={styles.navbar}>
<Text style={styles.navbarText}>Search</Text>
</View>
<ScrollView style={styles.resultContainer}>
{results.map((result, index) => (
<SearchResultItem key={index} {...result} close={close} />
))}
</ScrollView>
<View style={styles.searchBar}>
<TextInput
style={styles.input}
placeholder="Search pod"
value={input}
onChangeText={setInput}
placeholderTextColor="#666"
/>
{loading && <ActivityIndicator color="#f97316" />}
</View>
<Button title="hi" onPress={handleButton} />
</View>
);
}
function SearchResultItem({
image,
name,
url,
close,
}: SearchResult & {close: () => void}) {
async function save() {
// const r2 = await saveFeed({image, name, url});
// if ('ok' in r2) close();
}
return (
<View style={styles.resultItem}>
<Image source={{uri: image}} style={styles.resultImage} />
<Text style={styles.resultName}>{name}</Text>
<TouchableOpacity style={styles.saveButton} onPress={save}>
<Text style={styles.saveButtonText}>Save</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
navbar: {
padding: 16,
backgroundColor: 'white',
borderBottomWidth: 1,
borderBottomColor: '#e5e5e5',
},
navbarText: {
fontSize: 18,
fontWeight: '600',
},
resultContainer: {
flex: 1,
},
searchBar: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
borderTopWidth: 1,
borderTopColor: 'black',
},
input: {
flex: 1,
height: 40,
marginRight: 8,
padding: 8,
color: 'black',
},
resultItem: {
flexDirection: 'row',
alignItems: 'center',
padding: 24,
backgroundColor: '#d4d4d4',
marginBottom: 1,
},
resultImage: {
width: 32,
height: 32,
marginRight: 16,
},
resultName: {
flex: 1,
color: 'black',
},
saveButton: {
backgroundColor: '#f97316',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 4,
},
saveButtonText: {
color: 'white',
},
});
export default SearchScreen;

View File

View File

4
dialpods/src Normal file
View File

@ -0,0 +1,4 @@
declare module 'react-native-html-parser' {
const content: any;
export default content;
}

View File

@ -1,3 +1,25 @@
{ {
"extends": "@react-native/typescript-config/tsconfig.json" "extends": "@react-native/typescript-config/tsconfig.json",
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
} }