├── .gitignore
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── public
├── fanDark.png
├── favicon.ico
├── index.html
└── manifest.json
└── src
├── App.js
├── App.test.js
├── assets
├── img
│ ├── logo
│ │ ├── logoDark.png
│ │ └── logoLight.png
│ └── phones.png
└── styles
│ ├── animations
│ └── throb.scss
│ ├── components
│ ├── BtnFancy.scss
│ ├── Footer.scss
│ └── SignIn.scss
│ ├── fonts.scss
│ ├── main.scss
│ ├── pages
│ ├── Landing.scss
│ └── LoadingScreen.scss
│ └── reset.scss
├── components
├── BtnFancy
│ ├── BtnFancy.jsx
│ └── index.js
├── Footer
│ ├── Footer.jsx
│ └── index.js
├── Loading
│ ├── Loading.jsx
│ └── index.js
└── SignIn
│ ├── SignIn.jsx
│ └── index.js
├── hooks
└── Viewport.js
├── index.css
├── index.js
├── pages
├── Landing
│ ├── Landing.jsx
│ └── index.js
└── LoadingScreen
│ ├── LoadingScreen.jsx
│ └── index.js
├── serviceWorker.js
└── setupTests.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 |
107 | # Misc
108 | .DS_Store/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Andrew Mendez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Only Fans](https://onlyfans.fan/)
2 | ### Fan of fans? Come see some fans; ONLY fans!
3 |
4 |
5 | ###### onlyfans.fan is currently under construction. Check back later to fulfill your desire for fans.
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "onlyfans",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "node-sass": "^4.14.1",
7 | "react": "^17.0.1",
8 | "react-dom": "^17.0.1",
9 | "react-scripts": "4.0.3"
10 | },
11 | "scripts": {
12 | "start": "react-scripts start",
13 | "build": "react-scripts build",
14 | "test": "react-scripts test",
15 | "eject": "react-scripts eject"
16 | },
17 | "eslintConfig": {
18 | "extends": "react-app"
19 | },
20 | "browserslist": {
21 | "production": [
22 | ">0.2%",
23 | "not dead",
24 | "not op_mini all"
25 | ],
26 | "development": [
27 | "last 1 chrome version",
28 | "last 1 firefox version",
29 | "last 1 safari version"
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/fanDark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MendezAndrewM/onlyfans/bc7a5147cc49854f01fd42c6681b07307d1ebd75/public/fanDark.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MendezAndrewM/onlyfans/bc7a5147cc49854f01fd42c6681b07307d1ebd75/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Only Fans
15 |
16 |
17 | You need to enable JavaScript to run this app.
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Only Fans",
3 | "name": "Only Fans",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "fanDark.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "fanDark.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/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | // import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
3 | import LoadingScreen from './pages/LoadingScreen';
4 | import Landing from './pages/Landing';
5 | import './assets/styles/main.scss'
6 |
7 |
8 | function App() {
9 |
10 | // const [userKey, setUserKey] = useState('')
11 | const [theme, setTheme] = useState('light');
12 | const dark = theme === 'dark'
13 |
14 | return (
15 |
16 |
17 | Coming Soon - Under Construction
18 |
19 | {/*
*/}
20 |
21 | //
22 | //
23 | //
24 | //
25 | //
26 | //
27 | //
28 | //
29 | //
30 | //
31 | //
32 | //
33 | //
34 | //
35 | //
36 | //
37 | );
38 | }
39 |
40 |
41 |
42 |
43 | export default App;
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render( );
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/src/assets/img/logo/logoDark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MendezAndrewM/onlyfans/bc7a5147cc49854f01fd42c6681b07307d1ebd75/src/assets/img/logo/logoDark.png
--------------------------------------------------------------------------------
/src/assets/img/logo/logoLight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MendezAndrewM/onlyfans/bc7a5147cc49854f01fd42c6681b07307d1ebd75/src/assets/img/logo/logoLight.png
--------------------------------------------------------------------------------
/src/assets/img/phones.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MendezAndrewM/onlyfans/bc7a5147cc49854f01fd42c6681b07307d1ebd75/src/assets/img/phones.png
--------------------------------------------------------------------------------
/src/assets/styles/animations/throb.scss:
--------------------------------------------------------------------------------
1 | .throb {
2 | animation-name: throb;
3 | animation-duration: 3.5s;
4 | animation-iteration-count: infinite;
5 | animation-timing-function: ease-in-out;
6 | margin-bottom: 22px;
7 | }
8 |
9 | @keyframes throb {
10 | 0% { transform: scale(1); }
11 | 50% { transform: scale(1.2); }
12 | 100% { transform: scale(1); }
13 | }
--------------------------------------------------------------------------------
/src/assets/styles/components/BtnFancy.scss:
--------------------------------------------------------------------------------
1 | .BtnFancy {
2 | background-color: #0091ea;
3 | color: white;
4 | border-radius: 25px;
5 | margin: 8px;
6 | width: 88%;
7 | height: 48px;
8 | display: flex;
9 | flex-direction: row;
10 | justify-content: center;
11 | align-items: center;
12 | }
13 | .FancyLabel {
14 | font-size: 13px;
15 | }
--------------------------------------------------------------------------------
/src/assets/styles/components/Footer.scss:
--------------------------------------------------------------------------------
1 | .Footer {
2 | width: 100%;
3 | min-height: 16px;
4 | border-top: grey solid 1px;
5 | display: flex;
6 | flex-direction: row;
7 | justify-content: space-between;
8 | align-items: center;
9 | color: grey;
10 | margin-top: 24px;
11 | }
12 | .footWrapper {
13 | display: flex;
14 | flex-direction: row;
15 | justify-content: space-between;
16 | }
17 | .footItem {
18 | margin: 12px;
19 | }
--------------------------------------------------------------------------------
/src/assets/styles/components/SignIn.scss:
--------------------------------------------------------------------------------
1 | .form_col {
2 | width: 440px;
3 | min-height: 450px;
4 | margin-top: 12px;
5 | display: flex;
6 | justify-content: center;
7 | flex-direction: column;
8 | align-items: center;
9 | }
10 | .LogoDiv {
11 | display: flex;
12 | flex-direction: row;
13 | align-items: center;
14 | justify-content: center;
15 | }
16 | .formLogo {
17 | width: 237px;
18 | height: 59px;
19 | }
20 | .orBox {
21 | width: 90%;
22 | display: flex;
23 | flex-direction: row;
24 | justify-content: space-between;
25 | align-items: center;
26 | }
27 | .through {
28 | padding: 20px 0;
29 | position: relative;
30 | // display: block;
31 | overflow: hidden;
32 | clear: both;
33 | color: #8a96a3;
34 | text-transform: uppercase;
35 | padding: 40px 0;
36 | }
37 | .line {
38 | color: #b7c7d8;
39 | // background-color: #a4b3c3;
40 | // height: 1px;
41 | width: 40%;
42 | }
43 | .signUpForm {
44 | width: 100%;
45 | display: flex;
46 | flex-direction: column;
47 | justify-content: space-between;
48 | align-items: center;
49 | }
50 | .signUpLabel {
51 | width: 94%;
52 | display: flex;
53 | flex-direction: row;
54 | justify-content: center;
55 | align-items: center;
56 | }
57 | .signUpInput {
58 | width: 90%;
59 | border: none;
60 | border-bottom: 2px solid #8a96a3;;
61 | margin: 24px 6px;
62 | }
63 | .LoginBtn {
64 | background-color: #0091ea;
65 | color: white;
66 | border-radius: 25px;
67 | margin: 8px;
68 | width: 88%;
69 | height: 48px;
70 | display: flex;
71 | flex-direction: row;
72 | justify-content: center;
73 | align-items: center;
74 | }
75 | h3 {
76 | font-size: 18px;
77 | font-weight: 300;
78 | color: black;
79 | }
80 | .saL {
81 | margin: 10px auto;
82 | }
--------------------------------------------------------------------------------
/src/assets/styles/fonts.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital@1&display=swap');
2 |
3 | $NotoSans: 'Noto Sans', sans-serif;
--------------------------------------------------------------------------------
/src/assets/styles/main.scss:
--------------------------------------------------------------------------------
1 | @import "reset";
2 | @import "fonts";
3 | @import "./components/BtnFancy.scss";
4 | @import "./animations/throb.scss";
5 | @import "./pages/LoadingScreen.scss";
6 | @import "./pages/Landing.scss";
7 | @import "./components/SignIn.scss";
8 | @import "./components/Footer.scss";
9 |
10 | .App {
11 | background-color: white;
12 | width: 100%;
13 | min-height: 100vh;
14 | }
15 | .Container {
16 | width: 75%;
17 | padding: 0 15vw;
18 | }
--------------------------------------------------------------------------------
/src/assets/styles/pages/Landing.scss:
--------------------------------------------------------------------------------
1 | @import "../components/SignIn.scss";
2 |
3 | .LandingContainer {
4 | padding-top: 12px;
5 | display: flex;
6 | flex-direction: row;
7 | justify-content: space-around;
8 | }
9 |
--------------------------------------------------------------------------------
/src/assets/styles/pages/LoadingScreen.scss:
--------------------------------------------------------------------------------
1 | .LoadingScreen {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | height: 100%;
6 | }
7 | .ComingSoon {
8 | color: rgb(0, 146, 146);
9 | font-family: $NotoSans;
10 | font-size: 18px;
11 | z-index: 1;
12 | }
--------------------------------------------------------------------------------
/src/assets/styles/reset.scss:
--------------------------------------------------------------------------------
1 | html,
2 | body,
3 | ul,
4 | ol {
5 | margin: 0;
6 | padding: 0;
7 | }
--------------------------------------------------------------------------------
/src/components/BtnFancy/BtnFancy.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const BtnFancy = ({
4 | theme,
5 | label,
6 | link,
7 | svg,
8 | type
9 | }) => (
10 |
11 | {label}
12 |
13 | )
14 |
15 | export default BtnFancy;
--------------------------------------------------------------------------------
/src/components/BtnFancy/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './BtnFancy'
--------------------------------------------------------------------------------
/src/components/Footer/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Divider = () => | ;
4 | const Footer = ({ theme }) => {
5 | return (
6 |
7 |
8 |
©2020 Mendez-Solutions
9 |
10 |
Blog
11 |
12 |
Twitter
13 |
14 |
15 |
FAQs
16 |
17 |
Terms
18 |
19 |
Privacy
20 |
21 |
Contact
22 |
23 |
How it works
24 |
25 |
CC0
26 |
27 |
28 | 🌐 English ⌄
29 |
30 |
31 | )
32 | }
33 |
34 | export default Footer;
--------------------------------------------------------------------------------
/src/components/Footer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './Footer';
--------------------------------------------------------------------------------
/src/components/Loading/Loading.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import logoLight from './../../assets/img/logo/logoLight.png';
3 | import logoDark from './../../assets/img/logo/logoDark.png';
4 |
5 | const UnderConstruction = ({ theme }) => (
6 |
16 | );
17 |
18 | export default UnderConstruction;
19 |
20 | // const bg = {
21 | // large: [
22 | // 'https://live.staticflickr.com/4509/36649105643_51b3da35c5_k.jpg',
23 | // 'https://live.staticflickr.com/1960/31873844948_196f54450d_k.jpg',
24 | // 'https://live.staticflickr.com/8599/27575019843_7212df6aa4_5k.jpg',
25 | // 'https://live.staticflickr.com/8748/16357907284_03470457f4_4k.jpg',
26 | // ],
27 | // medium: [
28 | // 'https://live.staticflickr.com/4362/37217435685_a0cf602c4a_k.jpg',
29 | // 'https://live.staticflickr.com/5028/5563992757_565c672e3b_k.jpg',
30 | // 'https://live.staticflickr.com/2415/2041950312_62916f6bd9_h.jpg',
31 | // 'https://live.staticflickr.com/6196/6139354281_3557f02ddd_h.jpg'
32 | // ],
33 | // mobile: [
34 | // 'https://live.staticflickr.com/4406/37289196062_dc2fe8f4d9_k.jpg',
35 | // 'https://live.staticflickr.com/7037/6919629661_2da45bb884_3k.jpg',
36 | // 'https://live.staticflickr.com/7037/6919625059_98e1e7d7b6_3k.jpg',
37 | // 'https://live.staticflickr.com/8320/8022309149_2cde3349e9_h.jpg'
38 | // ]
39 | // };
--------------------------------------------------------------------------------
/src/components/Loading/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './Loading';
--------------------------------------------------------------------------------
/src/components/SignIn/SignIn.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import logoLight from './../../assets/img/logo/logoLight.png';
3 | import logoDark from './../../assets/img/logo/logoDark.png';
4 | import BtnFancy from '../../components/BtnFancy';
5 |
6 | const SignIn = ({ theme }) => {
7 | const handleSubmit = () => alert('Nah');
8 | return (
9 |
10 |
11 |
14 |
15 |
51 |
52 | )
53 | }
54 |
55 | export default SignIn;
--------------------------------------------------------------------------------
/src/components/SignIn/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './SignIn.jsx';
--------------------------------------------------------------------------------
/src/hooks/Viewport.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 |
3 | function getWindowDeminsions() {
4 | const { innerWidth: width, innerHeight: height } = window;
5 | return { width, height };
6 | }
7 |
8 | export default function useViewport() {
9 | const [useDimensions, setWindowDimensions] = useState(getWindowDeminsions());
10 |
11 | useEffect(() => {
12 | function handleResize() {
13 | setWindowDimensions(getWindowDeminsions());
14 | }
15 |
16 | window.addEventListener('resize', handleResize);
17 | return () => window.removeEventListener('resize', handleResize)
18 | }, []);
19 |
20 | return useDimensions;
21 | }
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | html {
2 | margin: 0;
3 | padding: 0;
4 | /* overflow: hidden; */
5 | }
6 | body {
7 | margin: 0;
8 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
9 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
10 | sans-serif;
11 | -webkit-font-smoothing: antialiased;
12 | -moz-osx-font-smoothing: grayscale;
13 | }
14 |
15 | code {
16 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
17 | monospace;
18 | }
19 |
--------------------------------------------------------------------------------
/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(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want your app to work offline and load faster, you can change
15 | // unregister() to register() below. Note this comes with some pitfalls.
16 | // Learn more about service workers: https://bit.ly/CRA-PWA
17 | serviceWorker.unregister();
18 |
--------------------------------------------------------------------------------
/src/pages/Landing/Landing.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import useViewport from '../../hooks/Viewport';
3 | import SignIn from '../../components/SignIn';
4 | import '../../assets/styles/main.scss'
5 | import Footer from '../../components/Footer';
6 |
7 | function SwiperMcSwipeWrapper() {
8 | return
9 | }
10 |
11 | export default function Landing() {
12 |
13 | const { width, height } = useViewport();
14 |
15 | return (
16 |
17 |
18 | {
19 | width >= 902 && (
20 |
21 |
22 |
23 | )
24 | }
25 |
26 |
27 |
28 |
29 | )
30 |
31 | }
--------------------------------------------------------------------------------
/src/pages/Landing/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './Landing';
--------------------------------------------------------------------------------
/src/pages/LoadingScreen/LoadingScreen.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Loading from '../../components/Loading';
3 | import '../../assets/styles/main.scss'
4 |
5 | const LoadingScreen = ({ theme, children }) => (
6 |
7 |
8 | { children }
9 |
10 | );
11 |
12 | export default LoadingScreen;
--------------------------------------------------------------------------------
/src/pages/LoadingScreen/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './LoadingScreen';
--------------------------------------------------------------------------------
/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.0/8 are 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 | headers: { 'Service-Worker': 'script' },
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready
134 | .then(registration => {
135 | registration.unregister();
136 | })
137 | .catch(error => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------