lots of stuff

lets see if I can authenticate and get pushed to the library selection
This commit is contained in:
Violet Caulfield
2024-10-17 14:39:38 -05:00
parent e7f8f282cc
commit dd1289441c
10 changed files with 156 additions and 78 deletions

View File

@@ -1,16 +0,0 @@
import { useMutation } from "@tanstack/react-query";
import { JellyfinCredentials } from "../types/jellyfin-credentials";
import { MutationKeys } from "../../enums/mutation-keys";
import { createPublicApi } from "../queries/functions/api";
import { fetchServer } from "../queries/functions/storage";
export const authenticateWithCredentials = useMutation({
mutationKey: [MutationKeys.AuthenticationWithCredentials],
mutationFn: async (credentials: JellyfinCredentials) => {
createPublicApi((await fetchServer()).url)
.authenticateUserByName(credentials.username, credentials.password!);
},
onSuccess(data, credentials, context) {
},
})

View File

@@ -1,20 +0,0 @@
import { useMutation } from "@tanstack/react-query";
import { MutationKeys } from "../../enums/mutation-keys";
import { JellyfinCredentials } from "../types/jellyfin-credentials";
import { mutateServer, mutateServerCredentials } from "./functions/storage";
import { JellifyServer } from "../../types/JellifyServer";
export const jellifyServerMutation = useMutation({
mutationKey: [MutationKeys.ServerUrl],
mutationFn: async (server: JellifyServer | undefined) => {
return mutateServer(server)
}
});
export const credentials = useMutation({
mutationKey: [MutationKeys.Credentials],
mutationFn: async (credentials: JellyfinCredentials) => {
return mutateServerCredentials(credentials)
},
});

View File

@@ -5,16 +5,17 @@ import { createStackNavigator } from "@react-navigation/stack";
import { useApiClientContext } from "../jellyfin-api-provider";
import { useColorScheme } from "react-native";
import { Colors } from "react-native-ui-lib";
import ServerLibrary from "./helpers/server-library";
export default function Login(): React.JSX.Element {
const { server } = useApiClientContext();
const { server, username } = useApiClientContext();
const Stack = createStackNavigator();
return (
<Stack.Navigator screenOptions={{ cardStyle: { backgroundColor: useColorScheme() === 'dark' ? 'black' : 'white' }}}>
{
{
(_.isUndefined(server) || _.isEmpty(server.url)) ? (
<Stack.Screen
name="ServerAddress"
@@ -30,18 +31,12 @@ export default function Login(): React.JSX.Element {
>
</Stack.Screen>
) : (
<Stack.Screen
name="ServerAuthentication"
component={ServerAuthentication}
options={{
title: `Sign in to ${server.name}`,
animationTypeForReplace: 'push',
headerStyle: {
backgroundColor: useColorScheme() === 'dark' ? '#000' : '#FFF',
},
headerTintColor: Colors.$iconPrimary
}}
/>
(_.isUndefined(username)) ? (
<Stack.Screen name="ServerAuthentication" component={ServerAuthentication} />
) : (
<Stack.Screen name="LibrarySelection" component={ServerLibrary}></Stack.Screen>
)
)
}
</Stack.Navigator>

View File

@@ -24,7 +24,7 @@ export default function ServerAddress(): React.JSX.Element {
const useServerMutation = useMutation({
mutationFn: serverMutation,
onSuccess: async (publicSystemInfoResponse, serverUrl, context) => {
onSuccess: async (publicSystemInfoResponse, serverUrl) => {
if (!!!publicSystemInfoResponse.data.Version)
throw new Error("Jellyfin instance did not respond");

View File

@@ -1,11 +1,16 @@
import React, { useEffect } from "react";
import React from "react";
import { useColorScheme } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { useMutation } from "@tanstack/react-query";
import { AsyncStorageKeys } from "../../../enums/async-storage-keys";
import { useApiClientContext } from "../../jellyfin-api-provider";
import { RadioGroup, RadioButton, TextField, View, Button, Card, Colors } from 'react-native-ui-lib';
import { TextField, View, Button, Colors } from 'react-native-ui-lib';
import { jellifyStyles } from "../../styles";
import { credentials } from "../../../api/mutators/storage";
import { JellyfinCredentials } from "../../../api/types/jellyfin-credentials";
import * as Keychain from "react-native-keychain"
import _ from "lodash";
import { client } from "../../../api/queries";
export default function ServerAuthentication(): React.JSX.Element {
const [username, setUsername] = React.useState('');
@@ -13,7 +18,34 @@ export default function ServerAuthentication(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const { setServer, setChangeServer } = useApiClientContext();
const { apiClient, setApiClient, server, setServer, setChangeServer } = useApiClientContext();
const useApiMutation = useMutation({
mutationFn: async (credentials: JellyfinCredentials) => {
return apiClient!.authenticateUserByName(credentials.username, credentials.password!);
},
onSuccess: async (authResult, credentials) => {
console.log(`Received auth response from ${server!.name}`)
if (_.isUndefined(authResult))
return Promise.reject(new Error("Authentication result was empty"))
if (authResult.status >= 400 || _.isEmpty(authResult.data.AccessToken))
return Promise.reject(new Error("Invalid credentials"))
if (_.isUndefined(authResult.data.User))
return Promise.reject(new Error("Unable to login"));
setApiClient(client.createApi(server!.url, (authResult.data.AccessToken as string)))
setUsername(authResult.data.User.Name!);
return await Keychain.setInternetCredentials(server!.url, credentials.username, (authResult.data.AccessToken as string));
},
onError: async (error: Error) => {
console.error("An error occurred connecting to the Jellyfin instance", error);
return await AsyncStorage.setItem(AsyncStorageKeys.ServerUrl, "");
}
});
const clearServer = useMutation({
mutationFn: async () => {
@@ -49,7 +81,7 @@ export default function ServerAuthentication(): React.JSX.Element {
<Button
label="Sign in"
color={Colors.$iconPrimary}
onPress={() => console.log("sign in pressed")}
onPress={() => useApiMutation.mutate({ username, password })}
size={Button.sizes.medium}
margin
/>

View File

@@ -1,8 +0,0 @@
import { Text } from "react-native";
export default function SignIn(): React.JSX.Element {
return (
<Text>Alyssa please be impressed</Text>
)
}

View File

@@ -5,10 +5,12 @@ import _ from "lodash";
import { JellyfinApiClientProvider, useApiClientContext } from "./jellyfin-api-provider";
import React, { } from "react";
import { NavigationContainer } from "@react-navigation/native";
import Login from "./Login/component";
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import Navigation from "./navigation";
import { jellifyStyles } from "./styles";
import { View } from "react-native-ui-lib";
import Login from "./Login/component";
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
export default function Jellify(): React.JSX.Element {
@@ -30,17 +32,23 @@ export default function Jellify(): React.JSX.Element {
function conditionalHomeRender(): React.JSX.Element {
const { apiClient } = useApiClientContext();
const { libraryId } = useApiClientContext();
const Stack = createNativeStackNavigator()
const Tab = createBottomTabNavigator();
return (
<View style={jellifyStyles.container}>
{ !_.isUndefined(apiClient) ? (
<Navigation />
) : (
<NavigationContainer>
<Login />
<Stack.Navigator>
{ !_.isUndefined(libraryId) ? (
<Tab.Screen name="Navigation" component={Navigation} />
) : (
<Stack.Screen name="Login" component={Login} />
)}
</Stack.Navigator>
</NavigationContainer>
)}
</View>
);
}

View File

@@ -14,8 +14,14 @@ interface JellyfinApiClientContext {
setServer: React.Dispatch<React.SetStateAction<JellifyServer | undefined>>;
changeServer: boolean;
setChangeServer: React.Dispatch<React.SetStateAction<boolean>>;
username: string | undefined;
setUsername: React.Dispatch<React.SetStateAction<string>>;
changeUser: boolean;
setChangeUser: React.Dispatch<React.SetStateAction<boolean>>;
libraryName: string | undefined;
setLibraryName: React.Dispatch<React.SetStateAction<string>>;
libraryId: string | undefined;
setLibraryId: React.Dispatch<React.SetStateAction<string>>;
changeLibrary: boolean;
setChangeLibrary: React.Dispatch<React.SetStateAction<boolean>>;
}
@@ -31,6 +37,10 @@ const JellyfinApiClientContextInitializer = () => {
const [changeUserRequested, setChangeUserRequested] = useState<boolean>(false);
const [changeLibraryRequested, setChangeLibraryRequested] = useState<boolean>(false);
const [userName, setUserName] = useState<string>("");
const [libraryName, setLibraryName] = useState<string>("");
const [libraryId, setLibraryId] = useState<string>("");
const { data: api, isPending: apiPending } = useApi();
const { data: jellyfinServer, isPending: serverPending } = useServer();
@@ -58,7 +68,14 @@ const JellyfinApiClientContextInitializer = () => {
changeUserRequested,
setChangeUserRequested,
changeLibraryRequested,
setChangeLibraryRequested }
setChangeLibraryRequested,
userName,
setUserName,
libraryName,
setLibraryName,
libraryId,
setLibraryId
};
}
export const JellyfinApiClientContext =
@@ -71,8 +88,14 @@ export const JellyfinApiClientContext =
setServer: () => {},
changeServer: false,
setChangeServer: () => {},
username: "",
setUsername: () => {},
changeUser: false,
setChangeUser: () => {},
libraryName: "",
setLibraryName: () => {},
libraryId: "",
setLibraryId: () => {},
changeLibrary: false,
setChangeLibrary: () => {},
});
@@ -87,16 +110,41 @@ export const JellyfinApiClientProvider = ({ children }: { children: ReactNode })
isServerPending,
changeServerRequested,
setChangeServerRequested,
userName,
setUserName,
changeUserRequested,
setChangeUserRequested,
libraryName,
setLibraryName,
libraryId,
setLibraryId,
changeLibraryRequested,
setChangeLibraryRequested
} = JellyfinApiClientContextInitializer();;
} = JellyfinApiClientContextInitializer();
// Add your logic to check if credentials are stored and initialize the API client here.
return (
<JellyfinApiClientContext.Provider value={{ apiClient, setApiClient, apiClientPending: isApiPending, server, setServer, serverPending: isServerPending, changeServer: changeServerRequested, changeUser: changeUserRequested, changeLibrary: changeLibraryRequested, setChangeServer: setChangeServerRequested, setChangeLibrary: setChangeLibraryRequested, setChangeUser: setChangeUserRequested }}>
<JellyfinApiClientContext.Provider value={{
apiClient,
setApiClient,
apiClientPending: isApiPending,
server,
setServer,
serverPending: isServerPending,
changeServer: changeServerRequested,
setChangeServer: setChangeServerRequested,
username: userName,
setUsername: setUserName,
changeUser: changeUserRequested,
setChangeUser: setChangeUserRequested,
libraryName: libraryName,
setLibraryName: setLibraryName,
libraryId: libraryId,
setLibraryId: setLibraryId,
changeLibrary: changeLibraryRequested,
setChangeLibrary: setChangeLibraryRequested
}}>
{children}
</JellyfinApiClientContext.Provider>
);

45
package-lock.json generated
View File

@@ -14,7 +14,9 @@
"@react-native-async-storage/async-storage": "^2.0.0",
"@react-native-community/masked-view": "^0.1.11",
"@react-native-masked-view/masked-view": "^0.3.1",
"@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/native": "^6.1.18",
"@react-navigation/native-stack": "^6.11.0",
"@react-navigation/stack": "^6.4.1",
"@tanstack/react-query": "^5.52.1",
"lodash": "^4.17.21",
@@ -24,7 +26,7 @@
"react-native-gesture-handler": "^2.20.0",
"react-native-keychain": "^8.2.0",
"react-native-reanimated": "^3.15.5",
"react-native-safe-area-context": "^4.11.0",
"react-native-safe-area-context": "^4.11.1",
"react-native-screens": "^3.34.0",
"react-native-track-player": "^4.1.1",
"react-native-ui-lib": "^7.32.0",
@@ -4881,6 +4883,24 @@
}
}
},
"node_modules/@react-navigation/bottom-tabs": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-6.6.1.tgz",
"integrity": "sha512-9oD4cypEBjPuaMiu9tevWGiQ4w/d6l3HNhcJ1IjXZ24xvYDSs0mqjUcdt8SWUolCvRrYc/DmNBLlT83bk0bHTw==",
"license": "MIT",
"dependencies": {
"@react-navigation/elements": "^1.3.31",
"color": "^4.2.3",
"warn-once": "^0.1.0"
},
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"react": "*",
"react-native": "*",
"react-native-safe-area-context": ">= 3.0.0",
"react-native-screens": ">= 3.0.0"
}
},
"node_modules/@react-navigation/core": {
"version": "6.4.17",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.17.tgz",
@@ -4926,6 +4946,23 @@
"react-native": "*"
}
},
"node_modules/@react-navigation/native-stack": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.11.0.tgz",
"integrity": "sha512-U5EcUB9Q2NQspCFwYGGNJm0h6wBCOv7T30QjndmvlawLkNt7S7KWbpWyxS9XBHSIKF57RgWjfxuJNTgTstpXxw==",
"license": "MIT",
"dependencies": {
"@react-navigation/elements": "^1.3.31",
"warn-once": "^0.1.0"
},
"peerDependencies": {
"@react-navigation/native": "^6.0.0",
"react": "*",
"react-native": "*",
"react-native-safe-area-context": ">= 3.0.0",
"react-native-screens": ">= 3.0.0"
}
},
"node_modules/@react-navigation/routers": {
"version": "6.1.9",
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz",
@@ -13676,9 +13713,9 @@
}
},
"node_modules/react-native-safe-area-context": {
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.11.0.tgz",
"integrity": "sha512-Bg7bozxEB+ZS+H3tVYs5yY1cvxNXgR6nRQwpSMkYR9IN5CbxohLnSprrOPG/ostTCd4F6iCk0c51pExEhifSKQ==",
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.11.1.tgz",
"integrity": "sha512-urF1m4nFiZFaWjsv2zj8J/hKvo4b2tJW+6CYU1mY4lKv1RwhG2eV8J/EHKuNlLhATZx3+6j7szrpHrQW2ZcAaQ==",
"license": "MIT",
"peerDependencies": {
"react": "*",

View File

@@ -16,7 +16,9 @@
"@react-native-async-storage/async-storage": "^2.0.0",
"@react-native-community/masked-view": "^0.1.11",
"@react-native-masked-view/masked-view": "^0.3.1",
"@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/native": "^6.1.18",
"@react-navigation/native-stack": "^6.11.0",
"@react-navigation/stack": "^6.4.1",
"@tanstack/react-query": "^5.52.1",
"lodash": "^4.17.21",
@@ -26,7 +28,7 @@
"react-native-gesture-handler": "^2.20.0",
"react-native-keychain": "^8.2.0",
"react-native-reanimated": "^3.15.5",
"react-native-safe-area-context": "^4.11.0",
"react-native-safe-area-context": "^4.11.1",
"react-native-screens": "^3.34.0",
"react-native-track-player": "^4.1.1",
"react-native-ui-lib": "^7.32.0",