update demo-dictionary-app,
This commit is contained in:
Binary file not shown.
After Width: | Height: | Size: 930 B |
BIN
03_source/mobile/public/assets/DemoDictionaryApp/icon/icon.png
Normal file
BIN
03_source/mobile/public/assets/DemoDictionaryApp/icon/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
@@ -66,7 +66,7 @@ import PrivacyAgreement from './pages/PrivacyAgreement';
|
|||||||
import AppRoute from './AppRoute';
|
import AppRoute from './AppRoute';
|
||||||
//
|
//
|
||||||
import DemoReactShop from './pages/DemoReactShop';
|
import DemoReactShop from './pages/DemoReactShop';
|
||||||
import DemoWeatherApp from './pages/WeatherDemo';
|
import DemoWeatherApp from './pages/DemoWeatherApp';
|
||||||
import DemoClubHouse from './pages/DemoClubHouse';
|
import DemoClubHouse from './pages/DemoClubHouse';
|
||||||
import DemoScoreBoard from './pages/DemoScoreBoard';
|
import DemoScoreBoard from './pages/DemoScoreBoard';
|
||||||
import DemoQuoteApp from './pages/DemoQuoteApp';
|
import DemoQuoteApp from './pages/DemoQuoteApp';
|
||||||
|
@@ -1,8 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
IonButton,
|
IonButton,
|
||||||
IonButtons,
|
IonButtons,
|
||||||
|
IonCard,
|
||||||
|
IonCardContent,
|
||||||
|
IonCardSubtitle,
|
||||||
|
IonCardTitle,
|
||||||
IonCol,
|
IonCol,
|
||||||
IonContent,
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
IonHeader,
|
IonHeader,
|
||||||
IonIcon,
|
IonIcon,
|
||||||
IonPage,
|
IonPage,
|
||||||
@@ -11,54 +16,27 @@ import {
|
|||||||
IonToolbar,
|
IonToolbar,
|
||||||
useIonRouter,
|
useIonRouter,
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
|
import { bookOutline, chevronBackOutline, heart, search } from 'ionicons/icons';
|
||||||
|
import { useStoreState } from 'pullstate';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
import { WordStore } from '../store';
|
||||||
|
import { getFavourites, getSearchCount } from '../store/Selectors';
|
||||||
|
|
||||||
import { Geolocation } from '@capacitor/geolocation';
|
const Tab1 = () => {
|
||||||
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 router = useIonRouter();
|
||||||
|
const pageRef = useRef();
|
||||||
const [currentWeather, setCurrentWeather] = useState(false);
|
const favourites = useStoreState(WordStore, getFavourites);
|
||||||
|
const searchCount = useStoreState(WordStore, getSearchCount);
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleBackClick() {
|
function handleBackClick() {
|
||||||
router.goBack();
|
router.goBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IonPage>
|
<IonPage ref={pageRef}>
|
||||||
<IonHeader>
|
<IonHeader>
|
||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonTitle>My Weather</IonTitle>
|
<IonTitle>Dashboard</IonTitle>
|
||||||
|
|
||||||
<IonButtons slot="end">
|
|
||||||
<IonButton onClick={() => getCurrentPosition()}>
|
|
||||||
<IonIcon icon={refreshOutline} color="primary" />
|
|
||||||
</IonButton>
|
|
||||||
</IonButtons>
|
|
||||||
|
|
||||||
<IonButtons slot="start">
|
<IonButtons slot="start">
|
||||||
<IonButton onClick={() => handleBackClick()}>
|
<IonButton onClick={() => handleBackClick()}>
|
||||||
@@ -71,25 +49,66 @@ function Tab1() {
|
|||||||
<IonHeader collapse="condense">
|
<IonHeader collapse="condense">
|
||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonTitle size="large">Dashboard</IonTitle>
|
<IonTitle size="large">Dashboard</IonTitle>
|
||||||
|
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton onClick={() => handleBackClick()}>
|
||||||
|
<IonIcon icon={chevronBackOutline} color="primary" />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
</IonToolbar>
|
</IonToolbar>
|
||||||
</IonHeader>
|
</IonHeader>
|
||||||
|
|
||||||
<IonRow className="ion-margin-start ion-margin-end ion-justify-content-center ion-text-center">
|
<IonGrid>
|
||||||
<IonCol size="12">
|
<IonRow className={`animate__animated animate__faster animate__fadeIn`}>
|
||||||
<h4>Here's your location based weather</h4>
|
<IonCol size="12">
|
||||||
</IonCol>
|
<IonCard>
|
||||||
</IonRow>
|
<IonCardContent>
|
||||||
|
<IonIcon icon={bookOutline} color="primary" style={{ fontSize: '2rem' }} />
|
||||||
|
<IonCardTitle>Ionic Dictionary App</IonCardTitle>
|
||||||
|
<p>Based on the English language</p>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
<div style={{ marginTop: '-1.5rem' }}>
|
<IonRow className={`animate__animated animate__faster animate__fadeIn`}>
|
||||||
{currentWeather ? (
|
<IonCol size="12">
|
||||||
<CurrentWeather currentWeather={currentWeather} />
|
<IonCard>
|
||||||
) : (
|
<IonCardContent>
|
||||||
<SkeletonDashboard />
|
<IonCardTitle>Did you know?</IonCardTitle>
|
||||||
)}
|
<p>There are 171, 146 words in the English language!</p>
|
||||||
</div>
|
<IonButton expand="block" className="ion-margin-top" routerLink="/search">
|
||||||
|
Search now →
|
||||||
|
</IonButton>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className={`animate__animated animate__faster animate__fadeIn`}>
|
||||||
|
<IonCol size="6">
|
||||||
|
<IonCard routerLink="/favourites">
|
||||||
|
<IonCardContent className="ion-text-center">
|
||||||
|
<IonIcon icon={heart} color="primary" />
|
||||||
|
<IonCardTitle>{favourites.length}</IonCardTitle>
|
||||||
|
<IonCardSubtitle>Favourites</IonCardSubtitle>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
<IonCol size="6">
|
||||||
|
<IonCard routerLink="/favourites">
|
||||||
|
<IonCardContent className="ion-text-center">
|
||||||
|
<IonIcon icon={search} color="primary" />
|
||||||
|
<IonCardTitle>{searchCount}</IonCardTitle>
|
||||||
|
<IonCardSubtitle>Searches</IonCardSubtitle>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
</IonContent>
|
</IonContent>
|
||||||
</IonPage>
|
</IonPage>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Tab1;
|
export default Tab1;
|
||||||
|
@@ -2,6 +2,7 @@ import {
|
|||||||
IonButton,
|
IonButton,
|
||||||
IonCol,
|
IonCol,
|
||||||
IonContent,
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
IonHeader,
|
IonHeader,
|
||||||
IonPage,
|
IonPage,
|
||||||
IonRow,
|
IonRow,
|
||||||
@@ -9,30 +10,32 @@ import {
|
|||||||
IonTitle,
|
IonTitle,
|
||||||
IonToolbar,
|
IonToolbar,
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
import { useState } from 'react';
|
import { useState, useRef } from 'react';
|
||||||
import { CurrentWeather } from '../components/CurrentWeather';
|
import { NoSearch } from '../components/NoSearch';
|
||||||
|
import { NoResultsWordCard, WordCard } from '../components/WordCard';
|
||||||
|
import { WordStore } from '../store';
|
||||||
|
import { searchWord } from '../utils';
|
||||||
|
|
||||||
function Tab2() {
|
const Tab2 = () => {
|
||||||
const [search, setSearch] = useState('');
|
const pageRef = useRef();
|
||||||
const [currentWeather, setCurrentWeather] = useState(false);
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
const [searchResult, setSearchResult] = useState(false);
|
||||||
|
const [animatedClass, setAnimatedClass] = useState('');
|
||||||
|
|
||||||
const performSearch = async () => {
|
const performSearch = async () => {
|
||||||
getAddress(search);
|
setAnimatedClass('animate__slideOutRight');
|
||||||
};
|
const result = searchTerm !== '' ? await searchWord(searchTerm) : undefined;
|
||||||
|
|
||||||
const getAddress = async (city) => {
|
setTimeout(() => setSearchResult(result === undefined ? 'none' : result), 250);
|
||||||
const response = await fetch(
|
setTimeout(() => setAnimatedClass('animate__slideInLeft'), 250);
|
||||||
`https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no`
|
|
||||||
);
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (data && data.current && data.location) {
|
WordStore.update((s) => {
|
||||||
setCurrentWeather(data);
|
s.searchCount++;
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IonPage>
|
<IonPage ref={pageRef}>
|
||||||
<IonHeader>
|
<IonHeader>
|
||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonTitle>Search</IonTitle>
|
<IonTitle>Search</IonTitle>
|
||||||
@@ -45,37 +48,36 @@ function Tab2() {
|
|||||||
</IonToolbar>
|
</IonToolbar>
|
||||||
</IonHeader>
|
</IonHeader>
|
||||||
|
|
||||||
<IonRow className="ion-justify-content-center ion-margin-top ion-align-items-center">
|
<IonGrid>
|
||||||
<IonCol size="7">
|
<IonRow className="ion-align-items-center">
|
||||||
<IonSearchbar
|
<IonCol size="9">
|
||||||
placeholder="Try 'London'"
|
<IonSearchbar
|
||||||
animated
|
animated
|
||||||
value={search}
|
value={searchTerm}
|
||||||
onIonChange={(e) => setSearch(e.target.value)}
|
onIonChange={(e) => setSearchTerm(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</IonCol>
|
</IonCol>
|
||||||
|
|
||||||
<IonCol size="5">
|
<IonCol size="3">
|
||||||
<IonButton
|
<IonButton color="primary" onClick={performSearch}>
|
||||||
expand="block"
|
Search
|
||||||
className="ion-margin-start ion-margin-end"
|
</IonButton>
|
||||||
onClick={performSearch}
|
</IonCol>
|
||||||
>
|
</IonRow>
|
||||||
Search
|
|
||||||
</IonButton>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
|
|
||||||
<div style={{ marginTop: '-0.8rem' }}>
|
{searchResult && searchResult !== 'none' && (
|
||||||
{currentWeather ? (
|
<WordCard word={searchResult} animatedClass={animatedClass} pageRef={pageRef} />
|
||||||
<CurrentWeather currentWeather={currentWeather} />
|
|
||||||
) : (
|
|
||||||
<h3 className="ion-text-center">Your search result will appear here</h3>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
{searchResult && searchResult === 'none' && (
|
||||||
|
<NoResultsWordCard word={searchResult} animatedClass={animatedClass} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!searchResult && <NoSearch />}
|
||||||
|
</IonGrid>
|
||||||
</IonContent>
|
</IonContent>
|
||||||
</IonPage>
|
</IonPage>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Tab2;
|
export default Tab2;
|
||||||
|
@@ -0,0 +1,45 @@
|
|||||||
|
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
|
||||||
|
import { useStoreState } from 'pullstate';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import { NoFavourites } from '../components/NoFavourites';
|
||||||
|
import { WordCard } from '../components/WordCard';
|
||||||
|
import { WordStore } from '../store';
|
||||||
|
import { getFavourites } from '../store/Selectors';
|
||||||
|
|
||||||
|
const Tab3 = () => {
|
||||||
|
const pageRef = useRef();
|
||||||
|
const favourites = useStoreState(WordStore, getFavourites);
|
||||||
|
const [animatedClass, setAnimatedClass] = useState('animate__slideInLeft');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage ref={pageRef}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle>Favourites</IonTitle>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonHeader collapse="condense">
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle size="large">Favourites</IonTitle>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
{favourites.map((favourite, index) => {
|
||||||
|
return (
|
||||||
|
<WordCard
|
||||||
|
key={index}
|
||||||
|
word={favourite}
|
||||||
|
animatedClass={animatedClass}
|
||||||
|
pageRef={pageRef}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{favourites.length < 1 && <NoFavourites />}
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Tab3;
|
@@ -0,0 +1,22 @@
|
|||||||
|
import { IonCol, IonLabel, IonRow } from '@ionic/react';
|
||||||
|
|
||||||
|
export const NoFavourites = () => (
|
||||||
|
<IonRow className="ion-text-center ion-justify-content-center ion-margin-top ion-padding-top">
|
||||||
|
<IonCol size="10" className="ion-padding-top ion-margin-top">
|
||||||
|
<IonLabel className="ion-padding-top ion-margin-top">
|
||||||
|
<h2>You don't have any favourites yet!</h2>
|
||||||
|
<p>
|
||||||
|
Any time you see the heart icon, press it to add the related word to your favourites and
|
||||||
|
quickly access it from here.
|
||||||
|
</p>
|
||||||
|
<lottie-player
|
||||||
|
src="https://assets9.lottiefiles.com/packages/lf20_LK2KVy.json"
|
||||||
|
background="transparent"
|
||||||
|
speed="1"
|
||||||
|
loop
|
||||||
|
autoplay
|
||||||
|
></lottie-player>
|
||||||
|
</IonLabel>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
);
|
@@ -0,0 +1,23 @@
|
|||||||
|
import { IonCol, IonLabel, IonRow } from '@ionic/react';
|
||||||
|
|
||||||
|
export const NoSearch = () => (
|
||||||
|
<IonRow className="ion-text-center ion-justify-content-center ion-margin-top">
|
||||||
|
<IonCol size="10">
|
||||||
|
<IonLabel>
|
||||||
|
<h2>Search for a word in the English language</h2>
|
||||||
|
<p>
|
||||||
|
This app will give you word meaninigs, phonetics, origin and also an audio clip so you can
|
||||||
|
hear what it sounds like.
|
||||||
|
</p>
|
||||||
|
<lottie-player
|
||||||
|
src="https://assets7.lottiefiles.com/packages/lf20_n2m0isqh.json"
|
||||||
|
mode="bounce"
|
||||||
|
background="transparent"
|
||||||
|
speed="0.8"
|
||||||
|
loop
|
||||||
|
autoplay
|
||||||
|
></lottie-player>
|
||||||
|
</IonLabel>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
);
|
@@ -0,0 +1,129 @@
|
|||||||
|
import {
|
||||||
|
IonBadge,
|
||||||
|
IonButton,
|
||||||
|
IonCard,
|
||||||
|
IonCardContent,
|
||||||
|
IonCardSubtitle,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonIcon,
|
||||||
|
IonNote,
|
||||||
|
IonRow,
|
||||||
|
useIonModal,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { checkmarkCircleOutline, chevronForward, closeCircleOutline } from 'ionicons/icons';
|
||||||
|
import WordModal from './WordModal';
|
||||||
|
|
||||||
|
export const WordCard = ({ word, animatedClass, pageRef }) => {
|
||||||
|
const closeModal = () => {
|
||||||
|
hideModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const openModal = () => {
|
||||||
|
showModal({
|
||||||
|
presentingElement: pageRef.current,
|
||||||
|
onDidDismiss: hideModal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const [showModal, hideModal] = useIonModal(WordModal, {
|
||||||
|
dismiss: closeModal,
|
||||||
|
word,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonRow className={`animate__animated animate__faster ${animatedClass}`}>
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonCard>
|
||||||
|
<IonCardContent>
|
||||||
|
<IonCardTitle>{word.word}</IonCardTitle>
|
||||||
|
<div className="ion-padding-bottom ion-padding-top">
|
||||||
|
{word.meanings &&
|
||||||
|
word.meanings.map((meaning, index) => {
|
||||||
|
return (
|
||||||
|
<span key={index}>
|
||||||
|
<IonBadge color="primary">{meaning.partOfSpeech}</IonBadge>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<IonNote color="white">{word.origin}</IonNote>
|
||||||
|
|
||||||
|
<IonRow className="ion-padding-top ion-align-items-center ion-justify-content-center ion-text-center">
|
||||||
|
<IonCol size="4">
|
||||||
|
<IonCardTitle>{word.meanings.length}</IonCardTitle>
|
||||||
|
<IonCardSubtitle>meanings</IonCardSubtitle>
|
||||||
|
</IonCol>
|
||||||
|
|
||||||
|
<IonCol size="4">
|
||||||
|
<IonCardTitle>{word.phonetics.length}</IonCardTitle>
|
||||||
|
<IonCardSubtitle>phonetics</IonCardSubtitle>
|
||||||
|
</IonCol>
|
||||||
|
<IonCol size="4">
|
||||||
|
<IonCardTitle>
|
||||||
|
<IonIcon
|
||||||
|
icon={
|
||||||
|
word.phonetics[0] && word.phonetics[0].audio
|
||||||
|
? checkmarkCircleOutline
|
||||||
|
: closeCircleOutline
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</IonCardTitle>
|
||||||
|
<IonCardSubtitle>audio</IonCardSubtitle>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonButton color="primary" expand="block" onClick={openModal}>
|
||||||
|
View
|
||||||
|
<IonIcon icon={chevronForward} />
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NoResultsWordCard = ({ word, animatedClass }) => {
|
||||||
|
return (
|
||||||
|
<IonRow className={`animate__animated animate__faster ${animatedClass}`}>
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonCard>
|
||||||
|
<IonCardContent>
|
||||||
|
<IonCardTitle>Whoops...</IonCardTitle>
|
||||||
|
<div className="ion-padding-bottom ion-padding-top">
|
||||||
|
<IonBadge color="primary">no results</IonBadge>
|
||||||
|
<IonBadge color="primary">found</IonBadge>
|
||||||
|
</div>
|
||||||
|
<IonNote color="white">
|
||||||
|
No results have been found for your search criteria! Please try another word.
|
||||||
|
</IonNote>
|
||||||
|
|
||||||
|
<IonRow className="ion-padding-top ion-align-items-center ion-justify-content-center ion-text-center">
|
||||||
|
<IonCol size="4">
|
||||||
|
<IonCardTitle>0</IonCardTitle>
|
||||||
|
<IonCardSubtitle>meanings</IonCardSubtitle>
|
||||||
|
</IonCol>
|
||||||
|
|
||||||
|
<IonCol size="4">
|
||||||
|
<IonCardTitle>0</IonCardTitle>
|
||||||
|
<IonCardSubtitle>phonetics</IonCardSubtitle>
|
||||||
|
</IonCol>
|
||||||
|
<IonCol size="4">
|
||||||
|
<IonCardTitle>
|
||||||
|
<IonIcon icon={closeCircleOutline} />
|
||||||
|
</IonCardTitle>
|
||||||
|
<IonCardSubtitle>audio</IonCardSubtitle>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
);
|
||||||
|
};
|
@@ -0,0 +1,10 @@
|
|||||||
|
import { IonText } from "@ionic/react";
|
||||||
|
|
||||||
|
export const WordCardHeading = ({ text }) => (
|
||||||
|
|
||||||
|
<div style={{ marginTop: "-1.5rem" }}>
|
||||||
|
<IonText color="light">
|
||||||
|
<h2 className="ion-padding-start">{ text }</h2>
|
||||||
|
</IonText>
|
||||||
|
</div>
|
||||||
|
);
|
@@ -0,0 +1,18 @@
|
|||||||
|
import { IonBadge } from '@ionic/react';
|
||||||
|
|
||||||
|
export const WordMeaning = ({ meaning, index }) => (
|
||||||
|
<div className={index > 0 ? 'ion-padding-top' : ''}>
|
||||||
|
<IonBadge key={index} color="primary">
|
||||||
|
{meaning.partOfSpeech}
|
||||||
|
</IonBadge>
|
||||||
|
<br />
|
||||||
|
{meaning.definitions.map((definition, index2) => {
|
||||||
|
return (
|
||||||
|
<p key={`definition_${index2}`} className={index2 > 0 ? 'ion-padding-top' : ''}>
|
||||||
|
{index2 + 1}.
|
||||||
|
{definition.definition}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
@@ -0,0 +1,122 @@
|
|||||||
|
import {
|
||||||
|
IonBadge,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCard,
|
||||||
|
IonCardContent,
|
||||||
|
IonCardHeader,
|
||||||
|
IonCardSubtitle,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonNote,
|
||||||
|
IonPage,
|
||||||
|
IonRow,
|
||||||
|
IonText,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { heart, heartOutline, play } from 'ionicons/icons';
|
||||||
|
import { useStoreState } from 'pullstate';
|
||||||
|
import { WordStore } from '../store';
|
||||||
|
import { getFavourites } from '../store/Selectors';
|
||||||
|
import { addToFavourites } from '../store/WordStore';
|
||||||
|
import { WordCardHeading } from './WordCardHeading';
|
||||||
|
import { WordMeaning } from './WordMeaning';
|
||||||
|
|
||||||
|
const WordModal = ({ dismiss, word }) => {
|
||||||
|
const favourites = useStoreState(WordStore, getFavourites);
|
||||||
|
const isFavourite = favourites.includes(word);
|
||||||
|
const audio = word.phonetics[0] ? word.phonetics[0].audio : false;
|
||||||
|
|
||||||
|
const playAudio = () => {
|
||||||
|
const audioElement = new Audio(`https:${audio}`);
|
||||||
|
audioElement.play();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton onClick={() => addToFavourites(word)}>
|
||||||
|
<IonIcon icon={isFavourite ? heart : heartOutline} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
<IonTitle>View Word</IonTitle>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonButton onClick={dismiss}>Close</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow className="animate__animated animate__faster animate__slideInUp">
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonCard>
|
||||||
|
<IonCardContent>
|
||||||
|
<IonCardTitle>{word.word}</IonCardTitle>
|
||||||
|
<div className="ion-padding-bottom ion-padding-top">
|
||||||
|
{word.meanings &&
|
||||||
|
word.meanings.map((meaning, index) => {
|
||||||
|
return (
|
||||||
|
<span key={`meaning_${index}`}>
|
||||||
|
<IonBadge key={index} color="primary">
|
||||||
|
{meaning.partOfSpeech}
|
||||||
|
</IonBadge>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<IonNote color="white">{word.origin}</IonNote>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
{audio && (
|
||||||
|
<IonRow className="animate__animated animate__faster animate__slideInUp">
|
||||||
|
<IonCol size="12">
|
||||||
|
<WordCardHeading text="Audio Clip" />
|
||||||
|
|
||||||
|
<IonCard>
|
||||||
|
<IonCardContent>
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonButton color="primary" expand="block" onClick={playAudio}>
|
||||||
|
<IonIcon icon={play} />
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<IonRow className="animate__animated animate__faster animate__slideInUp">
|
||||||
|
<IonCol size="12">
|
||||||
|
<WordCardHeading text="Meanings" />
|
||||||
|
|
||||||
|
<IonCard>
|
||||||
|
<IonCardContent>
|
||||||
|
{word.meanings &&
|
||||||
|
word.meanings.map((meaning, index) => {
|
||||||
|
return <WordMeaning key={index} index={index} meaning={meaning} />;
|
||||||
|
})}
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WordModal;
|
@@ -1,38 +1,44 @@
|
|||||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
||||||
|
|
||||||
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
import { cloudOutline, heart, search, searchOutline, statsChart } from 'ionicons/icons';
|
||||||
import { Route, Redirect } from 'react-router';
|
import { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
import Tab1 from './AppPages/Tab1';
|
||||||
import Tab2 from './AppPages/Tab2';
|
import Tab2 from './AppPages/Tab2';
|
||||||
|
import Tab3 from './AppPages/Tab3';
|
||||||
|
|
||||||
function DemoWeatherApp() {
|
import './style.scss';
|
||||||
|
|
||||||
|
function DemoDictionaryApp() {
|
||||||
return (
|
return (
|
||||||
<IonTabs>
|
<IonTabs className="demo-dictionary-app">
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path="/demo-weather-app/tab1">
|
<Route exact path="/demo-dictionary-app/dashboard">
|
||||||
<Tab1 />
|
<Tab1 />
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path="/demo-weather-app/tab2">
|
<Route exact path="/demo-dictionary-app/search">
|
||||||
<Tab2 />
|
<Tab2 />
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path="/demo-weather-app">
|
<Route path="/demo-dictionary-app/favourites">
|
||||||
<Redirect to="/demo-weather-app/tab1" />
|
<Tab3 />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
|
<Redirect exact path="/demo-dictionary-app" to="/demo-dictionary-app/dashboard" />
|
||||||
</IonRouterOutlet>
|
</IonRouterOutlet>
|
||||||
{/* */}
|
{/* */}
|
||||||
<IonTabBar slot="bottom">
|
<IonTabBar slot="bottom">
|
||||||
<IonTabButton tab="tab1" href="/demo-weather-app/tab1">
|
<IonTabButton tab="dashboard" href="/demo-dictionary-app/dashboard">
|
||||||
<IonIcon icon={cloudOutline} />
|
<IonIcon icon={statsChart} />
|
||||||
<IonLabel>Dashboard</IonLabel>
|
|
||||||
</IonTabButton>
|
</IonTabButton>
|
||||||
<IonTabButton tab="tab2" href="/demo-weather-app/tab2">
|
<IonTabButton tab="search" href="/demo-dictionary-app/search">
|
||||||
<IonIcon icon={searchOutline} />
|
<IonIcon icon={search} />
|
||||||
<IonLabel>Search</IonLabel>
|
</IonTabButton>
|
||||||
|
<IonTabButton tab="favourites" href="/demo-dictionary-app/favourites">
|
||||||
|
<IonIcon icon={heart} />
|
||||||
</IonTabButton>
|
</IonTabButton>
|
||||||
</IonTabBar>
|
</IonTabBar>
|
||||||
</IonTabs>
|
</IonTabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DemoWeatherApp;
|
export default DemoDictionaryApp;
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const getState = state => state;
|
||||||
|
|
||||||
|
// General getters
|
||||||
|
export const getFavourites = createSelector(getState, state => state.favourites);
|
||||||
|
export const getPopularWords = createSelector(getState, state => state.popularWords);
|
||||||
|
export const getSearchCount = createSelector(getState, state => state.searchCount);
|
@@ -0,0 +1,29 @@
|
|||||||
|
import { Store } from "pullstate";
|
||||||
|
|
||||||
|
const WordStore = new Store({
|
||||||
|
|
||||||
|
favourites: [],
|
||||||
|
popularWords: [],
|
||||||
|
searchCount: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
export default WordStore;
|
||||||
|
|
||||||
|
export const addToFavourites = (passedWord) => {
|
||||||
|
|
||||||
|
const currentFavourites = WordStore.getRawState().favourites;
|
||||||
|
const added = !currentFavourites.includes(passedWord);
|
||||||
|
|
||||||
|
WordStore.update(s => {
|
||||||
|
|
||||||
|
if (currentFavourites.includes(passedWord)) {
|
||||||
|
|
||||||
|
s.favourites = currentFavourites.filter(word => word !== passedWord);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
s.favourites = [ ...s.favourites, passedWord ];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return added;
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
export { default as WordStore } from "./WordStore";
|
@@ -1,103 +1,240 @@
|
|||||||
#about-page {
|
/* Ionic Variables and Theming. For more info, please see:
|
||||||
ion-toolbar {
|
http://ionicframework.com/docs/theming/ */
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
/** Ionic CSS Variables **/
|
||||||
left: 0;
|
.demo-dictionary-app {
|
||||||
right: 0;
|
* {
|
||||||
|
/** primary **/
|
||||||
|
--ion-color-primary: #953cd0;
|
||||||
|
--ion-color-primary-rgb: 149, 60, 208;
|
||||||
|
--ion-color-primary-contrast: #ffffff;
|
||||||
|
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-primary-shade: #8335b7;
|
||||||
|
--ion-color-primary-tint: #a050d5;
|
||||||
|
|
||||||
--background: transparent;
|
/** secondary **/
|
||||||
--color: white;
|
--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: #ffffff;
|
||||||
|
--ion-color-light-rgb: 255, 255, 255;
|
||||||
|
--ion-color-light-contrast: #000000;
|
||||||
|
--ion-color-light-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-light-shade: #e0e0e0;
|
||||||
|
--ion-color-light-tint: #ffffff;
|
||||||
|
|
||||||
|
--ion-background-color: #1e1b27 !important;
|
||||||
|
--ion-tab-bar-color-selected: #953cd0;
|
||||||
|
--ion-tab-bar-color: #412f6e;
|
||||||
|
--ion-text-color: white;
|
||||||
|
--ion-tab-bar-background: #191620;
|
||||||
|
--ion-toolbar-background: #191620 !important;
|
||||||
|
--ion-item-background: #000000 !important;
|
||||||
|
|
||||||
|
--ion-card-background: #272333 !important;
|
||||||
|
--ion-modal-background: #272333 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-toolbar ion-back-button,
|
ion-tab-bar {
|
||||||
ion-toolbar ion-button,
|
--border-style: none;
|
||||||
ion-toolbar ion-menu-button {
|
border: none;
|
||||||
--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
|
* Dark Colors
|
||||||
|
* -------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.ios .about-info {
|
ion-modal {
|
||||||
--ion-padding: 19px;
|
-ms-overflow-style: none; /* for Internet Explorer, Edge */
|
||||||
|
scrollbar-width: none; /* for Firefox */
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ios .about-info h3 {
|
body {
|
||||||
font-weight: 700;
|
overflow: hidden !important;
|
||||||
|
--ion-color-primary: #953cd0;
|
||||||
|
--ion-color-primary-rgb: 149, 60, 208;
|
||||||
|
--ion-color-primary-contrast: #ffffff;
|
||||||
|
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-primary-shade: #8335b7;
|
||||||
|
--ion-color-primary-tint: #a050d5;
|
||||||
|
|
||||||
|
--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: #ffffff;
|
||||||
|
--ion-color-light-rgb: 255, 255, 255;
|
||||||
|
--ion-color-light-contrast: #000000;
|
||||||
|
--ion-color-light-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-light-shade: #e0e0e0;
|
||||||
|
--ion-color-light-tint: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#date-input-popover {
|
|
||||||
--offset-y: -var(--ion-safe-area-bottom);
|
|
||||||
|
|
||||||
--max-width: 90%;
|
|
||||||
--width: 336px;
|
|
||||||
}
|
|
||||||
|
21
03_source/mobile/src/pages/DemoDictionaryApp/utils.js
Normal file
21
03_source/mobile/src/pages/DemoDictionaryApp/utils.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { WordStore } from './store';
|
||||||
|
|
||||||
|
const API_URL = 'https://api.dictionaryapi.dev/api/v2/entries/en/';
|
||||||
|
|
||||||
|
export const searchWord = async (word, returnOne = true) => {
|
||||||
|
const response = await fetch(`${API_URL}${word.toLowerCase()}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
return returnOne ? data[0] : data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchPopularWords = async () => {
|
||||||
|
const words = ['mobile', 'applications', 'ionic', 'framework'];
|
||||||
|
|
||||||
|
words.forEach(async (word) => {
|
||||||
|
const wordData = await searchWord(word, false);
|
||||||
|
WordStore.update((s) => {
|
||||||
|
s.popularWords = [...s.popularWords, wordData[0]];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
@@ -252,6 +252,14 @@ const SettingsPage: React.FC<SettingsProps> = ({
|
|||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(paths.DEMO_DICTIONARY_APP, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo Dictionary App</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
</IonContent>
|
</IonContent>
|
||||||
|
|
||||||
{/* REQ0058/logout */}
|
{/* REQ0058/logout */}
|
||||||
|
@@ -1,62 +0,0 @@
|
|||||||
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 (
|
|
||||||
<IonCol size="6">
|
|
||||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
|
||||||
<IonCol size="3">
|
|
||||||
{!property.isIcon && (
|
|
||||||
<img alt={property.alt} src={property.icon} height="32" width="32" />
|
|
||||||
)}
|
|
||||||
{property.isIcon && (
|
|
||||||
<IonIcon icon={property.icon} color="medium" style={{ fontSize: '2rem' }} />
|
|
||||||
)}
|
|
||||||
</IonCol>
|
|
||||||
|
|
||||||
<IonCol size="9">
|
|
||||||
<IonCardSubtitle>{property.label}</IonCardSubtitle>
|
|
||||||
<IonNote>{property.value}</IonNote>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
</IonCol>
|
|
||||||
);
|
|
||||||
};
|
|
@@ -1,48 +0,0 @@
|
|||||||
import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react';
|
|
||||||
import { WeatherProperty } from './WeatherProperty';
|
|
||||||
|
|
||||||
export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => (
|
|
||||||
<IonGrid>
|
|
||||||
<IonCard>
|
|
||||||
<IonCardContent className="ion-text-center">
|
|
||||||
<IonText color="primary">
|
|
||||||
<h1>
|
|
||||||
{currentWeather.location.region},{' '}
|
|
||||||
<span style={{ color: 'gray' }}>{currentWeather.location.country}</span>
|
|
||||||
</h1>
|
|
||||||
</IonText>
|
|
||||||
|
|
||||||
<div className="ion-margin-top">
|
|
||||||
<img
|
|
||||||
alt="condition"
|
|
||||||
src={currentWeather.current.condition.icon.replace('//', 'https://')}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<IonText color="dark">
|
|
||||||
<h1 style={{ fontWeight: 'bold' }}>{currentWeather.current.condition.text}</h1>
|
|
||||||
</IonText>
|
|
||||||
|
|
||||||
<IonText color="medium">
|
|
||||||
<p>{new Date(currentWeather.location.localtime).toDateString()}</p>
|
|
||||||
</IonText>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
|
|
||||||
{currentWeather.current.temp_c}℃
|
|
||||||
</IonCardTitle>
|
|
||||||
|
|
||||||
<IonGrid className="ion-margin-top">
|
|
||||||
<IonRow>
|
|
||||||
<WeatherProperty type="wind" currentWeather={currentWeather} />
|
|
||||||
<WeatherProperty type="feelsLike" currentWeather={currentWeather} />
|
|
||||||
</IonRow>
|
|
||||||
|
|
||||||
<IonRow className="ion-margin-top">
|
|
||||||
<WeatherProperty type="indexUV" currentWeather={currentWeather} />
|
|
||||||
<WeatherProperty type="pressure" currentWeather={currentWeather} />
|
|
||||||
</IonRow>
|
|
||||||
</IonGrid>
|
|
||||||
</IonCardContent>
|
|
||||||
</IonCard>
|
|
||||||
</IonGrid>
|
|
||||||
);
|
|
@@ -1,117 +0,0 @@
|
|||||||
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 = () => (
|
|
||||||
<IonGrid>
|
|
||||||
<IonCard>
|
|
||||||
<IonCardContent className="ion-text-center">
|
|
||||||
<IonText color="primary">
|
|
||||||
<h1>
|
|
||||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
|
||||||
</h1>
|
|
||||||
</IonText>
|
|
||||||
|
|
||||||
<div className="ion-margin-top">
|
|
||||||
<IonThumbnail>
|
|
||||||
<IonSkeletonText animated style={{ width: '2rem', height: '2rem' }} />
|
|
||||||
</IonThumbnail>
|
|
||||||
|
|
||||||
<IonText color="dark">
|
|
||||||
<h1 style={{ fontWeight: 'bold' }}>
|
|
||||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
|
||||||
</h1>
|
|
||||||
</IonText>
|
|
||||||
|
|
||||||
<IonText color="medium">
|
|
||||||
<p>
|
|
||||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
|
||||||
</p>
|
|
||||||
</IonText>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
|
|
||||||
<IonSkeletonText animated style={{ height: '3rem', width: '30%', textAlign: 'center' }} />
|
|
||||||
</IonCardTitle>
|
|
||||||
|
|
||||||
<IonGrid className="ion-margin-top">
|
|
||||||
<IonRow>
|
|
||||||
<IonCol size="6">
|
|
||||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
|
||||||
<IonCol size="3">
|
|
||||||
<img alt="wind" src="/assets/WeatherDemo/wind.png" height="32" width="32" />
|
|
||||||
</IonCol>
|
|
||||||
|
|
||||||
<IonCol size="9">
|
|
||||||
<IonCardSubtitle>Wind</IonCardSubtitle>
|
|
||||||
<IonNote>
|
|
||||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
|
||||||
</IonNote>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
</IonCol>
|
|
||||||
|
|
||||||
<IonCol size="6">
|
|
||||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
|
||||||
<IonCol size="3">
|
|
||||||
<IonIcon icon={thermometerOutline} color="medium" style={{ fontSize: '2rem' }} />
|
|
||||||
</IonCol>
|
|
||||||
|
|
||||||
<IonCol size="9">
|
|
||||||
<IonCardSubtitle>Feels like</IonCardSubtitle>
|
|
||||||
<IonNote>
|
|
||||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
|
||||||
</IonNote>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
|
|
||||||
<IonRow className="ion-margin-top">
|
|
||||||
<IonCol size="6">
|
|
||||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
|
||||||
<IonCol size="3">
|
|
||||||
<IonIcon icon={sunnyOutline} color="medium" style={{ fontSize: '2rem' }} />
|
|
||||||
</IonCol>
|
|
||||||
|
|
||||||
<IonCol size="9">
|
|
||||||
<IonCardSubtitle>Index UV</IonCardSubtitle>
|
|
||||||
<IonNote>
|
|
||||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
|
||||||
</IonNote>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
</IonCol>
|
|
||||||
|
|
||||||
<IonCol size="6">
|
|
||||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
|
||||||
<IonCol size="3">
|
|
||||||
<IonIcon icon={pulseOutline} color="medium" style={{ fontSize: '2rem' }} />
|
|
||||||
</IonCol>
|
|
||||||
|
|
||||||
<IonCol size="9">
|
|
||||||
<IonCardSubtitle>Pressure</IonCardSubtitle>
|
|
||||||
<IonNote>
|
|
||||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
|
||||||
</IonNote>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
</IonCol>
|
|
||||||
</IonRow>
|
|
||||||
</IonGrid>
|
|
||||||
</IonCardContent>
|
|
||||||
</IonCard>
|
|
||||||
</IonGrid>
|
|
||||||
);
|
|
Reference in New Issue
Block a user