first commit
This commit is contained in:
commit
94fb17e79f
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Expo
|
||||||
|
.expo/
|
||||||
|
dist/
|
||||||
|
web-build/
|
||||||
|
|
||||||
|
# Native
|
||||||
|
*.orig.*
|
||||||
|
*.jks
|
||||||
|
*.p8
|
||||||
|
*.p12
|
||||||
|
*.key
|
||||||
|
*.mobileprovision
|
||||||
|
|
||||||
|
# Metro
|
||||||
|
.metro-health-check*
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.*
|
||||||
|
yarn-debug.*
|
||||||
|
yarn-error.*
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
|
||||||
|
# The following patterns were generated by expo-cli
|
||||||
|
|
||||||
|
expo-env.d.ts
|
||||||
|
# @end expo-cli
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"expo": {
|
||||||
|
"name": "apartment-clone",
|
||||||
|
"slug": "apartment-clone",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"icon": "./assets/images/icon.png",
|
||||||
|
"scheme": "myapp",
|
||||||
|
"userInterfaceStyle": "automatic",
|
||||||
|
"splash": {
|
||||||
|
"image": "./assets/images/splash.png",
|
||||||
|
"resizeMode": "contain",
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"assetBundlePatterns": [
|
||||||
|
"**/*"
|
||||||
|
],
|
||||||
|
"ios": {
|
||||||
|
"supportsTablet": true
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"adaptiveIcon": {
|
||||||
|
"foregroundImage": "./assets/images/adaptive-icon.png",
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"web": {
|
||||||
|
"bundler": "metro",
|
||||||
|
"output": "static",
|
||||||
|
"favicon": "./assets/images/favicon.png"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"expo-router"
|
||||||
|
],
|
||||||
|
"experiments": {
|
||||||
|
"typedRoutes": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
//This file serves as the index.tsx file (main)
|
||||||
|
|
||||||
|
|
||||||
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||||
|
import { Link, Tabs } from 'expo-router';
|
||||||
|
import { Pressable } from 'react-native';
|
||||||
|
|
||||||
|
import { appTheme } from "../../theme";
|
||||||
|
|
||||||
|
|
||||||
|
import Colors from '../../constants/Colors';
|
||||||
|
|
||||||
|
// creatign tab bar icons and passing establishing properties to be passed during exectution
|
||||||
|
// see more icon families at https://icons.expo.fyi/
|
||||||
|
|
||||||
|
function TabBarIcon(props: {
|
||||||
|
name: React.ComponentProps<typeof MaterialCommunityIcons>['name'];
|
||||||
|
color: string;
|
||||||
|
}) {
|
||||||
|
return <MaterialCommunityIcons size={28} style={{ marginBottom: -3 }} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating the tab bar at the bottom
|
||||||
|
|
||||||
|
export default function TabLayout() {
|
||||||
|
// const colorScheme = useColorScheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
screenOptions={{
|
||||||
|
tabBarActiveTintColor: appTheme["color-primary-500"],
|
||||||
|
}}>
|
||||||
|
|
||||||
|
|
||||||
|
<Tabs.Screen
|
||||||
|
name="search_tab" //defining which tab should be rendered, this tab will call a screen component from the component folder
|
||||||
|
options={{
|
||||||
|
title: '',
|
||||||
|
tabBarLabel: 'Search',
|
||||||
|
tabBarIcon: ({ color }) => <TabBarIcon name="magnify" color={color} />, //using the tabBarIcon option within tabs and calling the TabBarIcon function created above to pass the established properties to it
|
||||||
|
|
||||||
|
// Info circle at the top right
|
||||||
|
// headerRight: () => (
|
||||||
|
// <Link href="/modal" asChild>
|
||||||
|
// <Pressable>
|
||||||
|
// {({ pressed }) => (
|
||||||
|
// <FontAwesome
|
||||||
|
// name="info-circle"
|
||||||
|
// size={25}
|
||||||
|
// color={Colors[colorScheme ?? 'light'].text}
|
||||||
|
// style={{ marginRight: 15, opacity: pressed ? 0.5 : 1 }}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// </Pressable>
|
||||||
|
// </Link>
|
||||||
|
// ),
|
||||||
|
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Tabs.Screen
|
||||||
|
name="saved_tab"
|
||||||
|
options={{
|
||||||
|
title: '',
|
||||||
|
tabBarLabel: 'Saved',
|
||||||
|
tabBarIcon: ({ color }) => <TabBarIcon name="heart-outline" color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Tabs.Screen
|
||||||
|
name="account_tab"
|
||||||
|
options={{
|
||||||
|
title: '',
|
||||||
|
tabBarLabel: 'Account',
|
||||||
|
tabBarIcon: ({ color }) => <TabBarIcon name="account-circle-outline" color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import AccountScreenInfo from '../../components/AccountScreenInfo';
|
||||||
|
import { Text, View } from '../../components/Themed';
|
||||||
|
|
||||||
|
export default function AccountScreen() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{/* <Text style={styles.title}>Account Screen</Text> */}
|
||||||
|
{/* <View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" /> */}
|
||||||
|
|
||||||
|
<AccountScreenInfo />
|
||||||
|
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
marginVertical: 30,
|
||||||
|
height: 1,
|
||||||
|
width: '80%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
//use rnfes to create a new template
|
||||||
|
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import SavedScreenInfo from '../../components/SavedScreenInfo';
|
||||||
|
import { Text, View } from '../../components/Themed';
|
||||||
|
|
||||||
|
export default function SavedScreen() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{/* <Text style={styles.title}>Saved Screen</Text>
|
||||||
|
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" /> */}
|
||||||
|
|
||||||
|
<SavedScreenInfo />
|
||||||
|
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
marginVertical: 30,
|
||||||
|
height: 1,
|
||||||
|
width: '80%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { Text, View } from '../../components/Themed';
|
||||||
|
|
||||||
|
import SearchScreenInfo from '../../components/SearchScreenInfo';
|
||||||
|
|
||||||
|
export default function SearchScreen() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
|
||||||
|
<SearchScreenInfo />
|
||||||
|
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
marginVertical: 30,
|
||||||
|
height: 1,
|
||||||
|
width: '80%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { ScrollViewStyleReset } from 'expo-router/html';
|
||||||
|
|
||||||
|
// This file is web-only and used to configure the root HTML for every
|
||||||
|
// web page during static rendering.
|
||||||
|
// The contents of this function only run in Node.js environments and
|
||||||
|
// do not have access to the DOM or browser APIs.
|
||||||
|
export default function Root({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charSet="utf-8" />
|
||||||
|
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
|
||||||
|
{/*
|
||||||
|
This viewport disables scaling which makes the mobile website act more like a native app.
|
||||||
|
However this does reduce built-in accessibility. If you want to enable scaling, use this instead:
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||||
|
*/}
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1.00001,viewport-fit=cover"
|
||||||
|
/>
|
||||||
|
{/*
|
||||||
|
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
|
||||||
|
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
|
||||||
|
*/}
|
||||||
|
<ScrollViewStyleReset />
|
||||||
|
|
||||||
|
{/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
|
||||||
|
<style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
|
||||||
|
{/* Add any additional <head> elements that you want globally available on web... */}
|
||||||
|
</head>
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responsiveBackground = `
|
||||||
|
body {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Link, Stack } from 'expo-router';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { Text, View } from '../components/Themed';
|
||||||
|
|
||||||
|
export default function NotFoundScreen() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack.Screen options={{ title: 'Oops!' }} />
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>This screen doesn't exist.</Text>
|
||||||
|
|
||||||
|
<Link href="/" style={styles.link}>
|
||||||
|
<Text style={styles.linkText}>Go to home screen!</Text>
|
||||||
|
</Link>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
marginTop: 15,
|
||||||
|
paddingVertical: 15,
|
||||||
|
},
|
||||||
|
linkText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#2e78b7',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
//This file serves as the app.tsx file (app)
|
||||||
|
|
||||||
|
import FontAwesome from '@expo/vector-icons/FontAwesome';
|
||||||
|
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
|
||||||
|
import { useFonts } from 'expo-font';
|
||||||
|
import { SplashScreen, Stack } from 'expo-router';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useColorScheme } from 'react-native';
|
||||||
|
|
||||||
|
import * as eva from '@eva-design/eva';
|
||||||
|
import { ApplicationProvider, Layout, Text } from '@ui-kitten/components';
|
||||||
|
|
||||||
|
import { appTheme } from "../theme";
|
||||||
|
|
||||||
|
export {
|
||||||
|
// Catch any errors thrown by the Layout component.
|
||||||
|
ErrorBoundary,
|
||||||
|
} from 'expo-router';
|
||||||
|
|
||||||
|
export const unstable_settings = {
|
||||||
|
// Ensure that reloading on `/modal` keeps a back button present.
|
||||||
|
initialRouteName: '(tabs)',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||||
|
SplashScreen.preventAutoHideAsync();
|
||||||
|
|
||||||
|
export default function RootLayout() {
|
||||||
|
const [loaded, error] = useFonts({
|
||||||
|
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
|
||||||
|
...FontAwesome.font,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Expo Router uses Error Boundaries to catch errors in the navigation tree.
|
||||||
|
useEffect(() => {
|
||||||
|
if (error) throw error;
|
||||||
|
}, [error]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loaded) {
|
||||||
|
SplashScreen.hideAsync();
|
||||||
|
}
|
||||||
|
}, [loaded]);
|
||||||
|
|
||||||
|
if (!loaded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
//This application provider comes from ui kitten implementation
|
||||||
|
<ApplicationProvider {...eva} theme={appTheme}>
|
||||||
|
|
||||||
|
<RootLayoutNav />
|
||||||
|
|
||||||
|
</ApplicationProvider>
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RootLayoutNav() {
|
||||||
|
const colorScheme = useColorScheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||||
|
|
||||||
|
{/* Creating a stacklayout */}
|
||||||
|
<Stack>
|
||||||
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||||
|
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
|
||||||
|
</Stack>
|
||||||
|
</ThemeProvider>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
//This file is needed so that the react native with expo can work
|
||||||
|
|
||||||
|
import { Redirect } from "expo-router";
|
||||||
|
|
||||||
|
const StartPage = () => {
|
||||||
|
return <Redirect href="/(tabs)/search_tab" />;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StartPage;
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import { Platform, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import SearchScreenInfo from '../components/SearchScreenInfo';
|
||||||
|
import { Text, View } from '../components/Themed';
|
||||||
|
|
||||||
|
export default function ModalScreen() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>Modal</Text>
|
||||||
|
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
|
||||||
|
<SearchScreenInfo path="app/modal.tsx" />
|
||||||
|
|
||||||
|
{/* Use a light status bar on iOS to account for the black space above the modal */}
|
||||||
|
<StatusBar style={Platform.OS === 'ios' ? 'light' : 'auto'} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
marginVertical: 30,
|
||||||
|
height: 1,
|
||||||
|
width: '80%',
|
||||||
|
},
|
||||||
|
});
|
||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
|
|
@ -0,0 +1,10 @@
|
||||||
|
module.exports = function (api) {
|
||||||
|
api.cache(true);
|
||||||
|
return {
|
||||||
|
presets: ['babel-preset-expo'],
|
||||||
|
plugins: [
|
||||||
|
// Required for expo-router
|
||||||
|
'expo-router/babel',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
|
||||||
|
// import React from 'react';
|
||||||
|
// import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
// import Colors from '../constants/Colors';
|
||||||
|
// import { ExternalLink } from './ExternalLink';
|
||||||
|
// import { MonoText } from './StyledText';
|
||||||
|
// import { Text, View } from './Themed';
|
||||||
|
|
||||||
|
|
||||||
|
// export default function AccountScreenInfo({ path }: { path: string }) {
|
||||||
|
// return (
|
||||||
|
// <View>
|
||||||
|
// <View style={styles.getStartedContainer}>
|
||||||
|
// <Text
|
||||||
|
// style={styles.getStartedText}
|
||||||
|
// lightColor="rgba(0,0,0,0.8)"
|
||||||
|
// darkColor="rgba(255,255,255,0.8)">
|
||||||
|
// Apartment Clone Account Screen:
|
||||||
|
// </Text>
|
||||||
|
|
||||||
|
// <View
|
||||||
|
// style={[styles.codeHighlightContainer, styles.homeScreenFilename]}
|
||||||
|
// darkColor="rgba(255,255,255,0.05)"
|
||||||
|
// lightColor="rgba(0,0,0,0.05)">
|
||||||
|
// <MonoText>{path}</MonoText>
|
||||||
|
// </View>
|
||||||
|
|
||||||
|
// <Text
|
||||||
|
// style={styles.getStartedText}
|
||||||
|
// lightColor="rgba(0,0,0,0.8)"
|
||||||
|
// darkColor="rgba(255,255,255,0.8)">
|
||||||
|
// This is where your code will live.
|
||||||
|
// </Text>
|
||||||
|
// </View>
|
||||||
|
|
||||||
|
// <View style={styles.helpContainer}>
|
||||||
|
// <ExternalLink
|
||||||
|
// style={styles.helpLink}
|
||||||
|
// href="https://docs.expo.io/get-started/create-a-new-app/#opening-the-app-on-your-phonetablet">
|
||||||
|
// <Text style={styles.helpLinkText} lightColor={Colors.light.tint}>
|
||||||
|
// Tap here if your app doesn't automatically update after making changes
|
||||||
|
// </Text>
|
||||||
|
// </ExternalLink>
|
||||||
|
// </View>
|
||||||
|
// </View>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const styles = StyleSheet.create({
|
||||||
|
// getStartedContainer: {
|
||||||
|
// alignItems: 'center',
|
||||||
|
// marginHorizontal: 50,
|
||||||
|
// },
|
||||||
|
// homeScreenFilename: {
|
||||||
|
// marginVertical: 7,
|
||||||
|
// },
|
||||||
|
// codeHighlightContainer: {
|
||||||
|
// borderRadius: 3,
|
||||||
|
// paddingHorizontal: 4,
|
||||||
|
// },
|
||||||
|
// getStartedText: {
|
||||||
|
// fontSize: 17,
|
||||||
|
// lineHeight: 24,
|
||||||
|
// textAlign: 'center',
|
||||||
|
// },
|
||||||
|
// helpContainer: {
|
||||||
|
// marginTop: 15,
|
||||||
|
// marginHorizontal: 20,
|
||||||
|
// alignItems: 'center',
|
||||||
|
// },
|
||||||
|
// helpLink: {
|
||||||
|
// paddingVertical: 15,
|
||||||
|
// },
|
||||||
|
// helpLinkText: {
|
||||||
|
// textAlign: 'center',
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
import { StyleSheet, Text, View } from 'react-native'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Screen from './Screen'
|
||||||
|
|
||||||
|
const AccountScreenInfo = () => {
|
||||||
|
return (
|
||||||
|
<Screen>
|
||||||
|
|
||||||
|
<Text style={styles.title}>
|
||||||
|
AccountScreenInfo component is being displayed here
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
</Screen>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AccountScreenInfo
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'normal',
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
marginVertical: 30,
|
||||||
|
height: 1,
|
||||||
|
width: '80%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -0,0 +1,187 @@
|
||||||
|
import {
|
||||||
|
Animated,
|
||||||
|
StyleSheet,
|
||||||
|
LayoutChangeEvent,
|
||||||
|
View,
|
||||||
|
TouchableOpacity,
|
||||||
|
Platform,
|
||||||
|
FlatList,
|
||||||
|
} from 'react-native'
|
||||||
|
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { HEADERHEIGHT, LISTMARGIN } from '../constants'
|
||||||
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||||
|
import { appTheme } from '../theme';
|
||||||
|
import { Button, Text } from '@ui-kitten/components';
|
||||||
|
import Row from './Row';
|
||||||
|
|
||||||
|
const AnimatedListHeader = ({scrollAnimation}:{scrollAnimation: Animated.Value}) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const [offsetAnimation] = useState(new Animated.Value(0));
|
||||||
|
const [clampedScroll, setClampedScroll] = useState(
|
||||||
|
Animated.diffClamp(
|
||||||
|
Animated.add(
|
||||||
|
scrollAnimation.interpolate({
|
||||||
|
inputRange: [0,1],
|
||||||
|
outputRange: [0,1],
|
||||||
|
extrapolateLeft: "clamp"
|
||||||
|
}),
|
||||||
|
offsetAnimation
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const navbarTranslate = clampedScroll.interpolate({
|
||||||
|
inputRange : [0, HEADERHEIGHT],
|
||||||
|
outputRange: [0, -HEADERHEIGHT],
|
||||||
|
extrapolate: "clamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
const onLayout = (event: LayoutChangeEvent) => {
|
||||||
|
let { height } = event.nativeEvent.layout;
|
||||||
|
|
||||||
|
setClampedScroll(
|
||||||
|
Animated.diffClamp(
|
||||||
|
Animated.add(
|
||||||
|
scrollAnimation.interpolate({
|
||||||
|
inputRange: [0,1],
|
||||||
|
outputRange: [0,1],
|
||||||
|
extrapolateLeft: "clamp"
|
||||||
|
}),
|
||||||
|
offsetAnimation
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
height
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterButtons = [
|
||||||
|
{
|
||||||
|
iconName: "filter-variant",
|
||||||
|
onPress: () => console.log("filter all"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Price",
|
||||||
|
onPress: () => console.log("price"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Move In Date",
|
||||||
|
onPress: () => console.log("move in date"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Pets",
|
||||||
|
onPress: () => console.log("pets"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.View
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
left: 0,
|
||||||
|
zIndex: 1000,
|
||||||
|
height: HEADERHEIGHT,
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
transform: [{ translateY: navbarTranslate }],
|
||||||
|
}}
|
||||||
|
onLayout={onLayout}
|
||||||
|
>
|
||||||
|
<View style={styles.AnimatedHeaderStyle}>
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.TouchableOpacityStyle} onPress={() => console.log("navigate to the input screen")}>
|
||||||
|
|
||||||
|
<Row style={styles.RowStyle}>
|
||||||
|
<MaterialCommunityIcons name="magnify" color={appTheme["color-primary-500"]} size={28} />
|
||||||
|
<Text style={styles.Text}>Find a Location</Text>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<FlatList
|
||||||
|
style={styles.FlatListStyle}
|
||||||
|
data={filterButtons}
|
||||||
|
horizontal
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
renderItem={({ item, index }) => {
|
||||||
|
if (item.iconName) {
|
||||||
|
return <Button
|
||||||
|
appearance={'ghost'}
|
||||||
|
style={[styles.Searchbtn, {width: 48}]}
|
||||||
|
onPress={item.onPress}
|
||||||
|
accessoryLeft={
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={item.iconName as any}
|
||||||
|
size={20}
|
||||||
|
color={appTheme["color-primary-500"]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
return <Button
|
||||||
|
appearance={'ghost'}
|
||||||
|
style={styles.Searchbtn}
|
||||||
|
onPress={item.onPress}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
}}
|
||||||
|
keyExtractor={(_, index) => index.toString()}
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</FlatList>
|
||||||
|
|
||||||
|
</View>
|
||||||
|
|
||||||
|
|
||||||
|
</Animated.View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AnimatedListHeader
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
|
||||||
|
AnimatedHeaderStyle: {
|
||||||
|
marginHorizontal: LISTMARGIN
|
||||||
|
},
|
||||||
|
TouchableOpacityStyle: {
|
||||||
|
marginTop: Platform.OS === "ios" ? 50 : 5,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: appTheme["search-border-default"],
|
||||||
|
borderRadius: 30,
|
||||||
|
padding: 10,
|
||||||
|
},
|
||||||
|
RowStyle: {
|
||||||
|
alignItems: "center"
|
||||||
|
},
|
||||||
|
Text: {
|
||||||
|
marginLeft: 10
|
||||||
|
|
||||||
|
},
|
||||||
|
Searchbtn: {
|
||||||
|
borderRadius: 30,
|
||||||
|
borderColor: appTheme["search-border-default"],
|
||||||
|
marginHorizontal: 3,
|
||||||
|
|
||||||
|
},
|
||||||
|
FlatListStyle: {
|
||||||
|
marginTop: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { StyleSheet, Text, Pressable } from 'react-native'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { appTheme } from "../theme";
|
||||||
|
|
||||||
|
export default function ButtonTest(props: any) {
|
||||||
|
|
||||||
|
const { onPress, title = 'Save', style } = props;
|
||||||
|
return (
|
||||||
|
<Pressable style={style.button} onPress={onPress}>
|
||||||
|
<Text style={style.text}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingVertical: 12,
|
||||||
|
paddingHorizontal: 32,
|
||||||
|
borderRadius: 4,
|
||||||
|
elevation: 3,
|
||||||
|
backgroundColor: appTheme["color-primary-500"],
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: 21,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
letterSpacing: 0.25,
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Dimensions,
|
||||||
|
StyleSheet,
|
||||||
|
ViewStyle,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||||
|
import { Text, Button } from "@ui-kitten/components";
|
||||||
|
import { appTheme } from "../theme";
|
||||||
|
import { Property } from '../types/property';
|
||||||
|
|
||||||
|
import ImageCarousel from './ImageCarousel';
|
||||||
|
import Row from './Row';
|
||||||
|
import CardInformation from './CardInformation';
|
||||||
|
|
||||||
|
const LISTMARGIN = 10;
|
||||||
|
const WIDTH = Dimensions.get("screen").width - LISTMARGIN * 2;
|
||||||
|
|
||||||
|
|
||||||
|
//each property listing is shown as a card, using this component
|
||||||
|
const Card = ({ property, style,}:{property: Property; style?: ViewStyle;}) => {
|
||||||
|
return (
|
||||||
|
<View style={{ marginVertical: 5 }}>
|
||||||
|
|
||||||
|
<ImageCarousel images={property.images}/>
|
||||||
|
|
||||||
|
{/* Everything under the image is wrapped in this view */}
|
||||||
|
<CardInformation property={property}/>
|
||||||
|
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Card
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
|
||||||
|
button: {
|
||||||
|
width: "49%",
|
||||||
|
},
|
||||||
|
defaultMarginTop: {
|
||||||
|
marginTop: 5
|
||||||
|
},
|
||||||
|
rowJustification: {
|
||||||
|
justifyContent: "space-between"
|
||||||
|
},
|
||||||
|
informationContainer: {
|
||||||
|
paddingVertical: 10,
|
||||||
|
paddingHorizontal: 5,
|
||||||
|
borderColor: "#d3d3d3",
|
||||||
|
borderBottomLeftRadius: 5,
|
||||||
|
borderBottomRightRadius: 5,
|
||||||
|
borderWidth: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Dimensions,
|
||||||
|
StyleSheet,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||||
|
import { Text, Button } from "@ui-kitten/components";
|
||||||
|
import { appTheme } from "../theme";
|
||||||
|
import { Property } from '../types/property';
|
||||||
|
|
||||||
|
import Row from './Row';
|
||||||
|
|
||||||
|
const LISTMARGIN = 10;
|
||||||
|
const WIDTH = Dimensions.get("screen").width - LISTMARGIN * 2;
|
||||||
|
|
||||||
|
const CardInformation = ({ property}:{property: Property}) => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={styles.informationContainer}>
|
||||||
|
|
||||||
|
{/* Property text data - Row component imported */}
|
||||||
|
<Row
|
||||||
|
style={styles.rowJustification}>
|
||||||
|
|
||||||
|
<Text category={"h6"}>
|
||||||
|
${property.rentLow.toLocaleString()} -{" "}
|
||||||
|
{property.rentHigh.toLocaleString()}
|
||||||
|
</Text>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="heart-outline"
|
||||||
|
color={appTheme["color-primary-500"]}
|
||||||
|
size={24}/>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Text category={"p1"}>
|
||||||
|
{property.bedroomLow.toLocaleString()} -{" "}
|
||||||
|
{property.bedroomHigh.toLocaleString()} Beds
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text category={"p1"} style={styles.defaultMarginTop}>
|
||||||
|
{property.name}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text category={"p1"}>
|
||||||
|
{property.street}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text category={"p1"}>
|
||||||
|
{property.city}, {property.state}, {property.zip}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text category={"p1"} style={styles.defaultMarginTop}>
|
||||||
|
{property.tags.map((tag, index) =>
|
||||||
|
index === property.tags.length - 1 ? tag : tag + ", "
|
||||||
|
//looping (mapping) through the tags and comparing the index (i) or position of the loop to the number of propertys in the array. If its less than the length minus 1, we need to add a comma and a space after the word.
|
||||||
|
// ? means if true
|
||||||
|
// : means else
|
||||||
|
// expression after else could be simplified with `${tag}, `
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{/* Button data - Row component imported */}
|
||||||
|
|
||||||
|
<Row
|
||||||
|
style={{
|
||||||
|
marginTop: 5,
|
||||||
|
justifyContent: "space-between"
|
||||||
|
}}>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
appearance={"ghost"}
|
||||||
|
style={[
|
||||||
|
styles.button,
|
||||||
|
{ borderColor: appTheme["color-primary-500"]},
|
||||||
|
]}
|
||||||
|
size="medium"
|
||||||
|
onPress={() => console.log("email the property manager")}>
|
||||||
|
Email
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style={[
|
||||||
|
styles.button,
|
||||||
|
{ borderColor: appTheme["color-primary-500"]},
|
||||||
|
]}
|
||||||
|
size="medium"
|
||||||
|
onPress={() => console.log("call the property manager")}>
|
||||||
|
Call
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* Calling the buttontest component and passing it props */}
|
||||||
|
{/* <ButtonTest
|
||||||
|
onPress={() => console.log("testing the new button")}
|
||||||
|
title='Test'
|
||||||
|
style={{
|
||||||
|
|
||||||
|
}}
|
||||||
|
/> */}
|
||||||
|
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CardInformation
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
|
||||||
|
|
||||||
|
button: {
|
||||||
|
width: "49%",
|
||||||
|
},
|
||||||
|
defaultMarginTop: {
|
||||||
|
marginTop: 5
|
||||||
|
},
|
||||||
|
rowJustification: {
|
||||||
|
justifyContent: "space-between"
|
||||||
|
},
|
||||||
|
informationContainer: {
|
||||||
|
paddingVertical: 10,
|
||||||
|
paddingHorizontal: 5,
|
||||||
|
borderColor: "#d3d3d3",
|
||||||
|
borderBottomLeftRadius: 5,
|
||||||
|
borderBottomRightRadius: 5,
|
||||||
|
borderWidth: 1,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Link } from 'expo-router';
|
||||||
|
import * as WebBrowser from 'expo-web-browser';
|
||||||
|
import React from 'react';
|
||||||
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
|
export function ExternalLink(
|
||||||
|
props: Omit<React.ComponentProps<typeof Link>, 'href'> & { href: string }
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
hrefAttrs={{
|
||||||
|
// On web, launch the link in a new tab.
|
||||||
|
target: '_blank',
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
// @ts-expect-error: External URLs are not typed.
|
||||||
|
href={props.href}
|
||||||
|
onPress={(e) => {
|
||||||
|
if (Platform.OS !== 'web') {
|
||||||
|
// Prevent the default behavior of linking to the default browser on native.
|
||||||
|
e.preventDefault();
|
||||||
|
// Open the link in an in-app browser.
|
||||||
|
WebBrowser.openBrowserAsync(props.href as string);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { StyleSheet, FlatList, Pressable, Image } from 'react-native'
|
||||||
|
import React from 'react'
|
||||||
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||||
|
import { WIDTH } from '../constants';
|
||||||
|
|
||||||
|
import { useState, useRef } from 'react';
|
||||||
|
|
||||||
|
//we will be taking in an array of images as the argument
|
||||||
|
const ImageCarousel = ({ images }:{ images: string[] }) => {
|
||||||
|
|
||||||
|
const flatListRef = useRef<FlatList | null>(null)
|
||||||
|
const viewConfig = { viewAreaCoveragePercentThreshold: 95 };
|
||||||
|
const [activeIndex, setActiveIndex] = useState(0)
|
||||||
|
const onViewRef = useRef(({changed}: {changed: any}) => {
|
||||||
|
if (changed[0].isViewable) {
|
||||||
|
setActiveIndex(changed[0].index);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//handle the scrolling of images when the chevrons are pressed
|
||||||
|
const handlePressLeft = () => {
|
||||||
|
if (activeIndex === 0)
|
||||||
|
return flatListRef.current?.scrollToIndex({
|
||||||
|
animated: false,
|
||||||
|
index: images.length - 1,
|
||||||
|
});
|
||||||
|
flatListRef.current?.scrollToIndex({
|
||||||
|
index: activeIndex - 1,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePressRight = () => {
|
||||||
|
if (activeIndex === images.length - 1)
|
||||||
|
return flatListRef.current?.scrollToIndex({
|
||||||
|
animated: false,
|
||||||
|
index: 0,
|
||||||
|
});
|
||||||
|
flatListRef.current?.scrollToIndex({
|
||||||
|
index: activeIndex + 1,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Flatlist to scrolll through a single property image */}
|
||||||
|
<FlatList
|
||||||
|
style={{
|
||||||
|
flexGrow: 0
|
||||||
|
}}
|
||||||
|
ref={(ref) => (flatListRef.current = ref)}
|
||||||
|
data={images}
|
||||||
|
horizontal
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
snapToAlignment='center'
|
||||||
|
pagingEnabled
|
||||||
|
onViewableItemsChanged={onViewRef.current}
|
||||||
|
viewabilityConfig={viewConfig}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<Image
|
||||||
|
source={{ uri: item }}
|
||||||
|
|
||||||
|
style={ styles.image }
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
keyExtractor={(item) => item}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Chevrons */}
|
||||||
|
<Pressable
|
||||||
|
style={[
|
||||||
|
styles.chevron,
|
||||||
|
{left: 5}
|
||||||
|
]}
|
||||||
|
onPress={handlePressLeft}
|
||||||
|
>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="chevron-left"
|
||||||
|
color="white"
|
||||||
|
size={45}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
|
<Pressable
|
||||||
|
style={[
|
||||||
|
styles.chevron,
|
||||||
|
{right: 5}
|
||||||
|
]}
|
||||||
|
onPress={handlePressRight}
|
||||||
|
>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="chevron-right"
|
||||||
|
color="white"
|
||||||
|
size={45}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageCarousel
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
image: {
|
||||||
|
height: 225,
|
||||||
|
width: WIDTH,
|
||||||
|
borderTopLeftRadius: 5,
|
||||||
|
borderTopRightRadius: 5
|
||||||
|
},
|
||||||
|
chevron: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 95
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { StyleSheet, View, ViewStyle } from 'react-native'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Row = ({children, style}:{children: any, style: ViewStyle}) => {
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, style]}>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Row
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: "row",
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
|
||||||
|
// import React from 'react';
|
||||||
|
// import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
// import Colors from '../constants/Colors';
|
||||||
|
// import { ExternalLink } from './ExternalLink';
|
||||||
|
// import { MonoText } from './StyledText';
|
||||||
|
// import { Text, View } from './Themed';
|
||||||
|
|
||||||
|
|
||||||
|
// export default function SavedScreenInfo({ path }: { path: string }) {
|
||||||
|
// return (
|
||||||
|
// <View>
|
||||||
|
// <View style={styles.getStartedContainer}>
|
||||||
|
// <Text
|
||||||
|
// style={styles.getStartedText}
|
||||||
|
// lightColor="rgba(0,0,0,0.8)"
|
||||||
|
// darkColor="rgba(255,255,255,0.8)">
|
||||||
|
// Apartment Clone Saved Screen:
|
||||||
|
// </Text>
|
||||||
|
|
||||||
|
// <View
|
||||||
|
// style={[styles.codeHighlightContainer, styles.homeScreenFilename]}
|
||||||
|
// darkColor="rgba(255,255,255,0.05)"
|
||||||
|
// lightColor="rgba(0,0,0,0.05)">
|
||||||
|
// <MonoText>{path}</MonoText>
|
||||||
|
// </View>
|
||||||
|
|
||||||
|
// <Text
|
||||||
|
// style={styles.getStartedText}
|
||||||
|
// lightColor="rgba(0,0,0,0.8)"
|
||||||
|
// darkColor="rgba(255,255,255,0.8)">
|
||||||
|
// This is where your code will live.
|
||||||
|
// </Text>
|
||||||
|
// </View>
|
||||||
|
|
||||||
|
// <View style={styles.helpContainer}>
|
||||||
|
// <ExternalLink
|
||||||
|
// style={styles.helpLink}
|
||||||
|
// href="https://docs.expo.io/get-started/create-a-new-app/#opening-the-app-on-your-phonetablet">
|
||||||
|
// <Text style={styles.helpLinkText} lightColor={Colors.light.tint}>
|
||||||
|
// Tap here if your app doesn't automatically update after making changes
|
||||||
|
// </Text>
|
||||||
|
// </ExternalLink>
|
||||||
|
// </View>
|
||||||
|
// </View>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const styles = StyleSheet.create({
|
||||||
|
// getStartedContainer: {
|
||||||
|
// alignItems: 'center',
|
||||||
|
// marginHorizontal: 50,
|
||||||
|
// },
|
||||||
|
// homeScreenFilename: {
|
||||||
|
// marginVertical: 7,
|
||||||
|
// },
|
||||||
|
// codeHighlightContainer: {
|
||||||
|
// borderRadius: 3,
|
||||||
|
// paddingHorizontal: 4,
|
||||||
|
// },
|
||||||
|
// getStartedText: {
|
||||||
|
// fontSize: 17,
|
||||||
|
// lineHeight: 24,
|
||||||
|
// textAlign: 'center',
|
||||||
|
// },
|
||||||
|
// helpContainer: {
|
||||||
|
// marginTop: 15,
|
||||||
|
// marginHorizontal: 20,
|
||||||
|
// alignItems: 'center',
|
||||||
|
// },
|
||||||
|
// helpLink: {
|
||||||
|
// paddingVertical: 15,
|
||||||
|
// },
|
||||||
|
// helpLinkText: {
|
||||||
|
// textAlign: 'center',
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
import { View, Text, StyleSheet } from 'react-native'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Screen from './Screen'
|
||||||
|
|
||||||
|
const SavedScreenInfo = () => {
|
||||||
|
return (
|
||||||
|
<Screen>
|
||||||
|
|
||||||
|
<Text style={styles.title}>
|
||||||
|
SavedScreenInfo component is being displayed here
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
</Screen>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SavedScreenInfo
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'normal',
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
marginVertical: 30,
|
||||||
|
height: 1,
|
||||||
|
width: '80%',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// To address the issue of text being above the staus bar
|
||||||
|
|
||||||
|
import {
|
||||||
|
SafeAreaView,
|
||||||
|
StyleSheet,
|
||||||
|
ViewStyle,
|
||||||
|
Platform,
|
||||||
|
StatusBar } from 'react-native'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Screen = ({
|
||||||
|
children, //taking children as props
|
||||||
|
style
|
||||||
|
|
||||||
|
}:{
|
||||||
|
children: any; //returning children as any props
|
||||||
|
style?: ViewStyle;
|
||||||
|
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView
|
||||||
|
style={[styles.container, style]} //passing multiple styles to an array. We are using our own styles at bottom but adding this props in case we want to pass in specific styles
|
||||||
|
>
|
||||||
|
<StatusBar barStyle={"dark-content"}/>
|
||||||
|
{children}
|
||||||
|
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Screen
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
|
||||||
|
//if the operating system is android, we are going to give it a padding top, if not, set the height to zero.
|
||||||
|
paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
|
||||||
|
import {
|
||||||
|
Animated,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import Screen from './Screen';
|
||||||
|
import { HEADERHEIGHT, LISTMARGIN } from '../constants';
|
||||||
|
import Card from './Card';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { useState } from 'react'
|
||||||
|
import AnimatedListHeader from './AnimatedListHeader';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const SearchScreenInfo = () => {
|
||||||
|
|
||||||
|
const properties = [{
|
||||||
|
id: 1,
|
||||||
|
images: [
|
||||||
|
"https://images.pexels.com/photos/1571460/pexels-photo-1571460.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
||||||
|
"https://images.pexels.com/photos/8031889/pexels-photo-8031889.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
|
||||||
|
],
|
||||||
|
rentLow: 3750,
|
||||||
|
rentHigh: 31054,
|
||||||
|
bedroomLow: 1,
|
||||||
|
bedroomHigh: 5,
|
||||||
|
name: "The Hamilton",
|
||||||
|
street: "555 NE 34th St",
|
||||||
|
city: "Miami",
|
||||||
|
state: "Florida",
|
||||||
|
zip: 33137,
|
||||||
|
tags: ["Parking"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
images: [
|
||||||
|
"https://images.pexels.com/photos/1571460/pexels-photo-1571460.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
||||||
|
"https://images.pexels.com/photos/8031889/pexels-photo-8031889.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
|
||||||
|
],
|
||||||
|
rentLow: 3750,
|
||||||
|
rentHigh: 31054,
|
||||||
|
bedroomLow: 1,
|
||||||
|
bedroomHigh: 5,
|
||||||
|
name: "The Hamilton",
|
||||||
|
street: "555 NE 34th St",
|
||||||
|
city: "Miami",
|
||||||
|
state: "Florida",
|
||||||
|
zip: 33137,
|
||||||
|
tags: ["Parking"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
images: [
|
||||||
|
"https://images.pexels.com/photos/1571460/pexels-photo-1571460.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
||||||
|
"https://images.pexels.com/photos/8031889/pexels-photo-8031889.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
|
||||||
|
],
|
||||||
|
rentLow: 3750,
|
||||||
|
rentHigh: 31054,
|
||||||
|
bedroomLow: 1,
|
||||||
|
bedroomHigh: 5,
|
||||||
|
name: "The Hamilton",
|
||||||
|
street: "555 NE 34th St",
|
||||||
|
city: "Miami",
|
||||||
|
state: "Florida",
|
||||||
|
zip: 33137,
|
||||||
|
tags: ["Parking"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
images: [
|
||||||
|
"https://images.pexels.com/photos/1571460/pexels-photo-1571460.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
||||||
|
"https://images.pexels.com/photos/8031889/pexels-photo-8031889.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
|
||||||
|
],
|
||||||
|
rentLow: 3750,
|
||||||
|
rentHigh: 31054,
|
||||||
|
bedroomLow: 1,
|
||||||
|
bedroomHigh: 5,
|
||||||
|
name: "The Hamilton",
|
||||||
|
street: "555 NE 34th St",
|
||||||
|
city: "Miami",
|
||||||
|
state: "Florida",
|
||||||
|
zip: 33137,
|
||||||
|
tags: ["Parking"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
images: [
|
||||||
|
"https://images.pexels.com/photos/1571460/pexels-photo-1571460.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
||||||
|
"https://images.pexels.com/photos/8031889/pexels-photo-8031889.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
|
||||||
|
],
|
||||||
|
rentLow: 3750,
|
||||||
|
rentHigh: 31054,
|
||||||
|
bedroomLow: 1,
|
||||||
|
bedroomHigh: 5,
|
||||||
|
name: "The Hamilton",
|
||||||
|
street: "555 NE 34th St",
|
||||||
|
city: "Miami",
|
||||||
|
state: "Florida",
|
||||||
|
zip: 33137,
|
||||||
|
tags: ["Parking"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const [scrollAnimation] = useState(new Animated.Value(0));
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Screen>
|
||||||
|
|
||||||
|
<AnimatedListHeader scrollAnimation={scrollAnimation} />
|
||||||
|
|
||||||
|
|
||||||
|
{/* Main FlatList to handle scrolling to view the properties listing */}
|
||||||
|
|
||||||
|
<Animated.FlatList
|
||||||
|
//Code to animate the header
|
||||||
|
|
||||||
|
onScroll={Animated.event([
|
||||||
|
{
|
||||||
|
nativeEvent: {
|
||||||
|
contentOffset: {
|
||||||
|
y: scrollAnimation
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{useNativeDriver: true}
|
||||||
|
)}
|
||||||
|
contentContainerStyle={{paddingTop: HEADERHEIGHT - 20}}
|
||||||
|
bounces={false}
|
||||||
|
scrollEventThrottle={16}
|
||||||
|
data={properties}
|
||||||
|
keyExtractor={(item) => item.id.toString()}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
style={{ marginHorizontal: LISTMARGIN }}
|
||||||
|
renderItem={({item}) => (
|
||||||
|
|
||||||
|
<Card property={item}/>
|
||||||
|
|
||||||
|
)}
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Screen>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchScreenInfo
|
||||||
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { Text, TextProps } from './Themed';
|
||||||
|
|
||||||
|
export function MonoText(props: TextProps) {
|
||||||
|
return <Text {...props} style={[props.style, { fontFamily: 'SpaceMono' }]} />;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* Learn more about Light and Dark modes:
|
||||||
|
* https://docs.expo.io/guides/color-schemes/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Text as DefaultText, useColorScheme, View as DefaultView } from 'react-native';
|
||||||
|
|
||||||
|
import Colors from '../constants/Colors';
|
||||||
|
|
||||||
|
type ThemeProps = {
|
||||||
|
lightColor?: string;
|
||||||
|
darkColor?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TextProps = ThemeProps & DefaultText['props'];
|
||||||
|
export type ViewProps = ThemeProps & DefaultView['props'];
|
||||||
|
|
||||||
|
export function useThemeColor(
|
||||||
|
props: { light?: string; dark?: string },
|
||||||
|
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
|
||||||
|
) {
|
||||||
|
const theme = useColorScheme() ?? 'light';
|
||||||
|
const colorFromProps = props[theme];
|
||||||
|
|
||||||
|
if (colorFromProps) {
|
||||||
|
return colorFromProps;
|
||||||
|
} else {
|
||||||
|
return Colors[theme][colorName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Text(props: TextProps) {
|
||||||
|
const { style, lightColor, darkColor, ...otherProps } = props;
|
||||||
|
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
|
||||||
|
|
||||||
|
return <DefaultText style={[{ color }, style]} {...otherProps} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function View(props: ViewProps) {
|
||||||
|
const { style, lightColor, darkColor, ...otherProps } = props;
|
||||||
|
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||||
|
|
||||||
|
return <DefaultView style={[{ backgroundColor }, style]} {...otherProps} />;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
|
import { MonoText } from '../StyledText';
|
||||||
|
|
||||||
|
it(`renders correctly`, () => {
|
||||||
|
const tree = renderer.create(<MonoText>Snapshot test!</MonoText>).toJSON();
|
||||||
|
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Dimensions, Platform, StatusBar } from "react-native";
|
||||||
|
|
||||||
|
export const LISTMARGIN = 10;
|
||||||
|
export const WIDTH = Dimensions.get("screen").width - LISTMARGIN * 2;
|
||||||
|
|
||||||
|
const baseHeight = 160;
|
||||||
|
const isoNotch = 40;
|
||||||
|
const isoHeight = baseHeight + isoNotch;
|
||||||
|
let androidHeight = baseHeight;
|
||||||
|
let androidNotch = 0;
|
||||||
|
if (StatusBar.currentHeight) androidNotch = StatusBar.currentHeight;
|
||||||
|
androidHeight += androidNotch;
|
||||||
|
|
||||||
|
export const HEADERHEIGHT = Platform.OS === "ios" ? isoHeight : androidHeight;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
const tintColorLight = '#2f95dc';
|
||||||
|
const tintColorDark = '#fff';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
light: {
|
||||||
|
text: '#000',
|
||||||
|
background: '#fff',
|
||||||
|
tint: tintColorLight,
|
||||||
|
tabIconDefault: '#ccc',
|
||||||
|
tabIconSelected: tintColorLight,
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
text: '#fff',
|
||||||
|
background: '#000',
|
||||||
|
tint: tintColorDark,
|
||||||
|
tabIconDefault: '#ccc',
|
||||||
|
tabIconSelected: tintColorDark,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||||
|
const { getDefaultConfig } = require('expo/metro-config');
|
||||||
|
|
||||||
|
/** @type {import('expo/metro-config').MetroConfig} */
|
||||||
|
const config = getDefaultConfig(__dirname, {
|
||||||
|
// [Web-only]: Enables CSS support in Metro.
|
||||||
|
isCSSEnabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"name": "apartment-clone",
|
||||||
|
"main": "expo-router/entry",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"start": "expo start",
|
||||||
|
"android": "expo start --android",
|
||||||
|
"ios": "expo start --ios",
|
||||||
|
"web": "expo start --web",
|
||||||
|
"test": "jest --watchAll"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"preset": "jest-expo"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@eva-design/eva": "^2.2.0",
|
||||||
|
"@expo/vector-icons": "^13.0.0",
|
||||||
|
"@react-navigation/native": "^6.0.2",
|
||||||
|
"@ui-kitten/components": "^5.3.1",
|
||||||
|
"expo": "~49.0.15",
|
||||||
|
"expo-font": "~11.4.0",
|
||||||
|
"expo-linking": "~5.0.2",
|
||||||
|
"expo-router": "^2.0.0",
|
||||||
|
"expo-splash-screen": "~0.20.5",
|
||||||
|
"expo-status-bar": "~1.6.0",
|
||||||
|
"expo-system-ui": "~2.4.0",
|
||||||
|
"expo-web-browser": "~12.3.2",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-dom": "18.2.0",
|
||||||
|
"react-native": "0.72.6",
|
||||||
|
"react-native-gesture-handler": "~2.12.0",
|
||||||
|
"react-native-safe-area-context": "4.6.3",
|
||||||
|
"react-native-screens": "~3.22.0",
|
||||||
|
"react-native-svg": "13.9.0",
|
||||||
|
"react-native-web": "~0.19.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.20.0",
|
||||||
|
"@types/react": "~18.2.14",
|
||||||
|
"jest": "^29.2.1",
|
||||||
|
"jest-expo": "~49.0.0",
|
||||||
|
"react-test-renderer": "18.2.0",
|
||||||
|
"typescript": "^5.1.3"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"react-refresh": "~0.14.0"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"react-refresh": "~0.14.0"
|
||||||
|
},
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { light} from '@eva-design/eva';
|
||||||
|
|
||||||
|
export const appTheme = {
|
||||||
|
...light,
|
||||||
|
|
||||||
|
"color-primary-100": "#EFFBDB",
|
||||||
|
"color-primary-200": "#DDF8B9",
|
||||||
|
"color-primary-300": "#BEEA91",
|
||||||
|
"color-primary-400": "#9DD56F",
|
||||||
|
"color-primary-500": "#72B944",
|
||||||
|
"color-primary-600": "#569F31",
|
||||||
|
"color-primary-700": "#3D8522",
|
||||||
|
"color-primary-800": "#286B15",
|
||||||
|
"color-primary-900": "#18580D",
|
||||||
|
"color-success-100": "#CEFDD5",
|
||||||
|
"color-success-200": "#9DFBB5",
|
||||||
|
"color-success-300": "#6CF59C",
|
||||||
|
"color-success-400": "#46EC91",
|
||||||
|
"color-success-500": "#0FE082",
|
||||||
|
"color-success-600": "#0AC081",
|
||||||
|
"color-success-700": "#07A17A",
|
||||||
|
"color-success-800": "#04816F",
|
||||||
|
"color-success-900": "#026B66",
|
||||||
|
"color-info-100": "#CBF7FD",
|
||||||
|
"color-info-200": "#98E9FB",
|
||||||
|
"color-info-300": "#64D2F5",
|
||||||
|
"color-info-400": "#3EB6EC",
|
||||||
|
"color-info-500": "#048FE0",
|
||||||
|
"color-info-600": "#026FC0",
|
||||||
|
"color-info-700": "#0253A1",
|
||||||
|
"color-info-800": "#013A81",
|
||||||
|
"color-info-900": "#00296B",
|
||||||
|
"color-warning-100": "#FEFDD0",
|
||||||
|
"color-warning-200": "#FDFBA1",
|
||||||
|
"color-warning-300": "#FAF672",
|
||||||
|
"color-warning-400": "#F5F04F",
|
||||||
|
"color-warning-500": "#EFE817",
|
||||||
|
"color-warning-600": "#CDC610",
|
||||||
|
"color-warning-700": "#ACA50B",
|
||||||
|
"color-warning-800": "#8A8507",
|
||||||
|
"color-warning-900": "#726D04",
|
||||||
|
"color-danger-100": "#FFEAD6",
|
||||||
|
"color-danger-200": "#FFCFAE",
|
||||||
|
"color-danger-300": "#FFAE85",
|
||||||
|
"color-danger-400": "#FF8E67",
|
||||||
|
"color-danger-500": "#FF5A35",
|
||||||
|
"color-danger-600": "#DB3A26",
|
||||||
|
"color-danger-700": "#B71F1A",
|
||||||
|
"color-danger-800": "#931016",
|
||||||
|
"color-danger-900": "#7A0A17",
|
||||||
|
"search-border-default": "#d3d3d3",
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"extends": "expo/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".expo/types/**/*.ts",
|
||||||
|
"expo-env.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
export type Property = {
|
||||||
|
id: number;
|
||||||
|
images: string[];
|
||||||
|
rentLow: number;
|
||||||
|
rentHigh: number;
|
||||||
|
bedroomLow: number;
|
||||||
|
bedroomHigh: number;
|
||||||
|
name: string;
|
||||||
|
street: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
zip: number;
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue