mirror of
https://github.com/Jellify-Music/App.git
synced 2026-05-24 22:08:56 -05:00
breaking login flow out into it's own provider
This commit is contained in:
@@ -5,12 +5,12 @@ import _ from "lodash";
|
||||
|
||||
export default function Home(): React.JSX.Element {
|
||||
|
||||
const { apiClient, username } = useApiClientContext();
|
||||
const { apiClient } = useApiClientContext();
|
||||
|
||||
return (
|
||||
<ScrollView paddingLeft={10}>
|
||||
<YStack alignContent='flex-start'>
|
||||
<H1>Hi { _.isUndefined(username) ? "there" : `, ${username}`}</H1>
|
||||
<H1>Hi there</H1>
|
||||
</YStack>
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
@@ -2,24 +2,29 @@ import _ from "lodash"
|
||||
import ServerAuthentication from "./helpers/server-authentication";
|
||||
import ServerAddress from "./helpers/server-address";
|
||||
import { createStackNavigator } from "@react-navigation/stack";
|
||||
import { useApiClientContext } from "../jellyfin-api-provider";
|
||||
import ServerLibrary from "./helpers/server-library";
|
||||
import { useAuthenticationContext } from "./provider";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function Login(): React.JSX.Element {
|
||||
|
||||
const { server, username } = useApiClientContext();
|
||||
const { serverAddress, username, triggerAuth, setTriggerAuth } = useAuthenticationContext();
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
useEffect(() => {
|
||||
setTriggerAuth(false);
|
||||
})
|
||||
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||
{
|
||||
(_.isUndefined(server) || _.isEmpty(server.url)) ? (
|
||||
(_.isUndefined(serverAddress)) ? (
|
||||
<Stack.Screen
|
||||
name="ServerAddress"
|
||||
options={{
|
||||
headerShown: false,
|
||||
animationTypeForReplace: (_.isUndefined(server) || _.isEmpty(server.url)) ? 'pop' : 'push'
|
||||
animationTypeForReplace: triggerAuth ? 'push' : 'pop'
|
||||
}}
|
||||
component={ServerAddress}
|
||||
/>
|
||||
|
||||
@@ -11,16 +11,17 @@ import { Button, Input, SizableText, useTheme, View, YStack, Stack, XStack, getF
|
||||
import { CheckboxWithLabel } from "../../helpers/checkbox-with-label";
|
||||
import { SwitchWithLabel } from "../../helpers/switch-with-label";
|
||||
import { buildApiClient } from "../../../api/client";
|
||||
import { useAuthenticationContext } from "../provider";
|
||||
|
||||
const http = "http://"
|
||||
const https = "https://"
|
||||
|
||||
export default function ServerAddress(): React.JSX.Element {
|
||||
|
||||
const { changeServer, server, setServer, setApiClient } = useApiClientContext();
|
||||
const { setServer, setApiClient } = useApiClientContext();
|
||||
const { serverAddress, setServerAddress } = useAuthenticationContext();
|
||||
|
||||
const [useHttps, setUseHttps] = useState(true)
|
||||
const [serverAddress, setServerAddress] = useState(server?.url ?? undefined);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
|
||||
@@ -6,12 +6,13 @@ import * as Keychain from "react-native-keychain"
|
||||
import { JellyfinCredentials } from "../../../api/types/jellyfin-credentials";
|
||||
import { Button, H2, Input, View } from "tamagui";
|
||||
import { client } from "../../../api/client";
|
||||
import { useAuthenticationContext } from "../provider";
|
||||
|
||||
export default function ServerAuthentication(): React.JSX.Element {
|
||||
const [username, setUsername] = React.useState('');
|
||||
const { username, setUsername, setServerAddress } = useAuthenticationContext();
|
||||
const [password, setPassword] = React.useState('');
|
||||
|
||||
const { apiClient, setApiClient, server, setServer, changeUser, setUsername: setContextUsername } = useApiClientContext();
|
||||
const { apiClient, setApiClient, server } = useApiClientContext();
|
||||
|
||||
const useApiMutation = useMutation({
|
||||
mutationFn: async (credentials: JellyfinCredentials) => {
|
||||
@@ -31,7 +32,6 @@ export default function ServerAuthentication(): React.JSX.Element {
|
||||
|
||||
console.log(`Successfully signed in to ${server!.name}`)
|
||||
setApiClient(client.createApi(server!.url, (authResult.data.AccessToken as string)))
|
||||
setContextUsername(credentials.username);
|
||||
return await Keychain.setInternetCredentials(server!.url, credentials.username, (authResult.data.AccessToken as string));
|
||||
|
||||
},
|
||||
@@ -43,8 +43,8 @@ export default function ServerAuthentication(): React.JSX.Element {
|
||||
|
||||
const clearServer = useMutation({
|
||||
mutationFn: async () => {
|
||||
setContextUsername(undefined);
|
||||
setServer(undefined);
|
||||
setServerAddress(undefined);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
@@ -75,9 +75,13 @@ export default function ServerAuthentication(): React.JSX.Element {
|
||||
/>
|
||||
|
||||
<Button
|
||||
disabled={_.isEmpty(username) || _.isEmpty(password)}
|
||||
onPress={() => {
|
||||
console.log(`Signing in to ${server!.name}`);
|
||||
useApiMutation.mutate({ username, password })
|
||||
|
||||
if (!_.isUndefined(username)) {
|
||||
console.log(`Signing in to ${server!.name}`);
|
||||
useApiMutation.mutate({ username, password })
|
||||
}
|
||||
}}
|
||||
>
|
||||
Sign in
|
||||
|
||||
@@ -6,21 +6,24 @@ import { JellifyLibrary } from "../../../types/JellifyLibrary";
|
||||
import { useLibraries } from "../../../api/queries/libraries";
|
||||
import { client } from "../../../api/client";
|
||||
import { mutateServerCredentials } from "../../../api/mutators/functions/storage";
|
||||
import { useAuthenticationContext } from "../provider";
|
||||
|
||||
export default function ServerLibrary(): React.JSX.Element {
|
||||
|
||||
const [musicLibrary, setMusicLibrary] = useState<JellifyLibrary | undefined>(undefined);
|
||||
|
||||
const [musicLibraryName, setMusicLibraryName] = useState<string>("")
|
||||
const { setUsername, libraryName, setLibraryName, libraryId, setLibraryId } = useAuthenticationContext();
|
||||
|
||||
const { apiClient, server, setApiClient, setUsername } = useApiClientContext();
|
||||
const { apiClient, server, setApiClient } = useApiClientContext();
|
||||
|
||||
const { data: musicLibraries, isPending: musicLibrariesPending } = useLibraries(apiClient!);
|
||||
|
||||
|
||||
const clearUser = useMutation({
|
||||
mutationFn: async () => {
|
||||
setUsername(undefined)
|
||||
|
||||
setUsername(undefined);
|
||||
|
||||
// Reset API client so that we don't attempt to auth as a user
|
||||
setApiClient(client.createApi(server!.url))
|
||||
return Promise.resolve();
|
||||
@@ -43,7 +46,7 @@ export default function ServerLibrary(): React.JSX.Element {
|
||||
}}
|
||||
>Switch User</Button>
|
||||
|
||||
<Select value={musicLibraryName}></Select>
|
||||
<Select value={libraryName}></Select>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import React, { createContext, ReactNode, SetStateAction, useContext, useState } from "react";
|
||||
|
||||
interface JellyfinAuthenticationContext {
|
||||
username: string | undefined;
|
||||
setUsername: React.Dispatch<SetStateAction<string | undefined>>;
|
||||
serverAddress: string | undefined;
|
||||
setServerAddress: React.Dispatch<SetStateAction<string | undefined>>;
|
||||
libraryName: string | undefined;
|
||||
setLibraryName: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
libraryId: string | undefined;
|
||||
setLibraryId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
triggerAuth: boolean;
|
||||
setTriggerAuth: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const JellyfinAuthenticationContextInitializer = () => {
|
||||
const [username, setUsername] = useState<string | undefined>(undefined);
|
||||
|
||||
const [serverAddress, setServerAddress] = useState<string | undefined>(undefined);
|
||||
|
||||
const [libraryName, setLibraryName] = useState<string | undefined>(undefined);
|
||||
|
||||
const [libraryId, setLibraryId] = useState<string | undefined>(undefined);
|
||||
|
||||
const [triggerAuth, setTriggerAuth] = useState<boolean>(true);
|
||||
|
||||
return {
|
||||
username,
|
||||
setUsername,
|
||||
serverAddress,
|
||||
setServerAddress,
|
||||
libraryName,
|
||||
setLibraryName,
|
||||
libraryId,
|
||||
setLibraryId,
|
||||
triggerAuth,
|
||||
setTriggerAuth
|
||||
};
|
||||
}
|
||||
|
||||
const JellyfinAuthenticationContext =
|
||||
createContext<JellyfinAuthenticationContext>({
|
||||
username: undefined,
|
||||
setUsername: () => {},
|
||||
serverAddress: undefined,
|
||||
setServerAddress: () => {},
|
||||
libraryName: undefined,
|
||||
setLibraryName: () => {},
|
||||
libraryId: undefined,
|
||||
setLibraryId: () => {},
|
||||
triggerAuth: true,
|
||||
setTriggerAuth: () => {},
|
||||
});
|
||||
|
||||
export const JellyfinAuthenticationProvider: ({ children }: {
|
||||
children: ReactNode;
|
||||
}) => React.JSX.Element = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
const {
|
||||
username,
|
||||
setUsername,
|
||||
serverAddress,
|
||||
setServerAddress,
|
||||
libraryName,
|
||||
setLibraryName,
|
||||
libraryId,
|
||||
setLibraryId,
|
||||
triggerAuth,
|
||||
setTriggerAuth,
|
||||
} = JellyfinAuthenticationContextInitializer();
|
||||
|
||||
return (
|
||||
<JellyfinAuthenticationContext.Provider value={{
|
||||
username,
|
||||
setUsername,
|
||||
serverAddress,
|
||||
setServerAddress,
|
||||
libraryName,
|
||||
setLibraryName,
|
||||
libraryId,
|
||||
setLibraryId,
|
||||
triggerAuth,
|
||||
setTriggerAuth,
|
||||
}}>
|
||||
{ children }
|
||||
</JellyfinAuthenticationContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAuthenticationContext = () => useContext(JellyfinAuthenticationContext)
|
||||
@@ -7,6 +7,7 @@ import React, { } from "react";
|
||||
import { DarkTheme, DefaultTheme, NavigationContainer } from "@react-navigation/native";
|
||||
import Navigation from "./navigation";
|
||||
import Login from "./Login/component";
|
||||
import { JellyfinAuthenticationProvider } from "./Login/provider";
|
||||
|
||||
export default function Jellify(): React.JSX.Element {
|
||||
|
||||
@@ -30,14 +31,17 @@ function conditionalHomeRender(): React.JSX.Element {
|
||||
|
||||
const isDarkMode = useColorScheme() === 'dark';
|
||||
|
||||
const { libraryId } = useApiClientContext();
|
||||
// If library ID hasn't been set, we haven't completed the auth flow
|
||||
const { library } = useApiClientContext();
|
||||
|
||||
return (
|
||||
<NavigationContainer theme={isDarkMode ? DarkTheme : DefaultTheme}>
|
||||
{ !_.isUndefined(libraryId) ? (
|
||||
{ !_.isUndefined(library) ? (
|
||||
<Navigation />
|
||||
) : (
|
||||
<Login />
|
||||
<JellyfinAuthenticationProvider>
|
||||
<Login />
|
||||
</JellyfinAuthenticationProvider>
|
||||
)}
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
@@ -4,46 +4,21 @@ import { useApi } from '../api/queries';
|
||||
import _ from 'lodash';
|
||||
import { JellifyServer } from '../types/JellifyServer';
|
||||
import { useCredentials, useServer } from '../api/queries/keychain';
|
||||
import { JellifyLibrary } from '../types/JellifyLibrary';
|
||||
|
||||
interface JellyfinApiClientContext {
|
||||
apiClient: Api | undefined;
|
||||
apiClientPending: boolean;
|
||||
setApiClient: React.Dispatch<React.SetStateAction<Api | undefined>>;
|
||||
server: JellifyServer | undefined;
|
||||
serverPending: boolean;
|
||||
setServer: React.Dispatch<React.SetStateAction<JellifyServer | undefined>>;
|
||||
changeServer: boolean;
|
||||
setChangeServer: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
username: string | undefined;
|
||||
setUsername: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
isUsernamePending: boolean;
|
||||
setIsUsernamePending: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
changeUser: boolean;
|
||||
setChangeUser: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
libraryName: string | undefined;
|
||||
setLibraryName: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
libraryId: string | undefined;
|
||||
setLibraryId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
changeLibrary: boolean;
|
||||
setChangeLibrary: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
library: JellifyLibrary | undefined;
|
||||
setLibrary: React.Dispatch<React.SetStateAction<JellifyLibrary | undefined>>;
|
||||
}
|
||||
|
||||
const JellyfinApiClientContextInitializer = () => {
|
||||
const [apiClient, setApiClient] = useState<Api | undefined>(undefined);
|
||||
const [server, setServer] = useState<JellifyServer | undefined>(undefined);
|
||||
|
||||
const [isServerPending, setIsServerPending] = useState<boolean>(true);
|
||||
const [isApiPending, setIsApiPending] = useState<boolean>(true);
|
||||
|
||||
const [changeServerRequested, setChangeServerRequested] = useState<boolean>(false);
|
||||
const [changeUserRequested, setChangeUserRequested] = useState<boolean>(false);
|
||||
const [changeLibraryRequested, setChangeLibraryRequested] = useState<boolean>(false);
|
||||
|
||||
const [userName, setUserName] = useState<string | undefined>(undefined);
|
||||
const [isUsernamePending, setIsUsernamePending] = useState<boolean>(false)
|
||||
|
||||
const [libraryName, setLibraryName] = useState<string | undefined>(undefined);
|
||||
const [libraryId, setLibraryId] = useState<string | undefined>(undefined);
|
||||
const [library, setLibrary] = useState<JellifyLibrary | undefined>(undefined);
|
||||
|
||||
const { data: api, isPending: apiPending } = useApi();
|
||||
const { data: jellyfinServer, isPending: serverPending } = useServer();
|
||||
@@ -52,10 +27,6 @@ const JellyfinApiClientContextInitializer = () => {
|
||||
useEffect(() => {
|
||||
setApiClient(api);
|
||||
setServer(jellyfinServer);
|
||||
setIsApiPending(apiPending);
|
||||
setIsServerPending(serverPending);
|
||||
setUserName(credentials?.username ?? undefined)
|
||||
setIsUsernamePending(credentialsPending);
|
||||
}, [
|
||||
api,
|
||||
apiPending,
|
||||
@@ -63,56 +34,26 @@ const JellyfinApiClientContextInitializer = () => {
|
||||
credentialsPending,
|
||||
jellyfinServer,
|
||||
serverPending,
|
||||
userName,
|
||||
isUsernamePending,
|
||||
]);
|
||||
|
||||
return {
|
||||
apiClient,
|
||||
apiClient,
|
||||
setApiClient,
|
||||
isApiPending,
|
||||
server,
|
||||
server,
|
||||
setServer,
|
||||
isServerPending,
|
||||
changeServerRequested,
|
||||
setChangeServerRequested,
|
||||
changeUserRequested,
|
||||
setChangeUserRequested,
|
||||
changeLibraryRequested,
|
||||
setChangeLibraryRequested,
|
||||
userName,
|
||||
setUserName,
|
||||
isUsernamePending,
|
||||
setIsUsernamePending,
|
||||
libraryName,
|
||||
setLibraryName,
|
||||
libraryId,
|
||||
setLibraryId
|
||||
library,
|
||||
setLibrary,
|
||||
};
|
||||
}
|
||||
|
||||
export const JellyfinApiClientContext =
|
||||
createContext<JellyfinApiClientContext>({
|
||||
apiClient: undefined,
|
||||
apiClientPending: true,
|
||||
setApiClient: () => {},
|
||||
server: undefined,
|
||||
serverPending: true,
|
||||
setServer: () => {},
|
||||
changeServer: false,
|
||||
setChangeServer: () => {},
|
||||
username: undefined,
|
||||
setUsername: () => {},
|
||||
isUsernamePending: false,
|
||||
setIsUsernamePending: () => {},
|
||||
changeUser: false,
|
||||
setChangeUser: () => {},
|
||||
libraryName: undefined,
|
||||
setLibraryName: () => {},
|
||||
libraryId: undefined,
|
||||
setLibraryId: () => {},
|
||||
changeLibrary: false,
|
||||
setChangeLibrary: () => {},
|
||||
library: undefined,
|
||||
setLibrary: () => {},
|
||||
});
|
||||
|
||||
export const JellyfinApiClientProvider: ({ children }: {
|
||||
@@ -121,24 +62,10 @@ export const JellyfinApiClientProvider: ({ children }: {
|
||||
const {
|
||||
apiClient,
|
||||
setApiClient,
|
||||
isApiPending,
|
||||
server,
|
||||
setServer,
|
||||
isServerPending,
|
||||
changeServerRequested,
|
||||
setChangeServerRequested,
|
||||
userName,
|
||||
setUserName,
|
||||
isUsernamePending,
|
||||
setIsUsernamePending,
|
||||
changeUserRequested,
|
||||
setChangeUserRequested,
|
||||
libraryName,
|
||||
setLibraryName,
|
||||
libraryId,
|
||||
setLibraryId,
|
||||
changeLibraryRequested,
|
||||
setChangeLibraryRequested
|
||||
library,
|
||||
setLibrary,
|
||||
} = JellyfinApiClientContextInitializer();
|
||||
|
||||
// Add your logic to check if credentials are stored and initialize the API client here.
|
||||
@@ -147,24 +74,10 @@ export const JellyfinApiClientProvider: ({ children }: {
|
||||
<JellyfinApiClientContext.Provider value={{
|
||||
apiClient,
|
||||
setApiClient,
|
||||
apiClientPending: isApiPending,
|
||||
server,
|
||||
setServer,
|
||||
serverPending: isServerPending,
|
||||
changeServer: changeServerRequested,
|
||||
setChangeServer: setChangeServerRequested,
|
||||
username: userName,
|
||||
setUsername: setUserName,
|
||||
isUsernamePending,
|
||||
setIsUsernamePending,
|
||||
changeUser: changeUserRequested,
|
||||
setChangeUser: setChangeUserRequested,
|
||||
libraryName: libraryName,
|
||||
setLibraryName: setLibraryName,
|
||||
libraryId: libraryId,
|
||||
setLibraryId: setLibraryId,
|
||||
changeLibrary: changeLibraryRequested,
|
||||
setChangeLibrary: setChangeLibraryRequested
|
||||
library,
|
||||
setLibrary
|
||||
}}>
|
||||
{children}
|
||||
</JellyfinApiClientContext.Provider>
|
||||
|
||||
Reference in New Issue
Block a user