feat: add PWA support

This commit is contained in:
divlo committed 2020-12-28 13:07:21 +01:00
1 parent c594577415
commit c285d3b69e
72 files changed
+2892 -1339

No files matched your search

+2
View File
@@ -1,2 +1,4 @@
node_modules
build
dist
.next
+1 -1
View File
@@ -1,3 +1,3 @@
COMPOSE_PROJECT_NAME="function.divlo.fr-website"
NEXT_PUBLIC_API_URL = "http://localhost:8080"
NEXT_PUBLIC_API_URL="http://localhost:8080"
CONTAINER_API_URL="http://functionproject-api:8080"
+33
View File
@@ -0,0 +1,33 @@
import Link from 'next/link'
export default function Footer() {
return (
<>
<footer className="footer">
<p className="footer-text text-center">
<Link href="/about">
<a>FunctionProject</a>
</Link>
&nbsp;- Version 2.3 <br />
<a href="https://divlo.fr/" target="_blank" rel="noopener noreferrer">
Divlo
</a>{' '}
| Tous droits réservés
</p>
</footer>
<style jsx>{`
.footer {
border-top: var(--border-header-footer);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.footer-text {
line-height: 2.5;
}
`}</style>
</>
)
}
-10
View File
@@ -1,10 +0,0 @@
.footer {
border-top: var(--border-header-footer);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.footer-text {
line-height: 2.5;
}
-19
View File
@@ -1,19 +0,0 @@
import Link from 'next/link'
import './Footer.css'
export default function Footer () {
return (
<footer className='footer'>
<p className='footer-text text-center'>
<Link href='/about'>
<a>FunctionProject</a>
</Link>
&nbsp;- Version 2.3 <br />
<a href='https://divlo.fr/' target='_blank' rel='noopener noreferrer'>
Divlo
</a>{' '}
| Tous droits réservés
</p>
</footer>
)
}
@@ -3,7 +3,6 @@ import htmlParser from 'html-react-parser'
import Loader from '../Loader'
import useAPI from '../../hooks/useAPI'
import api from '../../utils/api'
import '../../public/css/pages/admin.css'
const AddEditFunction = props => {
const [, categories] = useAPI('/categories')
@@ -3,8 +3,6 @@ import dynamic from 'next/dynamic'
import { complex } from '../../utils/sunEditorConfig'
import api from '../../utils/api'
import FunctionArticle from '../FunctionPage/FunctionArticle'
import 'notyf/notyf.min.css'
import '../../public/css/suneditor.min.css'
const SunEditor = dynamic(() => import('suneditor-react'), { ssr: false })
@@ -1,6 +1,5 @@
import { useState } from 'react'
import api from '../../utils/api'
import 'notyf/notyf.min.css'
const EditFormFunction = props => {
const [inputsArray, setInputsArray] = useState(
@@ -1,67 +0,0 @@
.FunctionCard {
display: flex;
align-items: center;
position: relative;
flex-direction: column;
word-wrap: break-word;
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
border: 1px solid black;
border-radius: 1rem;
margin: 0 0 50px 0;
cursor: pointer;
transition: all 0.3s;
color: var(--text-color);
text-decoration: none !important;
}
.FunctionCard__container {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.FunctionCard:hover {
transform: translateY(-7px);
}
/* col-md */
@media (min-width: 768px) {
.FunctionCard {
margin: 0 30px 50px 30px;
}
}
/* col-xl */
@media (min-width: 1200px) {
.FunctionCard {
margin: 0 20px 50px 20px;
}
}
.FunctionCard__top {
display: flex;
flex-direction: column;
align-items: center;
flex-grow: 1;
}
.FunctionCard__image {
width: 150px;
}
.FunctionCard__title {
font-size: 1.4em;
margin: 0;
color: var(--important);
font-weight: 300;
}
.FunctionCard__description {
margin: 20px 0 10px 0;
}
.FunctionCard__info {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.FunctionCard__category {
border-radius: 0.5em;
padding: 0.5em;
margin-right: 20px;
font-size: 16.4px;
}
+123 -52
View File
@@ -3,7 +3,6 @@ import { useState, forwardRef, memo } from 'react'
import date from 'date-and-time'
import Loader from '../Loader'
import { API_URL } from '../../utils/api'
import './FunctionCard.css'
const FunctionCard = memo(
forwardRef((props, ref) => {
@@ -13,66 +12,138 @@ const FunctionCard = memo(
setIsLoading(false)
}
const handleError = event => {
const handleError = (event) => {
event.target.src = API_URL + '/images/functions/default.png'
}
const isFormOrArticle = props.type === 'form' || props.type === 'article'
return (
<Link
{...(props.isAdmin
? {
href: '/admin/[slug]',
as: `/admin/${props.slug}`
}
: {
href: isFormOrArticle
? '/functions/[slug]'
: `/functions/${props.slug}`,
as: `/functions/${props.slug}`
})}
>
{/* FunctionCard a une hauteur pendant chargement */}
<a
ref={ref}
style={
isLoading ? { height: '360px', justifyContent: 'center' } : null
}
className='FunctionCard col-sm-24 col-md-10 col-xl-7'
<>
<Link
{...(props.isAdmin
? {
href: '/admin/[slug]',
as: `/admin/${props.slug}`
}
: {
href: isFormOrArticle
? '/functions/[slug]'
: `/functions/${props.slug}`,
as: `/functions/${props.slug}`
})}
>
{isLoading && <Loader width='125px' height='125px' />}
<div
className={`FunctionCard__container ${isLoading ? 'd-none' : ''}`}
{/* FunctionCard a une hauteur pendant chargement */}
<a
ref={ref}
style={
isLoading ? { height: '360px', justifyContent: 'center' } : null
}
className="FunctionCard col-sm-24 col-md-10 col-xl-7"
>
<div className='FunctionCard__top'>
<img
onLoad={handleLoad}
onError={handleError}
className='FunctionCard__image'
alt={props.title}
src={API_URL + props.image}
/>
<h2 className='FunctionCard__title'>{props.title}</h2>
<p className='FunctionCard__description text-center'>
{props.description}
</p>
{isLoading && <Loader width="125px" height="125px" />}
<div
className={`FunctionCard__container ${isLoading ? 'd-none' : ''}`}
>
<div className="FunctionCard__top">
<img
onLoad={handleLoad}
onError={handleError}
className="FunctionCard__image"
alt={props.title}
src={API_URL + props.image}
/>
<h2 className="FunctionCard__title">{props.title}</h2>
<p className="FunctionCard__description text-center">
{props.description}
</p>
</div>
<div className="FunctionCard__info">
<p
className="FunctionCard__category"
style={{ backgroundColor: props.categorie.color }}
>
{props.categorie.name}
</p>
<p className="FunctionCard__publication-date">
{date.format(new Date(props.createdAt), 'DD/MM/YYYY', false)}
</p>
</div>
</div>
<div className='FunctionCard__info'>
<p
className='FunctionCard__category'
style={{ backgroundColor: props.categorie.color }}
>
{props.categorie.name}
</p>
<p className='FunctionCard__publication-date'>
{date.format(new Date(props.createdAt), 'DD/MM/YYYY', false)}
</p>
</div>
</div>
</a>
</Link>
</a>
</Link>
<style jsx>{`
.FunctionCard {
display: flex;
align-items: center;
position: relative;
flex-direction: column;
word-wrap: break-word;
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
border: 1px solid black;
border-radius: 1rem;
margin: 0 0 50px 0;
cursor: pointer;
transition: all 0.3s;
color: var(--text-color);
text-decoration: none !important;
}
.FunctionCard__container {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.FunctionCard:hover {
transform: translateY(-7px);
}
/* col-md */
@media (min-width: 768px) {
.FunctionCard {
margin: 0 30px 50px 30px;
}
}
/* col-xl */
@media (min-width: 1200px) {
.FunctionCard {
margin: 0 20px 50px 20px;
}
}
.FunctionCard__top {
display: flex;
flex-direction: column;
align-items: center;
flex-grow: 1;
}
.FunctionCard__image {
width: 150px;
}
.FunctionCard__title {
font-size: 1.4em;
margin: 0;
color: var(--important);
font-weight: 300;
}
.FunctionCard__description {
margin: 20px 0 10px 0;
}
.FunctionCard__info {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.FunctionCard__category {
border-radius: 0.5em;
padding: 0.5em;
margin-right: 20px;
font-size: 16.4px;
}
`}</style>
</>
)
})
)
@@ -6,7 +6,6 @@ import { UserContext } from '../../../contexts/UserContext'
import ReactMarkdown from 'react-markdown'
import CodeBlock from '../../CodeBlock'
import api, { API_URL } from '../../../utils/api'
import './CommentCard.css'
const CommentCard = forwardRef((props, ref) => {
const { isAuth, user } = useContext(UserContext)
@@ -4,7 +4,6 @@ import { UserContext } from '../../../contexts/UserContext'
import CommentCard from '../CommentCard/CommentCard'
import Loader from '../../Loader'
import api from '../../../utils/api'
import './FunctionComments.css'
const FunctionComments = ({ functionId }) => {
// State pour poster un commentaire
@@ -6,7 +6,6 @@ import { faStar as farStar } from '@fortawesome/free-regular-svg-icons'
import date from 'date-and-time'
import { UserContext } from '../../contexts/UserContext'
import api, { API_URL } from '../../utils/api'
import '../FunctionCard/FunctionCard.css'
const FunctionComponentTop = props => {
const { isAuth, user } = useContext(UserContext)
@@ -6,7 +6,6 @@ import api from '../../utils/api'
import fr from 'date-fns/locale/fr'
import { registerLocale } from 'react-datepicker'
import date from 'date-and-time'
import 'react-datepicker/dist/react-datepicker.css'
registerLocale('fr', fr)
@@ -1,5 +1,4 @@
import SwipeableViews from 'react-swipeable-views'
import './FunctionTabs.css'
const FunctionTabs = props => {
return (
@@ -4,7 +4,6 @@ import FunctionCard from '../FunctionCard/FunctionCard'
import Loader from '../Loader'
import api from '../../utils/api'
import useAPI from '../../hooks/useAPI'
import './FunctionsList.css'
let pageFunctions = 1
const FunctionsList = props => {
+39 -34
View File
@@ -1,44 +1,49 @@
import Head from 'next/head'
const HeadTag = ({ title, image, description }) => (
<Head>
<title>{title || ''}</title>
<link rel='icon' type='image/png' href={image} />
const HeadTag = (props) => {
const {
title = 'FunctionProject',
image = '/images/FunctionProject_icon_small.png',
description = "Apprenez la programmation grâce à l'apprentissage par projet alias fonction.",
url = 'https://function.divlo.fr/'
} = props
{/* Meta Tag */}
<meta name='viewport' content='width=device-width, initial-scale=1' />
<meta name='description' content={description} />
<link rel='canonical' href='function.divlo.fr' />
<meta name='Language' content='fr' />
<meta name='theme-color' content='#ffd800' />
return (
<Head>
<title>{title}</title>
<link rel="icon" type="image/png" href={image} />
{/* Open Graph Metadata */}
<meta property='og:title' content={title} />
<meta property='og:type' content='website' />
<meta property='og:url' content='https://function.divlo.fr/' />
<meta property='og:image' content={image} />
<meta property='og:description' content={description} />
<meta property='og:locale' content='fr_FR' />
<meta property='og:site_name' content='FunctionProject' />
{/* Meta Tag */}
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content={description} />
<meta name="Language" content="fr" />
<meta name="theme-color" content="#ffd800" />
{/* Twitter card Metadata */}
<meta name='twitter:card' content='summary' />
<meta name='twitter:description' content={description} />
<meta name='twitter:title' content={title} />
<meta name='twitter:site' content='@Divlo_FR' />
<meta name='twitter:image:src' content={image} />
<meta name='twitter:creator' content='@Divlo_FR' />
{/* Open Graph Metadata */}
<meta property="og:title" content={title} />
<meta property="og:type" content="website" />
<meta property="og:url" content={url} />
<meta property="og:image" content={image} />
<meta property="og:description" content={description} />
<meta property="og:locale" content="fr_FR" />
<meta property="og:site_name" content={title} />
{/* Preloader script */}
<script src='/js/preloader.js' />
</Head>
)
{/* Twitter card Metadata */}
<meta name="twitter:card" content="summary" />
<meta name="twitter:description" content={description} />
<meta name="twitter:title" content={title} />
<meta name="twitter:image:src" content={image} />
HeadTag.defaultProps = {
title: 'FunctionProject',
description:
"Apprenez la programmation grâce à l'apprentissage par projet alias fonction.",
image: '/images/FunctionProject_icon_small.png'
{/* PWA Data */}
<link rel="manifest" href="/manifest.json" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<link rel="apple-touch-icon" href={image} />
{/* Preloader script */}
<script src="/js/preloader.js" />
</Head>
)
}
export default HeadTag
-1
View File
@@ -3,7 +3,6 @@ import { UserContext } from '../../contexts/UserContext'
import Link from 'next/link'
import { useRouter } from 'next/router'
import NavigationLink from './NavigationLink'
import './Header.css'
export default function Header () {
const { isAuth, logoutUser, user } = useContext(UserContext)
@@ -1,6 +1,5 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
import './Header.css'
export default function NavigationLink (props) {
const { pathname } = useRouter()
-1
View File
@@ -1,7 +1,6 @@
import Link from 'next/link'
import { forwardRef, memo } from 'react'
import { API_URL } from '../../utils/api'
import './UserCard.css'
const UserCard = memo(
forwardRef((props, ref) => {
+10 -2
View File
@@ -1,3 +1,11 @@
const withCSS = require('@zeit/next-css')
const withFonts = require('next-fonts')
module.exports = withFonts(withCSS())
const withPWA = require('next-pwa')
module.exports = withFonts(
withPWA({
pwa: {
disable: process.env.NODE_ENV !== 'production',
dest: 'public'
}
})
)
+2608 -1110
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -27,7 +27,6 @@
"@fortawesome/free-regular-svg-icons": "^5.15.1",
"@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/react-fontawesome": "^0.1.14",
"@zeit/next-css": "^1.0.1",
"axios": "^0.21.1",
"date-and-time": "^0.14.2",
"date-fns": "^2.16.1",
@@ -36,6 +35,7 @@
"html-react-parser": "^1.1.1",
"next": "^10.0.4",
"next-fonts": "^1.5.1",
"next-pwa": "^5.0.1",
"notyf": "^3.9.0",
"nprogress": "^0.2.0",
"react": "17.0.1",
-1
View File
@@ -1,6 +1,5 @@
import Link from 'next/link'
import HeadTag from '../components/HeadTag'
import '../public/css/pages/404.css'
const Error404 = () => (
<>
+24 -5
View File
@@ -4,17 +4,36 @@ import NProgress from 'nprogress'
/* Components Imports */
import Header from '../components/Header/Header'
import Footer from '../components/Footer/Footer'
import Footer from '../components/Footer'
/* Contexts Imports */
import UserContextProvider from '../contexts/UserContext'
/* CSS Imports */
import 'notyf/notyf.min.css'
import 'react-datepicker/dist/react-datepicker.css'
import '../public/fonts/Montserrat/Montserrat.css'
import '../public/css/normalize.css'
import '../public/css/grid.css'
import '../public/css/general.css'
import '../public/css/nprogress.css'
import '../styles/suneditor.min.css'
import '../styles/normalize.css'
import '../styles/grid.css'
import '../styles/general.css'
import '../styles/nprogress.css'
import '../styles/pages/admin.css'
import '../styles/pages/404.css'
import '../styles/pages/index.css'
import '../styles/pages/profile.css'
import '../styles/pages/register-login.css'
import '../styles/pages/users.css'
import '../styles/pages/FunctionComponent.css'
import '../styles/pages/functions/chronometerTimer.css'
import '../styles/pages/functions/rightPrice.css'
import '../styles/pages/functions/toDoList.css'
import '../styles/components/Header.css'
import '../styles/components/FunctionTabs.css'
import '../styles/components/CommentCard.css'
import '../styles/components/FunctionComments.css'
import '../styles/components/FunctionsList.css'
import '../styles/components/UserCard.css'
Router.events.on('routeChangeStart', () => NProgress.start())
Router.events.on('routeChangeComplete', () => NProgress.done())
-2
View File
@@ -7,8 +7,6 @@ import EditArticleFunction from '../../components/FunctionAdmin/EditArticleFunct
import EditFormFunction from '../../components/FunctionAdmin/EditFormFunction'
import redirect from '../../utils/redirect'
import api, { API_URL } from '../../utils/api'
import '../../components/FunctionPage/FunctionTabs.css'
import '../../public/css/pages/admin.css'
const AdminFunctionComponent = props => {
const [slideIndex, setSlideIndex] = useState(0)
-1
View File
@@ -8,7 +8,6 @@ import Modal from '../../components/Modal'
import FunctionsList from '../../components/FunctionsList/FunctionsList'
import AddEditFunction from '../../components/FunctionAdmin/AddEditFunction'
import redirect from '../../utils/redirect'
import '../../public/css/pages/admin.css'
const Admin = props => {
const [isOpen, setIsOpen] = useState(false)
-1
View File
@@ -11,7 +11,6 @@ import htmlParser from 'html-react-parser'
import Loader from '../../components/Loader'
import useAPI from '../../hooks/useAPI'
import api from '../../utils/api'
import '../../public/css/pages/admin.css'
const defaultCategoryState = { name: '', color: '#ffffff' }
-1
View File
@@ -6,7 +6,6 @@ import { faCheck, faTrash } from '@fortawesome/free-solid-svg-icons'
import redirect from '../../utils/redirect'
import HeadTag from '../../components/HeadTag'
import api from '../../utils/api'
import '../../public/css/pages/admin.css'
const manageQuotes = props => {
const [quotesData, setQuotesData] = useState({
-1
View File
@@ -5,7 +5,6 @@ import FunctionComments from '../../components/FunctionPage/FunctionComments/Fun
import FunctionPage from '../../components/FunctionPage/FunctionPage'
import redirect from '../../utils/redirect'
import api from '../../utils/api'
import '../../public/css/pages/FunctionComponent.css'
const FunctionTabManager = props => {
if (props.type === 'form') {
@@ -9,8 +9,6 @@ import Loader from '../../components/Loader'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlay, faPause, faSync } from '@fortawesome/free-solid-svg-icons'
import api from '../../utils/api'
import '../../public/css/pages/FunctionComponent.css'
import '../../public/css/pages/functions/chronometerTimer.css'
let interval
function convertSeconds (seconds) {
@@ -12,9 +12,6 @@ import FunctionArticle from '../../components/FunctionPage/FunctionArticle'
import FunctionComments from '../../components/FunctionPage/FunctionComments/FunctionComments'
import Modal from '../../components/Modal'
import api from '../../utils/api'
import 'notyf/notyf.min.css'
import '../../public/css/pages/FunctionComponent.css'
import '../../public/css/pages/admin.css'
const CreateLink = ({ linksData, setLinksData }) => {
const { isAuth, user } = useContext(UserContext)
-2
View File
@@ -12,8 +12,6 @@ import FunctionArticle from '../../components/FunctionPage/FunctionArticle'
import FunctionComments from '../../components/FunctionPage/FunctionComments/FunctionComments'
import api from '../../utils/api'
import copyToClipboard from '../../utils/copyToClipboard'
import 'notyf/notyf.min.css'
import '../../public/css/pages/FunctionComponent.css'
const GenerateQuote = () => {
const [quote, setQuote] = useState({ quote: '', author: '' })
-2
View File
@@ -6,8 +6,6 @@ import FunctionArticle from '../../components/FunctionPage/FunctionArticle'
import FunctionComments from '../../components/FunctionPage/FunctionComments/FunctionComments'
import Loader from '../../components/Loader'
import api from '../../utils/api'
import '../../public/css/pages/FunctionComponent.css'
import '../../public/css/pages/functions/rightPrice.css'
const PlayRightPrice = () => {
const [isPlaying, setIsPlaying] = useState(false)
-2
View File
@@ -9,8 +9,6 @@ import FunctionTabs from '../../components/FunctionPage/FunctionTabs'
import FunctionArticle from '../../components/FunctionPage/FunctionArticle'
import FunctionComments from '../../components/FunctionPage/FunctionComments/FunctionComments'
import api from '../../utils/api'
import '../../public/css/pages/FunctionComponent.css'
import '../../public/css/pages/functions/toDoList.css'
const ManageToDo = () => {
const { isAuth, user } = useContext(UserContext)
-1
View File
@@ -4,7 +4,6 @@ import { autoPlay } from 'react-swipeable-views-utils'
import Link from 'next/link'
import HeadTag from '../components/HeadTag'
import Loader from '../components/Loader'
import '../public/css/pages/index.css'
const AutoPlaySwipeableViews = autoPlay(SwipeableViews)
-1
View File
@@ -13,7 +13,6 @@ import Loader from '../../components/Loader'
import ReactMarkdown from 'react-markdown'
import CodeBlock from '../../components/CodeBlock'
import api, { API_URL } from '../../utils/api'
import '../../public/css/pages/profile.css'
const Profile = props => {
const { isAuth, user, logoutUser } = useContext(UserContext)
-1
View File
@@ -4,7 +4,6 @@ import Loader from '../../components/Loader'
import HeadTag from '../../components/HeadTag'
import api from '../../utils/api'
import withoutAuth from '../../hoc/withoutAuth'
import '../../public/css/pages/register-login.css'
const forgotPassword = () => {
const [inputState, setInputState] = useState({})
-1
View File
@@ -3,7 +3,6 @@ import HeadTag from '../../components/HeadTag'
import Loader from '../../components/Loader'
import UserCard from '../../components/UserCard/UserCard'
import api from '../../utils/api'
import '../../public/css/pages/users.css'
const Users = () => {
let pageUsers = 1
-1
View File
@@ -6,7 +6,6 @@ import Loader from '../../components/Loader'
import HeadTag from '../../components/HeadTag'
import { UserContext } from '../../contexts/UserContext'
import withoutAuth from '../../hoc/withoutAuth'
import '../../public/css/pages/register-login.css'
const Login = () => {
const router = useRouter()
-1
View File
@@ -5,7 +5,6 @@ import HeadTag from '../../components/HeadTag'
import api from '../../utils/api'
import redirect from '../../utils/redirect'
import withoutAuth from '../../hoc/withoutAuth'
import '../../public/css/pages/register-login.css'
const newPassword = (props) => {
const [inputState, setInputState] = useState({})
-1
View File
@@ -4,7 +4,6 @@ import Loader from '../../components/Loader'
import HeadTag from '../../components/HeadTag'
import api from '../../utils/api'
import withoutAuth from '../../hoc/withoutAuth'
import '../../public/css/pages/register-login.css'
const Register = () => {
const [inputState, setInputState] = useState({ name: '', email: '', password: '' })
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

+51
View File
@@ -0,0 +1,51 @@
{
"name": "FunctionProject",
"short_name": "FunctionProject",
"icons": [
{
"src": "images/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "images/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "images/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "images/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "images/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "images/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffd800",
"background_color": "#181818",
"start_url": "/",
"display": "standalone"
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.