diff --git a/03_source/mobile/src/pages/Demo2FaExample/AppPages/Tab1.jsx b/03_source/mobile/src/pages/Demo2FaExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile/src/pages/Demo2FaExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/Demo2FaExample/AppPages/Tab2.jsx b/03_source/mobile/src/pages/Demo2FaExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile/src/pages/Demo2FaExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/Demo2FaExample/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/Demo2FaExample/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/Demo2FaExample/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/Demo2FaExample/components/CurrentWeather/index.tsx b/03_source/mobile/src/pages/Demo2FaExample/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/Demo2FaExample/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/Demo2FaExample/components/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/Demo2FaExample/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/Demo2FaExample/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/Demo2FaExample/index.tsx b/03_source/mobile/src/pages/Demo2FaExample/index.tsx new file mode 100644 index 0000000..e567d3c --- /dev/null +++ b/03_source/mobile/src/pages/Demo2FaExample/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function Demo2FaExample() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default Demo2FaExample; diff --git a/03_source/mobile/src/pages/Demo2FaExample/style.scss b/03_source/mobile/src/pages/Demo2FaExample/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/Demo2FaExample/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoAccordionTutorial/components/Accordion.tsx b/03_source/mobile/src/pages/DemoAccordionTutorial/components/Accordion.tsx new file mode 100644 index 0000000..7e495fa --- /dev/null +++ b/03_source/mobile/src/pages/DemoAccordionTutorial/components/Accordion.tsx @@ -0,0 +1,30 @@ +import { IonAccordion, IonAccordionGroup, IonIcon, IonItem, IonLabel, IonList } from '@ionic/react'; +import { topics } from '../data'; +import React from 'react'; + +export const Accordion: React.FC = () => { + return ( + + {topics.map((topic: any, index: number) => { + return ( + + + + {topic.header} + + + + {topic.options.map((option: any, index2: number) => { + return ( + + {option.label} + + ); + })} + + + ); + })} + + ); +}; diff --git a/03_source/mobile/src/pages/DemoAccordionTutorial/data.ts b/03_source/mobile/src/pages/DemoAccordionTutorial/data.ts new file mode 100644 index 0000000..10fb915 --- /dev/null +++ b/03_source/mobile/src/pages/DemoAccordionTutorial/data.ts @@ -0,0 +1,126 @@ +import { + bicycleOutline, + fastFoodOutline, + filmOutline, + gameControllerOutline, + libraryOutline, +} from 'ionicons/icons'; + +export const topics: any = [ + { + header: 'Attractions', + color: 'primary', + icon: filmOutline, + options: [ + { + label: 'Cinema', + }, + { + label: 'Bowling Alley', + }, + { + label: 'Crazy Golf', + }, + { + label: 'Theme Park', + }, + ], + }, + { + header: 'Dining', + color: 'success', + icon: fastFoodOutline, + options: [ + { + label: 'Breakfast & Brunch', + }, + { + label: 'New American', + }, + { + label: 'Sushi Bars', + }, + { + label: 'Filipino Food', + }, + { + label: 'Asian Fusion', + }, + { + label: 'Ramen Houses', + }, + { + label: 'Dinner Venues', + }, + ], + }, + { + header: 'Gaming', + color: 'warning', + icon: gameControllerOutline, + options: [ + { + label: 'Xbox', + }, + { + label: 'Playstation', + }, + { + label: 'Nintendo Switch', + }, + { + label: 'PC', + }, + { + label: 'Mobile', + }, + { + label: 'Dreamcast', + }, + ], + }, + { + header: 'Exercise', + color: 'secondary', + icon: bicycleOutline, + options: [ + { + label: 'Yoga', + }, + { + label: 'Pilates', + }, + { + label: 'Weight Training', + }, + { + label: 'Cardio', + }, + { + label: 'Zumba', + }, + ], + }, + { + header: 'Education', + color: 'danger', + icon: libraryOutline, + options: [ + { + label: 'School', + }, + { + label: 'High School', + }, + { + label: 'University Bachelors', + }, + { + label: 'University Masters', + }, + { + label: 'University pHD', + }, + ], + }, +]; diff --git a/03_source/mobile/src/pages/DemoAccordionTutorial/index.tsx b/03_source/mobile/src/pages/DemoAccordionTutorial/index.tsx index d1512fc..2b2c87c 100644 --- a/03_source/mobile/src/pages/DemoAccordionTutorial/index.tsx +++ b/03_source/mobile/src/pages/DemoAccordionTutorial/index.tsx @@ -3,8 +3,8 @@ import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } import { cloudOutline, searchOutline } from 'ionicons/icons'; import { Route, Redirect } from 'react-router'; -import Tab1 from './AppPages/Tab1'; -import Tab2 from './AppPages/Tab2'; +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; import Topic from './pages/Topic.jsx'; import Home from './pages/Home.jsx'; @@ -30,9 +30,7 @@ function DemoAccordionTutorial() { - - - {/* */} + {/* diff --git a/03_source/mobile/src/pages/DemoAccordionTutorial/pages/Home.tsx b/03_source/mobile/src/pages/DemoAccordionTutorial/pages/Home.tsx new file mode 100644 index 0000000..65a2e4a --- /dev/null +++ b/03_source/mobile/src/pages/DemoAccordionTutorial/pages/Home.tsx @@ -0,0 +1,97 @@ +import { + IonAccordion, + IonAccordionGroup, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonList, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { Accordion } from '../components/Accordion'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home: React.FC = () => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Accordion + + + handleBackClick()}> + + + + + + + + + Accordion + + + handleBackClick()}> + + + + + + + + + {/* + + + Languages + + + + + English + + + Spanish + + + Italian + + + + + + + Languages 2 + + + + + English + + + Spanish + + + Italian + + + + */} + + + ); +}; + +export default Home; diff --git a/03_source/mobile/src/pages/DemoAccordionTutorial/pages/Topic.tsx b/03_source/mobile/src/pages/DemoAccordionTutorial/pages/Topic.tsx new file mode 100644 index 0000000..54c48e7 --- /dev/null +++ b/03_source/mobile/src/pages/DemoAccordionTutorial/pages/Topic.tsx @@ -0,0 +1,49 @@ +import { + IonBackButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonLabel, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useParams } from 'react-router'; + +const Topic = () => { + const { topic } = useParams<{ topic: string }>(); + + return ( + + + + + + + + {topic} + + + + + + {topic} + + + + + + + This is the page for the topic: {topic}. + + + + + + ); +}; + +export default Topic; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoColorTutorial/components/ExploreContainer.css b/03_source/mobile/src/pages/DemoColorTutorial/components/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/components/ExploreContainer.css @@ -0,0 +1,24 @@ +.container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.container strong { + font-size: 20px; + line-height: 26px; +} + +.container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; +} + +.container a { + text-decoration: none; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoColorTutorial/components/ExploreContainer.tsx b/03_source/mobile/src/pages/DemoColorTutorial/components/ExploreContainer.tsx new file mode 100644 index 0000000..354df7b --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/components/ExploreContainer.tsx @@ -0,0 +1,16 @@ +import './ExploreContainer.css'; + +interface ContainerProps { + name: string; +} + +const ExploreContainer: React.FC = ({ name }) => { + return ( +
+ {name} +

Explore UI Components

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/index.tsx b/03_source/mobile/src/pages/DemoColorTutorial/index.tsx new file mode 100644 index 0000000..6090ccb --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/index.tsx @@ -0,0 +1,76 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, ellipse, searchOutline, square, triangle } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; + +import './style.scss'; + +function DemoColorTutorial() { + return ( + + {/* + + + + + + + + + + + + */} + + + + + + + + + + + + + + + + + {/* + + + Dashboard + + + + Search + + */} + + {/* update path base from `/` to `/demo-color-tutorial` */} + + + Tab 1 + + + + Tab 2 + + + + Tab 3 + + + + ); +} + +export default DemoColorTutorial; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab1.css b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab1.tsx b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab1.tsx new file mode 100644 index 0000000..cc52b85 --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab1.tsx @@ -0,0 +1,48 @@ +import { IonBadge, IonButton, IonCheckbox, IonContent, IonHeader, IonItem, IonLabel, IonNote, IonPage, IonRadio, IonTitle, IonToggle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab1.css'; + +const Tab1: React.FC = () => { + return ( + + + + Tab 1 + + + + + + Tab 1 + + + + + Button + + + + Toggle + + + + + Checkbox + + + + + Badge + 14 notifications + + + + Note + 3 unread + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab2.css b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab2.tsx b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab2.tsx new file mode 100644 index 0000000..05458aa --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2: React.FC = () => { + return ( + + + + Tab 2 + + + + + + Tab 2 + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab3.css b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab3.tsx b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab3.tsx new file mode 100644 index 0000000..3a29b8a --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3: React.FC = () => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile/src/pages/DemoColorTutorial/style.scss b/03_source/mobile/src/pages/DemoColorTutorial/style.scss new file mode 100644 index 0000000..5430344 --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/style.scss @@ -0,0 +1,256 @@ +.demo-color-tutorial { + /* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + + /** Ionic CSS Variables **/ + * { + /** alans color **/ + --ion-color-alans-color: #6c9400; + --ion-color-alans-color-rgb: 108, 148, 0; + --ion-color-alans-color-contrast: #ffffff; + --ion-color-alans-color-contrast-rgb: 255, 255, 255; + --ion-color-alans-color-shade: #5f8200; + --ion-color-alans-color-tint: #7b9f1a; + + /** primary **/ + --ion-color-primary: #be402f; + --ion-color-primary-rgb: 190, 64, 47; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #a73829; + --ion-color-primary-tint: #c55344; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + .ion-color-alans-color { + --ion-color-base: var(--ion-color-alans-color); + --ion-color-base-rgb: var(--ion-color-alans-color-rgb); + --ion-color-contrast: var(--ion-color-alans-color-contrast); + --ion-color-contrast-rgb: var(--ion-color-alans-color-contrast-rgb); + --ion-color-shade: var(--ion-color-alans-color-shade); + --ion-color-tint: var(--ion-color-alans-color-tint); + } + + @media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } + } +} diff --git a/03_source/mobile/src/pages/DemoColorTutorial/theme/variables.css b/03_source/mobile/src/pages/DemoColorTutorial/theme/variables.css new file mode 100644 index 0000000..5f9d075 --- /dev/null +++ b/03_source/mobile/src/pages/DemoColorTutorial/theme/variables.css @@ -0,0 +1,254 @@ +/* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + +/** Ionic CSS Variables **/ +:root { + /** alans color **/ + --ion-color-alans-color: #6c9400; + --ion-color-alans-color-rgb: 108, 148, 0; + --ion-color-alans-color-contrast: #ffffff; + --ion-color-alans-color-contrast-rgb: 255, 255, 255; + --ion-color-alans-color-shade: #5f8200; + --ion-color-alans-color-tint: #7b9f1a; + + /** primary **/ + --ion-color-primary: #be402f; + --ion-color-primary-rgb: 190, 64, 47; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #a73829; + --ion-color-primary-tint: #c55344; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; +} + +.ion-color-alans-color { + --ion-color-base: var(--ion-color-alans-color); + --ion-color-base-rgb: var(--ion-color-alans-color-rgb); + --ion-color-contrast: var(--ion-color-alans-color-contrast); + --ion-color-contrast-rgb: var(--ion-color-alans-color-contrast-rgb); + --ion-color-shade: var(--ion-color-alans-color-shade); + --ion-color-tint: var(--ion-color-alans-color-tint); +} + +@media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } +} diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/index.tsx b/03_source/mobile/src/pages/DemoEcommerceExample/index.tsx new file mode 100644 index 0000000..543bf33 --- /dev/null +++ b/03_source/mobile/src/pages/DemoEcommerceExample/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoEcommerceExample() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoEcommerceExample; diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/style.scss b/03_source/mobile/src/pages/DemoEcommerceExample/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoEcommerceExample/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoFacebookClone/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoFacebookClone/index.tsx b/03_source/mobile/src/pages/DemoFacebookClone/index.tsx new file mode 100644 index 0000000..9ff24b1 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFacebookClone/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoFacebookClone() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoFacebookClone; diff --git a/03_source/mobile/src/pages/DemoFacebookClone/style.scss b/03_source/mobile/src/pages/DemoFacebookClone/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoFacebookClone/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/index.tsx b/03_source/mobile/src/pages/DemoFastFoodApp/index.tsx new file mode 100644 index 0000000..26759f1 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFastFoodApp/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoFastFoodApp() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoFastFoodApp; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/style.scss b/03_source/mobile/src/pages/DemoFastFoodApp/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoFastFoodApp/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoFloatingTabs/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/index.tsx b/03_source/mobile/src/pages/DemoFloatingTabs/index.tsx new file mode 100644 index 0000000..8509111 --- /dev/null +++ b/03_source/mobile/src/pages/DemoFloatingTabs/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoFloatingTabs() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoFloatingTabs; diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/style.scss b/03_source/mobile/src/pages/DemoFloatingTabs/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoFloatingTabs/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoInstagramClone/index.tsx b/03_source/mobile/src/pages/DemoInstagramClone/index.tsx new file mode 100644 index 0000000..c438e5b --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoInstagramClone() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoInstagramClone; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/style.scss b/03_source/mobile/src/pages/DemoInstagramClone/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/index.tsx b/03_source/mobile/src/pages/DemoKanbanBoard/index.tsx new file mode 100644 index 0000000..b85f50a --- /dev/null +++ b/03_source/mobile/src/pages/DemoKanbanBoard/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoKanbanBoard() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoKanbanBoard; diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/style.scss b/03_source/mobile/src/pages/DemoKanbanBoard/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoKanbanBoard/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoOrderingApp/index.tsx b/03_source/mobile/src/pages/DemoOrderingApp/index.tsx new file mode 100644 index 0000000..a1ed13a --- /dev/null +++ b/03_source/mobile/src/pages/DemoOrderingApp/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoOrderingApp() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoOrderingApp; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/style.scss b/03_source/mobile/src/pages/DemoOrderingApp/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoOrderingApp/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoProfileExample/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoProfileExample/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoProfileExample/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoProfileExample/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoProfileExample/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoProfileExample/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoProfileExample/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoProfileExample/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoProfileExample/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoProfileExample/index.tsx b/03_source/mobile/src/pages/DemoProfileExample/index.tsx new file mode 100644 index 0000000..83b92dd --- /dev/null +++ b/03_source/mobile/src/pages/DemoProfileExample/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoProfileExample() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoProfileExample; diff --git a/03_source/mobile/src/pages/DemoProfileExample/style.scss b/03_source/mobile/src/pages/DemoProfileExample/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoProfileExample/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoPullstateTutorial/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/index.tsx b/03_source/mobile/src/pages/DemoPullstateTutorial/index.tsx new file mode 100644 index 0000000..0b8f21b --- /dev/null +++ b/03_source/mobile/src/pages/DemoPullstateTutorial/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoPullstateTutorial() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoPullstateTutorial; diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/style.scss b/03_source/mobile/src/pages/DemoPullstateTutorial/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoPullstateTutorial/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/index.tsx b/03_source/mobile/src/pages/DemoReactAddToCart/index.tsx new file mode 100644 index 0000000..a84db3f --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactAddToCart/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactAddToCart() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactAddToCart; diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/style.scss b/03_source/mobile/src/pages/DemoReactAddToCart/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactAddToCart/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactCalculator/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactCalculator/index.tsx b/03_source/mobile/src/pages/DemoReactCalculator/index.tsx new file mode 100644 index 0000000..8d5c5ef --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactCalculator/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactCalculator() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactCalculator; diff --git a/03_source/mobile/src/pages/DemoReactCalculator/style.scss b/03_source/mobile/src/pages/DemoReactCalculator/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactCalculator/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/index.tsx b/03_source/mobile/src/pages/DemoReactDrawingCanvas/index.tsx new file mode 100644 index 0000000..aa0d0f3 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactDrawingCanvas/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactDrawingCanvas() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactDrawingCanvas; diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/style.scss b/03_source/mobile/src/pages/DemoReactDrawingCanvas/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactDrawingCanvas/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactHookFormExample/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactHookFormExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactHookFormExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactHookFormExample/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactHookFormExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactHookFormExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactHookFormExample/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactHookFormExample/index.tsx b/03_source/mobile/src/pages/DemoReactHookFormExample/index.tsx new file mode 100644 index 0000000..e5b0304 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactHookFormExample/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactHookFormExample() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactHookFormExample; diff --git a/03_source/mobile/src/pages/DemoReactHookFormExample/style.scss b/03_source/mobile/src/pages/DemoReactHookFormExample/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactHookFormExample/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactItemList/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactItemList/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactItemList/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactItemList/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactItemList/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactItemList/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactItemList/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactItemList/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactItemList/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactItemList/index.tsx b/03_source/mobile/src/pages/DemoReactItemList/index.tsx new file mode 100644 index 0000000..f5bfc42 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactItemList/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactItemList() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactItemList; diff --git a/03_source/mobile/src/pages/DemoReactItemList/style.scss b/03_source/mobile/src/pages/DemoReactItemList/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactItemList/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactLifecycles/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactLifecycles/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLifecycles/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactLifecycles/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactLifecycles/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLifecycles/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLifecycles/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactLifecycles/index.tsx b/03_source/mobile/src/pages/DemoReactLifecycles/index.tsx new file mode 100644 index 0000000..1a34023 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLifecycles/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactLifecycles() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactLifecycles; diff --git a/03_source/mobile/src/pages/DemoReactLifecycles/style.scss b/03_source/mobile/src/pages/DemoReactLifecycles/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLifecycles/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactLogin/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactLogin/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLogin/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactLogin/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactLogin/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLogin/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactLogin/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactLogin/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLogin/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactLogin/index.tsx b/03_source/mobile/src/pages/DemoReactLogin/index.tsx new file mode 100644 index 0000000..fbd5d16 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLogin/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactLogin() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactLogin; diff --git a/03_source/mobile/src/pages/DemoReactLogin/style.scss b/03_source/mobile/src/pages/DemoReactLogin/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactLogin/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactMarvelApp/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMarvelApp/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactMarvelApp/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMarvelApp/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/index.tsx b/03_source/mobile/src/pages/DemoReactMarvelApp/index.tsx new file mode 100644 index 0000000..6f20bff --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMarvelApp/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactMarvelApp() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactMarvelApp; diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/style.scss b/03_source/mobile/src/pages/DemoReactMarvelApp/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMarvelApp/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/index.tsx b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/index.tsx new file mode 100644 index 0000000..fe31374 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactMovieAppWithAlgolia() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactMovieAppWithAlgolia; diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/style.scss b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactNotes/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactNotes/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactNotes/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactNotes/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactNotes/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactNotes/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactNotes/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactNotes/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactNotes/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactNotes/index.tsx b/03_source/mobile/src/pages/DemoReactNotes/index.tsx new file mode 100644 index 0000000..ad6dd84 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactNotes/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactNotes() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactNotes; diff --git a/03_source/mobile/src/pages/DemoReactNotes/style.scss b/03_source/mobile/src/pages/DemoReactNotes/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactNotes/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactOnboardingUi/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactOnboardingUi/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactOnboardingUi/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactOnboardingUi/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/index.tsx b/03_source/mobile/src/pages/DemoReactOnboardingUi/index.tsx new file mode 100644 index 0000000..e881af9 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactOnboardingUi/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactOnboardingUi() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactOnboardingUi; diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/style.scss b/03_source/mobile/src/pages/DemoReactOnboardingUi/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactOnboardingUi/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactProfileDashboardUi/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactProfileDashboardUi/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactProfileDashboardUi/index.tsx b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/index.tsx new file mode 100644 index 0000000..741bc42 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactProfileDashboardUi() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactProfileDashboardUi; diff --git a/03_source/mobile/src/pages/DemoReactProfileDashboardUi/style.scss b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactProfileDashboardUi/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactQrCode/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactQrCode/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQrCode/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactQrCode/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactQrCode/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQrCode/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQrCode/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactQrCode/index.tsx b/03_source/mobile/src/pages/DemoReactQrCode/index.tsx new file mode 100644 index 0000000..7c0fd59 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQrCode/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactQrCode() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactQrCode; diff --git a/03_source/mobile/src/pages/DemoReactQrCode/style.scss b/03_source/mobile/src/pages/DemoReactQrCode/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQrCode/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactQuotes/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactQuotes/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQuotes/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactQuotes/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactQuotes/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQuotes/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactQuotes/index.tsx b/03_source/mobile/src/pages/DemoReactQuotes/index.tsx new file mode 100644 index 0000000..185faa2 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQuotes/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactQuotes() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactQuotes; diff --git a/03_source/mobile/src/pages/DemoReactQuotes/style.scss b/03_source/mobile/src/pages/DemoReactQuotes/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactQuotes/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactShopUi/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactShopUi/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactShopUi/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactShopUi/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactShopUi/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactShopUi/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactShopUi/index.tsx b/03_source/mobile/src/pages/DemoReactShopUi/index.tsx new file mode 100644 index 0000000..9981c04 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactShopUi/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactShopUi() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactShopUi; diff --git a/03_source/mobile/src/pages/DemoReactShopUi/style.scss b/03_source/mobile/src/pages/DemoReactShopUi/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactShopUi/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/index.tsx b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/index.tsx new file mode 100644 index 0000000..59ce634 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactTabsMenusCustom() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactTabsMenusCustom; diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/style.scss b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactTabsMenusCustom/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactThemeSwitcher/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoReactThemeSwitcher/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactThemeSwitcher/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactThemeSwitcher/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoReactThemeSwitcher/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactThemeSwitcher/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactThemeSwitcher/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactThemeSwitcher/index.tsx b/03_source/mobile/src/pages/DemoReactThemeSwitcher/index.tsx new file mode 100644 index 0000000..cd163cc --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactThemeSwitcher/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoReactThemeSwitcher() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoReactThemeSwitcher; diff --git a/03_source/mobile/src/pages/DemoReactThemeSwitcher/style.scss b/03_source/mobile/src/pages/DemoReactThemeSwitcher/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactThemeSwitcher/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/ChatStore.js b/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/ChatStore.js new file mode 100644 index 0000000..3b4e6fb --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/ChatStore.js @@ -0,0 +1,384 @@ +import { Store } from 'pullstate'; + +const ChatStore = new Store({ + + chats: [ + { + id: 1, + contact_id: 1, + chats: [ + + { + id: 1, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: true, + sent: false, + date: "20:05", + read: false, + starred: false + }, + { + id: 2, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: false, + sent: true, + date: "20:05", + read: false, + starred: false + }, + { + id: 3, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: true, + sent: false, + date: "20:05", + read: false, + starred: false + }, + { + id: 4, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: false, + sent: true, + date: "20:05", + read: false, + starred: false + }, + { + id: 5, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: true, + sent: false, + date: "20:05", + read: false, + starred: false + }, + { + id: 6, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: false, + sent: true, + date: "20:05", + read: false, + starred: false + }, + { + id: 7, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: true, + sent: false, + date: "20:05", + read: false, + starred: false + }, + { + id: 8, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: false, + sent: true, + date: "20:05", + read: false, + starred: false + }, + { + id: 9, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: true, + sent: false, + date: "20:05", + read: false, + starred: false + }, + { + id: 10, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: false, + sent: true, + date: "20:05", + read: false, + starred: false + }, + { + id: 11, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: true, + sent: false, + date: "20:05", + read: false, + starred: false + }, + { + id: 12, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: false, + sent: true, + date: "20:05", + read: false, + starred: false + }, + { + id: 13, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: true, + sent: false, + date: "20:05", + read: false, + starred: false + }, + { + id: 14, + preview: "This is a test whatsapp message inside the whatsapp clone app", + received: false, + sent: true, + date: "20:10", + read: false, + starred: false + } + ] + }, + { + id: 2, + contact_id: 2, + chats: [ + + { + id: 1, + preview: "Excited for the Ioniconf 2021! The 23rd can't come quick enough", + received: false, + sent: true, + date: "Yesterday", + read: true, + starred: false + } + ] + }, + { + id: 3, + contact_id: 3, + chats: [ + + { + id: 1, + preview: "Excited for the Ioniconf 2021! The 23rd can't come quick enough", + received: false, + sent: true, + date: "Saturday", + read: true, + starred: false + } + ] + }, + { + id: 4, + contact_id: 4, + chats: [ + + { + id: 1, + preview: "Hey Alan, this is a test whatsapp message", + received: true, + sent: false, + date: "Friday", + read: false, + starred: false + } + ] + }, + { + id: 5, + contact_id: 5, + chats: [ + + { + id: 1, + preview: "Hey Alan, this is a test whatsapp message", + received: true, + sent: false, + date: "Thursday", + read: false, + starred: false + } + ] + }, + { + id: 6, + contact_id: 6, + chats: [ + + { + id: 1, + preview: "Hey Alan, this is a test received whatsapp message", + received: true, + sent: false, + date: "Wednesday", + read: false, + starred: false + }, + { + id: 2, + preview: "Hey Josh, this is a test sent whatsapp message", + received: true, + sent: true, + date: "Wednesday", + read: true, + starred: false + }, + { + id: 3, + preview: "Awesome man! This seems to be coming together OK!", + received: true, + sent: false, + date: "Wednesday", + read: true, + starred: false + } + ] + }, + { + id: 7, + contact_id: 7, + chats: [ + + { + id: 1, + preview: "Goodluck on the quest to space, the moon and mars!", + received: true, + sent: false, + date: "08/06/2021", + read: true, + starred: false + } + ] + }, + { + id: 8, + contact_id: 8, + chats: [ + + { + id: 1, + preview: "Hey Bill, This is a test sent message to your whatsapp", + received: true, + sent: false, + date: "05/06/2021", + read: true, + starred: false + } + ] + }, + { + id: 9, + contact_id: 10, + chats: [ + + { + id: 1, + preview: "This is Alans WhatsApp clone using the Ionic Framework and CapacitorJS for some native functionality.", + received: true, + sent: false, + date: "Wednesday", + read: false, + starred: false + }, + { + id: 2, + preview: "Yep, and don't forget the Ionic Animations for the slick UI transitions you see, and some Ionic Gestures for sliding etc, as well as some other things!", + received: true, + sent: true, + date: "Wednesday", + read: true, + starred: false + }, + { + id: 3, + preview: "Cool! It looks good, can we get another teaser?", + received: true, + sent: false, + date: "Wednesday", + read: true, + starred: false + }, + { + id: 4, + preview: "Sure... I'll post one on Twitter now!", + received: false, + sent: true, + date: "Wednesday", + read: true, + starred: false + } + ] + }, + ] +}); + +export const getNotificationCount = (allChats) => { + + let notificationCount = 0; + + allChats.forEach(chats => { + + chats.chats.forEach(chat => { + + if (!chat.read) { + + notificationCount++; + } + }) + }); + + return notificationCount; +} + +export const markAllAsRead = contactId => { + + ChatStore.update(s => { + + const chatIndex = s.chats.findIndex(chat => chat.contact_id === parseInt(contactId)); + + s.chats[chatIndex].chats.forEach(chat => { + + chat.read = true; + }); + }); +} + +export const sendChatMessage = (contactId, message, reply = false, replyID = false, image = false, imagePath = false) => { + + const today = new Date(); + const currentTime = `${ today.getHours() < 10 ? `0${ today.getHours() }` : today.getHours() }:${ today.getMinutes() < 10 ? `0${ today.getMinutes() }` : today.getMinutes() }`; + + ChatStore.update(s => { + + const chatIndex = s.chats.findIndex(chat => chat.contact_id === parseInt(contactId)); + const newChat = { + + id: s.chats[chatIndex].length + 1, + preview: message, + received: false, + sent: true, + date: currentTime, + read: true, + starred: false, + reply, + replyID, + image: image, + imagePath: imagePath + }; + + s.chats[chatIndex].chats.push(newChat); + }); +} + +export const starChatMessage = (contactId, messageId) => { + + ChatStore.update(s => { + + const chatIndex = s.chats.findIndex(chat => chat.contact_id === parseInt(contactId)); + const messageIndex = s.chats[chatIndex].chats.findIndex(message => message.id === parseInt(messageId)); + + s.chats[chatIndex].chats[messageIndex].starred = !s.chats[chatIndex].chats[messageIndex].starred; + }); +} + +export default ChatStore; \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/ContactStore.js b/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/ContactStore.js new file mode 100644 index 0000000..655aa50 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/ContactStore.js @@ -0,0 +1,59 @@ +import { Store } from 'pullstate'; + +const ContactStore = new Store({ + + contacts: [ + { + id: 1, + name: "Amy Sister", + avatar: "/assets/amy.jpeg" + }, + { + id: 2, + name: "Max Lynch", + avatar: "/assets/max.jpeg" + }, + { + id: 3, + name: "Mike Hartington", + avatar: "/assets/mike.jpeg" + }, + { + id: 4, + name: "Henk Jurriens", + avatar: "/assets/henk.jpeg" + }, + { + id: 5, + name: "Simon Grimm", + avatar: "/assets/simon.jpeg" + }, + { + id: 6, + name: "Josh Morony", + avatar: "/assets/josh.jpeg" + }, + { + id: 7, + name: "Elon Musk", + avatar: "/assets/elon.jpeg" + }, + { + id: 8, + name: "Bill Gates", + avatar: "/assets/bill.jpeg" + }, + { + id: 9, + name: "Mark Zuckerberg", + avatar: "/assets/mark.jpeg" + }, + { + id: 10, + name: "Ionic Framework (not)", + avatar: "/assets/ionic.png" + }, + ] +}); + +export default ContactStore; \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/Selectors.js b/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/Selectors.js new file mode 100644 index 0000000..e6aefe7 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/Selectors.js @@ -0,0 +1,13 @@ +import { createSelector } from 'reselect'; + +const getState = state => state; + +// General getters +export const getContacts = createSelector(getState, state => state.contacts); +export const getChats = createSelector(getState, state => state.chats); + +// More specific getters +export const getChat = contactId => createSelector(getState, state => state.chats.filter(c => parseInt(c.contact_id) === parseInt(contactId))[0].chats); +export const getContact = contactId => createSelector(getState, state => state.contacts.filter(c => parseInt(c.id) === parseInt(contactId))[0]); + +export const getChatNotificationCount = contactId => createSelector(getState, state => (state.chats.filter(c => parseInt(c.contact_id) === parseInt(contactId))[0].chats).filter(chat => chat.read === false)); \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/index.js b/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/index.js new file mode 100644 index 0000000..c88c7f4 --- /dev/null +++ b/03_source/mobile/src/pages/DemoReactWhatsAppClone/store/index.js @@ -0,0 +1,2 @@ +export { default as ContactStore } from "./ContactStore"; +export { default as ChatStore } from "./ChatStore"; \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoSkeletonText/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoSkeletonText/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile/src/pages/DemoSkeletonText/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoSkeletonText/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoSkeletonText/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile/src/pages/DemoSkeletonText/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoSkeletonText/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoSkeletonText/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoSkeletonText/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoSkeletonText/components/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoSkeletonText/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoSkeletonText/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoSkeletonText/components/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoSkeletonText/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoSkeletonText/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoSkeletonText/index.tsx b/03_source/mobile/src/pages/DemoSkeletonText/index.tsx new file mode 100644 index 0000000..c33e8e7 --- /dev/null +++ b/03_source/mobile/src/pages/DemoSkeletonText/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoSkeletonText() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoSkeletonText; diff --git a/03_source/mobile/src/pages/DemoSkeletonText/style.scss b/03_source/mobile/src/pages/DemoSkeletonText/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoSkeletonText/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/index.tsx b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/index.tsx new file mode 100644 index 0000000..82337e3 --- /dev/null +++ b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoStickyBottomSheetExample() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoStickyBottomSheetExample; diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/style.scss b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoStickyBottomSheetExample/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoStorageExample/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoStorageExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile/src/pages/DemoStorageExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoStorageExample/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoStorageExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile/src/pages/DemoStorageExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoStorageExample/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoStorageExample/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoStorageExample/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoStorageExample/components/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoStorageExample/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoStorageExample/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoStorageExample/components/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoStorageExample/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoStorageExample/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoStorageExample/index.tsx b/03_source/mobile/src/pages/DemoStorageExample/index.tsx new file mode 100644 index 0000000..9779afd --- /dev/null +++ b/03_source/mobile/src/pages/DemoStorageExample/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoStorageExample() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoStorageExample; diff --git a/03_source/mobile/src/pages/DemoStorageExample/style.scss b/03_source/mobile/src/pages/DemoStorageExample/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoStorageExample/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoSwiperjsTutorial/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoSwiperjsTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile/src/pages/DemoSwiperjsTutorial/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoSwiperjsTutorial/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoSwiperjsTutorial/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile/src/pages/DemoSwiperjsTutorial/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoSwiperjsTutorial/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoSwiperjsTutorial/index.tsx b/03_source/mobile/src/pages/DemoSwiperjsTutorial/index.tsx new file mode 100644 index 0000000..c0a996f --- /dev/null +++ b/03_source/mobile/src/pages/DemoSwiperjsTutorial/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoSwiperjsTutorial() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoSwiperjsTutorial; diff --git a/03_source/mobile/src/pages/DemoSwiperjsTutorial/style.scss b/03_source/mobile/src/pages/DemoSwiperjsTutorial/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoSwiperjsTutorial/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoWeatherAppUi/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile/src/pages/DemoWeatherAppUi/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoWeatherAppUi/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile/src/pages/DemoWeatherAppUi/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/components/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoWeatherAppUi/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoWeatherAppUi/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/index.tsx b/03_source/mobile/src/pages/DemoWeatherAppUi/index.tsx new file mode 100644 index 0000000..b45bc18 --- /dev/null +++ b/03_source/mobile/src/pages/DemoWeatherAppUi/index.tsx @@ -0,0 +1,40 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; + +function DemoWeatherAppUi() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoWeatherAppUi; diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/style.scss b/03_source/mobile/src/pages/DemoWeatherAppUi/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile/src/pages/DemoWeatherAppUi/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/NOTES.md b/03_source/mobile/src/pages/NOTES.md new file mode 100644 index 0000000..7545f8f --- /dev/null +++ b/03_source/mobile/src/pages/NOTES.md @@ -0,0 +1,36 @@ +demo-accordion-tutorial +demo-banking-ui +demo-capacitor-google-maps-tutorial +demo-color-tutorial +demo-ecommerce-example +demo-facebook-clone +demo-fast-food-app +demo-floating-tabs +demo-instagram-clone +demo-kanban-board +demo-ordering-app +demo-profile-example +demo-pullstate-tutorial +demo-react-add-to-cart +demo-react-calculator +demo-react-drawing-canvas +demo-react-hook-form-example +demo-react-item-list +demo-react-lifecycles +demo-react-login +demo-react-marvel-app +demo-react-movie-app-with-algolia +demo-react-notes +demo-react-onboarding-ui +demo-react-profile-dashboard-ui +demo-react-qr-code +demo-react-quotes +demo-react-shop-ui +demo-react-tabs-menus-custom +demo-react-theme-switcher +demo-react-whatsapp-clone +demo-skeleton-text +demo-sticky-bottom-sheet-example +demo-storage-example +demo-swiperjs-tutorial +demo-weather-app-ui diff --git a/03_source/mobile/src/pages/TestComponents/AddToCartButton.jsx b/03_source/mobile/src/pages/TestComponents/AddToCartButton.jsx new file mode 100644 index 0000000..33810a5 --- /dev/null +++ b/03_source/mobile/src/pages/TestComponents/AddToCartButton.jsx @@ -0,0 +1,58 @@ +import { CreateAnimation, IonButton, IonIcon } from "@ionic/react"; +import { cartOutline } from "ionicons/icons"; +import { useRef, useState } from "react"; +import { addToCart } from "../store/CartStore"; + +export const AddToCartButton = ({product}) => { + + const animationRef = useRef(); + const [hidden, setHidden] = useState(true); + + const floatStyle = { + + display: hidden ? "none" : "", + position: "absolute" + }; + + const floatGrowAnimation = { + + property: "transform", + fromValue: "translateY(0) scale(1)", + toValue: "translateY(-55px) scale(1.5)" + }; + + const colorAnimation = { + + property: "color", + fromValue: "green", + toValue: "green" + }; + + const mainAnimation = { + + duration: 1500, + iterations: "1", + fromTo: [ floatGrowAnimation, colorAnimation ], + easing: "cubic-bezier(0.25, 0.7, 0.25, 0.7)" + }; + + const handleAddToCart = async product => { + + setHidden(false); + await animationRef.current.animation.play(); + setHidden(true); + addToCart(product); + } + + return ( + + handleAddToCart(product)}> +   + Add to Cart + + + + + + ); +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/TestComponents/Breadcrumbs.jsx b/03_source/mobile/src/pages/TestComponents/Breadcrumbs.jsx new file mode 100644 index 0000000..a5b3263 --- /dev/null +++ b/03_source/mobile/src/pages/TestComponents/Breadcrumbs.jsx @@ -0,0 +1,23 @@ +import { IonBreadcrumb, IonBreadcrumbs, IonIcon } from "@ionic/react"; +import { fastFoodOutline } from "ionicons/icons"; +import { useState } from "react"; + +export const Breadcrumbs = () => { + + const [maxItems, setMaxItems] = useState(2); + + const handleClick = () => { + + setMaxItems(undefined); + } + + return ( + + + Page 1 + Page 2 + Page 3 + Page 4 + + ); +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/TestComponents/CartModal.jsx b/03_source/mobile/src/pages/TestComponents/CartModal.jsx new file mode 100644 index 0000000..ce7c030 --- /dev/null +++ b/03_source/mobile/src/pages/TestComponents/CartModal.jsx @@ -0,0 +1,82 @@ +import { useStoreState } from "pullstate"; +import { useEffect, useState } from "react"; +import { CartStore } from "../store"; +import { addToCart } from "../store/CartStore"; +import { getCart } from "../store/Selectors"; + +const { IonPage, IonHeader, IonToolbar, IonTitle, IonButtons, IonIcon, IonContent, IonGrid, IonRow, IonItem, IonLabel, IonText, IonThumbnail, IonFooter, IonCol, IonButton, IonItemSliding, IonItemOptions, IonItemOption } = require("@ionic/react"); +const { close } = require("ionicons/icons"); + +export const CartModal = props => { + + const cart = useStoreState(CartStore, getCart); + const [totalPrice, setTotalPrice] = useState(0); + + useEffect(() => { + + let total = 0; + cart.forEach(item => total += parseInt(item.price.replace("£", ""))); + setTotalPrice(total); + }, [cart]); + + return ( + + + + Cart + + + + + + + + + + + +

{cart.length} products in your cart

+ +

Review products and checkout

+
+
+
+
+
+ + {cart.map((item, index) => ( + + + + item + + +

{item.title}

+

{item.price}

+
+
+ + + addToCart(item)}> + Remove + + +
+ ))} +
+ + + + +

Total

+
+ + +

£{totalPrice.toFixed(2)}

+
+
+ Checkout → +
+
+ ); +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/TestComponents/FilterModal.jsx b/03_source/mobile/src/pages/TestComponents/FilterModal.jsx new file mode 100644 index 0000000..067aac7 --- /dev/null +++ b/03_source/mobile/src/pages/TestComponents/FilterModal.jsx @@ -0,0 +1,36 @@ +import { IonButton, IonCol, IonContent, IonGrid, IonHeader, IonRow, IonTitle, IonToolbar } from "@ionic/react"; + +export const FilterModal = ({productsRef, filterCriteria, setFilterCriteria, dismiss, filters}) => { + + const filterProducts = async filter => { + + await productsRef.current.classList.add("animate__fadeOutLeft"); + + setTimeout(() => { + productsRef.current.classList.remove("animate__fadeOutLeft"); + productsRef.current.classList.add("animate__fadeInRight"); + setFilterCriteria(filter); + }, 500); + dismiss(); + } + + return ( + + + + + Filter + + + + + {filters.map(f => ( + + filterProducts(f)}>{f} + + ))} + + + + ); + } \ No newline at end of file diff --git a/03_source/mobile/src/pages/TestComponents/ProductModal.css b/03_source/mobile/src/pages/TestComponents/ProductModal.css new file mode 100644 index 0000000..8884088 --- /dev/null +++ b/03_source/mobile/src/pages/TestComponents/ProductModal.css @@ -0,0 +1,88 @@ +ion-card { + margin: 0; + /* margin-top: var(--ion-safe-area-top); */ + z-index: -1; + + border-radius: 0px; + + background-size: cover; + background-position: center; + background-repeat: no-repeat; + box-shadow: none; + aspect-ratio: 1 / 1; +} + +@supports not (aspect-ratio: 1 / 1) { + ion-card::before { + float: left; + padding-top: 100%; + content: ''; + } + + ion-card::after { + display: block; + content: ''; + clear: both; + } +} + +ion-card-header { + position: absolute; + bottom: 0; + width: 100%; + + /* background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 100%); */ + background: rgba(0, 0, 0, 0.5) +} + +ion-card-title, +ion-card-subtitle { + color: white; +} + +ion-card-header ion-card-title { + + margin: 0 0 6px 0; + font-size: 22px; +} + +ion-card-header ion-card-subtitle { + + text-transform: none; + font-weight: 500; + font-size: 16px; +} + +ion-card-content { + height: calc(60px + var(--ion-safe-area-top)); + background: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.5) 100%); +} + +#close-button { + position: fixed; + + top: max(var(--ion-safe-area-top), 16px); + right: 8px; +} + +#fave-button { + position: fixed; + + top: max(var(--ion-safe-area-top), 16px); + left: 8px; +} + +#product-view-buttons { + + z-index: 10; + background: linear-gradient(360deg, rgba(0, 0, 0, 0) 0%, rgba(82, 82, 82, 0.9) 100%) !important; + position: absolute; + width: 100%; + height: 4rem; +} + +.sticky-bottom { + + position: fixed; + bottom: 0; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/TestComponents/ProductModal.jsx b/03_source/mobile/src/pages/TestComponents/ProductModal.jsx new file mode 100644 index 0000000..a519b87 --- /dev/null +++ b/03_source/mobile/src/pages/TestComponents/ProductModal.jsx @@ -0,0 +1,76 @@ +import { IonButton, IonButtons, IonCard, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCol, IonContent, IonFooter, IonIcon, IonLabel, IonNote, IonRow, IonText, IonToolbar } from "@ionic/react"; +import { closeCircle, heart, heartOutline } from "ionicons/icons"; +import { useStoreState } from "pullstate"; +import { useRef } from "react"; + +import { checkFavourites } from "../store/Selectors"; +import { addToFavourites } from "../store/FavouritesStore"; +import { FavouritesStore } from "../store"; + +import "./ProductModal.css"; +import { ProductReviews } from "./ProductReviews"; +import { ProductSpecificationsAccordion } from "./ProductSpecificationsAccordion"; +import { AddToCartButton } from "./AddToCartButton"; + +export const ProductModal = props => { + + const { dismiss, category = false, product } = props; + const isFavourite = useStoreState(FavouritesStore, checkFavourites(product)); + const contentRef = useRef(null); + + return ( + <> + + + + + + + addToFavourites(product, category)} id="fave-button"> + + + + + + + {product.title} + {product.price} + + + +
+ + + + + shop + {category ? category : "Favourite"} + + + + + +

Product Description

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam elit felis, molestie id venenatis at, commodo ac tortor. Pellentesque tempus aliquet purus, sed vulputate elit tempus ut. + +

Product Specifications

+ +
+
+ + + + + + {product.price} + + + + + + + + + + ); +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/TestComponents/ProductReviews.jsx b/03_source/mobile/src/pages/TestComponents/ProductReviews.jsx new file mode 100644 index 0000000..d564773 --- /dev/null +++ b/03_source/mobile/src/pages/TestComponents/ProductReviews.jsx @@ -0,0 +1,23 @@ +import { IonCol, IonIcon, IonNote } from "@ionic/react"; +import { star } from "ionicons/icons"; +import { useEffect, useState } from "react"; +import { randomCount } from "../utils"; + +export const ProductReviews = () => { + + // This count could come from the product (if real data was fed) + const [reviewCount, setReviewCount] = useState(0); + + useEffect(() => { + + setReviewCount(randomCount()); + }, []); + + return ( + + +    + {reviewCount} review{reviewCount > 1 && "s"} + + ); +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/TestComponents/ProductSpecificationsAccordion.jsx b/03_source/mobile/src/pages/TestComponents/ProductSpecificationsAccordion.jsx new file mode 100644 index 0000000..77ede79 --- /dev/null +++ b/03_source/mobile/src/pages/TestComponents/ProductSpecificationsAccordion.jsx @@ -0,0 +1,58 @@ +import { IonAccordion, IonAccordionGroup, IonItem, IonLabel, IonList, IonNote } from "@ionic/react"; +import { useRef } from "react"; +import { productSpecs } from "../utils"; + +export const ProductSpecificationsAccordion = ({type, contentRef}) => { + + const accordionGroupRef = useRef(null); + + const log = () => { + + const selectedAccordion = accordionGroupRef.current.value; + + if (selectedAccordion) { + + setTimeout(() => contentRef.current.scrollToBottom(400), 200); + } + } + + return ( + + {Object.keys(productSpecs).map((spec, index) => { + + const {header, options, wrapText = false, noteColor = false} = productSpecs[spec]; + + return ( + + + + {header} + + + + + {options.map((option, index2) => { + + const {label, value} = option; + + return ( + + + +

{label}

+
+ + + {noteColor ? (value ? "In stock" : "Out of stock") : value} + + +
+ ); + })} +
+
+ ); + })} +
+ ); +} \ No newline at end of file