Add onboarding component

This commit is contained in:
Lei Nelissen
2020-08-09 17:49:36 +02:00
parent 2155320f6b
commit 0712e6b8ca
8 changed files with 106 additions and 7 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -11,6 +11,8 @@ const Background = styled.View`
const Container = styled.View`
border-radius: 20px;
flex: 1;
justify-content: center;
align-items: center;
`;
const Modal: React.FC = ({ children }) => {

View File

@@ -0,0 +1,76 @@
import React, { useCallback, useEffect } from 'react';
import styled from 'styled-components/native';
import { THEME_COLOR } from 'CONSTANTS';
import { Button } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { NavigationProp } from 'screens';
import { useTypedSelector } from 'store';
import { useDispatch } from 'react-redux';
import { setOnboardingStatus } from 'store/settings/actions';
const Container = styled.SafeAreaView`
background-color: ${THEME_COLOR};
flex: 1;
justify-content: center;
`;
const TextContainer = styled.ScrollView`
padding: 25px;
`;
const Text = styled.Text`
text-align: center;
color: white;
margin-bottom: 10px;
`;
const ButtonContainer = styled.View`
margin-top: 50px;
`;
const Logo = styled.Image`
width: 150px;
height: 150px;
margin: 0 auto 50px auto;
`;
function Onboarding() {
// Get account from Redux and dispatcher
const account = useTypedSelector(state => state.settings.jellyfin);
const dispatch = useDispatch();
// Also retrieve the navigation handler so that we can open the modal in
// which the Jellyfin server is set
const navigation = useNavigation<NavigationProp>();
const handleClick = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
// We'll also respond to any change in the account, setting the onboarding
// status to true, so that the app becomes available.
useEffect(() => {
if (account) {
dispatch(setOnboardingStatus(true));
}
}, [account, dispatch]);
return (
<Container>
<TextContainer contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
<Logo source={require('../../assets/app-icon-white.png')} />
<Text >
Welcome!
</Text>
<Text>
Jellyfin Audio Player will allow you to stream your music library from anywhere, with full support for background audio and casting.
</Text>
<Text>
In order to get started, you need a Jellyfin server. Click the button below to enter your Jellyfin server address and login to it.
</Text>
<ButtonContainer>
<Button title="Set Jellyfin Server" color="#ffffff" onPress={handleClick} />
</ButtonContainer>
</TextContainer>
</Container>
);
}
export default Onboarding;

View File

@@ -10,6 +10,8 @@ import PlayPauseIcon from 'assets/play-pause-fill.svg';
import NotesIcon from 'assets/notes.svg';
import GearIcon from 'assets/gear.svg';
import { THEME_COLOR } from 'CONSTANTS';
import { useTypedSelector } from 'store';
import Onboarding from './Onboarding';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
@@ -34,6 +36,14 @@ function getIcon(route: string): React.FC<any> | null {
}
function Screens() {
const isOnboardingComplete = useTypedSelector(state => state.settings.isOnboardingComplete);
// GUARD: If onboarding has not been completed, we instead render the
// onboarding component, so that the user can get setup in the app.
if (!isOnboardingComplete) {
return <Onboarding />;
}
return (
<Tab.Navigator
screenOptions={({ route }) => ({

View File

@@ -33,7 +33,9 @@ export default function SetJellyfinServer() {
/>
) : (
<View style={{ padding: 20 }}>
<Text style={colors.text}>Please enter your Jellyfin server URL first. Make sure to include the protocol and port</Text>
<Text style={colors.text}>
Please enter your Jellyfin server URL. Make sure to include the protocol and port
</Text>
<Input
placeholder="https://jellyfin.yourserver.io/"
onChangeText={setServerUrl}

View File

@@ -1,12 +1,14 @@
import { configureStore, getDefaultMiddleware, combineReducers } from '@reduxjs/toolkit';
import { useSelector, TypedUseSelectorHook } from 'react-redux';
import AsyncStorage from '@react-native-community/async-storage';
import { persistStore, persistReducer } from 'redux-persist';
import { persistStore, persistReducer, PersistConfig } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/es/stateReconciler/autoMergeLevel2';
// import logger from 'redux-logger';
const persistConfig = {
const persistConfig: PersistConfig<AppState> = {
key: 'root',
storage: AsyncStorage,
stateReconciler: autoMergeLevel2
};
import settings from './settings';
@@ -26,7 +28,7 @@ const store = configureStore({
),
});
export type AppState = ReturnType<typeof store.getState>;
export type AppState = ReturnType<typeof reducers>;
export type AppDispatch = typeof store.dispatch;
export type AsyncThunkAPI = { state: AppState, dispatch: AppDispatch };
export const useTypedSelector: TypedUseSelectorHook<AppState> = useSelector;

View File

@@ -1,4 +1,5 @@
import { createAction } from '@reduxjs/toolkit';
export const setJellyfinCredentials = createAction<{ access_token: string, user_id: string, uri: string, deviced_id: string; }>('SET_JELLYFIN_CREDENTIALS');
export const setBitrate = createAction<number>('SET_BITRATE');
export const setBitrate = createAction<number>('SET_BITRATE');
export const setOnboardingStatus = createAction<boolean>('SET_ONBOARDING_STATUS');

View File

@@ -1,5 +1,5 @@
import { createReducer } from '@reduxjs/toolkit';
import { setBitrate, setJellyfinCredentials } from './actions';
import { setBitrate, setJellyfinCredentials, setOnboardingStatus } from './actions';
interface State {
jellyfin?: {
@@ -9,10 +9,12 @@ interface State {
device_id: string;
}
bitrate: number;
isOnboardingComplete: boolean;
}
const initialState: State = {
bitrate: 140000000
bitrate: 140000000,
isOnboardingComplete: false,
};
const settings = createReducer(initialState, {
@@ -24,6 +26,10 @@ const settings = createReducer(initialState, {
...state,
bitrate: action.payload,
}),
[setOnboardingStatus.type]: (state, action) => ({
...state,
isOnboardingComplete: action.payload,
})
});
export default settings;