Commit 61125920 authored by Dherik Jenitan Devakumar's avatar Dherik Jenitan Devakumar
Browse files

Merge branch '15-cleanup' into 'main'

Resolve "Cleanup"

Closes #15

See merge request !18
parents 7626a5b8 2fda3b47
......@@ -6,7 +6,8 @@ interface FavButtonProps {
isFavorited: boolean;
userName: string;
id: string;
}
onPressed: (newValue: boolean, id: string) => void;
}
/**
* This is a component for favoriting a movie. We take in three props.
......@@ -15,7 +16,7 @@ interface FavButtonProps {
* @param isFavorited, userName, id
* @returns a button with Heart.
*/
const FavButton: React.FC<FavButtonProps> =({isFavorited, userName, id}) => {
const FavButton: React.FC<FavButtonProps> =({isFavorited, userName, id, onPressed}) => {
const [favorited, setFavorited] = useState(isFavorited);
......@@ -41,6 +42,9 @@ const FavButton: React.FC<FavButtonProps> =({isFavorited, userName, id}) => {
}
}
/* Sets movie as favorite through API and changes local state through callback
to parent component. The state is changed to keep track of local favorites not stored in redux store. */
function clickFavorite(didUserFavorite: boolean) {
if (didUserFavorite) {
setFavorite();
......@@ -48,6 +52,7 @@ const FavButton: React.FC<FavButtonProps> =({isFavorited, userName, id}) => {
else {
removeFavorite();
}
onPressed(!favorited, id)
setFavorited(!favorited);
}
......
import * as React from 'react';
import { Modal, StyleSheet, View } from 'react-native';
import { Portal, Text, Button, Provider, Title, TextInput, IconButton } from 'react-native-paper';
import { Button, Title, TextInput, IconButton } from 'react-native-paper';
import { useAppDispatch } from '../../services/hooks';
import { loginAsUser, logOut } from './loginslice';
import { useState } from 'react';
......@@ -33,9 +33,16 @@ const LoginModal:React.FC<SignInProps> = ({isLoginModalVisible, setIsModalVisibl
'Quicksand-Regular': require('../../assets/fonts/Quicksand-Regular.ttf'),
})
/* Calls redux dispatch to change state when logging out and removes modal */
const handleLogOut = () => {
logOut()
setIsModalVisible(false);
}
/* Calls redux dispatch to log in and set user and removes modal */
const handleSubmit = () => {
setUser(value);
setIsModalVisible;
setIsModalVisible(false);
}
if (!fontsLoaded) {
......@@ -81,7 +88,7 @@ const LoginModal:React.FC<SignInProps> = ({isLoginModalVisible, setIsModalVisibl
>
<Button
mode="contained"
onPress={() => logOut()}
onPress={handleLogOut}
color="white"
labelStyle={{
fontFamily: 'Quicksand-Regular',
......
......@@ -6,6 +6,7 @@ const initialState: IUserState = {
userName: undefined,
};
/* Reducer and actions that changes state */
const UserSlice = createSlice({
name: "userSlice",
initialState,
......
import React, { useState } from "react";
import { selectUserName, selectUserIsLoggedIn } from "../../services/selectors"
import React from "react";
import { selectUserIsLoggedIn, selectUserName } from "../../services/selectors"
import { useSelector } from "react-redux";
import {
formatDateAsString,
......@@ -23,16 +23,20 @@ interface ModalProps {
* @param movie to show, onCloseClick function to close/open the modal
* @returns a Modal with MovieDetal.
*/
const MovieModal: React.FC<ModalProps> = ({movie, setIsModalVisible, isModalVisible}) => {
const MovieModal: React.FC<ModalProps> = ({movie, setIsModalVisible, isModalVisible}:ModalProps) => {
const userName = useSelector(selectUserName)
const isLoggedIn = useState(true);
const isLoggedIn = useSelector(selectUserIsLoggedIn)
const [fontsLoaded] = useFonts({
'Quicksand-Regular': require('../../assets/fonts/Quicksand-Regular.ttf'),
'Quicksand-Medium': require('../../assets/fonts/Quicksand-Medium.ttf'),
'Quicksand-SemiBold': require('../../assets/fonts/Quicksand-Medium.ttf'),
})
/* Returns an array with the current user (username) removed */
function getReducedArray(array: Array<string>): Array<string> {
if (userName === undefined) {
return []
}
const temp = [...array]
const index = temp.indexOf(userName, 0);
if (index > -1) {
......@@ -41,6 +45,7 @@ const MovieModal: React.FC<ModalProps> = ({movie, setIsModalVisible, isModalVisi
return temp;
}
/* Shows the modal based on the prop value from parent component */
if(isModalVisible){
return (
<Modal
......
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import { selectMovies, selectUserIsLoggedIn, selectUserName } from "../../services/selectors";
import { searchMovies_getMoviesBySearch } from "../../services/__generated__/searchMovies"
import FavButton from "../favButton";
import { Card, Title } from "react-native-paper";
import { StyleSheet, SafeAreaView, FlatList, View } from "react-native";
import { StyleSheet, SafeAreaView, FlatList, View, Text } from "react-native";
import { useFonts } from "@expo-google-fonts/inter";
import AppLoading from 'expo-app-loading';
import MovieModal from "../moviedetail/MovieModal";
import { TouchableOpacity, TouchableWithoutFeedback } from 'react-native';
import { LogBox } from 'react-native';
/**
* This is a component displaying all the movies.
* We are using MUI Card components and rendering
* them based on movies fetched form the database.
* The movies are fetched form the database.
*/
/* Interface for object with movie */
interface IMovieObject {
item: searchMovies_getMoviesBySearch;
}
......@@ -25,25 +25,58 @@ interface MovieTableProps {
fetchMore: () => void;
}
/* Interface for object with favorite information */
type favoritedMovie = {
id:string;
isFavorited:boolean;
}
const MovieTable: React.FC<MovieTableProps> = ({fetchMore}) => {
const movies = useSelector(selectMovies);
const isLoggedIn = useSelector(selectUserIsLoggedIn);
const userName = useSelector(selectUserName)
const [modalVisible, setModalVisible] = useState(false);
const [modalMovie, setModalMovie] = useState(null);
const [modalMovie, setModalMovie] = useState<searchMovies_getMoviesBySearch>();
const [favoritedInSession, setFavoritedInSession] = useState<Array<favoritedMovie>>([]);
const [fontsLoaded] = useFonts({
'Quicksand-Regular': require('../../assets/fonts/Quicksand-Regular.ttf')
})
/* Returns true or false based on component state. If component state does not contain a value for the movie,
the favorited value for the logged in user of the movie is returned */
function isFavorited(movie: searchMovies_getMoviesBySearch): boolean {
if (movie === null || !userName ) {
return false;
}
let favoritedArray = favoritedInSession.filter(favoritedInSession => favoritedInSession.id === movie.id)
if (favoritedArray.length > 0 ) {
return favoritedArray[0].isFavorited;
}
return movie.favoritedByUser.includes(userName)
}
useEffect(() => {
//Flatlist needs to be in ScrollView in our case.
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
}, [])
/* Returns a reduced array with the favorited movie only occuring once, if it already exists, update with new value */
function handleFavorize(newValue: boolean, id: string) {
let alreadyInFavorites = favoritedInSession.filter(favoritedInSession => favoritedInSession.id === id)
if (alreadyInFavorites.length > 0) {
let newFav = alreadyInFavorites[0]
newFav.isFavorited = newValue
} else {
setFavoritedInSession((state) => [...state, {id:id, isFavorited:newValue}])
}
}
const Movie = ({item}: IMovieObject) => (
<Card
style={styles.cardContainer}
......@@ -59,7 +92,7 @@ const MovieTable: React.FC<MovieTableProps> = ({fetchMore}) => {
</View>
<Card.Content style={styles.contentContainer}>
{ isLoggedIn
? <FavButton isFavorited={isFavorited(item)} userName={userName !== undefined ? userName : ""} id={item.id}/>
? <FavButton isFavorited={isFavorited(item)} userName={userName !== undefined ? userName : ""} id={item.id} onPressed = {(newValue: boolean, id: string) => handleFavorize(newValue, id)}/>
: null
}
<Title style={styles.title}>{item?.title}</Title>
......@@ -75,6 +108,7 @@ const MovieTable: React.FC<MovieTableProps> = ({fetchMore}) => {
);
};
if (!fontsLoaded) {
return <AppLoading />
} else {
......@@ -89,8 +123,9 @@ const MovieTable: React.FC<MovieTableProps> = ({fetchMore}) => {
paddingBottom:350,
}}
onEndReached={fetchMore}
/>
<MovieModal isModalVisible={modalVisible} setIsModalVisible={setModalVisible} movie={modalMovie}/>
>
</FlatList>
{modalMovie ? <MovieModal isModalVisible={modalVisible} setIsModalVisible={setModalVisible} movie={modalMovie}/> : null}
</SafeAreaView>
)
}
......
import * as React from "react";
import { useSelector } from "react-redux";
import { useAppDispatch } from "../../services/hooks";
import { Dispatch } from "redux";
import { setSearchQuery } from "../../pages/mainPageSlice";
import { logOut } from "../login/loginslice"
import { selectUserIsLoggedIn } from "../../services/selectors";
import { useState } from "react";
import { Appbar } from "react-native-paper";
import { StyleSheet } from "react-native";
import { useFonts, Quicksand_600SemiBold} from '@expo-google-fonts/quicksand';
import { useFonts} from '@expo-google-fonts/quicksand';
import AppLoading from "expo-app-loading";
import LoginModal from "../login/login";
const actionDispatch = (dispatch: Dispatch) => ({
setSearch: (query: string) => dispatch(setSearchQuery(query)),
setLogOut: () => dispatch(logOut()),
});
interface NavBarProps{
isLoginModalVisible:boolean;
onCloseClick: () => void;
closeSideBar: () => void;
isSideBarVisible:boolean;
......@@ -28,12 +15,10 @@ interface NavBarProps{
* This is the header for our app. We use AppBar component from MUI.
* The component both have the search function and userLogIn.
*
* @param isLoginModalVisible, onCloseClick
* @returns header to show.
*/
const NavBar: React.FC<NavBarProps> = ({isLoginModalVisible, onCloseClick, closeSideBar, isSideBarVisible}) => {
const {setLogOut} = actionDispatch(useAppDispatch());
const isLoggedIn = useSelector(selectUserIsLoggedIn)
const NavBar: React.FC<NavBarProps> = ({onCloseClick, closeSideBar, isSideBarVisible}) => {
let [fontsLoaded] = useFonts({
'Quicksand-SemiBold': require('../../assets/fonts/Quicksand-SemiBold.ttf'),
......
......@@ -2,8 +2,8 @@ import React,{ FunctionComponent, useState } from "react";
import { useAppDispatch } from "../../services/hooks";
import { Dispatch } from "redux";
import { setFilterGenres } from "../../pages/mainPageSlice";
import { FlatList, StyleSheet, Text, View } from "react-native";
import { Title, Button } from "react-native-paper";
import { FlatList, StyleSheet, View } from "react-native";
import { Title } from "react-native-paper";
import { useFonts } from "@expo-google-fonts/quicksand";
import AppLoading from "expo-app-loading";
import { CheckBox } from 'react-native-elements'
......@@ -12,6 +12,7 @@ const actionDispatch = (dispatch: Dispatch) => ({
setFilter: (filter:object) => dispatch(setFilterGenres(filter)),
});
/* All genres */
const genres = [
{id:"1", name: "Action", isChecked: false},
{id: "2", name: "Adventure", isChecked: false},
......@@ -49,7 +50,12 @@ const FilterGenreComp: FunctionComponent = () => {
'Quicksand-Regular': require('../../assets/fonts/Quicksand-Regular.ttf'),
})
const onSelectionsChange = (id:string) => {
/**
* Updates selected genres.
*
* @param id id of genre
*/
const onSelectionsChange = (id:string) => {
let temp = selectedGenres.map((genre) => {
if (id === genre.id) {
return { ...genre, isChecked: !genre.isChecked };
......@@ -60,6 +66,11 @@ const FilterGenreComp: FunctionComponent = () => {
updateFilters(temp);
}
/**
* Method used to filter by genre.
*
* @param genreArray All selected genre to filter by
*/
function updateFilters(genreArray: Array<{ id: string; name: string; isChecked: boolean; }>) {
var filteredArray = genreArray.filter(genre => genre.isChecked === true).map(({name}) => name)
setFilter(filteredArray)
......
......@@ -51,7 +51,7 @@ const FilterByYear: FunctionComponent = () => {
>Filter by year</Title>
<TextInput
label="From"
keyboardType="numeric"
keyboardType="number-pad"
placeholder="Type year here"
maxLength={4}
onChangeText={(newValue: string) => {
......@@ -60,7 +60,7 @@ const FilterByYear: FunctionComponent = () => {
/>
<TextInput
label="To"
keyboardType="numeric"
keyboardType="number-pad"
placeholder="Type year here"
maxLength={4}
onChangeText={(newValue: string) => {
......
import React, { useState } from "react";
import { useAppDispatch } from "../../services/hooks";
import { Dispatch } from "redux";
import { useSelector } from "react-redux";
import { setSearchQuery } from "../../pages/mainPageSlice";
import { FunctionComponent } from "react";
import { View, StyleSheet } from "react-native";
import { View } from "react-native";
import { Button, Searchbar, Title } from "react-native-paper";
import { useFonts } from "@expo-google-fonts/quicksand";
import AppLoading from "expo-app-loading";
import FilterByGenre from "./FilterByGenre";
const actionDispatch = (dispatch: Dispatch) => ({
setSearch: (query: string) => dispatch(setSearchQuery(query)),
});
const SearchComp: FunctionComponent = () => {
const [localSearch, setLocalSearch] = useState<string>("");
const [localSearch, setLocalSearch] = useState<string | null>(null);
const { setSearch } = actionDispatch(useAppDispatch());
let [fontsLoaded] = useFonts({
'Quicksand-Medium': require('../../assets/fonts/Quicksand-Medium.ttf'),
......@@ -24,7 +21,7 @@ const SearchComp: FunctionComponent = () => {
})
const searchEvent = () => {
setSearch(localSearch);
if (localSearch !== null) setSearch(localSearch);
}
const onChangeSearch = (query:string) => setLocalSearch(query);
......@@ -46,7 +43,7 @@ const SearchComp: FunctionComponent = () => {
<Searchbar
placeholder="Search..."
onChangeText={onChangeSearch}
value={localSearch}
value={localSearch ? localSearch : ""}
iconColor="black"
inputStyle={{
fontFamily: 'Quicksand-Regular',
......
import React, { FunctionComponent, useState } from "react";
import { TouchableOpacity, Text, StyleSheet, ScrollView, SafeAreaView, View } from "react-native";
import React, { FunctionComponent, } from "react";
import { StyleSheet, ScrollView,View } from "react-native";
import FilterByYear from "./FilterByYear";
import FilterGenreComp from "./FilterByGenre";
......@@ -8,14 +8,10 @@ import SearchComp from './Search';
/**
* Global SideBar
* Menu that contains search and filter options
*/
const SideBar: FunctionComponent = () => {
interface SideBarProps {
isSideBarVisible: boolean;
closeSideBar: () => void;
}
const SideBar: FunctionComponent<SideBarProps> = ({isSideBarVisible, closeSideBar}) => {
return (
......@@ -37,7 +33,7 @@ const styles = StyleSheet.create({
display:'flex',
},
scrollContainer: {
paddingBottom:50,
paddingBottom:450,
}
})
export default SideBar;
......@@ -21,7 +21,6 @@ const actionDispatch = (dispatch: Dispatch) => ({
*
*/
export default function SortDropDown() {
const [open, setOpen] = useState(false);
const [sortBy, setSortBy] = useState('');
const { setCriteria } = actionDispatch(useAppDispatch());
......
import React from 'react';
import { View, Text } from 'react-native';
import { useSelector } from 'react-redux';
import {selectUserIsLoggedIn, selectUserName} from '../../services/selectors';
import {selectUserName} from '../../services/selectors';
import { StyleSheet } from 'react-native';
import { useFonts } from 'expo-font';
import AppLoading from 'expo-app-loading';
......
import { ApolloClient, InMemoryCache } from "@apollo/client";
import {DB_URL} from "@env";
//import "dotenv/config"
export const apolloClient = new ApolloClient({
uri: process.env.DB_URL ? process.env.DB_URL : "http://localhost:4000/graphql",
uri: "http://it2810-29.idi.ntnu.no:4000/graphql",
cache: new InMemoryCache(),
});
......@@ -15,11 +15,10 @@ import { Dispatch } from "redux";
import { setMovies } from "./mainPageSlice";
import { searchMovies } from "../services/__generated__/searchMovies";
import { useAppDispatch } from "../services/hooks";
import { BottomScrollListener } from "react-bottom-scroll-listener";
import SortDropDown from "../components/sortdropdown";
import MovieTable from "../components/moviesview";
import UserDisplay from "../components/userDisplay";
import { Dimensions, ScrollView, StyleSheet, useWindowDimensions, View } from "react-native";
import { StyleSheet, useWindowDimensions, View } from "react-native";
import styles from "./styles";
import LoginModal from "../components/login/login";
import MenuDrawer from "react-native-side-drawer";
......@@ -64,12 +63,13 @@ export const MainPage: FunctionComponent = () => {
}
};
/* Fetches new set of movies if one of the filtering or sorting values are changed */
useEffect(() => {
fetchMovies();
}, [filterSearchQuery, filterGenre, filterDateStart, filterDateEnd, sortBy]);
/* Shows login modal to opposite of previous state */
useEffect(() => {
}, [width, height])
const [isLoginModalVisible, setIsLoginModalVisible] = useState(false);
const toggleLogInModal = () => {
......@@ -115,7 +115,6 @@ export const MainPage: FunctionComponent = () => {
<SortDropDown />
</View>
<View>
{/* <BottomScrollListener onBottom={fetchMovies} debounce={0}/> */}
<MovieTable fetchMore={fetchMovies}/>
</View>
</View>
......@@ -126,8 +125,4 @@ export const MainPage: FunctionComponent = () => {
);
};
const inlineStyles = StyleSheet.create({
containerStyle: {
}
})
......@@ -12,10 +12,12 @@ const initialState: IMoviesList = {
sortByCriteria: "",
};
/* Redux toolkit reducers with action and reducer in one method */
const MainPageSlice = createSlice({
name: "mainPage",
initialState,
reducers: {
/* Adds payload to movie state and increases page offset */
setMovies(state, action) {
state.nextPage += 1;
if (state.movies !== null) {
......
import { apolloClient } from "../graphql";
import { searchMovies } from "./__generated__/searchMovies";
import { removeMovieAsFavorite } from "./__generated__/removeMovieAsFavorite";
import { setMovieAsFavorite } from "./__generated__/setMovieAsFavorite";
import { GET_MOVIES_BY_SEARCH } from "./movieQueries";
import { SET_FAVORITE_MOVIE } from "./movieQueries";
import { REMOVE_MOVIE_AS_FAVORITE } from "./movieQueries";
......
/* Converts date object to UNIX date format (seconds after 1970) */
export function convertDateToUnixDate(date: Date) {
return Math.round(date.getTime() / 1000);
}
/* Converts UNIX date format to date object */
export function convertUnixDateToDate(unixNumber: number) {
const date = new Date(unixNumber * 1000);
return date;
}
/* Makes date object readable */
export function formatDateAsString(date: Date) {
let year = date.getFullYear();
let month = date.getMonth() + 1;
......
/*NOT USING*/
// export enum SortBy {
// AlphabeticalAsc = "title",
// AlphabeticalDesc = "-title",
// YearAsc = "release_date",
// YearDesc = "-release_date",
// Clear = "",
// }
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