Commit cb6f9439 authored by Simen Kristoffersen's avatar Simen Kristoffersen
Browse files

Merge branch '16-fikse-layout-av-filmvisning' into 'master'

#16 - Fikset styling av filmvisning

Closes #16

See merge request !14
parents ec63ee8a 4df7a273
import React from 'react';
import { StyleSheet, TextInput } from 'react-native';
import { View } from '../components/Themed';
import React from "react";
import { StyleSheet } from "react-native";
import { View } from "../components/Themed";
import {
FilterKeys,
FilterValues,
SortDirection,
SortKeys,
} from '../constants/filterOptions/interface';
import { genres, sortDirections, sortValues } from '../constants/filterOptions';
import SelectInput from './SelectInput';
import SearchInput from './SearchInput';
} from "../constants/filterOptions/interface";
import { genres, sortDirections, sortValues } from "../constants/filterOptions";
import SelectInput from "./SelectInput";
import SearchInput from "./SearchInput";
export type FilterPaneProps = {
onSearchChange: (value: string) => void;
......@@ -29,20 +29,21 @@ const FilterPane: React.FC<FilterPaneProps> = ({
<View style={styles.row}>
<View>
<SelectInput
defaultValue="Genres"
defaultValue="All genres"
data={genres}
onChange={(value) => onFilterChange('genres', value ? [value] : [])}
onChange={(value) => onFilterChange("genres", value ? [value] : [])}
/>
</View>
<View style={styles.inlineRow}>
<SelectInput
defaultValue="Order by"
data={sortValues}
onChange={(value) => onSortChange(value ? value : 'title')}
onChange={(value) => onSortChange(value ? value : "title")}
/>
<SelectInput
data={sortDirections}
onChange={(value) => onSortDirectionChange(value ? value : 'asc')}
style={{ marginRight: 4 }}
onChange={(value) => onSortDirectionChange(value ? value : "asc")}
/>
</View>
</View>
......@@ -59,22 +60,19 @@ const FilterPane: React.FC<FilterPaneProps> = ({
const styles = StyleSheet.create({
container: {
marginTop: 8,
marginHorizontal: 16,
width: '95%',
display: 'flex',
width: "100%",
paddingVertical: "2%",
},
row: {
flex: 1,
display: 'flex',
flexDirection: 'row',
paddingVertical: 4,
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: "row",
paddingVertical: 5,
justifyContent: "space-between",
},
inlineRow: {
display: 'flex',
flexDirection: 'row',
flexShrink: 1,
flexGrow: 0,
flexDirection: "row",
justifyContent: "flex-end",
},
});
......
import React, { useEffect, useState } from 'react';
import { Image, StyleSheet, Text, View } from 'react-native';
import { AirbnbRating } from 'react-native-ratings';
import { MovieEntity } from '../store/ducks/movies/types';
import { Dimensions } from 'react-native';
import React, { useEffect, useState } from "react";
import { Image, StyleSheet, Text, View } from "react-native";
import { AirbnbRating } from "react-native-ratings";
import { MovieEntity } from "../store/ducks/movies/types";
import { Dimensions } from "react-native";
import { useThemeColor } from "./Themed";
interface MovieCardProps {
movie: MovieEntity;
}
const window = Dimensions.get('window');
const screen = Dimensions.get('screen');
const window = Dimensions.get("window");
const screen = Dimensions.get("screen");
/**
* Movie Card component
*/
......@@ -26,13 +27,15 @@ const MovieCard = (props: MovieCardProps): JSX.Element => {
};
useEffect(() => {
Dimensions.addEventListener('change', ({ window, screen }) => {
Dimensions.addEventListener("change", ({ window, screen }) => {
setDimensions({ window, screen });
});
}, []);
return (
<View style={styles.card}>
<View
style={[styles.card, { backgroundColor: useThemeColor({}, "component") }]}
>
<Image
source={{
uri: movie.poster,
......@@ -40,7 +43,11 @@ const MovieCard = (props: MovieCardProps): JSX.Element => {
style={[styles.poster, { width: iw(), height: ih() }]}
/>
<View style={[styles.cardInfo, { maxWidth: iw() }]}>
<Text style={styles.titleText}>{movie.title}</Text>
<Text
style={[styles.titleText, { color: useThemeColor({}, "inputText") }]}
>
{movie.title}
</Text>
<AirbnbRating
count={5}
defaultRating={movie.rating}
......@@ -57,33 +64,32 @@ const styles = StyleSheet.create({
poster: {
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
resizeMode: 'contain',
resizeMode: "contain",
maxWidth: 300,
},
card: {
backgroundColor: 'white',
flex: 1 / 2,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
display: "flex",
justifyContent: "center",
alignItems: "center",
marginHorizontal: 4,
marginVertical: 4,
borderRadius: 5,
textAlign: 'center',
textAlign: "center",
shadowColor: '#171717',
shadowColor: "#171717",
shadowOffset: { width: -2, height: 4 },
shadowOpacity: 0.2,
shadowRadius: 3,
elevation: 1,
},
titleText: {
textAlign: 'center',
fontWeight: 'bold',
textAlign: "center",
fontWeight: "bold",
},
cardInfo: {
padding: 4,
textAlign: 'center',
textAlign: "center",
minHeight: 75,
},
});
......
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState } from "react";
import {
ActivityIndicator,
FlatList,
StyleSheet,
Text,
View,
} from 'react-native';
import { useSelector } from 'react-redux';
import { FetchMovieParams, MovieEntity } from '../store/ducks/movies/types';
import { ApplicationState } from '../store/interface';
import MovieCard from './MovieCard';
Text,
} from "react-native";
import { useSelector } from "react-redux";
import { FetchMovieParams, MovieEntity } from "../store/ducks/movies/types";
import { ApplicationState } from "../store/interface";
import MovieCard from "./MovieCard";
import { useThemeColor } from "./Themed";
export type MovieTableProps = {
path: string;
......@@ -31,6 +32,8 @@ const MovieTable: React.FC<MovieTableProps> = ({
const { documentCount } = useSelector(
({ movies }: ApplicationState) => movies
);
// Fetch theme color for the spinner
const color = useThemeColor({}, "inputText");
const [isFull, setIsfull] = useState(false);
......@@ -43,7 +46,7 @@ const MovieTable: React.FC<MovieTableProps> = ({
{movies.length > 0 || moviesLoading ? (
<FlatList
style={styles.movieFlatList}
columnWrapperStyle={{ flex: 1, justifyContent: 'space-around' }}
columnWrapperStyle={{ flex: 1, justifyContent: "space-around" }}
contentContainerStyle={styles.movieItems}
data={movies}
numColumns={2}
......@@ -68,13 +71,15 @@ const MovieTable: React.FC<MovieTableProps> = ({
<>
{!isFull ? (
<>
<Text style={styles.loading}>Loading...</Text>
<Text style={[styles.loading, { color }]}>Loading...</Text>
<ActivityIndicator size="large" color="#00ff00" />
</>
) : (
<>
{movies.length > 4 && (
<Text style={styles.loading}>--- No more movies ---</Text>
<Text style={[styles.loading, { color }]}>
--- No more movies ---
</Text>
)}
</>
)}
......@@ -82,7 +87,7 @@ const MovieTable: React.FC<MovieTableProps> = ({
)}
/>
) : (
<Text style={styles.loading}>No Movies Found</Text>
<Text style={[styles.loading, { color }]}>No Movies Found</Text>
)}
</View>
);
......@@ -90,29 +95,27 @@ const MovieTable: React.FC<MovieTableProps> = ({
const styles = StyleSheet.create({
movieItems: {
justifyContent: 'center',
justifyContent: "center",
flexGrow: 1 / 2,
backgroundColor: 'white',
},
movieFlatList: {
width: '100%',
height: '100%',
width: "100%",
height: "100%",
marginBottom: 4,
},
movieList: {
marginTop: 4,
width: '95%',
height: '95%',
alignItems: 'center',
textAlign: 'center',
height: "95%",
alignItems: "center",
textAlign: "center",
flex: 1,
},
loading: {
flex: 1,
fontSize: 20,
fontWeight: 'bold',
fontSize: 16,
fontWeight: "bold",
marginBottom: 16,
textAlign: 'center',
textAlign: "center",
},
});
......
import React from 'react';
import { StyleSheet, TextInput } from 'react-native';
import React from "react";
import { StyleSheet, TextInput } from "react-native";
import { useThemeColor } from "./Themed";
type IProps = {
searchThreshold: number;
placeholder: string;
......@@ -11,11 +12,18 @@ const SearchInput: React.FC<IProps> = (props: IProps) => {
// Return string value if the string's length is above the set threshold
// else, return an empty string
function handleChange(value: string) {
onChange(value.length >= searchThreshold ? value : '');
onChange(value.length >= searchThreshold ? value : "");
}
return (
<TextInput
style={styles.input}
style={[
styles.input,
{
backgroundColor: useThemeColor({}, "component"),
borderColor: useThemeColor({}, "border"),
color: useThemeColor({}, "inputText"),
},
]}
placeholder={props.placeholder}
onChangeText={handleChange}
/>
......@@ -24,12 +32,11 @@ const SearchInput: React.FC<IProps> = (props: IProps) => {
const styles = StyleSheet.create({
input: {
height: 40,
fontSize: 18,
borderWidth: 1,
borderRadius: 15,
borderColor: '#333',
width: '100%',
marginLeft: 4,
marginRight: 4,
paddingVertical: 4,
width: "100%",
paddingHorizontal: 12,
},
});
......
import React from 'react';
import { StyleProp, StyleSheet, TextStyle } from 'react-native';
import { Picker } from '@react-native-picker/picker';
import { capitalize } from '../utils/textTransform';
import React from "react";
import {
StyleProp,
StyleSheet,
TextStyle,
View,
ViewStyle,
} from "react-native";
import { capitalize } from "../utils/textTransform";
import SelectDowndown from "react-native-select-dropdown";
import { useThemeColor } from "./Themed";
import { useTheme } from "react-native-elements";
type IProps<T> = {
data: T[];
style?: ViewStyle;
defaultValue?: string;
onChange?: (value: T | null) => void;
};
function SelectInput<T extends string>(props: IProps<T>) {
const { data, defaultValue, onChange } = props;
const { defaultValue, onChange } = props;
let { data } = props;
// Append default value if it's defined
if (defaultValue) {
data = [defaultValue as T, ...data];
}
// Return 'null' if default value is selected
function dispatchValue(value: T | string) {
if (onChange) {
onChange(value === defaultValue ? null : (value as T));
onChange(value === defaultValue ? null : (value.toLowerCase() as T));
}
}
return (
<Picker<T> style={styles.input} onValueChange={dispatchValue}>
{defaultValue && <Picker.Item label={defaultValue} />}
{data.length > 0 &&
data.map((item) => (
<Picker.Item
key={item}
label={capitalize(item)}
value={item.toLowerCase()}
/>
))}
</Picker>
<SelectDowndown
buttonStyle={[
styles.button,
props.style,
{
backgroundColor: useThemeColor({}, "component"),
borderColor: useThemeColor({}, "border"),
},
]}
buttonTextStyle={[styles.text, { color: useThemeColor({}, "inputText") }]}
dropdownStyle={{
backgroundColor: useThemeColor({}, "component"),
}}
rowStyle={{ borderColor: useThemeColor({}, "border") }}
rowTextStyle={[styles.text, { color: useThemeColor({}, "inputText") }]}
data={data}
onSelect={(selected) => dispatchValue(selected)}
defaultValueByIndex={0}
buttonTextAfterSelection={(selected, index) => capitalize(selected)}
rowTextForSelection={(item) => capitalize(item)}
/>
);
}
const styles = StyleSheet.create({
input: {
minWidth: 100,
button: {
height: 40,
maxWidth: 100,
borderWidth: 1,
borderRadius: 15,
marginLeft: 4,
marginRight: 4,
padding: 4,
padding: 0,
},
text: {
paddingVertical: 0,
marginVertical: 0,
fontSize: 14,
},
});
......
......@@ -44,6 +44,7 @@ export function View(props: ViewProps) {
{ light: lightColor, dark: darkColor },
"background"
);
//const color = useThemeColor({ light: lightColor, dark: darkColor }, "text");
return <DefaultView style={[{ backgroundColor }, style]} {...otherProps} />;
}
const tintColorLight = '#2f95dc';
const tintColorDark = '#fff';
const tintColorLight = "#2f95dc";
const tintColorDark = "#fff";
export default {
light: {
text: '#000',
background: '#fff',
text: "#000",
inputText: "#333",
background: "#fff",
component: "rgb(240, 240, 240)",
border: "rgb(221, 221, 221)",
tint: tintColorLight,
tabIconDefault: '#ccc',
tabIconDefault: "#ccc",
tabIconSelected: tintColorLight,
},
dark: {
text: '#fff',
background: '#000',
text: "#fff",
inputText: "rgb(220, 220, 220)",
background: "#000",
component: "rgb(18, 18, 18)",
border: "rgb(39, 39, 41)",
tint: tintColorDark,
tabIconDefault: '#ccc',
tabIconDefault: "#ccc",
tabIconSelected: tintColorDark,
},
};
......@@ -2,17 +2,17 @@ import { Genre } from "../../store/ducks/movies/types";
import { SortDirection, SortKeys } from "./interface";
export const genres: Genre[] = [
"Action",
"Adventure",
"Comedy",
"Drama",
"Fantasy",
"Horror",
"Mystery",
"Romance",
"Science Fiction",
"Thriller",
"Western",
"action",
"adventure",
"comedy",
"drama",
"fantasy",
"horror",
"mystery",
"romance",
"science fiction",
"thriller",
"western",
];
type DefaultValue = [string];
export const sortValues: SortKeys[] = ["title", "year", "rating"];
......
......@@ -3,26 +3,26 @@
* https://reactnavigation.org/docs/getting-started
*
*/
import { FontAwesome, MaterialIcons } from '@expo/vector-icons';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { FontAwesome, MaterialIcons } from "@expo/vector-icons";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import {
DarkTheme,
DefaultTheme,
NavigationContainer,
} from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import * as React from 'react';
import { ColorSchemeName, Pressable } from 'react-native';
import { useDispatch } from 'react-redux';
import Colors from '../constants/Colors';
import useColorScheme from '../hooks/useColorScheme';
import ModalScreen from '../screens/ModalScreen';
import MovieTableScreen from '../screens/MovieTableScreen';
import NotFoundScreen from '../screens/NotFoundScreen';
import Profile from '../screens/Profile';
import { signOut } from '../store/ducks/auth/actions';
import { RootStackParamList, RootTabParamList } from '../types';
import LinkingConfiguration from './LinkingConfiguration';
} from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import * as React from "react";
import { ColorSchemeName, Pressable } from "react-native";
import { useDispatch } from "react-redux";
import Colors from "../constants/Colors";
import useColorScheme from "../hooks/useColorScheme";
import ModalScreen from "../screens/ModalScreen";
import MovieTableScreen from "../screens/MovieTableScreen";
import NotFoundScreen from "../screens/NotFoundScreen";
import Profile from "../screens/Profile";
import { signOut } from "../store/ducks/auth/actions";
import { RootStackParamList, RootTabParamList } from "../types";
import LinkingConfiguration from "./LinkingConfiguration";
export default function Navigation({
colorScheme,
......@@ -32,7 +32,7 @@ export default function Navigation({
return (
<NavigationContainer
linking={LinkingConfiguration}
theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}
theme={colorScheme === "dark" ? DarkTheme : DefaultTheme}
>
<RootNavigator />
</NavigationContainer>
......@@ -53,12 +53,12 @@ function RootNavigator() {
component={BottomTabNavigator}
options={{ headerShown: false }}
/>
<Stack.Screen
{/* <Stack.Screen
name="NotFound"
component={NotFoundScreen}
options={{ title: 'Oops!' }}
/>
<Stack.Group screenOptions={{ presentation: 'modal' }}>
/> */}
<Stack.Group screenOptions={{ presentation: "modal" }}>
<Stack.Screen name="Modal" component={ModalScreen} />
</Stack.Group>
</Stack.Navigator>
......@@ -86,7 +86,7 @@ const BottomTabNavigator = () => {
name="Movies"
component={MovieTableScreen}
options={{
title: 'Movies',
title: "Movies",
tabBarIcon: ({ color }) => (
<MaterialIcons
name="movie"
......@@ -101,7 +101,7 @@ const BottomTabNavigator = () => {
name="Profile"
component={Profile}
options={{
title: 'Profile',
title: "Profile",
tabBarIcon: ({ color }) => (
<MaterialIcons
name="person-outline"
......