From 56d43062c98e26685c8084a3243b4f309f80d351 Mon Sep 17 00:00:00 2001 From: louiscklaw Date: Thu, 5 Jun 2025 14:05:24 +0800 Subject: [PATCH] update, --- .../AppPages/Tab1.jsx | 96 +++++++++++++ .../AppPages/Tab2.jsx | 81 +++++++++++ .../CurrentWeather/WeatherProperty.tsx | 62 +++++++++ .../components/CurrentWeather/index.tsx | 48 +++++++ .../components/ExploreContainer.css | 24 ++++ .../components/ExploreContainer.tsx | 14 ++ .../components/MarkerInfoWindow.jsx | 54 ++++++++ .../components/SkeletonDashboard/index.tsx | 117 ++++++++++++++++ .../data/index.js | 8 ++ .../DemoCapacitorGoogleMapsTutorial/index.tsx | 34 +++++ .../pages/Home.css | 5 + .../pages/Home.jsx | 128 ++++++++++++++++++ .../style.scss | 2 + 13 files changed, 673 insertions(+) create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab2.jsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/WeatherProperty.tsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/index.tsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.css create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/SkeletonDashboard/index.tsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx create mode 100644 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/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/DemoCapacitorGoogleMapsTutorial/AppPages/Tab2.jsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/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/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/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/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/index.tsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/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/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.css b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/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/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx new file mode 100644 index 0000000..1b4b3c0 --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx @@ -0,0 +1,14 @@ +import './ExploreContainer.css'; + +interface ContainerProps { } + +const ExploreContainer: React.FC = () => { + return ( +
+ Ready to create an app? +

Start with Ionic UI Components

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx new file mode 100644 index 0000000..a98804b --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx @@ -0,0 +1,54 @@ +import { IonButton, IonCol, IonContent, IonGrid, IonIcon, IonLabel, IonNote, IonRow } from '@ionic/react'; +import { globeOutline, heartOutline, locationOutline, navigateOutline, phonePortraitOutline } from 'ionicons/icons'; + +export const MarkerInfoWindow = ({ marker, dismiss }) => { + return ( + + + + + +

{marker.title}

+ {marker.description} +
+
+
+ + + + + + + {marker.address} + + + + + + + + {marker.website} + + + + + + + + {marker.phone} + + + + + +   Favourite + + + +   Navigate + + +
+
+ ); +}; diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/SkeletonDashboard/index.tsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/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/DemoCapacitorGoogleMapsTutorial/data/index.js b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js new file mode 100644 index 0000000..8750b5c --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js @@ -0,0 +1,8 @@ +export const markers = [ + + {lat: 54.5258924, lng: -6.0812478, title: "Man Lee", description: "Chinese Takeaway", address: "122 Ballymacash Rd, Lisburn BT28 3EZ", website: "https://m.facebook.com/pages/Man-Lee/228415820694835", phone: "028 92 662853"}, + {lat: 54.5097827, lng: -6.0572343, title: "Cam Hing", description: "Chinese Takeaway", address: "70 Longstone St, Lisburn BT28 1TR", website: "https://camhinglisbunr.com", phone: "028 92 677928"}, + {lat: 54.5095162, lng: -6.0595896, title: "Golden Garden", description: "Chinese Takeaway", address: "140 Longstone St, Lisburn BT28 1TR", website: "https://golden-garden.business.site", phone: "028 92 671311"}, + {lat: 54.5091808, lng: -6.0363902, title: "Pagoda", description: "Chinese Takeaway", address: "79 Sloan St, Lisburn BT27 5AG", website: "https://pagodalisburn.com", phone: "028 92 665289"}, + {lat: 54.5989611, lng: -5.9972126, title: "Little Wing", description: "Pizzeria", address: "10 Ann St, Belfast BT1 4EF", website: "https://littlewingpizzeria.com", phone: "028 90 247000"} +]; \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx new file mode 100644 index 0000000..70dcf42 --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx @@ -0,0 +1,34 @@ +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 Home from './pages/Home'; + +import './style.scss'; + +function DemoCapacitorGoogleMapsTutorial() { + return ( + + + + + + + + + + + + + + + + + ); +} + +export default DemoCapacitorGoogleMapsTutorial; diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css new file mode 100644 index 0000000..912345b --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css @@ -0,0 +1,5 @@ +capacitor-google-map { + display: inline-block; + width: 100%; + height: 86vh; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx new file mode 100644 index 0000000..b1ba0ef --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx @@ -0,0 +1,128 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonModal, + useIonRouter, + useIonViewWillEnter, +} from '@ionic/react'; +import { useRef, useState } from 'react'; +import './Home.css'; + +import { GoogleMap } from '@capacitor/google-maps'; +import { markers } from '../data'; +import { MarkerInfoWindow } from '../components/MarkerInfoWindow.jsx'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home = () => { + // This key is now dead! + // Replace with your own :) + // Remember to secure keys using env files or requesting from server! + // This was for demo purposes :) + const key = 'AIzaSyBJwKYcub1yNcDd2V8iu4ZfGvDi4eW_fpU'; + let newMap; + const mapRef = useRef(null); + + const [selectedMarker, setSelectedMarker] = useState(null); + + const [present, dismiss] = useIonModal(MarkerInfoWindow, { + marker: selectedMarker, + }); + + const modalOptions = { + initialBreakpoint: 0.4, + breakpoints: [0, 0.4], + backdropBreakpoint: 0, + onDidDismiss: () => dismiss(), + }; + + const [mapConfig, setMapConfig] = useState({ + zoom: 10, + center: { + lat: markers[0].lat, + lng: markers[0].lng, + }, + }); + + const markerClick = (marker) => { + setSelectedMarker(markers.filter((m) => m.lat === marker.latitude && m.lng === marker.longitude)[0]); + present(modalOptions); + }; + + const addMapMarker = async (marker) => { + await newMap.addMarker({ + coordinate: { + lat: marker.lat, + lng: marker.lng, + }, + title: marker.title, + }); + }; + + const addMapMarkers = () => markers.forEach((marker) => addMapMarker(marker)); + + const createMap = async () => { + if (!mapRef.current) return; + + newMap = await GoogleMap.create({ + id: 'google-map', + element: mapRef.current, + apiKey: key, + config: mapConfig, + }); + + newMap.setOnMarkerClickListener((marker) => markerClick(marker)); + addMapMarkers(); + }; + + useIonViewWillEnter(() => createMap()); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Capacitor Google Map + + + handleBackClick()}> + + + + + + + + + Capacitor Google Map + + + handleBackClick()}> + + + + + + + + + + + + + + ); +}; + +export default Home; diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss new file mode 100644 index 0000000..697843e --- /dev/null +++ b/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss @@ -0,0 +1,2 @@ +.demo-capacitor-google-maps-tutorial { +}