diff --git a/App.tsx b/App.tsx index ed28f4d9..98f84b56 100644 --- a/App.tsx +++ b/App.tsx @@ -25,8 +25,8 @@ SplashScreen.preventAutoHideAsync(); export default function App() { const shouldDelete = false; - const queryClient = new QueryClient(); + useOnAppStateChange(); useChangeScreenOrientation(); @@ -36,7 +36,7 @@ export default function App() { } /** - * supports auto refetch on reconnect for react-query + * support auto refetch on network reconnect for react-query */ onlineManager.setEventListener(setOnline => { return NetInfo.addEventListener(state => { diff --git a/package.json b/package.json index 6cebc253..fc8bffbf 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@react-navigation/stack": "^6.3.20", "@shopify/flash-list": "1.6.4", "@tanstack/react-query": "^5.17.10", + "@types/qs": "^6.9.16", "async-mutex": "^0.4.1", "axios": "^1.7.7", "clsx": "^2.1.0", @@ -67,9 +68,11 @@ "expo-status-bar": "~1.12.1", "expo-updates": "~0.25.27", "express": "^4.21.0", + "lodash": "^4.17.21", "newrelic-react-native-agent": "^1.4.5", "pino": "^8.17.2", "pino-pretty": "^10.3.1", + "qs": "^6.13.0", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.5", @@ -97,7 +100,7 @@ "@testing-library/react-native": "^12.4.3", "@types/express": "^5.0.0", "@types/jest": "^29.5.11", - "@types/lodash": "^4.14.202", + "@types/lodash": "^4.17.9", "@types/react": "~18.2.14", "@types/react-native-vector-icons": "^6.4.18", "@types/react-native-video": "^5.0.19", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7bb21470..5f65258f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: '@tanstack/react-query': specifier: ^5.17.10 version: 5.56.2(react@18.2.0) + '@types/qs': + specifier: ^6.9.16 + version: 6.9.16 async-mutex: specifier: ^0.4.1 version: 0.4.1 @@ -122,6 +125,9 @@ importers: express: specifier: ^4.21.0 version: 4.21.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 newrelic-react-native-agent: specifier: ^1.4.5 version: 1.4.5(encoding@0.1.13)(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0))(react@18.2.0) @@ -131,6 +137,9 @@ importers: pino-pretty: specifier: ^10.3.1 version: 10.3.1 + qs: + specifier: ^6.13.0 + version: 6.13.0 react: specifier: 18.2.0 version: 18.2.0 @@ -208,7 +217,7 @@ importers: specifier: ^29.5.11 version: 29.5.13 '@types/lodash': - specifier: ^4.14.202 + specifier: ^4.17.9 version: 4.17.9 '@types/react': specifier: ~18.2.14 @@ -2306,8 +2315,8 @@ packages: '@types/prop-types@15.7.11': resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} - '@types/qs@6.9.11': - resolution: {integrity: sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==} + '@types/qs@6.9.16': + resolution: {integrity: sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==} '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} @@ -6750,9 +6759,6 @@ packages: object-filter@1.0.2: resolution: {integrity: sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA==} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - object-inspect@1.13.2: resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} engines: {node: '>= 0.4'} @@ -12525,14 +12531,14 @@ snapshots: '@types/express-serve-static-core@4.17.42': dependencies: '@types/node': 22.7.4 - '@types/qs': 6.9.11 + '@types/qs': 6.9.16 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 '@types/express-serve-static-core@5.0.0': dependencies: '@types/node': 22.7.4 - '@types/qs': 6.9.11 + '@types/qs': 6.9.16 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -12540,14 +12546,14 @@ snapshots: dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 4.17.42 - '@types/qs': 6.9.11 + '@types/qs': 6.9.16 '@types/serve-static': 1.15.5 '@types/express@5.0.0': dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 5.0.0 - '@types/qs': 6.9.11 + '@types/qs': 6.9.16 '@types/serve-static': 1.15.5 '@types/glob@7.2.0': @@ -12631,7 +12637,7 @@ snapshots: '@types/prop-types@15.7.11': {} - '@types/qs@6.9.11': {} + '@types/qs@6.9.16': {} '@types/range-parser@1.2.7': {} @@ -14635,7 +14641,7 @@ snapshots: is-string: 1.0.7 is-typed-array: 1.1.12 is-weakref: 1.0.2 - object-inspect: 1.13.1 + object-inspect: 1.13.2 object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 @@ -18353,8 +18359,6 @@ snapshots: object-filter@1.0.2: {} - object-inspect@1.13.1: {} - object-inspect@1.13.2: {} object-is@1.1.6: diff --git a/src/components/CategoryCard.tsx b/src/components/CategoryCard.tsx index c69f2fda..1b19db8b 100644 --- a/src/components/CategoryCard.tsx +++ b/src/components/CategoryCard.tsx @@ -5,7 +5,14 @@ import { import { Category } from '@app/services/twitchService'; import { NavigationProp, useNavigation } from '@react-navigation/native'; import { Image } from 'expo-image'; -import { Pressable, View, Text } from 'react-native'; +import { + Pressable, + View, + Text, + StyleSheet, + ViewStyle, + ImageStyle, +} from 'react-native'; interface Props { category: Category; @@ -15,7 +22,7 @@ const IMAGE_ASPECT_RATIO = 110 / 155; const IMAGE_HEIGHT = 90; const IMAGE_WIDTH = IMAGE_HEIGHT * IMAGE_ASPECT_RATIO; -const CategoryCard = ({ category }: Props) => { +export default function CategoryCard({ category }: Props) { const { navigate } = useNavigation>(); return ( @@ -31,30 +38,15 @@ const CategoryCard = ({ category }: Props) => { }) } > - - + + { ); -}; -export default CategoryCard; +} + +const styles = StyleSheet.create<{ + container: ViewStyle; + image: ImageStyle; + wrapper: ViewStyle; +}>({ + container: { + marginBottom: 17, + marginLeft: 16, + display: 'flex', + flexDirection: 'row', + alignItems: 'flex-start', + justifyContent: 'space-between', + }, + image: { + width: IMAGE_WIDTH, + height: IMAGE_HEIGHT, + }, + wrapper: { + marginRight: 16, + }, +}); diff --git a/src/components/Chat/Chat.tsx b/src/components/Chat.tsx similarity index 98% rename from src/components/Chat/Chat.tsx rename to src/components/Chat.tsx index a0751537..51a86053 100644 --- a/src/components/Chat/Chat.tsx +++ b/src/components/Chat.tsx @@ -16,7 +16,7 @@ interface Message { content: ReactNode; } -const Chat = ({ channels, twitchChannelId }: Props) => { +export default function Chat({ channels, twitchChannelId }: Props) { const { auth, user } = useAuthContext(); const navigation = useNavigation(); const [notice, setNotice] = useState(''); @@ -148,5 +148,4 @@ const Chat = ({ channels, twitchChannelId }: Props) => { ); -}; -export default Chat; +} diff --git a/src/components/Chat/ChatMessage.tsx b/src/components/Chat/ChatMessage.tsx deleted file mode 100644 index 45a7f1de..00000000 --- a/src/components/Chat/ChatMessage.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Text, View } from 'react-native'; - -const ChatMessage = () => { - return ( - - TODO: chat message - - ); -}; -export default ChatMessage; diff --git a/src/components/DismissableKeyboard.tsx b/src/components/DismissableKeyboard.tsx index b7cf05bc..479af162 100644 --- a/src/components/DismissableKeyboard.tsx +++ b/src/components/DismissableKeyboard.tsx @@ -5,11 +5,10 @@ interface Props { children: ReactNode; } -const DismissableKeyboard = ({ children }: Props) => { +export default function DismissableKeyboard({ children }: Props) { return ( {children} ); -}; -export default DismissableKeyboard; +} diff --git a/src/components/Image.tsx b/src/components/Image.tsx index b87f0e7e..0fb674f1 100644 --- a/src/components/Image.tsx +++ b/src/components/Image.tsx @@ -3,14 +3,9 @@ import { Image as ExpoImage, ImageProps } from 'expo-image'; const blurhash = '|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj['; -const Image = ({ placeholder = blurhash, ...props }: ImageProps) => { - return ( - - ); -}; -export default Image; +export default function Image({ + placeholder = blurhash, + ...props +}: ImageProps) { + return ; +} diff --git a/src/components/SafeAreaContainer.tsx b/src/components/SafeAreaContainer.tsx index f9c91c23..b9ca4529 100644 --- a/src/components/SafeAreaContainer.tsx +++ b/src/components/SafeAreaContainer.tsx @@ -5,7 +5,7 @@ interface Props { children: ReactNode; } -const SafeAreaContainer = ({ children }: Props) => { +export default function SafeAreaContainer({ children }: Props) { return ( { {children} ); -}; -export default SafeAreaContainer; +} diff --git a/src/components/Seperator.tsx b/src/components/Seperator.tsx index d2d92d4d..ce1d0456 100644 --- a/src/components/Seperator.tsx +++ b/src/components/Seperator.tsx @@ -5,7 +5,7 @@ interface Props { size?: number; } -const Seperator = ({ color = 'blue', size = 0.5 }: Props) => { +export default function Seperator({ color = 'blue', size = 0.5 }: Props) { return ( { }} /> ); -}; -export default Seperator; +} diff --git a/src/components/SettingsItem.tsx b/src/components/SettingsItem.tsx index 195e1ae2..d9ba404b 100644 --- a/src/components/SettingsItem.tsx +++ b/src/components/SettingsItem.tsx @@ -25,7 +25,7 @@ interface Props { contents: ContentItem[]; } -const SettingsItem = ({ contents }: Props) => { +export default function SettingsItem({ contents }: Props) { return ( Settings @@ -66,7 +66,7 @@ const SettingsItem = ({ contents }: Props) => { /> ); -}; +} const styles = StyleSheet.create({ item: { @@ -90,5 +90,3 @@ const styles = StyleSheet.create({ alignItems: 'center', }, }); - -export default SettingsItem; diff --git a/src/components/StreamCard.tsx b/src/components/StreamCard.tsx index 3caa88ac..ded67a61 100644 --- a/src/components/StreamCard.tsx +++ b/src/components/StreamCard.tsx @@ -16,7 +16,7 @@ interface Props { stream: Stream; } -const StreamCard = ({ stream }: Props) => { +export default function StreamCard({ stream }: Props) { const { navigate } = useNavigation>(); const [broadcasterImage, setBroadcasterImage] = useState(); @@ -175,5 +175,4 @@ const StreamCard = ({ stream }: Props) => { ); -}; -export default StreamCard; +} diff --git a/src/components/Tags.tsx b/src/components/Tags.tsx index 87091eab..4ce2e671 100644 --- a/src/components/Tags.tsx +++ b/src/components/Tags.tsx @@ -4,7 +4,7 @@ interface Props { tags: string[]; } -const Tags = ({ tags }: Props) => { +export default function Tags({ tags }: Props) { return ( { /> ); -}; -export default Tags; +} diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx index f1cd88bb..e3a664d1 100644 --- a/src/context/AuthContext.tsx +++ b/src/context/AuthContext.tsx @@ -175,6 +175,8 @@ export const AuthContextProvider = ({ children }: Props) => { setUser(undefined); await SecureStore.deleteItemAsync(StorageKeys.authToken); + + // TODO: tighten access to axios internals here twitchApi.defaults.headers.common.Authorization = undefined; await getAnonToken(); diff --git a/src/lib/chat/emotes/bttvStore.ts b/src/lib/chat/emotes/bttvStore.ts index 1636c732..267477d1 100644 --- a/src/lib/chat/emotes/bttvStore.ts +++ b/src/lib/chat/emotes/bttvStore.ts @@ -5,6 +5,7 @@ import { GLOBAL_CHANNEL_KEY } from '../config'; const emotes: ChannelEmotes = new Map(); +// TODO: rework from map to object export const getBttvEmotes = async (channelId: string): Promise => { // eslint-disable-next-line no-param-reassign let storedGlobalEmotes = emotes.get(GLOBAL_CHANNEL_KEY); diff --git a/src/navigation/Home/HomeTabsNavigator.tsx b/src/navigation/Home/HomeTabsNavigator.tsx index 313967dc..42bab4cd 100644 --- a/src/navigation/Home/HomeTabsNavigator.tsx +++ b/src/navigation/Home/HomeTabsNavigator.tsx @@ -8,7 +8,7 @@ import { BottomTabHeaderProps } from '@react-navigation/bottom-tabs'; import Feather from 'react-native-vector-icons/Feather'; import { HomeTabs, HomeTabsRoutes } from './HomeTabs'; -const HomeTabsNavigator = () => { +export default function HomeTabsNavigator() { const { auth } = useAuthContext(); return ( { /> ); -}; - -export default HomeTabsNavigator; +} diff --git a/src/navigation/RootNavigator.tsx b/src/navigation/RootNavigator.tsx index 2fb970b5..329f0ebc 100644 --- a/src/navigation/RootNavigator.tsx +++ b/src/navigation/RootNavigator.tsx @@ -7,7 +7,7 @@ import HomeTabsNavigator from './Home/HomeTabsNavigator'; import { RootRoutes, RootStack } from './RootStack'; import StreamNavigator from './Stream/StreamStackNavigator'; -const RootNavigator = () => { +export default function RootNavigator() { return ( { /> ); -}; -export default RootNavigator; +} diff --git a/src/navigation/RootStack.tsx b/src/navigation/RootStack.tsx index 0849a895..31826dcc 100644 --- a/src/navigation/RootStack.tsx +++ b/src/navigation/RootStack.tsx @@ -15,6 +15,7 @@ export enum RootRoutes { Category = 'Category', } +// TODO: rework this into seperate navigators export type RootStackParamList = { [RootRoutes.AuthLoading]: undefined; [RootRoutes.Welcome]: undefined; diff --git a/src/navigation/Stream/StreamStackNavigator.tsx b/src/navigation/Stream/StreamStackNavigator.tsx index ac3a3146..994825e7 100644 --- a/src/navigation/Stream/StreamStackNavigator.tsx +++ b/src/navigation/Stream/StreamStackNavigator.tsx @@ -1,7 +1,7 @@ import LiveStreamScreen from '@app/screens/Stream/LiveStreamScreen'; import { StreamRoutes, StreamStack } from './StreamStack'; -const StreamStackNavigator = () => { +export default function StreamStackNavigator() { return ( { /> ); -}; - -export default StreamStackNavigator; +} diff --git a/src/queries/twitchQueries.ts b/src/queries/twitchQueries.ts index a11bcd8b..11c5e1a1 100644 --- a/src/queries/twitchQueries.ts +++ b/src/queries/twitchQueries.ts @@ -9,19 +9,19 @@ import twitchService, { import { UseQueryOptions } from '@tanstack/react-query'; const twitchQueries = { - getStream(userLogin: string): UseQueryOptions { + getStream(userLogin: string): UseQueryOptions { return { queryKey: ['stream', userLogin], queryFn: () => twitchService.getStream(userLogin), }; }, - getChannel(userId: string): UseQueryOptions { + getChannel(userId: string): UseQueryOptions { return { queryKey: ['channel', userId], queryFn: () => twitchService.getChannel(userId), }; }, - getTopCategories(): UseQueryOptions { + getTopCategories(): UseQueryOptions { return { queryKey: ['topCategories'], queryFn: () => twitchService.getTopCategories(), diff --git a/src/screens/AuthLoadingScreen.tsx b/src/screens/AuthLoadingScreen.tsx index 1868b1d4..1189c209 100644 --- a/src/screens/AuthLoadingScreen.tsx +++ b/src/screens/AuthLoadingScreen.tsx @@ -2,11 +2,14 @@ import { RootStackScreenProps, RootRoutes } from '@app/navigation/RootStack'; import { useEffect } from 'react'; import { Text, View } from 'react-native'; -const AuthLoadingScreen = ({ +export default function AuthLoadingScreen({ navigation, -}: RootStackScreenProps) => { +}: RootStackScreenProps) { const { navigate } = navigation; + // TODO: actually acquire auth tokens here + // TODO: move this to different stack + useEffect(() => { // eslint-disable-next-line react-hooks/exhaustive-deps setTimeout(() => { @@ -28,5 +31,4 @@ const AuthLoadingScreen = ({ ); -}; -export default AuthLoadingScreen; +} diff --git a/src/screens/Category/CategoryScreen.tsx b/src/screens/Category/CategoryScreen.tsx index 16ed8e7c..5c0ac970 100644 --- a/src/screens/Category/CategoryScreen.tsx +++ b/src/screens/Category/CategoryScreen.tsx @@ -8,9 +8,9 @@ import { useQueries } from '@tanstack/react-query'; import { Image } from 'expo-image'; import { FlatList, SafeAreaView, Text, View } from 'react-native'; -const CategoryScreen = ({ +export default function CategoryScreen({ route, -}: CategoryStackScreenProps) => { +}: CategoryStackScreenProps) { const { id } = route.params; const [categoryQueryResult, streamsByCategoryQueryResult] = useQueries({ @@ -39,13 +39,19 @@ const CategoryScreen = ({ flexDirection: 'column', }} > - Loading... + + Loading... + ); } if (isErrorCategory || isErrorStreams) { - return Something went wrong :(; + return ( + + Something went wrong + + ); } return ( @@ -80,5 +86,4 @@ const CategoryScreen = ({ ); -}; -export default CategoryScreen; +} diff --git a/src/screens/FollowingScreen.tsx b/src/screens/FollowingScreen.tsx index 691cfd0f..0a8675b7 100644 --- a/src/screens/FollowingScreen.tsx +++ b/src/screens/FollowingScreen.tsx @@ -3,10 +3,8 @@ import { useAuthContext } from '@app/context/AuthContext'; import twitchQueries from '@app/queries/twitchQueries'; import { Stream } from '@app/services/twitchService'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -// eslint-disable-next-line import/no-named-as-default -import Constants from 'expo-constants'; import { useMemo, useState } from 'react'; -import { FlatList, Platform, View, Text } from 'react-native'; +import { FlatList, View, Text } from 'react-native'; export interface Section { key: string; @@ -14,7 +12,7 @@ export interface Section { isTitle?: boolean; } -const FollowingScreen = () => { +export default function FollowingScreen() { const { user } = useAuthContext(); const [refreshing, setRefreshing] = useState(false); const queryClient = useQueryClient(); @@ -61,7 +59,11 @@ const FollowingScreen = () => { } if (refreshing || isLoading) { - return loading...; + return ( + + loading... + + ); } return ( @@ -81,9 +83,4 @@ const FollowingScreen = () => { ); -}; - -export default FollowingScreen; - -export const statusBarHeight = - Platform.OS === 'android' ? Constants.statusBarHeight : 0; +} diff --git a/src/screens/SearchScreen.tsx b/src/screens/SearchScreen.tsx index 3204a5d1..2827b23e 100644 --- a/src/screens/SearchScreen.tsx +++ b/src/screens/SearchScreen.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import DismissableKeyboard from '@app/components/DismissableKeyboard'; import useDebouncedCallback from '@app/hooks/useDebouncedCallback'; import { HomeTabsParamList } from '@app/navigation/Home/HomeTabs'; @@ -7,6 +6,7 @@ import twitchService, { SearchChannelResponse, } from '@app/services/twitchService'; import elapsedStreamTime from '@app/utils/elapsedStreamTime'; +import { statusBarHeight } from '@app/utils/statusBarHeight'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { NavigationProp, useNavigation } from '@react-navigation/native'; import { useEffect, useRef, useState } from 'react'; @@ -18,17 +18,21 @@ import { View, TextInput, Text, + StyleSheet, + ViewStyle, + ImageStyle, } from 'react-native'; import Feather from 'react-native-vector-icons/Feather'; import Image from '../components/Image'; -import { statusBarHeight } from './FollowingScreen'; interface SearchHistoryItem { date: Date; query: string; } -const SearchScreen = () => { +// TODO: seperate out into a search component + +export default function SearchScreen() { const [query, setQuery] = useState(''); const ref = useRef(null); const [searchResults, setSearchResults] = useState( @@ -83,8 +87,9 @@ const SearchScreen = () => { 'previousSearches', JSON.stringify(newPrevSearches), ); - const res = await AsyncStorage.getItem('previousSearches'); - setSearchHistory(JSON.parse(res as string)); + const previousSearchResults = + await AsyncStorage.getItem('previousSearches'); + setSearchHistory(JSON.parse(previousSearchResults as string)); }, 400); // eslint-disable-next-line no-shadow @@ -95,18 +100,8 @@ const SearchScreen = () => { }; return ( - - + + { { setSearchResults([]); setQuery(''); @@ -143,17 +138,12 @@ const SearchScreen = () => { search(query)} /> )} - + {searchResults.length > 0 && ( <> { { // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // @ts-ignore FIX ME - navigation navigate(StreamRoutes.LiveStream, { screen: StreamRoutes.LiveStream, params: { @@ -177,13 +167,7 @@ const SearchScreen = () => { }, }); }} - style={{ - flexDirection: 'row', - marginBottom: 14, - marginLeft: 14, - alignContent: 'center', - alignItems: 'center', - }} + style={styles.list} > { .replace('{width}', '40') .replace('{height}', '40'), }} - style={{ - borderRadius: 20, - marginRight: 5, - width: 40, - height: 40, - }} + style={styles.avatar} /> {item.display_name} {item.is_live && ( @@ -208,14 +187,7 @@ const SearchScreen = () => { alignItems: 'center', }} > - + {elapsedStreamTime(item.started_at)} )} @@ -226,13 +198,14 @@ const SearchScreen = () => { )} + {/* TODO: create a search history component */} {searchHistory && ( data={searchHistory} renderItem={({ item }) => { return ( { handleQuery(item.query); }} @@ -260,6 +233,58 @@ const SearchScreen = () => { )} ); -}; +} -export default SearchScreen; +const styles = StyleSheet.create<{ + wrapper: ViewStyle; + container: ViewStyle; + icon: ViewStyle; + searchResultsWrapper: ViewStyle; + list: ViewStyle; + avatar: ImageStyle; + elapsed: ViewStyle; + history: ViewStyle; +}>({ + wrapper: { + flex: 1, + paddingTop: statusBarHeight, + }, + container: { + flexDirection: 'row', + padding: 2, + }, + icon: { + alignSelf: 'center', + marginRight: 15, + }, + searchResultsWrapper: { + marginBottom: 14, + marginLeft: 14, + }, + list: { + flexDirection: 'row', + marginBottom: 14, + marginLeft: 14, + alignContent: 'center', + alignItems: 'center', + }, + avatar: { + borderRadius: 20, + marginRight: 1, + width: 40, + height: 40, + }, + elapsed: { + width: 10, + height: 10, + borderRadius: 25, + backgroundColor: 'red', + marginRight: 4, + }, + history: { + flex: 1, + flexDirection: 'row', + marginBottom: 12, + paddingLeft: 14, + }, +}); diff --git a/src/screens/Stream/LiveStreamScreen.tsx b/src/screens/Stream/LiveStreamScreen.tsx index 54f3eead..a9daa879 100644 --- a/src/screens/Stream/LiveStreamScreen.tsx +++ b/src/screens/Stream/LiveStreamScreen.tsx @@ -1,4 +1,4 @@ -import Chat from '@app/components/Chat/Chat'; +import Chat from '@app/components/Chat'; import Image from '@app/components/Image'; import Seperator from '@app/components/Seperator'; import Tags from '@app/components/Tags'; @@ -11,7 +11,7 @@ import { useQueries } from '@tanstack/react-query'; import { Dimensions, SafeAreaView, Text, View } from 'react-native'; import WebView from 'react-native-webview'; -const LiveStreamScreen = () => { +export default function LiveStreamScreen() { const route = useRoute>(); const { landscape } = useIsLandscape(); @@ -78,6 +78,7 @@ const LiveStreamScreen = () => { allowsInlineMediaPlayback /> ) : ( + // user is offline { ); -}; -export default LiveStreamScreen; +} diff --git a/src/screens/Top/Categories/index.tsx b/src/screens/Top/Categories/index.tsx index 4f859d03..3475c038 100644 --- a/src/screens/Top/Categories/index.tsx +++ b/src/screens/Top/Categories/index.tsx @@ -6,7 +6,7 @@ import { useMemo, useState } from 'react'; import { FlatList, Text, View } from 'react-native'; import Feather from 'react-native-vector-icons/Feather'; -const TopCategoriesScreen = () => { +export default function CategoriesSecreen() { const [refreshing, setRefreshing] = useState(false); const queryClient = useQueryClient(); @@ -48,7 +48,7 @@ const TopCategoriesScreen = () => { { keyExtractor={(_item, index) => index.toString()} /> ); -}; -export default TopCategoriesScreen; +} diff --git a/src/screens/Top/Streams/index.tsx b/src/screens/Top/Streams/index.tsx index f411f16d..03a8de7c 100644 --- a/src/screens/Top/Streams/index.tsx +++ b/src/screens/Top/Streams/index.tsx @@ -7,7 +7,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useMemo, useState } from 'react'; import { FlatList, Text, View } from 'react-native'; -const TopStreamsScreen = () => { +export default function TopStreamsScreen() { const [refreshing, _setRefreshing] = useState(false); const _queryClient = useQueryClient(); const topStreamQuery = useMemo(() => twitchQueries.getTopStreams(), []); @@ -55,7 +55,8 @@ const TopStreamsScreen = () => { } return ( - + // eslint-disable-next-line react/jsx-no-useless-fragment + <> {streams && streams.length > 0 && ( data={streams} @@ -63,7 +64,6 @@ const TopStreamsScreen = () => { keyExtractor={item => item.id} /> )} - + ); -}; -export default TopStreamsScreen; +} diff --git a/src/screens/Top/TopScreen.tsx b/src/screens/Top/TopScreen.tsx index 9ae2510c..19e604ea 100644 --- a/src/screens/Top/TopScreen.tsx +++ b/src/screens/Top/TopScreen.tsx @@ -4,12 +4,13 @@ import { TouchableOpacity, useWindowDimensions, View, + StyleSheet, } from 'react-native'; import { TabView, SceneMap } from 'react-native-tab-view'; import TopCategoriesScreen from './Categories'; import TopStreamsScreen from './Streams'; -const TopScreen = () => { +export default function TopScreen() { const layout = useWindowDimensions(); const [index, setIndex] = useState(0); @@ -30,22 +31,15 @@ const TopScreen = () => { onIndexChange={setIndex} initialLayout={{ width: layout.width, height: layout.height }} renderTabBar={props => ( - + {props.navigationState.routes.map((route, i) => ( props.jumpTo(route.key)} - style={{ - marginTop: 5, - borderBottomWidth: 1.95, - padding: 6, - borderBottomColor: index === i ? 'purple' : 'transparent', - }} + style={[ + styles.tab, + { borderBottomColor: index === i ? 'purple' : 'transparent' }, + ]} > {route.title} @@ -54,5 +48,19 @@ const TopScreen = () => { )} /> ); -}; -export default TopScreen; +} + +const styles = StyleSheet.create({ + tabBarContainer: { + flexDirection: 'row', + justifyContent: 'center', + paddingHorizontal: 5, + paddingVertical: 15, + }, + tab: { + marginTop: 2, + borderBottomWidth: 2.15, + padding: 4, + marginHorizontal: 10, + }, +}); diff --git a/src/screens/authentication/AuthLoading.tsx b/src/screens/authentication/AuthLoading.tsx index 87045de8..d0a085d8 100644 --- a/src/screens/authentication/AuthLoading.tsx +++ b/src/screens/authentication/AuthLoading.tsx @@ -2,9 +2,9 @@ import { useEffect } from 'react'; import { Text, View } from 'react-native'; import { RootRoutes, RootStackScreenProps } from '../../navigation/RootStack'; -const AuthLoadingScreen = ({ +export default function AuthLoadingScreen({ navigation, -}: RootStackScreenProps) => { +}: RootStackScreenProps) { const { navigate } = navigation; useEffect(() => { @@ -28,5 +28,4 @@ const AuthLoadingScreen = ({ ); -}; -export default AuthLoadingScreen; +} diff --git a/src/screens/authentication/LoginScreen.tsx b/src/screens/authentication/LoginScreen.tsx index 0982dbf9..c6714bb3 100644 --- a/src/screens/authentication/LoginScreen.tsx +++ b/src/screens/authentication/LoginScreen.tsx @@ -11,7 +11,7 @@ import { View, Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); -const LoginScreen = () => { +export default function LoginScreen() { const { login } = useAuthContext(); const { navigate } = useNavigation>(); @@ -22,7 +22,7 @@ const LoginScreen = () => { revocationEndpoint: 'https://id.twitch.tv/oauth2/revoke', } as const; - const proxyUrl = 'http://localhost:6500/api/proxy'; // TODO: change this to env vars once we release + const proxyUrl = 'http://localhost:6500/api/proxy'; // TODO: don't hardcode this once we're ready to publish const [request, response, promptAsync] = useAuthRequest( { @@ -35,8 +35,8 @@ const LoginScreen = () => { responseType: 'token', usePKCE: true, extraParams: { - // @ts-expect-error - Twitch requires force_verify to be a boolean whereas - // the types are Record + // @ts-expect-error - Twitch requires force_verify to be a boolean but + // the react-native types are Record force_verify: true, }, }, @@ -48,6 +48,7 @@ const LoginScreen = () => { if (response?.type === 'success') { navigate(HomeTabsRoutes.Top); } + // TODO: alert user }; useEffect(() => { @@ -61,15 +62,7 @@ const LoginScreen = () => { return ( -