mirror of
https://github.com/Jellify-Music/App.git
synced 2026-02-23 12:18:41 -06:00
lots of stuff
lets see if I can authenticate and get pushed to the library selection
This commit is contained in:
@@ -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) {
|
||||
|
||||
},
|
||||
})
|
||||
@@ -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)
|
||||
},
|
||||
});
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
/>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Text } from "react-native";
|
||||
|
||||
export default function SignIn(): React.JSX.Element {
|
||||
|
||||
return (
|
||||
<Text>Alyssa please be impressed</Text>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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
45
package-lock.json
generated
@@ -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": "*",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user