From 15fd9b7653c4e5dd7463086c227a82cac5b1ec2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Mesnil?= <50322149+theo-mesnil@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:12:56 +0100 Subject: [PATCH] refactor: rework api call (#115) --- package.json | 13 ++- src/api/api.ts | 37 +++++- src/api/discover.ts | 9 +- src/api/genres.ts | 9 +- src/api/logo.ts | 5 +- src/api/movie.ts | 30 +++-- src/api/person.ts | 26 ++--- src/api/search.ts | 9 +- src/api/trending.ts | 5 +- src/api/tv.ts | 26 ++--- src/api/user.ts | 44 +++++++ src/app/(tabs)/_layout.tsx | 15 +++ src/app/(tabs)/user.tsx | 45 ++++++++ src/app/_layout.tsx | 65 ++++++----- src/app/login/webview.tsx | 48 ++++++++ src/components/LoginButton/index.tsx | 13 +++ src/contexts/UserContext.tsx | 66 +++++++++++ src/locales/en-US.json | 1 + src/locales/fr-FR.json | 1 + yarn.lock | 164 +++++++++++++++------------ 20 files changed, 455 insertions(+), 176 deletions(-) create mode 100644 src/api/user.ts create mode 100644 src/app/(tabs)/user.tsx create mode 100644 src/app/login/webview.tsx create mode 100644 src/components/LoginButton/index.tsx create mode 100644 src/contexts/UserContext.tsx diff --git a/package.json b/package.json index 77f9709..7aa40e3 100644 --- a/package.json +++ b/package.json @@ -29,16 +29,17 @@ }, "dependencies": { "@expo-google-fonts/poppins": "^0.2.3", + "@react-native-async-storage/async-storage": "2.1.0", "@react-native-masked-view/masked-view": "0.3.2", - "@react-navigation/bottom-tabs": "^7.0.0", - "@react-navigation/elements": "^1.3.30", - "@react-navigation/native": "^7.0.0", - "@react-navigation/stack": "^6.3.29", + "@react-navigation/bottom-tabs": "^7.2.0", + "@react-navigation/elements": "^2.2.5", + "@react-navigation/native": "^7.0.14", + "@react-navigation/stack": "^7.1.1", "@shopify/flash-list": "1.7.1", "@tanstack/react-query": "^5.62.8", "axios": "^1.7.9", "date-fns": "^4.1.0", - "expo": "~52.0.20", + "expo": "~52.0.23", "expo-blur": "~14.0.1", "expo-constants": "~17.0.3", "expo-dev-client": "~5.0.6", @@ -61,7 +62,7 @@ "react-native-gesture-handler": "~2.20.2", "react-native-linear-gradient": "^2.8.3", "react-native-safe-area-context": "4.12.0", - "react-native-screens": "~4.1.0", + "react-native-screens": "~4.4.0", "react-native-svg": "15.8.0", "react-native-web": "~0.19.13", "react-native-webview": "13.12.5", diff --git a/src/api/api.ts b/src/api/api.ts index e190995..a81cac9 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -1,3 +1,6 @@ +import type { AxiosResponse } from 'axios'; +import axios from 'axios'; + import { LOCALE_I18N } from 'constants/locales'; export const BASE_API_URL = 'https://api.themoviedb.org/3/'; @@ -18,9 +21,19 @@ export type GetApiUrlProps = { query: string; }; -export type GetApiUrlReturn = { +type HttpMethod = 'get' | 'post' | 'put' | 'delete'; + +type GetApiUrlReturn = { + callApi: ( + page?: number, + method?: HttpMethod + ) => Promise>; queryParams?: string[]; - queryUrl: (page?: number) => string; +}; + +export type CallApiProps = { + method?: HttpMethod; + page?: number; }; export function getApi({ params, query }: GetApiUrlProps): GetApiUrlReturn { @@ -37,11 +50,27 @@ export function getApi({ params, query }: GetApiUrlProps): GetApiUrlReturn { const queryUrl = (page?: number) => { const pageParam = page ? `&page=${page}` : ''; - return `${BASE_API_URL}${query}?api_key=${process.env.EXPO_PUBLIC_THEMOVIEDB_API_KEY}&language=${LOCALE_I18N}${pageParam}${queryParamsUrl}`; + return `${BASE_API_URL}${query}?language=${LOCALE_I18N}${pageParam}${queryParamsUrl}`; + }; + + const callApi = ( + page?: number, + method: HttpMethod = 'get', + body?: object + ): Promise> => { + return axios({ + method, + url: queryUrl(page), + headers: { + Accept: 'application/json', + Authorization: `Bearer ${process.env.EXPO_PUBLIC_THEMOVIEDB_API_KEY}` + }, + params: body + }); }; return { queryParams, - queryUrl + callApi }; } diff --git a/src/api/discover.ts b/src/api/discover.ts index 63dcccf..4fa5439 100644 --- a/src/api/discover.ts +++ b/src/api/discover.ts @@ -1,6 +1,5 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import type { AxiosResponse } from 'axios'; -import axios from 'axios'; import { LOCALE } from 'constants/locales'; @@ -30,7 +29,7 @@ export type UseGetDiscoverMovieApiProps = { export function useGetDiscoverTv(props?: UseGetDiscoverTvApiProps) { const { maxPages = 30, params } = props || {}; - const { queryParams, queryUrl } = getApi({ + const { callApi, queryParams } = getApi({ query: 'discover/tv', params }); @@ -39,7 +38,7 @@ export function useGetDiscoverTv(props?: UseGetDiscoverTvApiProps) { queryKey: ['discover', 'tv', ...queryParams, LOCALE], queryFn: async ({ pageParam }) => { const { data }: AxiosResponse = - await axios.get(queryUrl(pageParam)); + await callApi(pageParam); return data; }, initialPageParam: 1, @@ -52,7 +51,7 @@ export function useGetDiscoverTv(props?: UseGetDiscoverTvApiProps) { export function useGetDiscoverMovie(props?: UseGetDiscoverMovieApiProps) { const { maxPages = 30, params } = props || {}; - const { queryParams, queryUrl } = getApi({ + const { callApi, queryParams } = getApi({ query: 'discover/movie', params }); @@ -61,7 +60,7 @@ export function useGetDiscoverMovie(props?: UseGetDiscoverMovieApiProps) { queryKey: ['discover', 'movie', ...queryParams, LOCALE], queryFn: async ({ pageParam }) => { const { data }: AxiosResponse = - await axios.get(queryUrl(pageParam)); + await callApi(pageParam); return data; }, initialPageParam: 1, diff --git a/src/api/genres.ts b/src/api/genres.ts index cf7f6bb..04ec773 100644 --- a/src/api/genres.ts +++ b/src/api/genres.ts @@ -1,6 +1,5 @@ import { useQuery } from '@tanstack/react-query'; import type { AxiosResponse } from 'axios'; -import axios from 'axios'; import { LOCALE } from 'constants/locales'; @@ -13,7 +12,7 @@ export type UseGetGenreTvListApiResponse = paths['/3/genre/tv/list']['get']['responses']['200']['content']['application/json']; export function useGetGenreMovieList() { - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: 'genre/movie/list' }); @@ -21,7 +20,7 @@ export function useGetGenreMovieList() { queryKey: ['genre', 'movie', 'list', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data?.genres; } @@ -29,7 +28,7 @@ export function useGetGenreMovieList() { } export function useGetGenreTvList() { - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: 'genre/tv/list' }); @@ -37,7 +36,7 @@ export function useGetGenreTvList() { queryKey: ['genre', 'tv', 'list', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data.genres; } }); diff --git a/src/api/logo.ts b/src/api/logo.ts index a043ba8..9397b15 100644 --- a/src/api/logo.ts +++ b/src/api/logo.ts @@ -1,7 +1,6 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; import type { AxiosResponse } from 'axios'; -import axios from 'axios'; import type { Locale } from 'constants/locales'; import { LOCALE } from 'constants/locales'; @@ -53,7 +52,7 @@ export function useGetContentLogo( const locales = `${LOCALE},en`; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `${type}/${id}/images`, params: [ { @@ -67,7 +66,7 @@ export function useGetContentLogo( queryKey: [type, id, 'images', 'logo', locales], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return formatImageToLogo(data, LOCALE); }, diff --git a/src/api/movie.ts b/src/api/movie.ts index 819881a..6524b0b 100644 --- a/src/api/movie.ts +++ b/src/api/movie.ts @@ -1,7 +1,6 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; import type { AxiosResponse } from 'axios'; -import axios from 'axios'; import { LOCALE, REGION_CODE } from 'constants/locales'; import type { NetworkId } from 'types/content'; @@ -57,15 +56,14 @@ export type UseGetMovie = UseQueryResult< export function useGetMovie(props?: UseGetMovieApiProps): UseGetMovie { const { id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `movie/${id}` }); return useQuery({ queryKey: ['movie', id, LOCALE], queryFn: async () => { - const { data }: AxiosResponse = - await axios.get(queryUrl()); + const { data }: AxiosResponse = await callApi(); const networkId = getNetworkFromUrl(data.homepage); @@ -102,7 +100,7 @@ export function useGetMovie(props?: UseGetMovieApiProps): UseGetMovie { export function useGetMovieCredits(props?: UseGetMovieEnabledApiProps) { const { enabled, id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `movie/${id}/credits` }); @@ -110,7 +108,7 @@ export function useGetMovieCredits(props?: UseGetMovieEnabledApiProps) { queryKey: ['movie', id, 'credits', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return { cast: data.cast.slice(0, 30) @@ -125,7 +123,7 @@ export function useGetMovieImages(props?: UseGetMovieEnabledApiProps) { const locales = `${LOCALE},en`; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `movie/${id}/images`, params: [ { @@ -139,7 +137,7 @@ export function useGetMovieImages(props?: UseGetMovieEnabledApiProps) { queryKey: ['movie', id, 'images', locales], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; }, @@ -148,7 +146,7 @@ export function useGetMovieImages(props?: UseGetMovieEnabledApiProps) { } export function useGetMovieNowPlaying() { - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: 'movie/now_playing', params: [{ name: 'region', value: REGION_CODE }] }); @@ -157,7 +155,7 @@ export function useGetMovieNowPlaying() { queryKey: ['movies', 'now_playing', REGION_CODE, LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; } @@ -165,7 +163,7 @@ export function useGetMovieNowPlaying() { } export function useGetMovieUpcoming() { - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: 'movie/upcoming', params: [{ name: 'region', value: REGION_CODE }] }); @@ -174,7 +172,7 @@ export function useGetMovieUpcoming() { queryKey: ['movies', 'upcoming', REGION_CODE, LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; } @@ -184,7 +182,7 @@ export function useGetMovieUpcoming() { export function useGetMovieSimilar(props?: UseGetMovieEnabledApiProps) { const { enabled, id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `movie/${id}/similar` }); @@ -192,7 +190,7 @@ export function useGetMovieSimilar(props?: UseGetMovieEnabledApiProps) { queryKey: ['movie', id, 'similar', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; }, @@ -203,7 +201,7 @@ export function useGetMovieSimilar(props?: UseGetMovieEnabledApiProps) { export function useGetMovieVideos(props?: UseGetMovieApiProps) { const { id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `movie/${id}/videos` }); @@ -211,7 +209,7 @@ export function useGetMovieVideos(props?: UseGetMovieApiProps) { queryKey: ['movie', id, 'videos', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; }, diff --git a/src/api/person.ts b/src/api/person.ts index cf51552..16f044a 100644 --- a/src/api/person.ts +++ b/src/api/person.ts @@ -1,7 +1,6 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; import type { AxiosResponse } from 'axios'; -import axios from 'axios'; import { LOCALE } from 'constants/locales'; @@ -45,15 +44,14 @@ export type UseGetPerson = UseQueryResult< export function useGetPerson(props?: UseGetPersonApiProps): UseGetPerson { const { id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `person/${id}` }); return useQuery({ queryKey: ['person', id, LOCALE], queryFn: async () => { - const { data }: AxiosResponse = - await axios.get(queryUrl()); + const { data }: AxiosResponse = await callApi(); return { biography: data.biography, @@ -74,7 +72,7 @@ export function useGetPersonMovieCredits( ) { const { id, isActing } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `person/${id}/movie_credits` }); @@ -82,7 +80,7 @@ export function useGetPersonMovieCredits( queryKey: ['person', id, 'movies', isActing, LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return isActing ? data.cast : data.crew; }, @@ -95,7 +93,7 @@ export function useGetPersonTvCredits( ) { const { id, isActing } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `person/${id}/tv_credits` }); @@ -103,7 +101,7 @@ export function useGetPersonTvCredits( queryKey: ['person', id, 'tv', isActing, LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return isActing ? data.cast : data.crew; }, @@ -116,7 +114,7 @@ export function useGetPersonCredits( ) { const { id, isActing } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `person/${id}/combined_credits` }); @@ -124,7 +122,7 @@ export function useGetPersonCredits( queryKey: ['person', id, 'credits', isActing, LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return isActing ? data.cast : data.crew; }, @@ -137,7 +135,7 @@ export function useGetPersonImages(props?: UseGetPersonApiProps) { const locales = `${LOCALE},en`; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `person/${id}/images`, params: [ { @@ -151,7 +149,7 @@ export function useGetPersonImages(props?: UseGetPersonApiProps) { queryKey: ['person', id, 'tv', 'images', locales], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data.profiles; }, @@ -160,7 +158,7 @@ export function useGetPersonImages(props?: UseGetPersonApiProps) { } export function useGetPersonPopular() { - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: 'person/popular' }); @@ -168,7 +166,7 @@ export function useGetPersonPopular() { queryKey: ['person', 'popular', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; } diff --git a/src/api/search.ts b/src/api/search.ts index 726dcac..9ddd749 100644 --- a/src/api/search.ts +++ b/src/api/search.ts @@ -1,6 +1,5 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import type { AxiosResponse } from 'axios'; -import axios from 'axios'; import { LOCALE } from 'constants/locales'; @@ -22,7 +21,7 @@ export type UseGetSearchApiProps = { export function useGetSearch(props?: UseGetSearchApiProps) { const { maxPages = 30, params } = props || {}; - const { queryParams, queryUrl } = getApi({ + const { callApi, queryParams } = getApi({ query: 'search/multi', params }); @@ -30,9 +29,9 @@ export function useGetSearch(props?: UseGetSearchApiProps) { return useInfiniteQuery({ queryKey: ['search', 'multi', ...queryParams, LOCALE], queryFn: async ({ pageParam }) => { - const { data }: AxiosResponse = await axios.get( - queryUrl(pageParam) - ); + const { data }: AxiosResponse = + await callApi(pageParam); + return data; }, initialPageParam: 1, diff --git a/src/api/trending.ts b/src/api/trending.ts index 5d94d57..3a99608 100644 --- a/src/api/trending.ts +++ b/src/api/trending.ts @@ -1,6 +1,5 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import type { AxiosResponse } from 'axios'; -import axios from 'axios'; import { LOCALE } from 'constants/locales'; @@ -23,7 +22,7 @@ export type UseGetTrendingApiProps = { export function useGetTrending(props?: UseGetTrendingApiProps) { const { maxPages = 30, type = 'all' } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `trending/${type}/day` }); @@ -31,7 +30,7 @@ export function useGetTrending(props?: UseGetTrendingApiProps) { queryKey: ['trending', type, LOCALE], queryFn: async ({ pageParam }) => { const { data }: AxiosResponse = - await axios.get(queryUrl(pageParam)); + await callApi(pageParam); return data; }, initialPageParam: 1, diff --git a/src/api/tv.ts b/src/api/tv.ts index 1ab9e5f..42171b0 100644 --- a/src/api/tv.ts +++ b/src/api/tv.ts @@ -1,7 +1,6 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; import type { AxiosResponse } from 'axios'; -import axios from 'axios'; import { LOCALE } from 'constants/locales'; import type { NetworkId } from 'types/content'; @@ -74,15 +73,14 @@ export type UseGetTv = UseQueryResult< export function useGetTv(props?: UseGetTvApiProps): UseGetTv { const { id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `tv/${id}` }); return useQuery({ queryKey: ['tv', id, LOCALE], queryFn: async () => { - const { data }: AxiosResponse = - await axios.get(queryUrl()); + const { data }: AxiosResponse = await callApi(); const startYear = data.first_air_date ? new Date(data.first_air_date).getFullYear() @@ -130,7 +128,7 @@ export function useGetTv(props?: UseGetTvApiProps): UseGetTv { export function useGetTvSeason(props?: UseGetTvWithSeasonApiProps) { const { id, seasonNumber } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `tv/${id}/season/${seasonNumber}` }); @@ -138,7 +136,7 @@ export function useGetTvSeason(props?: UseGetTvWithSeasonApiProps) { queryKey: ['tv', id, 'season', seasonNumber, LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; }, @@ -149,7 +147,7 @@ export function useGetTvSeason(props?: UseGetTvWithSeasonApiProps) { export function useGetTvCredits(props?: UseGetTvEnabledApiProps) { const { enabled, id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `tv/${id}/aggregate_credits` }); @@ -157,7 +155,7 @@ export function useGetTvCredits(props?: UseGetTvEnabledApiProps) { queryKey: ['tv', id, 'credits', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return { cast: data.cast.slice(0, 30) @@ -172,7 +170,7 @@ export function useGetTvImages(props?: UseGetTvEnabledApiProps) { const locales = `${LOCALE},en`; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `tv/${id}/images`, params: [ { @@ -186,7 +184,7 @@ export function useGetTvImages(props?: UseGetTvEnabledApiProps) { queryKey: ['tv', id, 'images', locales], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; }, @@ -197,7 +195,7 @@ export function useGetTvImages(props?: UseGetTvEnabledApiProps) { export function useGetTvSimilar(props?: UseGetTvEnabledApiProps) { const { enabled, id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `tv/${id}/similar` }); @@ -205,7 +203,7 @@ export function useGetTvSimilar(props?: UseGetTvEnabledApiProps) { queryKey: ['tv', id, 'similar', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; }, @@ -216,7 +214,7 @@ export function useGetTvSimilar(props?: UseGetTvEnabledApiProps) { export function useGetTvVideos(props?: UseGetTvApiProps) { const { id } = props || {}; - const { queryUrl } = getApi({ + const { callApi } = getApi({ query: `tv/${id}/videos` }); @@ -224,7 +222,7 @@ export function useGetTvVideos(props?: UseGetTvApiProps) { queryKey: ['tv', id, 'videos', LOCALE], queryFn: async () => { const { data }: AxiosResponse = - await axios.get(queryUrl()); + await callApi(); return data; }, diff --git a/src/api/user.ts b/src/api/user.ts new file mode 100644 index 0000000..fb86583 --- /dev/null +++ b/src/api/user.ts @@ -0,0 +1,44 @@ +import { useMutation, useQuery } from '@tanstack/react-query'; +import type { AxiosResponse } from 'axios'; + +import { getApi } from './api'; +import type { paths } from './types'; + +export type UseGetRequestTokenApiResponse = + paths['/3/authentication/token/new']['get']['responses']['200']['content']['application/json']; +export type UseGetRequestSessionIdApiResponse = + paths['/3/authentication/session/new']['post']['responses']['200']['content']['application/json']; +export type UseGetRequestSessionIdApiBody = { + request_token: string; +}; + +export function useGetRequestToken() { + const { callApi } = getApi({ + query: 'authentication/token/new' + }); + + return useQuery({ + queryKey: ['authentication', 'token', 'new'], + queryFn: async () => { + const { data }: AxiosResponse = + await callApi(); + + return data; + } + }); +} + +export function useGetAuthenticationSession() { + const { callApi } = getApi({ + query: 'authentication/session/new' + }); + + return useMutation({ + mutationFn: async (body: UseGetRequestSessionIdApiBody) => { + const { data }: AxiosResponse = + await callApi(1, 'get', body); + + return data?.session_id; + } + }); +} diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index d072b2c..1ba5f3c 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -116,6 +116,21 @@ export default function Layout() { }) }} /> + + tabBarIcon({ + ...props, + icon: EyeIcon, + iconFocused: EyeFillIcon + }) + }} + /> ); } diff --git a/src/app/(tabs)/user.tsx b/src/app/(tabs)/user.tsx new file mode 100644 index 0000000..b9a6d37 --- /dev/null +++ b/src/app/(tabs)/user.tsx @@ -0,0 +1,45 @@ +import { useNavigation } from 'expo-router'; +import * as React from 'react'; +import { Animated } from 'react-native'; + +import { GradientHeader } from 'components/GradientHeader'; +import { Header } from 'components/Header'; +import { LoginButton } from 'components/LoginButton'; +import { Text } from 'components/Text'; +import { useSafeHeights } from 'constants/useSafeHeights'; +import { useUser } from 'contexts/UserContext'; +import { BasicLayout } from 'layouts/Basic'; +import type { HeaderOptions } from 'types/navigation'; + +export default function User() { + const [scrollYPosition, getScrollYPosition] = React.useState( + new Animated.Value(0) + ); + const { containerStyle } = useSafeHeights(); + const navigation = useNavigation(); + const { isLogged } = useUser(); + + const HeaderComponent = React.useCallback( + ({ options: { title } }: HeaderOptions) => ( +
+ ), + [scrollYPosition] + ); + + React.useEffect(() => { + navigation.setOptions({ + header: HeaderComponent + }); + }, [HeaderComponent, navigation]); + + return ( + + + {`isLogged: ${isLogged}`} + + + ); +} diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index 626fa75..113b825 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -13,6 +13,7 @@ import { IntlMessages } from 'locales'; import * as React from 'react'; import { StatusBar, StyleSheet, View } from 'react-native'; +import { UserProvider } from 'contexts/UserContext'; import { theme } from 'theme'; SplashScreen.preventAutoHideAsync(); @@ -51,34 +52,42 @@ export default function Layout() { /> - - null }}> - - - - - - + + + null }}> + + + + + + + + diff --git a/src/app/login/webview.tsx b/src/app/login/webview.tsx new file mode 100644 index 0000000..25bd7e9 --- /dev/null +++ b/src/app/login/webview.tsx @@ -0,0 +1,48 @@ +import { useRouter } from 'expo-router'; +import { useEffect, useState } from 'react'; +import { WebView } from 'react-native-webview'; + +import { useGetRequestToken } from 'api/user'; + +export default function TMDBWebView() { + const [url, setUrl] = useState(''); + const router = useRouter(); + const { data, isLoading } = useGetRequestToken(); + + const onNavigationChange = (navState: any) => { + const { url } = navState; + setUrl(url); + }; + + useEffect(() => { + const closeWebview = () => { + if (url?.includes('allow')) { + alert('Success'); + router.back(); + } else if (url?.includes('deny')) { + alert('Denied'); + router.back(); + } + }; + + closeWebview(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [url]); + + if (isLoading) { + return null; + } + + const uri = `https://www.themoviedb.org/authenticate/${data.request_token}`; + + console.log(uri); + + return ( + + ); +} diff --git a/src/components/LoginButton/index.tsx b/src/components/LoginButton/index.tsx new file mode 100644 index 0000000..0a423db --- /dev/null +++ b/src/components/LoginButton/index.tsx @@ -0,0 +1,13 @@ +import { useRouter } from 'expo-router'; + +import { Button } from 'components/Button'; + +export const LoginButton = () => { + const router = useRouter(); + + function handleClick() { + router.push('/login/webview'); + } + + return ; +}; diff --git a/src/contexts/UserContext.tsx b/src/contexts/UserContext.tsx new file mode 100644 index 0000000..a8019e1 --- /dev/null +++ b/src/contexts/UserContext.tsx @@ -0,0 +1,66 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import type { ReactNode } from 'react'; +import React, { createContext, useContext } from 'react'; + +import type { paths } from '../api/types'; + +type User = + paths['/3/account/{account_id}']['get']['responses']['200']['content']['application/json']; + +interface UserContextType { + isLoading: boolean; + isLogged: boolean; + login: () => void; + logout: () => void; + user: User | null; +} + +const UserContext = createContext(undefined); + +const setSessionId = async (value?: string) => { + try { + if (value) { + await AsyncStorage.setItem('session-id', value); + } else { + await AsyncStorage.removeItem('session-id'); + } + } catch (e) { + // saving error + } +}; + +const getSessionId = async () => { + try { + return await AsyncStorage.getItem('session-id'); + } catch (e) { + // error reading value + } +}; + +export function UserProvider({ children }: { children: ReactNode }) { + let user = null; + let isLoading = false; + let isLogged = false; + + const login = async () => { + alert('Login'); + }; + + const logout = () => { + isLogged = false; + user = null; + setSessionId(); + }; + + return ( + + {children} + + ); +} + +export function useUser() { + const context = useContext(UserContext); + + return context; +} diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 4e0151a..b821fbc 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -10,6 +10,7 @@ "CBwLG1": "Series on", "DOPilz": "Pictures", "EhtHdK": "y", + "EwRIOm": "User", "FQ0kXF": "season", "Fip4H8": "Images", "I5NMJ8": "More", diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index ee2f529..a64bafa 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -10,6 +10,7 @@ "CBwLG1": "Séries sur", "DOPilz": "Photos", "EhtHdK": " ans", + "EwRIOm": "", "FQ0kXF": "saison", "Fip4H8": "Images", "I5NMJ8": "Voir plus", diff --git a/yarn.lock b/yarn.lock index aae0dbc..b273cd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1927,10 +1927,10 @@ dotenv-expand "~11.0.6" getenv "^1.0.0" -"@expo/fingerprint@0.11.4": - version "0.11.4" - resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.11.4.tgz#f40bbc784e10a065b783091e0d060a7428d41a7c" - integrity sha512-FfcvHjrWjOJ17wiMfr1iQ1YDyjlj8qfxG+GDce0khrjNSkzRjVdCOIFsMvfVSBPnOPX5NuZlgMRvMkcPUtGClA== +"@expo/fingerprint@0.11.6": + version "0.11.6" + resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.11.6.tgz#7e01d436c1610c7dc1fc6898b2d90adaa19a39a0" + integrity sha512-hlVIfMEJYZIqIFMjeGRN5GhK/h6vJ3M4QVc1ZD8F0Bh7gMeI+jZkEyZdL5XT29jergQrksP638e2qFwgrGTw/w== dependencies: "@expo/spawn-async" "^1.7.2" arg "^5.0.2" @@ -2596,6 +2596,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.0" +"@react-native-async-storage/async-storage@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-2.1.0.tgz#84ca82af320c16d3d8e617508ea523fe786b6781" + integrity sha512-eAGQGPTAuFNEoIQSB5j2Jh1zm5NPyBRTfjRMfCN0W1OakC5WIB5vsDyIQhUweKN9XOE2/V07lqTMGsL0dGXNkA== + dependencies: + merge-options "^3.0.4" + "@react-native-masked-view/masked-view@0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@react-native-masked-view/masked-view/-/masked-view-0.3.2.tgz#7064533a573e3539ec912f59c1f457371bf49dd9" @@ -2778,73 +2785,67 @@ invariant "^2.2.4" nullthrows "^1.1.1" -"@react-navigation/bottom-tabs@^7.0.0": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.1.2.tgz#07fa281b3d5a53bdcbb1611164786043c17cc88b" - integrity sha512-ro+Z9O+uCj/Ag+97z5ywUfapOrboH8nxCG2JZXXiTX33fy846VoD7782aPr1Z4dS9ytD2ZyWlV0Yua/OJ8a8sQ== +"@react-navigation/bottom-tabs@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.2.0.tgz#5b336b823226647a263b4fe743655462796b6aaf" + integrity sha512-1LxjgnbPyFINyf9Qr5d1YE0pYhuJayg5TCIIFQmbcX4PRhX7FKUXV7cX8OzrKXEdZi/UE/VNXugtozPAR9zgvA== dependencies: - "@react-navigation/elements" "^2.2.3" + "@react-navigation/elements" "^2.2.5" color "^4.2.3" -"@react-navigation/core@^7.2.2": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.2.2.tgz#2ef5d0527c2333612f7096fc7fbb5d1ad41296e6" - integrity sha512-jAcwLkcNer8//udCsCftkQfa6/jEiv4IdF/gjXTqrBiNGvDUGUqxjr3J2LR4vATz/sFxAOjimEYctgNRnpk6+Q== +"@react-navigation/core@^7.3.1": + version "7.3.1" + resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.3.1.tgz#c6d4857fa2dd321d12ca87e200478c38c420f157" + integrity sha512-S3KCGvNsoqVk8ErAtQI2EAhg9185lahF5OY01ofrrD4Ij/uk3QEHHjoGQhR5l5DXSCSKr1JbMQA7MEKMsBiWZA== dependencies: - "@react-navigation/routers" "^7.1.1" + "@react-navigation/routers" "^7.1.2" escape-string-regexp "^4.0.0" - nanoid "3.3.7" + nanoid "3.3.8" query-string "^7.1.3" react-is "^18.2.0" use-latest-callback "^0.2.1" use-sync-external-store "^1.2.2" -"@react-navigation/elements@^1.3.30": - version "1.3.30" - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.30.tgz#a81371f599af1070b12014f05d6c09b1a611fd9a" - integrity sha512-plhc8UvCZs0UkV+sI+3bisIyn78wz9O/BiWZXpounu72k/R/Sj5PuZYFJ1fi6psvriUveMCGh4LeZckAZu2qiQ== - -"@react-navigation/elements@^2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.2.3.tgz#a7d7ac2deecea04929335f365b88ceae23f0a2b5" - integrity sha512-xelp+OCIfyKe1d3DOPuHybwVtYQt85njz38WOVteZIyJf7Xmp4HehELz6bL2yS7C3gVHXPPoempHuaat+te3Jg== +"@react-navigation/elements@^2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.2.5.tgz#0e2ca76e2003e96b417a3d7c2829bf1afd69193f" + integrity sha512-sDhE+W14P7MNWLMxXg1MEVXwkLUpMZJGflE6nQNzLmolJQIHgcia0Mrm8uRa3bQovhxYu1UzEojLZ+caoZt7Fg== dependencies: color "^4.2.3" -"@react-navigation/native-stack@^7.0.0": - version "7.1.13" - resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.1.13.tgz#da05de2136f1a44b4190913db706a6e8bcdc2ee1" - integrity sha512-0myuifLuDzKxM4qF4dHZ/6HQ7qjocN4ic1NdzgrYbXMXaGsv85qU5nCecrKiFLvcMwy4en6X1UTmlAFPTLaM0A== +"@react-navigation/native-stack@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.2.0.tgz#8aa489f88d662b3543a931b9cb934bb2e09a4893" + integrity sha512-mw7Nq9qQrGsmJmCTwIIWB7yY/3tWYXvQswx+HJScGAadIjemvytJXm1fcl3+YZ9T9Ym0aERcVe5kDs+ny3X4vA== dependencies: - "@react-navigation/elements" "^2.2.3" + "@react-navigation/elements" "^2.2.5" warn-once "^0.1.1" -"@react-navigation/native@^7.0.0": - version "7.0.12" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.0.12.tgz#e41e0e3cff841689b722e4d286c3212663a49855" - integrity sha512-FRn0mts152pb8rM4DChb7v71QhSmdt6MdVEX+5dz88vtLdHvLKm9i/8IiBU9oCGN9yGlfWMyd16xMPAa5PTskQ== +"@react-navigation/native@^7.0.14": + version "7.0.14" + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.0.14.tgz#b3ee2879038dcf0523d26516af88d3adc549ce5e" + integrity sha512-Gi6lLw4VOGSWAhmUdJOMauOKGK51/YA1CprjXm91sNfgERWvznqEMw8QmUQx9SEqYfi0LfZhbzpMst09SJ00lw== dependencies: - "@react-navigation/core" "^7.2.2" + "@react-navigation/core" "^7.3.1" escape-string-regexp "^4.0.0" fast-deep-equal "^3.1.3" - nanoid "3.3.7" + nanoid "3.3.8" use-latest-callback "^0.2.1" -"@react-navigation/routers@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.1.1.tgz#2544453bdeecc77d01099173a1eb102b39eca92f" - integrity sha512-OycWRj95p+/zENl9HU6tvvT6IUuxgVJirgsA0W9rQn3RC+9Hb0UVYA0+8avdt+WpMoWdrvwTxTXneB5mjYzHrw== +"@react-navigation/routers@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.1.2.tgz#647a63e383673de0c4fc10c64a17f551d5da0a17" + integrity sha512-emdEjpVDK8zbiu2GChC8oYIAub9i/OpNuQJekVsbyFCBz4/TzaBzms38Q53YaNhdIFNmiYLfHv/Y1Ub7KYfm3w== dependencies: - nanoid "3.3.7" + nanoid "3.3.8" -"@react-navigation/stack@^6.3.29": - version "6.3.29" - resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-6.3.29.tgz#b03b2f2baa36c06e4c9e8c7da80d62f83ad0b835" - integrity sha512-tzlGkoRgB6P7vgw7rHuWo3TL7Gzu6xh5LMf+zSdCuEiKp/qASzxYfnTEr9tOLbVs/gf+qeukEDheCSAJKVpBXw== +"@react-navigation/stack@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-7.1.1.tgz#0ae8902c5d72573a3c178b3fd19a65cc1971ad16" + integrity sha512-CBTKQlIkELp05zRiTAv5Pa7OMuCpKyBXcdB3PGMN2Mm55/5MkDsA1IaZorp/6TsVCdllITD6aTbGX/HA/88A6w== dependencies: - "@react-navigation/elements" "^1.3.30" + "@react-navigation/elements" "^2.2.5" color "^4.2.3" - warn-once "^0.1.0" "@remix-run/node@^2.12.0": version "2.15.0" @@ -5534,20 +5535,20 @@ expo-constants@~17.0.0, expo-constants@~17.0.3: "@expo/env" "~0.4.0" expo-dev-client@~5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/expo-dev-client/-/expo-dev-client-5.0.6.tgz#9c8d10bcb009668737c715a79ce4b32a45937ceb" - integrity sha512-UMrS21EQ5TMooQgLp+0YrH+UKpxvO+SzkrAdWyVTrjUJ1Qi1yQCbtcErXkYbAdLbItSF3H04fVvd0lzg0KL3Wg== + version "5.0.7" + resolved "https://registry.yarnpkg.com/expo-dev-client/-/expo-dev-client-5.0.7.tgz#7902cb0443224f6fbc106e88619fd4987e5f3d1c" + integrity sha512-Ui938ZTSBHqQW9W5LY8Qb8ezf03YqX2LeYoQBh+p3Oen9XAZ67qd0NiQtssJd5GvbS5C5JTtF0OQMMhJAtiIvA== dependencies: - expo-dev-launcher "5.0.19" + expo-dev-launcher "5.0.21" expo-dev-menu "6.0.14" expo-dev-menu-interface "1.9.2" expo-manifests "~0.15.0" expo-updates-interface "~1.0.0" -expo-dev-launcher@5.0.19: - version "5.0.19" - resolved "https://registry.yarnpkg.com/expo-dev-launcher/-/expo-dev-launcher-5.0.19.tgz#9ad4761720557ab9e7ffb83500928a0453f45c75" - integrity sha512-3zlOxpWVuV1RDe8OKoejL7XhzidHvtg3iEw564dhXePH5J+Ha9Owkqqiz7A27iqQrENjU/XWBtOxHszj+OBk4Q== +expo-dev-launcher@5.0.21: + version "5.0.21" + resolved "https://registry.yarnpkg.com/expo-dev-launcher/-/expo-dev-launcher-5.0.21.tgz#22c5e24929e2ee769552644615ea6ccc1fb23077" + integrity sha512-ZH/PB6COzxQMl9vvJB84hLNqU2X4gcoj+P6QgpWoANdoMLjl9Cm4u14XlEghZ7W3EHkesZUHl8dT+p/5QIiaNA== dependencies: ajv "8.11.0" expo-dev-menu "6.0.14" @@ -5645,16 +5646,16 @@ expo-modules-core@2.1.2: invariant "^2.2.4" expo-router@~4.0.14: - version "4.0.14" - resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-4.0.14.tgz#75f681c6bacd90adcd7c4f85a1ac0dcb0f4f37f3" - integrity sha512-/iA4+W7fjX1NNk0yVaYyQ+oz0MfOTk8K6RNHCdLViH58Jl1/ZH08g0VDhVCtIBCJPGTu+W5SoYtyvj7tNaR0uA== + version "4.0.15" + resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-4.0.15.tgz#bdc00b90bd60ab5ccb35ae51f31dcbc96c179949" + integrity sha512-5MDy7iVzgi8lheRunsR4lTKEKTNqukC3uYSWhY370Nakdd+E/Woz+Vw1M67/KrnvefTV5hF97bNUUMzY+fyojw== dependencies: "@expo/metro-runtime" "4.0.0" "@expo/server" "^0.5.0" "@radix-ui/react-slot" "1.0.1" - "@react-navigation/bottom-tabs" "^7.0.0" - "@react-navigation/native" "^7.0.0" - "@react-navigation/native-stack" "^7.0.0" + "@react-navigation/bottom-tabs" "^7.2.0" + "@react-navigation/native" "^7.0.14" + "@react-navigation/native-stack" "^7.2.0" client-only "^0.0.1" react-helmet-async "^1.3.0" react-native-helmet-async "2.0.4" @@ -5705,16 +5706,16 @@ expo-updates@~0.26.10: ignore "^5.3.1" resolve-from "^5.0.0" -expo@~52.0.20: - version "52.0.21" - resolved "https://registry.yarnpkg.com/expo/-/expo-52.0.21.tgz#49cf9342b0d914565f2d3f65579aa5c7f67bef55" - integrity sha512-+yYIvUczlM7zvqjwCtCH4OtLaX0F1/35oAlNmPK5lV1RIVVRjKfBeZ8kK+jNNUwY9gZnrk8oeN6E2qcqohyOcw== +expo@~52.0.23: + version "52.0.23" + resolved "https://registry.yarnpkg.com/expo/-/expo-52.0.23.tgz#68b79da2206afdce9c68f45f519f812bab9a2cfe" + integrity sha512-DR36Vkpz/ZLPci4fxDBG/pLk26nGK63vcZ+X4RZJfNBzi14DXZ939loP8YzWGV78Qp23qdPINczpo2727tqLxg== dependencies: "@babel/runtime" "^7.20.0" "@expo/cli" "0.22.7" "@expo/config" "~10.0.6" "@expo/config-plugins" "~9.0.12" - "@expo/fingerprint" "0.11.4" + "@expo/fingerprint" "0.11.6" "@expo/metro-config" "0.19.8" "@expo/vector-icons" "^14.0.0" babel-preset-expo "~12.0.4" @@ -6674,6 +6675,11 @@ is-path-inside@^3.0.2, is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -7734,6 +7740,13 @@ memoize-one@^6.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== +merge-options@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-3.0.4.tgz#84709c2aa2a4b24c1981f66c179fe5565cc6dbb7" + integrity sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ== + dependencies: + is-plain-obj "^2.1.0" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -8115,7 +8128,12 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nanoid@3.3.7, nanoid@^3.3.7: +nanoid@3.3.8: + version "3.3.8" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + +nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== @@ -8930,10 +8948,10 @@ react-native-safe-area-context@4.12.0: resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.12.0.tgz#17868522a55bbc6757418c94a1b4abdda6b045d9" integrity sha512-ukk5PxcF4p3yu6qMZcmeiZgowhb5AsKRnil54YFUUAXVIS7PJcMHGGC+q44fCiBg44/1AJk5njGMez1m9H0BVQ== -react-native-screens@~4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.1.0.tgz#3f703a1bb4fd65fd7963e616ddc373a97809c254" - integrity sha512-tCBwe7fRMpoi/nIgZxE86N8b2SH8d5PlfGaQO8lgqlXqIyvwqm3u1HJCaA0tsacPyzhW7vVtRfQyq9e1j0S2gA== +react-native-screens@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.4.0.tgz#3fcbcdf1bbb1be2736b10d43edc3d4e69c37b5aa" + integrity sha512-c7zc7Zwjty6/pGyuuvh9gK3YBYqHPOxrhXfG1lF4gHlojQSmIx2piNbNaV+Uykj+RDTmFXK0e/hA+fucw/Qozg== dependencies: react-freeze "^1.0.0" warn-once "^0.1.0" @@ -10446,9 +10464,9 @@ use-latest-callback@^0.2.1: integrity sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ== use-sync-external-store@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" - integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + version "1.4.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc" + integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw== util-deprecate@~1.0.1: version "1.0.2"