mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-05-31 11:08:26 +01:00
wip
This commit is contained in:
@@ -5,7 +5,6 @@ import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionLi
|
|||||||
import { Loader } from "@/components/Loader";
|
import { Loader } from "@/components/Loader";
|
||||||
import { MediaListSection } from "@/components/medialists/MediaListSection";
|
import { MediaListSection } from "@/components/medialists/MediaListSection";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import {
|
import {
|
||||||
@@ -30,7 +29,6 @@ export default function index() {
|
|||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [settings, _] = useSettings();
|
|
||||||
|
|
||||||
const [isConnected, setIsConnected] = useState<boolean | null>(null);
|
const [isConnected, setIsConnected] = useState<boolean | null>(null);
|
||||||
|
|
||||||
@@ -171,11 +169,7 @@ export default function index() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { data: mediaListCollections } = useQuery({
|
const { data: mediaListCollections } = useQuery({
|
||||||
queryKey: [
|
queryKey: ["mediaListCollections-home", user?.Id],
|
||||||
"mediaListCollections-home",
|
|
||||||
user?.Id,
|
|
||||||
settings?.mediaListCollectionIds,
|
|
||||||
],
|
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!api || !user?.Id) return [];
|
if (!api || !user?.Id) return [];
|
||||||
|
|
||||||
@@ -187,16 +181,9 @@ export default function index() {
|
|||||||
includeItemTypes: ["BoxSet"],
|
includeItemTypes: ["BoxSet"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const ids =
|
return [];
|
||||||
response.data.Items?.filter(
|
|
||||||
(c) =>
|
|
||||||
c.Name !== "cf_carousel" &&
|
|
||||||
settings?.mediaListCollectionIds?.includes(c.Id!)
|
|
||||||
) ?? [];
|
|
||||||
|
|
||||||
return ids;
|
|
||||||
},
|
},
|
||||||
enabled: !!api && !!user?.Id && settings?.usePopularPlugin === true,
|
enabled: !!api && !!user?.Id && false,
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -263,10 +250,6 @@ export default function index() {
|
|||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{mediaListCollections?.map((ml) => (
|
|
||||||
<MediaListSection key={ml.Id} collection={ml} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
<ScrollingCollectionList
|
<ScrollingCollectionList
|
||||||
title="Recently Added in Movies"
|
title="Recently Added in Movies"
|
||||||
data={recentlyAddedInMovies}
|
data={recentlyAddedInMovies}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { clearLogs, readFromLog } from "@/utils/log";
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { ScrollView, View } from "react-native";
|
import { ScrollView, View } from "react-native";
|
||||||
import { SettingToggles } from "@/components/settings/SettingToggles";
|
|
||||||
|
|
||||||
export default function settings() {
|
export default function settings() {
|
||||||
const { logout } = useJellyfin();
|
const { logout } = useJellyfin();
|
||||||
@@ -30,8 +29,6 @@ export default function settings() {
|
|||||||
<ListItem title="Server" subTitle={api?.basePath} />
|
<ListItem title="Server" subTitle={api?.basePath} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<SettingToggles />
|
|
||||||
|
|
||||||
<View className="flex flex-col space-y-2">
|
<View className="flex flex-col space-y-2">
|
||||||
<Button color="black" onPress={logout}>
|
<Button color="black" onPress={logout}>
|
||||||
Log out
|
Log out
|
||||||
|
|||||||
@@ -45,8 +45,6 @@ export default function RootLayout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Layout() {
|
function Layout() {
|
||||||
const [settings, updateSettings] = useSettings();
|
|
||||||
|
|
||||||
useKeepAwake();
|
useKeepAwake();
|
||||||
|
|
||||||
const queryClientRef = useRef<QueryClient>(
|
const queryClientRef = useRef<QueryClient>(
|
||||||
|
|||||||
39
components/Chromecast.tsx
Normal file
39
components/Chromecast.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import {
|
||||||
|
CastButton,
|
||||||
|
useCastDevice,
|
||||||
|
useDevices,
|
||||||
|
useRemoteMediaClient,
|
||||||
|
} from "react-native-google-cast";
|
||||||
|
import GoogleCast from "react-native-google-cast";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Chromecast: React.FC<Props> = ({ width = 48, height = 48 }) => {
|
||||||
|
const client = useRemoteMediaClient();
|
||||||
|
const castDevice = useCastDevice();
|
||||||
|
const devices = useDevices();
|
||||||
|
const sessionManager = GoogleCast.getSessionManager();
|
||||||
|
const discoveryManager = GoogleCast.getDiscoveryManager();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (!discoveryManager) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await discoveryManager.startDiscovery();
|
||||||
|
})();
|
||||||
|
}, [client, devices, castDevice, sessionManager, discoveryManager]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="rounded h-10 aspect-square flex items-center justify-center">
|
||||||
|
<CastButton style={{ tintColor: "white", height, width }} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
"@gorhom/bottom-sheet": "^4",
|
"@gorhom/bottom-sheet": "^4",
|
||||||
"@jellyfin/sdk": "^0.10.0",
|
"@jellyfin/sdk": "^0.10.0",
|
||||||
"@kesha-antonov/react-native-background-downloader": "^3.2.0",
|
"@kesha-antonov/react-native-background-downloader": "^3.2.0",
|
||||||
"@react-native-async-storage/async-storage": "1.23.1",
|
|
||||||
"@react-native-community/netinfo": "11.3.1",
|
"@react-native-community/netinfo": "11.3.1",
|
||||||
"@react-native-menu/menu": "^1.1.2",
|
"@react-native-menu/menu": "^1.1.2",
|
||||||
"@react-native-tvos/config-tv": "^0.0.10",
|
"@react-native-tvos/config-tv": "^0.0.10",
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { Api, Jellyfin } from "@jellyfin/sdk";
|
import { Api, Jellyfin } from "@jellyfin/sdk";
|
||||||
import { UserDto } from "@jellyfin/sdk/lib/generated-client/models";
|
import { UserDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
||||||
import { isLoaded } from "expo-font";
|
|
||||||
import { router, useSegments } from "expo-router";
|
import { router, useSegments } from "expo-router";
|
||||||
import { atom, useAtom } from "jotai";
|
import { atom, useAtom } from "jotai";
|
||||||
import React, {
|
import React, {
|
||||||
@@ -31,15 +29,14 @@ interface JellyfinContextValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const JellyfinContext = createContext<JellyfinContextValue | undefined>(
|
const JellyfinContext = createContext<JellyfinContextValue | undefined>(
|
||||||
undefined,
|
undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
const getOrSetDeviceId = async () => {
|
const getOrSetDeviceId = async () => {
|
||||||
let deviceId = await AsyncStorage.getItem("deviceId");
|
let deviceId = null;
|
||||||
|
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
deviceId = uuid.v4() as string;
|
deviceId = uuid.v4() as string;
|
||||||
await AsyncStorage.setItem("deviceId", deviceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return deviceId;
|
return deviceId;
|
||||||
@@ -58,7 +55,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
new Jellyfin({
|
new Jellyfin({
|
||||||
clientInfo: { name: "Streamyfin", version: "0.6.1" },
|
clientInfo: { name: "Streamyfin", version: "0.6.1" },
|
||||||
deviceInfo: { name: Platform.OS === "ios" ? "iOS" : "Android", id },
|
deviceInfo: { name: Platform.OS === "ios" ? "iOS" : "Android", id },
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -67,8 +64,9 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
const [user, setUser] = useAtom(userAtom);
|
const [user, setUser] = useAtom(userAtom);
|
||||||
|
|
||||||
const discoverServers = async (url: string): Promise<Server[]> => {
|
const discoverServers = async (url: string): Promise<Server[]> => {
|
||||||
const servers =
|
const servers = await jellyfin?.discovery.getRecommendedServerCandidates(
|
||||||
await jellyfin?.discovery.getRecommendedServerCandidates(url);
|
url
|
||||||
|
);
|
||||||
return servers?.map((server) => ({ address: server.address })) || [];
|
return servers?.map((server) => ({ address: server.address })) || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,7 +77,6 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
if (!apiInstance?.basePath) throw new Error("Failed to connect");
|
if (!apiInstance?.basePath) throw new Error("Failed to connect");
|
||||||
|
|
||||||
setApi(apiInstance);
|
setApi(apiInstance);
|
||||||
await AsyncStorage.setItem("serverUrl", server.address);
|
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("Failed to set server:", error);
|
console.error("Failed to set server:", error);
|
||||||
@@ -88,7 +85,6 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
|
|
||||||
const removeServerMutation = useMutation({
|
const removeServerMutation = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
await AsyncStorage.removeItem("serverUrl");
|
|
||||||
setApi(null);
|
setApi(null);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@@ -110,9 +106,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
|
|
||||||
if (auth.data.AccessToken && auth.data.User) {
|
if (auth.data.AccessToken && auth.data.User) {
|
||||||
setUser(auth.data.User);
|
setUser(auth.data.User);
|
||||||
await AsyncStorage.setItem("user", JSON.stringify(auth.data.User));
|
|
||||||
setApi(jellyfin.createApi(api?.basePath, auth.data?.AccessToken));
|
setApi(jellyfin.createApi(api?.basePath, auth.data?.AccessToken));
|
||||||
await AsyncStorage.setItem("token", auth.data?.AccessToken);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid username or password");
|
throw new Error("Invalid username or password");
|
||||||
}
|
}
|
||||||
@@ -124,7 +118,6 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
|
|
||||||
const logoutMutation = useMutation({
|
const logoutMutation = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
await AsyncStorage.removeItem("token");
|
|
||||||
setUser(null);
|
setUser(null);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@@ -132,36 +125,6 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { isLoading, isFetching } = useQuery({
|
|
||||||
queryKey: [
|
|
||||||
"initializeJellyfin",
|
|
||||||
user?.Id,
|
|
||||||
api?.basePath,
|
|
||||||
jellyfin?.clientInfo,
|
|
||||||
],
|
|
||||||
queryFn: async () => {
|
|
||||||
try {
|
|
||||||
const token = await AsyncStorage.getItem("token");
|
|
||||||
const serverUrl = await AsyncStorage.getItem("serverUrl");
|
|
||||||
const user = JSON.parse(
|
|
||||||
(await AsyncStorage.getItem("user")) as string,
|
|
||||||
) as UserDto;
|
|
||||||
|
|
||||||
if (serverUrl && token && user.Id && jellyfin) {
|
|
||||||
const apiInstance = jellyfin.createApi(serverUrl, token);
|
|
||||||
setApi(apiInstance);
|
|
||||||
setUser(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
staleTime: 0,
|
|
||||||
enabled: !user?.Id || !api || !jellyfin,
|
|
||||||
});
|
|
||||||
|
|
||||||
const contextValue: JellyfinContextValue = {
|
const contextValue: JellyfinContextValue = {
|
||||||
discoverServers,
|
discoverServers,
|
||||||
setServer: (server) => setServerMutation.mutateAsync(server),
|
setServer: (server) => setServerMutation.mutateAsync(server),
|
||||||
@@ -171,7 +134,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
logout: () => logoutMutation.mutateAsync(),
|
logout: () => logoutMutation.mutateAsync(),
|
||||||
};
|
};
|
||||||
|
|
||||||
useProtectedRoute(user, isLoading || isFetching);
|
useProtectedRoute(user);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<JellyfinContext.Provider value={contextValue}>
|
<JellyfinContext.Provider value={contextValue}>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { atom, useAtom } from "jotai";
|
import { atom, useAtom } from "jotai";
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
type Settings = {
|
type Settings = {
|
||||||
autoRotate?: boolean;
|
autoRotate?: boolean;
|
||||||
@@ -12,55 +10,27 @@ type Settings = {
|
|||||||
mediaListCollectionIds?: string[];
|
mediaListCollectionIds?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Default settings
|
||||||
*
|
const defaultSettings: Settings = {
|
||||||
* The settings atom is a Jotai atom that stores the user's settings.
|
autoRotate: true,
|
||||||
* It is initialized with a default value of null, which indicates that the settings have not been loaded yet.
|
forceLandscapeInVideoPlayer: false,
|
||||||
* The settings are loaded from AsyncStorage when the atom is read for the first time.
|
openFullScreenVideoPlayerByDefault: true,
|
||||||
*
|
usePopularPlugin: false,
|
||||||
*/
|
deviceProfile: "Expo",
|
||||||
|
forceDirectPlay: false,
|
||||||
// Utility function to load settings from AsyncStorage
|
mediaListCollectionIds: [],
|
||||||
const loadSettings = async (): Promise<Settings> => {
|
|
||||||
const jsonValue = await AsyncStorage.getItem("settings");
|
|
||||||
return jsonValue != null
|
|
||||||
? JSON.parse(jsonValue)
|
|
||||||
: {
|
|
||||||
autoRotate: true,
|
|
||||||
forceLandscapeInVideoPlayer: false,
|
|
||||||
openFullScreenVideoPlayerByDefault: false,
|
|
||||||
usePopularPlugin: false,
|
|
||||||
deviceProfile: "Expo",
|
|
||||||
forceDirectPlay: false,
|
|
||||||
mediaListCollectionIds: [],
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utility function to save settings to AsyncStorage
|
// Create an atom to store the settings in memory, initialized with default settings
|
||||||
const saveSettings = async (settings: Settings) => {
|
const settingsAtom = atom<Settings>(defaultSettings);
|
||||||
const jsonValue = JSON.stringify(settings);
|
|
||||||
await AsyncStorage.setItem("settings", jsonValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create an atom to store the settings in memory
|
// A hook to manage settings, providing a way to update them
|
||||||
const settingsAtom = atom<Settings | null>(null);
|
|
||||||
|
|
||||||
// A hook to manage settings, loading them on initial mount and providing a way to update them
|
|
||||||
export const useSettings = () => {
|
export const useSettings = () => {
|
||||||
const [settings, setSettings] = useAtom(settingsAtom);
|
const [settings, setSettings] = useAtom(settingsAtom);
|
||||||
|
|
||||||
useEffect(() => {
|
const updateSettings = (update: Partial<Settings>) => {
|
||||||
if (settings === null) {
|
const newSettings = { ...settings, ...update };
|
||||||
loadSettings().then(setSettings);
|
setSettings(newSettings);
|
||||||
}
|
|
||||||
}, [settings, setSettings]);
|
|
||||||
|
|
||||||
const updateSettings = async (update: Partial<Settings>) => {
|
|
||||||
if (settings) {
|
|
||||||
const newSettings = { ...settings, ...update };
|
|
||||||
setSettings(newSettings);
|
|
||||||
await saveSettings(newSettings);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return [settings, updateSettings] as const;
|
return [settings, updateSettings] as const;
|
||||||
|
|||||||
18
utils/log.ts
18
utils/log.ts
@@ -1,4 +1,4 @@
|
|||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
import { atom } from "jotai";
|
||||||
import { atomWithStorage, createJSONStorage } from "jotai/utils";
|
import { atomWithStorage, createJSONStorage } from "jotai/utils";
|
||||||
|
|
||||||
type LogLevel = "INFO" | "WARN" | "ERROR";
|
type LogLevel = "INFO" | "WARN" | "ERROR";
|
||||||
@@ -10,8 +10,7 @@ interface LogEntry {
|
|||||||
data?: any;
|
data?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const asyncStorage = createJSONStorage(() => AsyncStorage);
|
const logsAtom = atom([]);
|
||||||
const logsAtom = atomWithStorage("logs", [], asyncStorage);
|
|
||||||
|
|
||||||
export const writeToLog = async (
|
export const writeToLog = async (
|
||||||
level: LogLevel,
|
level: LogLevel,
|
||||||
@@ -25,23 +24,16 @@ export const writeToLog = async (
|
|||||||
data: data,
|
data: data,
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentLogs = await AsyncStorage.getItem("logs");
|
const logs: LogEntry[] = [];
|
||||||
const logs: LogEntry[] = currentLogs ? JSON.parse(currentLogs) : [];
|
|
||||||
logs.push(newEntry);
|
logs.push(newEntry);
|
||||||
|
|
||||||
const maxLogs = 100;
|
const maxLogs = 100;
|
||||||
const recentLogs = logs.slice(Math.max(logs.length - maxLogs, 0));
|
|
||||||
|
|
||||||
await AsyncStorage.setItem("logs", JSON.stringify(recentLogs));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const readFromLog = async (): Promise<LogEntry[]> => {
|
export const readFromLog = async (): Promise<LogEntry[]> => {
|
||||||
const logs = await AsyncStorage.getItem("logs");
|
return [];
|
||||||
return logs ? JSON.parse(logs) : [];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearLogs = async () => {
|
export const clearLogs = async () => {};
|
||||||
await AsyncStorage.removeItem("logs");
|
|
||||||
};
|
|
||||||
|
|
||||||
export default logsAtom;
|
export default logsAtom;
|
||||||
|
|||||||
24
yarn.lock
24
yarn.lock
@@ -1603,13 +1603,6 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-compose-refs" "1.0.0"
|
"@radix-ui/react-compose-refs" "1.0.0"
|
||||||
|
|
||||||
"@react-native-async-storage/async-storage@1.23.1":
|
|
||||||
version "1.23.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz#cad3cd4fab7dacfe9838dce6ecb352f79150c883"
|
|
||||||
integrity sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==
|
|
||||||
dependencies:
|
|
||||||
merge-options "^3.0.4"
|
|
||||||
|
|
||||||
"@react-native-community/cli-clean@13.6.9":
|
"@react-native-community/cli-clean@13.6.9":
|
||||||
version "13.6.9"
|
version "13.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-13.6.9.tgz#b6754f39c2b877c9d730feb848945150e1d52209"
|
resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-13.6.9.tgz#b6754f39c2b877c9d730feb848945150e1d52209"
|
||||||
@@ -5131,11 +5124,6 @@ is-path-inside@^3.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
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:
|
is-plain-object@^2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||||
@@ -6155,13 +6143,6 @@ memory-cache@~0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/memory-cache/-/memory-cache-0.2.0.tgz#7890b01d52c00c8ebc9d533e1f8eb17e3034871a"
|
resolved "https://registry.yarnpkg.com/memory-cache/-/memory-cache-0.2.0.tgz#7890b01d52c00c8ebc9d533e1f8eb17e3034871a"
|
||||||
integrity sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==
|
integrity sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==
|
||||||
|
|
||||||
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:
|
merge-stream@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||||
@@ -7315,11 +7296,6 @@ react-native-get-random-values@^1.11.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fast-base64-decode "^1.0.0"
|
fast-base64-decode "^1.0.0"
|
||||||
|
|
||||||
react-native-google-cast@^4.8.2:
|
|
||||||
version "4.8.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-native-google-cast/-/react-native-google-cast-4.8.2.tgz#584fea0f8038e817d075857a537bdbfff435d764"
|
|
||||||
integrity sha512-dmVjfjneit0IguqrjmmunrcjvqNcQQ+EZL4zwCxrrEI3dcfAwBZ1eIDxHaQtvMWP6BsHb2WIFJopPDULJXsvBw==
|
|
||||||
|
|
||||||
react-native-helmet-async@2.0.4:
|
react-native-helmet-async@2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-helmet-async/-/react-native-helmet-async-2.0.4.tgz#93f53a1ff22d6898039688a541653a2d6b6866bb"
|
resolved "https://registry.yarnpkg.com/react-native-helmet-async/-/react-native-helmet-async-2.0.4.tgz#93f53a1ff22d6898039688a541653a2d6b6866bb"
|
||||||
|
|||||||
Reference in New Issue
Block a user