Merge branch '3-home' into 'development'

Resolve "Home"

See merge request thschleicher/interaktive-systeme!3
This commit is contained in:
jastornig 2023-12-07 10:51:15 +00:00
commit 94ae74d604
10 changed files with 373 additions and 6 deletions

View file

@ -3,6 +3,7 @@ import {StyleSheet, View} from "react-native"
import { FontAwesome } from "@expo/vector-icons";
import {useThemeColor} from "../hooks/hooks";
import React from "react";
export default function Layout() {
// const selectedColor: string = useThemeColor( "tabIconSelected");

View file

@ -1,8 +1,15 @@
import { StyleSheet, View, Text } from 'react-native';
import { StyleSheet, View, Text, NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
import { useThemeColor } from "../hooks/hooks";
import { SafeAreaView } from 'react-native-safe-area-context'
import { ExpenseItem, Plus, Welcome, SearchBar } from '../components';
import { FlatList, TextInput } from 'react-native-gesture-handler';
import { useRef, useState } from 'react';
import React from 'react';
export default function Page() {
const [plusShow, setPlusShow] = useState(true);
const prevOffset = useRef(0);
const styles = StyleSheet.create({
container: {
flex: 1,
@ -16,10 +23,53 @@ export default function Page() {
}
});
const profile = require("../assets/images/profile.jpg")
const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>)=>{
const currentOffset = event.nativeEvent.contentOffset.y >= 0 ? event.nativeEvent.contentOffset.y : 0
const isScrollingUp : boolean = currentOffset <= prevOffset.current;
const isTop : boolean = currentOffset === 0
prevOffset.current = currentOffset
setPlusShow(isScrollingUp || isTop)
}
const data = [
{id:"1",category: "Category 1", color: "blue", date:"01.01.2023 18:00", title:"1 Super fancy spending with long name that will not display", value: "€ 30,00"},
{id:"2",category: "Category 2", color: "red", date:"01.01.2023 18:00", title:"2 Super fancy spending", value: "€ 30,00"},
{id:"3",category: "Category 3", color: "green", date:"01.01.2023 18:00", title:"3 Super fancy spending", value: "€ 30,00"},
{id:"4",category: "Category 4", color: "orange", date:"01.01.2023 18:00", title:"4 Super fancy spending", value: "€ 30,00"},
{id:"5",category: "Category 1", color: "blue", date:"01.01.2023 18:00", title:"5 Super fancy spending", value: "€ 30,00"},
{id:"6",category: "Category 2", color: "red", date:"01.01.2023 18:00", title:"6 Super fancy spending with long name that will not display", value: "€ 30,00"},
{id:"7",category: "Category 3", color: "green", date:"01.01.2023 18:00", title:"7 Super fancy spending", value: "€ 30,00"},
{id:"8",category: "Category 4", color: "orange", date:"01.01.2023 18:00", title:"8 Super fancy spending", value: "€ 30,00"},
{id:"9",category: "Category 1", color: "blue", date:"01.01.2023 18:00", title:"9 Super fancy spending", value: "€ 30,00"},
{id:"10" ,category: "Category 2", color: "red", date:"01.01.2023 18:00", title:"10 Super fancy spending", value: "€ 30,00"},
{id:"11" ,category: "Category 3", color: "green", date:"01.01.2023 18:00", title:"11 Super fancy spending", value: "€ 30,00"},
{id:"12" ,category: "Category 4", color: "orange", date:"01.01.2023 18:00", title:"12 Super fancy spending", value: "€ 30,00"},
]
return (
<View style={styles.container}>
<Text style={styles.text}>Home</Text>
</View>
<SafeAreaView style={{flex: 1}}>
{plusShow && <Plus/>}
{/* <Welcome name="My User" image={profile} onPress={()=>console.log("hello")}></Welcome> */}
<FlatList
data={data}
ListHeaderComponent={
<>
<Welcome name="My Dude" image={profile} onPress={()=>console.log("hello")}></Welcome>
<SearchBar placeholder='Type to Search...'></SearchBar>
</>
}
renderItem = {({item}) => <ExpenseItem category={item.category} color={item.color} date={item.date} title={item.title} value={item.value}/>}
keyExtractor={item => item.id}
ItemSeparatorComponent={()=><View style={{marginVertical: 5}}></View>}
onScroll={handleScroll}
scrollEventThrottle={20}
>
</FlatList>
</SafeAreaView>
);
}

BIN
assets/images/profile.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 KiB

View file

@ -0,0 +1,50 @@
import { View, Text, StyleSheet, Platform } from 'react-native'
import React from 'react'
import { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'
import { useThemeColor } from '../../../hooks/hooks'
import { SHADOWS } from '../../../constants/theme'
function generateBoxShadowStyle(
xOffset: number,
yOffset: number,
shadowColorIos: string,
shadowOpacity: number,
shadowRadius: number,
elevation: number,
shadowColorAndroid: string
):void {
if(Platform.OS === 'ios'){
styles.boxShadow = {
shadowColor: shadowColorIos,
shadowOffset : {width: xOffset, height: yOffset},
shadowOpacity,
shadowRadius,
backgroundColor: useThemeColor("backgroundColor")
}
}else if (Platform.OS === 'android'){
styles.boxShadow = {
elevation: elevation,
shadowColor: shadowColorAndroid,
};
}
}
export default function CustomCard(props : ViewProps) {
generateBoxShadowStyle(1, 1, '#171717', 0.2, 20, 10, '#171717')
return (
<View style={[styles.container, styles.boxShadow, props.style]}>
{props.children}
</View>
)
}
const styles = StyleSheet.create({
container:{
flexDirection: "row",
alignItems: "stretch",
alignContent: "space-between",
borderRadius: 20,
marginHorizontal: 10,
},
boxShadow: {},
})

View file

@ -0,0 +1,28 @@
import { View, Text, ViewProps, StyleSheet } from 'react-native'
import { AntDesign } from '@expo/vector-icons'
import React from 'react'
const Plus = (props : ViewProps) => {
return (
<View style={[style.plus, props.style]}>
{props.children}
<AntDesign name='plus' color={"white"} size={20}></AntDesign>
</View>
)
}
const style = StyleSheet.create({
plus:{
position: "absolute",
right: 20,
bottom: 20,
zIndex: 1,
backgroundColor: "orange",
padding: 20,
borderRadius: 500,
height: 60,
width: 60,
alignItems: 'center',
justifyContent: "center"
}
})
export default Plus

View file

@ -0,0 +1,68 @@
import { View, Text, ViewProps, StyleSheet, Button } from 'react-native'
import React from 'react'
import { TextInput, TouchableOpacity } from 'react-native-gesture-handler'
import { AntDesign } from '@expo/vector-icons';
import { useThemeColor } from '../../../hooks/hooks';
import { SIZES } from '../../../constants/theme';
type SearchBarProps = {placeholder: string} & ViewProps
export default function SearchBar(props: SearchBarProps) {
const [isActive, setIsactive] = React.useState(false);
const backgroundColor = useThemeColor("backgroundColor");
const handleChange = (text:string) : void => {
if(text !== ""){
if(!isActive){
setIsactive(true)
}
}else {
if(isActive){
setIsactive(false)
}
}
}
//TODO: Handle textCancel
return (
<View style={styles.container}>
<TextInput onChangeText = {handleChange} style={[{fontSize: SIZES.normal, height: "100%"}, styles.TextInput]} autoCorrect={false} keyboardType='web-search' placeholder={props.placeholder}></TextInput>
{isActive &&
<TouchableOpacity style={styles.cancel}>
<AntDesign size={15} name='closecircle'></AntDesign>
</TouchableOpacity>
}
</View>
)
}
const styles = StyleSheet.create({
container: {
marginHorizontal: 10,
marginBottom: 20,
flexDirection: 'row',
justifyContent: "center",
alignItems: "center",
height: 40,
backgroundColor: "red", //tochange
borderRadius: 10,
paddingHorizontal: 10
},
TextInput: {
flex: 1,
height: "100%",
justifyContent: "space-between",
alignItems: "center",
flexDirection: "row",
width: "100%",
},
cancel:{
height: "100%",
width: 30,
justifyContent: "center",
alignItems:"center",
}
})

View file

@ -0,0 +1,71 @@
import { View, Text, ViewProps, Image, GestureResponderEvent } from 'react-native'
import React from 'react'
import { MARGINS, SIZES, SHADOWS } from '../../../constants/theme'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { useThemeColor } from '../../../hooks/hooks'
type WelcomeProps = ViewProps & {name: string, image : any, onPress: () => void | undefined}
function formatDate(date: Date) : string {
const day = String(date.getDate()).padStart(2, '0')
const month = String(date.getMonth() + 1).padStart(2, '0')
const year = String(date.getFullYear())
return `${day}.${month}.${year}`
}
function getTimeOfDay(date: Date) : string {
const hour = date.getHours()
if(hour < 12){
return "morning"
} else if(hour >= 12 && hour < 18){
return "afternoon"
} else if(hour >= 18){
return "evening"
}
return "day"
}
export default function Welcome(props: WelcomeProps) {
const date = new Date()
const dateString = formatDate(date)
const timeOfDay = getTimeOfDay(date)
const onpress = props.onPress
const textcolor = useThemeColor("primaryText")
return (
<View style={{
marginVertical: 20,
marginHorizontal: MARGINS.normal,
}}>
<View style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between"
}}>
<TouchableOpacity onPress={onpress}>
<Image style={{
height: 60,
width: 60,
borderRadius: 200,
}} source={props.image} height={60} width={60} resizeMode='cover'></Image>
</TouchableOpacity>
<Text style={{
fontSize: SIZES.xlarge,
color: textcolor
}}>{dateString}</Text>
</View>
<View style={{
paddingTop: 10,
}}>
<Text style={{
fontSize: SIZES.xlarge,
color: textcolor
}}>Good {timeOfDay}, {props.name}</Text>
</View>
</View>
)
}

View file

@ -0,0 +1,61 @@
import { View, Text, StyleSheet, ColorValue } from 'react-native'
import React from 'react'
import { useThemeColor } from '../../../hooks/hooks';
import CustomCard from "../../common/customCard/CustomCard";
import { SIZES } from '../../../constants/theme';
export type ExpenseItemProps = {color: ColorValue, category: string, title: string, date: string, value : string}
export default function ExpenseItem(itemProps : ExpenseItemProps) {
const textColor = useThemeColor("primaryText");
const backgroundColor = useThemeColor("backgroundColor")
return (
<CustomCard>
<View style={[styles.colorTip, {backgroundColor: itemProps.color}]}></View>
<View style={[styles.textSection, {backgroundColor: backgroundColor}]}>
<Text style={{
fontSize: SIZES.normal,
color: textColor
}} numberOfLines={1}>{itemProps.category}</Text>
<Text style={{
fontSize: SIZES.large,
color: textColor
}} numberOfLines={1}>{itemProps.title}</Text>
<Text style={{
fontSize: SIZES.small,
color: textColor
}} numberOfLines={1}>{itemProps.date}</Text>
</View>
<View style={[styles.valueSection, {backgroundColor: backgroundColor}]}>
<Text style={{
fontSize: SIZES.xxLarge,
color: textColor
}} numberOfLines={1}>{itemProps.value}</Text>
</View>
</CustomCard>
)
}
const styles = StyleSheet.create({
colorTip: {
width: 20,
borderTopLeftRadius: 20,
borderBottomLeftRadius: 20,
},
textSection: {
flexDirection: "column",
alignContent: "space-between",
alignItems:"flex-start",
paddingLeft: 10,
flex:1,
alignSelf: "stretch",
paddingVertical: 5
},
valueSection: {
justifyContent:"center",
borderTopRightRadius: 20,
borderBottomRightRadius: 20,
}
})

15
components/index.jsx Normal file
View file

@ -0,0 +1,15 @@
//home
import ExpenseItem from "./home/expenseItem/expenseItem"
import Welcome from "./home/Welcome/Welcome"
//common
import Plus from "./common/plus/plus"
import SearchBar from "./common/searchBar/SearchBar"
export {
ExpenseItem,
Welcome,
Plus,
SearchBar
}

23
constants/theme.ts Normal file
View file

@ -0,0 +1,23 @@
const SIZES = {
small: 12,
normal: 17,
large: 20,
xlarge: 25,
xxLarge: 30
}
const MARGINS = {
normal: 10
}
const SHADOWS = {
light: {
shadowColor: "#ADB7C3",
shadowRadius: 20,
elevation: 10,
shadowOpacity: 0.6,
shadowOffset: { width: 1, height: 1 }
}
}
export {SIZES, SHADOWS, MARGINS}