Commit b6223b73 authored by Thor-Herman's avatar Thor-Herman
Browse files

Merge branch 'filter' into 'master'

Squashed commit of the following:

commit 56a93644
Author: Thor-Herman <44784559+Thor-Herman@users.noreply.github.com>
Date:   Fri Nov 13 16:19:26 2020 +0100

    Fix picker options text #7

commit 46c5ad51
Author: Thor-Herman <44784559+Thor-Herman@users.noreply.github.com>
Date:   Fri Nov 13 16:17:01 2020 +0100

    Refactor styling in orderingpicker #7 #5

commit 5c47a60a
Author: Thor-Herman <44784559+Thor-Herman@users.noreply.github.com>
Date:   Fri Nov 13 16:14:57 2020 +0100

    Make filters only apply on button click #7

commit 220ec103
Author: Thor-Herman <thorherman.eggelen@gmail.com>
Date:   Fri Nov 13 15:04:17 2020 +0100

    Style filtering page #5 #7

commit 468e418e
Author: Thor-Herman <thorherman.eggelen@gmail.com>
Date:   Fri Nov 13 14:11:16 2020 +0100

    Fix Picker SelectedValue bug #7

    selectedValue uses deep comparison to objects in the options, and it couldn't find those because of object pointers were different

commit 4a66c29f
Author: Thor-Herman <thorherman.eggelen@gmail.com>
Date:   Fri Nov 13 13:29:41 2020 +0100

    Extract OrderingPicker #7

commit 7e3d719b
Author: Thor-Herman <thorherman.eggelen@gmail.com>
Date:   Fri Nov 13 13:26:18 2020 +0100

    Add Ordering and Filters to FilterPage #7

    Fix keys issue for checkedGenres as well

commit d90d4f77
Author: Thor-Herman <thorherman.eggelen@gmail.com>
Date:   Fri Nov 13 13:25:38 2020 +0100

    Create FilterList #7

commit 2d6ef07c
Author: Thor-Herman <thorherman.eggelen@gmail.com>
Date:   Fri Nov 13 11:51:01 2020 +0100

    Extract Components from BrowsePage

commit 97014c64
Author: Thor-Herman <thorherman.eggelen@gmail.com>
Date:   Fri Nov 13 11:45:38 2020 +0100

    Style FilterButton #7 #5

commit f151469a
Author: Thor-Herman <thorherman.eggelen@gmail.com>
Date:   Fri Nov 13 11:38:00 2020 +0100

    Create FilterPage and route #7 #12
parent 504590b8
import {CHANGE_ORDERING, OrderingAction, OrderingActionPayload} from "../types/ordering";
import AppThunk from "../types";
import {searchMovies, searchMovieTitles} from "./movieActions";
import {CHANGE_FILTER, FilterAction, FilterFormData} from "../types/filter";
import {
CHANGE_ORDERING,
OrderingAction,
OrderingActionPayload,
} from '../types/ordering';
import AppThunk from '../types';
import { searchMovies, searchMovieTitles } from './movieActions';
import { CHANGE_FILTER, FilterAction, FilterFormData } from '../types/filter';
import _ from 'lodash';
const applyFilter = (action: OrderingAction | FilterAction): AppThunk =>
(dispatch, getState) => {
dispatch(action);
return new Promise((resolve) => {
dispatch(searchMovies(true));
resolve();
})
}
export const applyFilter = (action: OrderingAction | FilterAction): AppThunk => (
dispatch,
getState
) => {
dispatch(action);
return new Promise((resolve) => {
dispatch(searchMovies(true));
resolve();
});
};
export const changeOrdering = (newOrder: OrderingActionPayload) =>
applyFilter({type: CHANGE_ORDERING, payload: newOrder});
export const changeOrdering = (newOrder: OrderingActionPayload) => {
return { type: CHANGE_ORDERING, payload: newOrder };
};
export const changeFilters = (data: FilterFormData) => {
return applyFilter({type: CHANGE_FILTER, payload: {
genres: {
..._.omit(data, ["to", "from"]) // Will return every genre and not to and from properties
},
year: {to: data.to, from: data.from}
}});
}
\ No newline at end of file
return {
type: CHANGE_FILTER,
payload: {
genres: {
..._.omit(data, ['to', 'from']), // Will return every genre and not to and from properties
},
year: { to: data.to, from: data.from },
},
};
};
......@@ -17,7 +17,7 @@ export const decideFilters = ({ filter, ordering }: FilteringState) => {
const decideRegex = (filterGenres: { [key in Genres]: boolean }) => {
let regex = '(';
const checkedGenres = _.values(_.pickBy(filterGenres, (genre: boolean) => genre))
const checkedGenres = _.keys(_.pickBy(filterGenres, (genre: boolean) => genre))
for (let i = 0; i < checkedGenres.length; i++) {
regex += checkedGenres[i].toString();
regex += i === checkedGenres.length - 1 ? '' : '|'; // Last element, don't add final |
......
......@@ -10,11 +10,13 @@ import SearchBar from './SearchBar';
import Header from './Header';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';
import FilterPage from '../pages/FilterPage';
export type StackParamList = {
Browse: undefined;
Movie: { id: number };
Filter: undefined,
};
const Stack = createStackNavigator<StackParamList>();
......@@ -35,6 +37,7 @@ const App = () => {
<Stack.Navigator initialRouteName='Browse'>
<Stack.Screen name='Browse' component={BrowsePage} />
<Stack.Screen name='Movie' component={MoviePage} />
<Stack.Screen name='Filter' component={FilterPage} />
</Stack.Navigator>
</Container>
</NavigationContainer>
......
import _ from "lodash";
import { List, ListItem, CheckBox, Body, Item } from "native-base";
import React, { useState } from "react";
import { Text } from "react-native";
import { useDispatch, useSelector } from "react-redux";
import { changeFilters } from "../actions";
import { RootState } from "../reducers";
import { FilterState, Genres } from "../types/filter";
const genres: Array<Genres> = [
"Action",
"Comedy",
"Musical",
"Romance",
"Drama",
"Horror",
];
const FilterList = () => {
const checkedGenres: Record<Genres, boolean> = useSelector((state: RootState) => state.filtering.filter.genres);
const dispatch = useDispatch()
const onPress = (genre: Genres) => {
const checked = checkedGenres[genre];
dispatch(changeFilters({...checkedGenres, [genre]: !checked, to: "", from: ""}));
};
const filterItems = genres.map((genre) => (
<ListItem onPress={() => onPress(genre)} key={genre}>
<CheckBox checked={checkedGenres[genre]} />
<Body>
<Text style={{paddingLeft: 10}}>{genre}</Text>
</Body>
</ListItem>
));
return (
<>
<Text style={{fontSize: 17, paddingLeft: 15, paddingTop: 10, color: "grey"}}>Genres: </Text>
<List>{filterItems}</List>
</>
);
};
export default FilterList;
import _ from 'lodash';
import { Item, Label, Picker, Icon, Text } from 'native-base';
import React, { useState } from 'react';
import { Platform, StyleSheet } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { changeOrdering } from '../actions';
import { RootState } from '../reducers';
import { OrderingActionPayload, OrderingOptions } from '../types/ordering';
const orderingOptions = [
// The available sorting options
'title - asc',
'title - desc',
'year - asc',
'year - desc',
'length - asc',
'length - desc',
];
// Picker for choosing what to sort the results on
const OrderingPicker = () => {
const dispatch = useDispatch();
const selectedOrdering = useSelector(
(state: RootState) => state.filtering.ordering
);
const value = `${selectedOrdering.key} - ${selectedOrdering.order}`; // To make it equal to the orderingOptions
const onValueChange = (value: string) => {
// Converts back to object and then dispatches it
const splitValue = value.split('-');
const key = splitValue[0].trim() as OrderingOptions;
const order = splitValue[1].trim() as 'asc' | 'desc';
dispatch(changeOrdering({ key, order }));
};
// Each of the selectable options in the picker. Changed so that they start with an upper case
const pickerItems = orderingOptions.map((option) => (
<Picker.Item
label={`${option[0].toUpperCase() + option.slice(1)}`}
value={option}
key={option}
/>
));
return (
<Item picker style={styles.itemStyle}>
<Text style={styles.textStyle}>Order By..</Text>
<Picker
mode='dropdown'
iosIcon={<Icon name='arrow-down' />}
placeholder=''
placeholderStyle={{ color: '#bfc6ea' }}
placeholderIconColor='#007aff'
style={{ width: undefined }}
selectedValue={value}
onValueChange={onValueChange}
>
{pickerItems}
</Picker>
</Item>
);
};
const styles = StyleSheet.create({
itemStyle: {
display: 'flex',
padding: 5,
paddingLeft: 15,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
textStyle: { color: 'grey', fontSize: 17 },
});
export default OrderingPicker;
import { View, Button } from "native-base";
import React from "react";
import SearchBar from "./SearchBar";
import { StyleSheet, Text } from 'react-native';
import { BrowseScreenProps } from "../pages/BrowsePage";
// SearchBar and Filter Button combined
const Search = ({navigation}: BrowseScreenProps) => {
return (
<View style={styles.searchView}>
<SearchBar />
<Button
light
onPress={() => navigation.navigate("Filter")}
style={styles.filterButton}
>
<Text style={styles.filterText}>Filter</Text>
</Button>
</View>
);
};
const styles = StyleSheet.create({
searchView: {
display: "flex",
flexDirection: "row",
alignItems: "flex-start",
},
filterButton: {
margin: 5,
width: 60,
display: "flex",
alignItems: "center",
justifyContent: "center",
},
filterText: {
fontSize: 15,
},
});
export default Search;
import { StackNavigationProp } from '@react-navigation/stack';
import { Button, Card, Content } from 'native-base';
import React, { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import { TouchableNativeFeedback } from 'react-native-gesture-handler';
import { useDispatch, useSelector } from 'react-redux';
import { searchMovies } from '../actions';
import { StackParamList } from '../components/App';
import MovieCard from '../components/MovieCard';
import PaginationButton from '../components/PaginationButton';
import SearchBar from '../components/SearchBar';
import { selectMoviesByAllIdsOrdering } from '../selectors/movieSelector';
import { StackNavigationProp } from "@react-navigation/stack";
import { Button, Card, Content } from "native-base";
import React, { useEffect, useState } from "react";
import { View, Text, StyleSheet } from "react-native";
import { useDispatch, useSelector } from "react-redux";
import { searchMovies } from "../actions";
import { StackParamList } from "../components/App";
import MovieCard from "../components/MovieCard";
import PaginationButton from "../components/PaginationButton";
import Search from "../components/Search";
import SearchBar from "../components/SearchBar";
import { selectMoviesByAllIdsOrdering } from "../selectors/movieSelector";
type BrowseScreenNavigationProp = StackNavigationProp<StackParamList, 'Movie'>;
type Props = {
type BrowseScreenNavigationProp = StackNavigationProp<StackParamList, "Browse">;
export type BrowseScreenProps = {
navigation: BrowseScreenNavigationProp;
};
const BrowsePage = ({navigation}: Props) => {
const BrowsePage = ({ navigation }: BrowseScreenProps) => {
const dispatch = useDispatch();
const movies = useSelector(selectMoviesByAllIdsOrdering);
const movieElements = Object.values(movies).map((movie) => ( // List of all the movies that returned from the search
const movieElements = Object.values(movies).map((
movie // List of all the movies that returned from the search
) => (
<MovieCard
id={movie.id}
image={movie.image}
title={movie.title}
key={movie.id}
onPress={() => navigation.navigate('Movie', {id: movie.id})}
onPress={() => navigation.navigate("Movie", { id: movie.id })}
/>
));
const determineRender = () => {
return movieElements.length === 0 ? (
<Text>No movies could be found for those criteria :-(</Text>
) : (
<Card>
{movieElements}
</Card>
<Card>{movieElements}</Card>
);
};
useEffect(() => { // Does an initial search for all movies when page launches
useEffect(() => {
// Does an initial search for all movies when page launches
dispatch(searchMovies(true));
}, []);
return (
<Content>
<SearchBar />
<Search navigation={navigation} />
{determineRender()}
<PaginationButton />
</Content>
......
import React, { useState } from 'react';
import {
Button,
Form,
Icon,
Item,
Label,
Picker,
Text,
View,
} from 'native-base';
import { StackParamList } from '../components/App';
import { StackNavigationProp } from '@react-navigation/stack';
import { Genres } from '../types/filter';
import _ from 'lodash';
import FilterList from '../components/FilterList';
import OrderingPicker from '../components/OrderingPicker';
import { StyleSheet } from 'react-native';
import { useDispatch } from 'react-redux';
import { applyFilter, searchMovies } from '../actions';
type FilterScreenNavigationProp = StackNavigationProp<StackParamList, 'Filter'>;
type Props = {
navigation: FilterScreenNavigationProp;
};
const FilterPage = ({ navigation }: Props) => {
const dispatch = useDispatch();
return (
<View>
<View style={styles.filteringView}>
<Form>
<FilterList />
<OrderingPicker />
</Form>
</View>
<View style={styles.buttonView}>
<Button
info
onPress={() => {
dispatch(searchMovies(true));
navigation.goBack();
}}
style={styles.button}
>
<Text>Apply</Text>
</Button>
</View>
</View>
);
};
const styles = StyleSheet.create({
filteringView: { display: 'flex', justifyContent: 'space-between' },
buttonView: {
flexDirection: 'row',
justifyContent: 'center',
paddingTop: 160,
},
button: {
display: 'flex',
justifyContent: 'center',
padding: 15,
borderRadius: 10,
},
});
export default FilterPage;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment