├── src
├── api
│ └── config.js
├── components
│ ├── Image.js
│ ├── Loader.js
│ ├── NotFound.js
│ ├── NoImages.js
│ ├── Item.js
│ ├── Search.js
│ ├── Header.js
│ ├── Navigation.js
│ ├── Container.js
│ ├── Gallery.js
│ └── Form.js
├── setupTests.js
├── App.test.js
├── index.js
├── App.css
├── search-icon.svg
├── context
│ └── PhotoContext.js
├── App.js
├── index.css
└── serviceWorker.js
├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── snapscout.png
├── .travis.yml
├── .gitignore
├── README.md
├── package.json
└── CONTRIBUTING.md
/src/api/config.js:
--------------------------------------------------------------------------------
1 | export const apiKey = "636e1481b4f3c446d26b8eb6ebfe7127";
2 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/snapscout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DarkHorseCorder/React-ImgaeBrowser-FlickerAPI-Axios-useContext-Router/HEAD/snapscout.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DarkHorseCorder/React-ImgaeBrowser-FlickerAPI-Axios-useContext-Router/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DarkHorseCorder/React-ImgaeBrowser-FlickerAPI-Axios-useContext-Router/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DarkHorseCorder/React-ImgaeBrowser-FlickerAPI-Axios-useContext-Router/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/components/Image.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Image = ({ url, title }) => (
4 |
5 |
6 |
7 | );
8 |
9 | export default Image;
10 |
--------------------------------------------------------------------------------
/src/components/Loader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Loader = () => {
4 | return (
5 |
6 |
7 | );
8 | }
9 |
10 | export default Loader;
11 |
--------------------------------------------------------------------------------
/src/components/NotFound.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const NotFound = () => (
4 |
5 |
Page Not Found
6 |
7 | );
8 |
9 | export default NotFound;
10 |
--------------------------------------------------------------------------------
/src/components/NoImages.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const NoImages = () => (
4 |
5 |
No Images Found
6 |
Please try a different search term
7 |
8 | );
9 |
10 | export default NoImages;
11 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./App";
4 |
5 | it("renders without crashing", () => {
6 | const div = document.createElement("div");
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/Item.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Container from "./Container";
3 |
4 | const Item = ({ searchTerm }) => {
5 | return (
6 |
7 |
{searchTerm} Pictures
8 |
9 |
10 | );
11 | };
12 |
13 | export default Item;
14 |
--------------------------------------------------------------------------------
/src/components/Search.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Container from "./Container";
3 |
4 | const Search = ({ searchTerm }) => {
5 | return (
6 |
7 |
{searchTerm} Images
8 |
9 |
10 | );
11 | };
12 |
13 | export default Search;
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "stable"
4 | cache:
5 | directories:
6 | - node_modules
7 | script:
8 | - npm test
9 | - npm run build
10 |
11 | deploy:
12 | provider: pages
13 | skip_cleanup: true
14 | github_token: $github_token
15 | local_dir: build
16 | on:
17 | branch: master
18 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Form from "./Form";
3 | import Navigation from "./Navigation";
4 |
5 | const Header = ({ history, handleSubmit }) => {
6 | return (
7 |
8 |
Image Browser
9 |
10 |
11 |
12 | );
13 | };
14 |
15 | export default Header;
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | # misc
14 | .DS_Store
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
24 | # config
25 |
26 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/src/components/Navigation.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { NavLink } from 'react-router-dom';
3 |
4 | const Navigation = () => {
5 | return (
6 |
14 | );
15 | }
16 |
17 | export default Navigation;
18 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/Container.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect } from "react";
2 | import { PhotoContext } from "../context/PhotoContext";
3 | import Gallery from "./Gallery";
4 | import Loader from "./Loader";
5 |
6 | const Container = ({ searchTerm }) => {
7 | const { images, loading, runSearch } = useContext(PhotoContext);
8 | useEffect(() => {
9 | runSearch(searchTerm);
10 | // eslint-disable-next-line
11 | }, [searchTerm]);
12 |
13 | return (
14 |
15 | {loading ? : }
16 |
17 | );
18 | };
19 |
20 | export default Container;
21 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
14 |
15 |
16 | Snap Shot
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/Gallery.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import NoImages from "./NoImages";
3 | import Image from "./Image";
4 | const Gallery = props => {
5 | const results = props.data;
6 | let images;
7 | let noImages;
8 | // map variables to each item in fetched image array and return image component
9 | if (results.length > 0) {
10 | images = results.map(image => {
11 | let farm = image.farm;
12 | let server = image.server;
13 | let id = image.id;
14 | let secret = image.secret;
15 | let title = image.title;
16 | let url = `https://farm${farm}.staticflickr.com/${server}/${id}_${secret}_m.jpg`;
17 | return ;
18 | });
19 | } else {
20 | noImages = ; // return 'not found' component if no images fetched
21 | }
22 | return (
23 |
24 |
25 | {noImages}
26 |
27 | );
28 | };
29 |
30 | export default Gallery;
31 |
--------------------------------------------------------------------------------
/src/search-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Snap Shot
2 |
3 | 
4 |
5 | ### Summary
6 |
7 | Snap Shot is a gallery created using React,React Hooks, React Router and Context API. The Routes were setup for four default pages and a search page. Also the images were displayed using the Flickr API and axios to fetch data.
8 |
9 | ### Motivation
10 |
11 | The purpose of this project was to get familiar with React Hooks and Context API.
12 |
13 | ### Getting Started
14 |
15 | Click the demo link or clone/download the repository on your local machine.
16 | Create a config.js file in api folder inside src folders. In config.js file write
17 | `export const apiKey = "YOUR_FLIKR_API_KEY";`
18 |
19 | ##### Install dependencies
20 |
21 | `yarn install`
22 |
23 | ##### Run Snap Shot from the root directory.
24 |
25 | `yarn start`
26 |
27 | ### Built With
28 |
29 | - React js
30 | - React Router
31 | - React Hooks
32 | - Context API
33 | - Flickr API
34 |
35 | ### Features
36 |
37 | **1. Responsive Design.**
38 |
39 | **2. Search functionality added to search photos from API.**
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "snapshot",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "https://Yog9.github.io/SnapShot",
6 | "dependencies": {
7 | "@testing-library/jest-dom": "^4.2.4",
8 | "@testing-library/react": "^9.3.2",
9 | "@testing-library/user-event": "^7.1.2",
10 | "axios": "^0.19.2",
11 | "react": "^16.13.1",
12 | "react-dom": "^16.13.1",
13 | "react-router-dom": "^5.1.2",
14 | "react-scripts": "3.4.1"
15 | },
16 | "scripts": {
17 | "predeploy": "npm run build",
18 | "deploy": "gh-pages -d build",
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": "react-app"
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/context/PhotoContext.js:
--------------------------------------------------------------------------------
1 | import React, { createContext, useState } from "react";
2 | import axios from "axios";
3 | import { apiKey } from "../api/config";
4 | export const PhotoContext = createContext();
5 |
6 | const PhotoContextProvider = props => {
7 | const [images, setImages] = useState([]);
8 | const [loading, setLoading] = useState(true);
9 | const runSearch = query => {
10 | axios
11 | .get(
12 | `https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&format=json&nojsoncallback=1`
13 | )
14 | .then(response => {
15 | setImages(response.data.photos.photo);
16 | setLoading(false);
17 | })
18 | .catch(error => {
19 | console.log(
20 | "Encountered an error with fetching and parsing data",
21 | error
22 | );
23 | });
24 | };
25 | return (
26 |
27 | {props.children}
28 |
29 | );
30 | };
31 |
32 | export default PhotoContextProvider;
33 |
--------------------------------------------------------------------------------
/src/components/Form.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 |
3 | const Form = ({ handleSubmit, history }) => {
4 | const [searchEntry, setSearchEntry] = useState("");
5 | // update search text state
6 | const updateSearchInput = e => {
7 | setSearchEntry(e.target.value);
8 | };
9 | return (
10 |
35 | );
36 | };
37 |
38 | export default Form;
39 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | From opening a bug report to creating a pull request: every contribution is appreciated and welcome.
4 |
5 | ## Issues
6 | ### New Issue/Addition
7 | If you come across a new Issue or looking for adding new Solutions, you need to create an issue, Provide description for it and once it's reviewed it will be assigned to you and then you can start working on it.
8 |
9 | ### Existing Issue
10 | * To contribute to Existing issue you need to comment telling what is the issue you want to work at a time.
11 | * You can work on multiple issues but make sure not to pick more than 2 issues at a time.
12 | * Once reviewed it will be assigned to you then you can start working on it.
13 |
14 | ## Creating Pull Request
15 | * Make a fork of this repository.
16 | * Clone your forked repository to your local.
17 | * Create a new Branch from master branch.
18 | * Make changes/addition to it and commit to your new branch.
19 | * Make sure to add proper commit message describing what all question solutions you added/fixed. for e.g
20 | * if you solved an API issue:
21 | `Add: Solved an API issue`
22 | * Create a PR to this Repository from your forked repository providing description of what you did.
23 | * Once Reviewed, It will be merged to the Repo.
24 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PhotoContextProvider from "./context/PhotoContext";
3 | import { HashRouter, Route, Switch, Redirect } from "react-router-dom";
4 | import Header from "./components/Header";
5 | import Item from "./components/Item";
6 | import Search from "./components/Search";
7 | import NotFound from "./components/NotFound";
8 |
9 | class App extends Component {
10 | // Prevent page reload, clear input, set URL and push history on submit
11 | handleSubmit = (e, history, searchInput) => {
12 | e.preventDefault();
13 | e.currentTarget.reset();
14 | let url = `/search/${searchInput}`;
15 | history.push(url);
16 | };
17 |
18 | render() {
19 | return (
20 |
21 |
22 |
23 | (
25 |
29 | )}
30 | />
31 |
32 | }
36 | />
37 |
38 | }
41 | />
42 | } />
43 | } />
44 | } />
45 | (
48 |
49 | )}
50 | />
51 |
52 |
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
60 | export default App;
61 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | text-align: center;
3 | font-family: "Josefin Sans", Helvetica, Arial, sans-serif;
4 | color: #3f4850;
5 | }
6 |
7 |
8 | h2 {
9 | font-size: 2em;
10 | margin: 52px 0 40px;
11 | text-transform: capitalize;
12 | color: #051c33;
13 | }
14 |
15 | h1 {
16 | font-family: "Lobster", "Josefin Sans", Helvetica, Arial, sans-serif;
17 | font-size: 4em;
18 | color: #051c33;
19 | }
20 |
21 | ul {
22 | list-style-type: none;
23 | padding-left: 0;
24 | }
25 |
26 | a {
27 | text-decoration: none;
28 | }
29 |
30 | .is-hidden {
31 | border: 0;
32 | clip: rect(0 0 0 0);
33 | height: 1px;
34 | margin: -1px;
35 | overflow: hidden;
36 | padding: 0;
37 | position: absolute;
38 | width: 1px;
39 | }
40 |
41 | .search-form {
42 | max-width: 460px;
43 | display: flex;
44 | margin: 0 auto 32px;
45 | }
46 |
47 | .search-form button {
48 | outline: none;
49 | border: none;
50 | padding: 0px 15px;
51 | border-radius: 0 0.35em 0.35em 0;
52 | cursor: pointer;
53 | }
54 |
55 | .active {
56 | background-color: #051c33;
57 | cursor: pointer;
58 | }
59 |
60 | input {
61 | font-size: 1em;
62 | width: 100%;
63 | background-color: #edeff0;
64 | padding: 10px 15px;
65 | border: 3px solid #d7dbdf;
66 | border-right: none;
67 | border-radius: 0.35em 0 0 0.35em;
68 | outline: none;
69 | }
70 |
71 | .main-nav li {
72 | width: 100%;
73 | margin-bottom: 1em;
74 | }
75 |
76 | .main-nav a {
77 | display: block;
78 | background: #051c33;
79 | border-radius: 3px;
80 | padding: 5px;
81 | color: #fff;
82 | }
83 |
84 | .main-nav .active,
85 | .main-nav a:hover {
86 | background-color: #051c33;
87 | }
88 |
89 | .photo-container ul {
90 | display: flex;
91 | flex-flow: row wrap;
92 | justify-content: space-between;
93 | -ms-align-items: flex-start;
94 | align-items: flex-start;
95 | }
96 |
97 | .photo-container ul li {
98 | width: 220px;
99 | height: 165px;
100 | overflow: hidden;
101 | margin-bottom: 15px;
102 | }
103 |
104 | .photo-container ul img {
105 | width: 100%;
106 | transform: scale(1.25);
107 | transition: transform 1.25s;
108 | }
109 |
110 | .photo-container li:hover img {
111 | transform: scale(1.65);
112 | }
113 |
114 | .photo-container .not-found {
115 | width: 100%;
116 | }
117 |
118 | .loader {
119 | border: 16px solid #f3f3f3;
120 | /* Light grey */
121 | border-top: 16px solid #051c33;
122 | /* Blue */
123 | border-radius: 50%;
124 | width: 120px;
125 | height: 120px;
126 | animation: spin 2s linear infinite;
127 | margin-left: auto;
128 | margin-right: auto;
129 | }
130 |
131 | @keyframes spin {
132 | 0% {
133 | transform: rotate(0deg);
134 | }
135 |
136 | 100% {
137 | transform: rotate(360deg);
138 | }
139 | }
140 |
141 | .not-found {
142 | padding-top: 5%;
143 | }
144 |
145 | @media only screen and (max-width: 767px) {
146 | body {
147 | padding-top: 2em;
148 | }
149 |
150 | .photo-container li {
151 | margin: auto;
152 | }
153 | }
154 |
155 | @media only screen and (min-width: 768px) {
156 | body {
157 | padding-top: 4em;
158 | }
159 |
160 | .container {
161 | max-width: 960px;
162 | margin: auto;
163 | }
164 |
165 | .main-nav ul {
166 | display: flex;
167 | justify-content: center;
168 | }
169 |
170 | .main-nav li {
171 | margin: 10px;
172 | width: 100px;
173 | }
174 | }
175 |
176 | @media screen and (min-width: 800px) {
177 | .forkongithub {
178 | position: fixed;
179 | display: block;
180 | top: 0;
181 | right: 0;
182 | width: 200px;
183 | overflow: hidden;
184 | height: 200px;
185 | z-index: 9999;
186 | }
187 |
188 | .forkongithub a {
189 | width: 200px;
190 | position: absolute;
191 | top: 60px;
192 | right: -60px;
193 | transform: rotate(45deg);
194 | -webkit-transform: rotate(45deg);
195 | -ms-transform: rotate(45deg);
196 | -moz-transform: rotate(45deg);
197 | -o-transform: rotate(45deg);
198 | box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8);
199 | }
200 | }
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------