Files
HKSingleParty/03_source/mobile/src/pages/DemoReactMarvelApp/pages/Home.tsx
2025-06-06 23:18:53 +08:00

209 lines
6.1 KiB
TypeScript

import { useEffect, useState } from 'react';
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonPage,
IonRow,
IonTitle,
IonToast,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import './Home.css';
import {
addOutline,
chevronBackOutline,
expandOutline,
gridOutline,
informationCircleOutline,
searchOutline,
} from 'ionicons/icons';
import CharacterItem from '../components/CharacterItem';
const Home = (): React.JSX.Element => {
const [grid, setGrid] = useState<boolean>(true);
const [characters, setCharacters] = useState([]);
const [amountLoaded, setAmountLoaded] = useState<number>(20);
const [showToast, setShowToast] = useState<{ show: boolean; message: string }>({
show: false,
message: '',
});
useEffect(() => {
const buttInstall = document.getElementById('buttInstall');
window.addEventListener('beforeinstallprompt', (event) => {
console.log('👍', 'beforeinstallprompt', event);
// Save the event so it can be triggered later.
window.deferredPrompt = event;
buttInstall.classList.toggle('hidden', false);
});
window.addEventListener('appinstalled', (event) => {
console.log('👍', 'appinstalled', event);
// Clear the deferredPrompt so it can be garbage collected
window.deferredPrompt = null;
});
}, []);
useEffect(() => {
const getCharacters = async () => {
const response = await fetch(
'https://gateway.marvel.com/v1/public/characters?ts=alan12345&apikey=e5103c9197bf5466f65433de29139bf9&hash=13b1d704e92de2a50ae29777722bdd75&limit=20&orderBy=-modified'
);
const data = await response.json();
const results = data.data.results;
setCharacters(results);
};
getCharacters();
}, []);
const fetchMore = async (e) => {
// Fetch more characters
// How?
// Lets limit it by 20, and offset it by the amount loaded already
// E.g. 20, 40, 60 just like pagination :)
// Get the response into json
const response = await fetch(
`https://gateway.marvel.com/v1/public/characters?ts=alan12345&apikey=e5103c9197bf5466f65433de29139bf9&hash=13b1d704e92de2a50ae29777722bdd75&limit=20&offset=${amountLoaded}&orderBy=-modified`
);
const data = await response.json();
const results = data.data.results;
// Set the characters by adding the new results to the current
// Increment the amount loaded by 20 for the next iteration
// Complete the scroll action
setCharacters((prevResults) => [...prevResults, ...results]);
setAmountLoaded((prevAmount) => prevAmount + 20);
e.target.complete();
};
const addToHomeScreen = async () => {
const buttInstall = document.getElementById('buttInstall');
console.log('👍', 'buttInstall-clicked');
const promptEvent = window.deferredPrompt;
if (!promptEvent) {
// The deferred prompt isn't available.
return;
}
// Show the install prompt.
promptEvent.prompt();
// Log the result
const result = await promptEvent.userChoice;
console.log('👍', 'userChoice', result);
// Reset the deferred prompt variable, since
// prompt() can only be called once.
window.deferredPrompt = null;
// Hide the install button.
buttInstall.classList.toggle('hidden', true);
};
const router = useIonRouter();
function handleBackClick() {
router.goBack();
}
return (
<IonPage id="home-page">
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonButton onClick={() => handleBackClick()}>
<IonIcon icon={chevronBackOutline} color="primary" />
</IonButton>
</IonButtons>
<IonTitle>Marvel Characters</IonTitle>
<IonButtons slot="start">
<IonButton color="dark" routerLink="/info">
<IonIcon icon={informationCircleOutline} />
</IonButton>
</IonButtons>
<IonButtons slot="end">
<IonButton
color="dark"
onClick={() =>
setShowToast({
show: true,
message: 'We could easily add a search button here to search characters.',
})
}
>
<IonIcon icon={searchOutline} />
</IonButton>
<IonButton
id="buttInstall"
color="danger"
fill="solid"
size="small"
className="add-button hidden"
onClick={() => addToHomeScreen()}
>
<IonIcon icon={addOutline} />
&nbsp;&nbsp;Install App
</IonButton>
<IonButton color="danger" onClick={() => setGrid((grid) => !grid)}>
<IonIcon icon={grid ? expandOutline : gridOutline} />
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Marvel Characters</IonTitle>
</IonToolbar>
</IonHeader>
<IonRow>
{characters.length > 0 ? (
characters.map((character, index) => {
if (!character.thumbnail.path.includes('image_not_available')) {
return <CharacterItem key={`character_${index}`} grid={grid} details={character} />;
} else {
return null;
}
})
) : (
<CharacterItem load={true} />
)}
</IonRow>
<IonInfiniteScroll threshold="100px" onIonInfinite={fetchMore}>
<IonInfiniteScrollContent
loadingSpinner="bubbles"
loadingText="Getting more super heroes..."
></IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonContent>
<IonToast
isOpen={showToast.show}
onDidDismiss={() => setShowToast({ show: false, message: '' })}
message={showToast.message}
duration={3500}
color="danger"
/>
</IonPage>
);
};
export default Home;