pifi 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/pifi/public/asset-manifest.json +7 -7
- data/lib/pifi/public/index.html +1 -1
- data/lib/pifi/public/manifest.json +1 -1
- data/lib/pifi/public/{precache-manifest.ae51869c6b1904e9060d2af9380a0a09.js → precache-manifest.b2ad228b5bcabcdf0fb713c92d5fd6ef.js} +5 -5
- data/lib/pifi/public/service-worker.js +1 -1
- data/lib/pifi/public/static/css/{main.a50e54d2.chunk.css → main.b630c694.chunk.css} +2 -2
- data/lib/pifi/public/static/css/main.b630c694.chunk.css.map +1 -0
- data/lib/pifi/public/static/js/main.5341431d.chunk.js +2 -0
- data/lib/pifi/public/static/js/main.5341431d.chunk.js.map +1 -0
- data/lib/pifi/version.rb +1 -1
- metadata +7 -7
- data/lib/pifi/public/static/css/main.a50e54d2.chunk.css.map +0 -1
- data/lib/pifi/public/static/js/main.9b725bd5.chunk.js +0 -2
- data/lib/pifi/public/static/js/main.9b725bd5.chunk.js.map +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd1c1e59a5225081b61043f97f6dcb62c57e445dfc2180150705939e7a8b704a
|
4
|
+
data.tar.gz: b35646706ede81048647d11005164f701b7826a5eb1d2dcc7d7a4fed8c76bf85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13431a49fc99517f05d7d8b8fafe7e0deb34b3d7ea5ac9a27eb4f818d8feb40815903d4ebfe8dc551efbb9b758a938bfc133dd26abd14156d43843a5726568f8
|
7
|
+
data.tar.gz: 6610feb4d3636c22e2d88e2a334df20d878071be2ff5a3648ddf011a63f3637785d3226d9c336225113c931c6c35f5e8e8556b7621a549bf44b59c21cb56761e
|
@@ -1,18 +1,18 @@
|
|
1
1
|
{
|
2
2
|
"files": {
|
3
|
-
"main.css": "/static/css/main.
|
4
|
-
"main.js": "/static/js/main.
|
5
|
-
"main.js.map": "/static/js/main.
|
3
|
+
"main.css": "/static/css/main.b630c694.chunk.css",
|
4
|
+
"main.js": "/static/js/main.5341431d.chunk.js",
|
5
|
+
"main.js.map": "/static/js/main.5341431d.chunk.js.map",
|
6
6
|
"runtime-main.js": "/static/js/runtime-main.f04a0f25.js",
|
7
7
|
"runtime-main.js.map": "/static/js/runtime-main.f04a0f25.js.map",
|
8
8
|
"static/css/2.5dbdccff.chunk.css": "/static/css/2.5dbdccff.chunk.css",
|
9
9
|
"static/js/2.edc56e6f.chunk.js": "/static/js/2.edc56e6f.chunk.js",
|
10
10
|
"static/js/2.edc56e6f.chunk.js.map": "/static/js/2.edc56e6f.chunk.js.map",
|
11
11
|
"index.html": "/index.html",
|
12
|
-
"precache-manifest.
|
12
|
+
"precache-manifest.b2ad228b5bcabcdf0fb713c92d5fd6ef.js": "/precache-manifest.b2ad228b5bcabcdf0fb713c92d5fd6ef.js",
|
13
13
|
"service-worker.js": "/service-worker.js",
|
14
14
|
"static/css/2.5dbdccff.chunk.css.map": "/static/css/2.5dbdccff.chunk.css.map",
|
15
|
-
"static/css/main.
|
15
|
+
"static/css/main.b630c694.chunk.css.map": "/static/css/main.b630c694.chunk.css.map",
|
16
16
|
"static/js/2.edc56e6f.chunk.js.LICENSE": "/static/js/2.edc56e6f.chunk.js.LICENSE",
|
17
17
|
"static/media/player.scss": "/static/media/logo.91554ce9.svg"
|
18
18
|
},
|
@@ -20,7 +20,7 @@
|
|
20
20
|
"static/js/runtime-main.f04a0f25.js",
|
21
21
|
"static/css/2.5dbdccff.chunk.css",
|
22
22
|
"static/js/2.edc56e6f.chunk.js",
|
23
|
-
"static/css/main.
|
24
|
-
"static/js/main.
|
23
|
+
"static/css/main.b630c694.chunk.css",
|
24
|
+
"static/js/main.5341431d.chunk.js"
|
25
25
|
]
|
26
26
|
}
|
data/lib/pifi/public/index.html
CHANGED
@@ -1 +1 @@
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,user-scalable=no"/><meta name="description" content="MPD web client to listen to radio"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"><meta name="apple-mobile-web-app-title" content="PiFi Radio"><meta name="application-name" content="PiFi Radio"><meta name="msapplication-TileColor" content="#2b5797"><meta name="theme-color" content="#375a7f"><title>PiFi Radio</title><link href="https://stackpath.bootstrapcdn.com/" rel="dns-prefetch"><link href="" rel="stylesheet" title="theme"><link href="/static/css/2.5dbdccff.chunk.css" rel="stylesheet"><link href="/static/css/main.b630c694.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],Object.prototype.hasOwnProperty.call(a,t)&&a[t]&&i.push(a[t][0]),a[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),p()}function p(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==a[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},a={1:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=this.webpackJsonppifi_frontend=this.webpackJsonppifi_frontend||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;p()}([])</script><script src="/static/js/2.edc56e6f.chunk.js"></script><script src="/static/js/main.5341431d.chunk.js"></script></body></html>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
self.__precacheManifest = (self.__precacheManifest || []).concat([
|
2
2
|
{
|
3
|
-
"revision": "
|
3
|
+
"revision": "dedd4de2e60a17b38cb4d33a0c70afc4",
|
4
4
|
"url": "/index.html"
|
5
5
|
},
|
6
6
|
{
|
@@ -8,8 +8,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
|
|
8
8
|
"url": "/static/css/2.5dbdccff.chunk.css"
|
9
9
|
},
|
10
10
|
{
|
11
|
-
"revision": "
|
12
|
-
"url": "/static/css/main.
|
11
|
+
"revision": "7117ff74562d17e5733d",
|
12
|
+
"url": "/static/css/main.b630c694.chunk.css"
|
13
13
|
},
|
14
14
|
{
|
15
15
|
"revision": "e828c0a984dc06937d77",
|
@@ -20,8 +20,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
|
|
20
20
|
"url": "/static/js/2.edc56e6f.chunk.js.LICENSE"
|
21
21
|
},
|
22
22
|
{
|
23
|
-
"revision": "
|
24
|
-
"url": "/static/js/main.
|
23
|
+
"revision": "7117ff74562d17e5733d",
|
24
|
+
"url": "/static/js/main.5341431d.chunk.js"
|
25
25
|
},
|
26
26
|
{
|
27
27
|
"revision": "92793784c9eb98d269de",
|
@@ -14,7 +14,7 @@
|
|
14
14
|
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
|
15
15
|
|
16
16
|
importScripts(
|
17
|
-
"/precache-manifest.
|
17
|
+
"/precache-manifest.b2ad228b5bcabcdf0fb713c92d5fd6ef.js"
|
18
18
|
);
|
19
19
|
|
20
20
|
self.addEventListener('message', (event) => {
|
@@ -1,2 +1,2 @@
|
|
1
|
-
.navbar{position:-webkit-sticky!important;position:sticky!important;top:0;z-index:5}.player-controls{position:absolute!important;bottom:0;right:0;padding:2rem}.player{position:-webkit-sticky;position:sticky;top:4rem;height:70vh;min-height:500px;width:400px;min-width:300px;background:url(/static/media/logo.91554ce9.svg) 50% no-repeat;background-size:120px}.loader{z-index:10;display:flex;justify-content:center;align-items:center;height:100vh}.streams{min-width:300px;width:450px}.streams__item{cursor:pointer}.streams__item.active{cursor:default}.streams__header{font-size:1.3rem;margin-top:1em;padding:.5rem 1.25rem}.list-group{margin-bottom:1rem!important}@media (max-width:750px){.list-group{margin-bottom:6rem!important}}.mini-player{max-width:600px;margin:auto;display:flex;align-items:center;justify-content:space-between;height:100%}.drawer{z-index:5!important;height:5rem;cursor:pointer;touch-action:none;-webkit-transition:all .25s ease-out;transition:all .25s ease-out}.drawer--open{height:100%;display:flex;justify-content:space-evenly;flex-direction:column;align-items:center}.body--drawer{overflow:hidden}.backdrop{z-index:10;position:fixed;width:100%;height:100%;background-color:#222;opacity:0;visibility:hidden;-webkit-transition:all .2s ease;transition:all .2s ease;cursor:default;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center}.backdrop--visible{opacity:.94;visibility:visible}.body--backdrop{overflow:hidden}.app-main{display:flex;justify-content:space-evenly}.drawer{visibility:hidden}@media (max-width:750px){.drawer{visibility:visible}}@media (max-width:750px){.app-main>.player{display:none}}body{margin:0;box-sizing:border-box;overflow-y:scroll}button[disabled]{cursor:default}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
2
|
-
/*# sourceMappingURL=main.
|
1
|
+
.navbar{position:-webkit-sticky!important;position:sticky!important;top:0;z-index:5}.player-controls{position:absolute!important;bottom:0;right:0;padding:2rem}.player{position:-webkit-sticky;position:sticky;top:4rem;height:70vh;min-height:500px;width:400px;min-width:300px;background:url(/static/media/logo.91554ce9.svg) 50% no-repeat;background-size:120px}.loader{z-index:10;display:flex;justify-content:center;align-items:center;height:100vh}.streams{min-width:300px;width:450px}.streams__item{cursor:pointer}.streams__item.active{cursor:default}.streams__header{font-size:1.3rem;margin-top:1em;padding:.5rem 1.25rem}.list-group{margin-bottom:1rem!important}@media (max-width:750px){.list-group{margin-bottom:6rem!important}}.mini-player{max-width:600px;margin:auto;display:flex;align-items:center;justify-content:space-between;height:100%}.drawer{z-index:5!important;height:5rem;cursor:pointer;touch-action:none;-webkit-transition:all .25s ease-out;transition:all .25s ease-out}.drawer--open{height:100%;display:flex;justify-content:space-evenly;flex-direction:column;align-items:center}@media (max-width:750px){.body--drawer{overflow:hidden}}.backdrop{z-index:10;position:fixed;width:100%;height:100%;background-color:#222;opacity:0;visibility:hidden;-webkit-transition:all .2s ease;transition:all .2s ease;cursor:default;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center}.backdrop--visible{opacity:.94;visibility:visible}.body--backdrop{overflow:hidden}.app-main{display:flex;justify-content:space-evenly}.drawer{visibility:hidden}@media (max-width:750px){.drawer{visibility:visible}}@media (max-width:750px){.app-main>.player{display:none}}body{margin:0;box-sizing:border-box;overflow-y:scroll}button[disabled]{cursor:default}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
2
|
+
/*# sourceMappingURL=main.b630c694.chunk.css.map */
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["navBar.scss","playerControls.scss","player.scss","loader.scss","streams.scss","miniPlayer.scss","drawer.scss","../variables.scss","backdrop.scss","App.scss","index.scss"],"names":[],"mappings":"AAAA,QACE,iCAA2B,CAA3B,yBAA2B,CAC3B,KAAM,CACN,SAAU,CCHZ,iBACE,2BAA6B,CAC7B,QAAY,CACZ,OAAW,CACX,YAAa,CCJf,QACE,uBAAgB,CAAhB,eAAgB,CAChB,QAAS,CACT,WAAY,CACZ,gBAAiB,CACjB,WAAY,CACZ,eAAgB,CAChB,6DACA,CAAA,qBAAsB,CCRxB,QACE,UAAW,CACX,YAAa,CACb,sBAAuB,CACvB,kBAAmB,CACnB,YAAa,CCHf,SACE,eAAgB,CAChB,WAAY,CACb,eAGC,cAAe,CADjB,sBAII,cAAe,CAChB,iBAID,gBAAiB,CACjB,cAAe,CACf,qBAAuB,CACxB,YAGC,4BAA8B,CAE9B,yBAHF,YAII,4BAA+C,CAElD,CC3BD,aACE,eAAgB,CAChB,WAAY,CACZ,YAAa,CACb,kBAAmB,CACnB,6BAA8B,CAC9B,WAAY,CCJd,QACE,mBAAqB,CACrB,WCHkB,CDIlB,cAAe,CACf,iBAAkB,CAClB,oCAAA,CAAA,4BAA8B,CAE9B,cACE,WAAY,CACZ,YAAa,CACb,4BAA6B,CAC7B,qBAAsB,CACtB,kBAAmB,CACpB,yBAKH,cAEI,eAAgB,CAEnB,CExBD,UACE,UAAW,CACX,cAAe,CACf,UAAW,CACX,WAAY,CACZ,qBAAyB,CACzB,SAAU,CACV,iBAAkB,CAClB,+BAAyB,CAAzB,uBAAyB,CACzB,cAAe,CACf,YAAa,CACb,qBAAsB,CACtB,sBAAuB,CACvB,kBAAmB,CACnB,iBAAkB,CAElB,mBACE,WAAa,CACb,kBAAmB,CACpB,gBAID,eAAgB,CCrBlB,UACE,YAAa,CACb,4BAA6B,CAC9B,QAGC,iBAAkB,CAElB,yBAHF,QAII,kBAAmB,CAEtB,CAGC,yBADF,kBAEI,YAAa,CAEhB,CCnBD,KACE,QAAS,CACT,qBAAsB,CACtB,iBAAkB,CACnB,iBAGC,cAAe,CAChB,UAGC,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB","file":"main.b630c694.chunk.css","sourcesContent":[".navbar {\n position: sticky !important;\n top: 0;\n z-index: 5;\n}\n",".player-controls {\n position: absolute !important;\n bottom: 0rem;\n right: 0rem;\n padding: 2rem;\n}\n",".player {\n position: sticky;\n top: 4rem; /* we set this dinamically */\n height: 70vh;\n min-height: 500px;\n width: 400px;\n min-width: 300px;\n background: url('../logo.svg') center no-repeat;\n background-size: 120px;\n}\n",".loader {\n z-index: 10;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n}\n","@import '../variables.scss';\n\n.streams {\n min-width: 300px;\n width: 450px;\n}\n\n.streams__item {\n cursor: pointer;\n\n &.active {\n cursor: default;\n }\n}\n\n.streams__header {\n font-size: 1.3rem;\n margin-top: 1em;\n padding: 0.5rem 1.25rem;\n}\n\n.list-group {\n margin-bottom: 1rem !important;\n\n @media (max-width: $mobile-width) {\n margin-bottom: 1rem + $drawer-height !important; /* drawer */\n }\n}\n",".mini-player {\n max-width: 600px;\n margin: auto;\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: 100%;\n}\n","@import '../variables.scss';\n\n.drawer {\n z-index: 5 !important;\n height: $drawer-height;\n cursor: pointer;\n touch-action: none; // don't scroll on touch\n transition: all 0.25s ease-out;\n\n &--open {\n height: 100%;\n display: flex;\n justify-content: space-evenly;\n flex-direction: column;\n align-items: center;\n }\n}\n\n// The media query is for the edge case of an open drawer and the\n// window resized past the breakpoint. This brings the scrollbar back.\n.body--drawer {\n @media (max-width: $mobile-width) {\n overflow: hidden;\n }\n}\n","$mobile-width: 750px;\n$drawer-height: 5rem;\n",".backdrop {\n z-index: 10;\n position: fixed;\n width: 100%;\n height: 100%;\n background-color: #222222;\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n cursor: default;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n text-align: center;\n\n &--visible {\n opacity: 0.94;\n visibility: visible;\n }\n}\n\n.body--backdrop {\n overflow: hidden;\n}\n","@import './variables.scss';\n\n.app-main {\n display: flex;\n justify-content: space-evenly;\n}\n\n.drawer {\n visibility: hidden;\n\n @media (max-width: $mobile-width) {\n visibility: visible;\n }\n}\n\n.app-main > .player {\n @media (max-width: $mobile-width) {\n display: none;\n }\n}\n","body {\n margin: 0;\n box-sizing: border-box;\n overflow-y: scroll; // so elements don't move if scroll is off\n}\n\nbutton[disabled] {\n cursor: default;\n}\n\n.ellipsis {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n"]}
|
@@ -0,0 +1,2 @@
|
|
1
|
+
(this.webpackJsonppifi_frontend=this.webpackJsonppifi_frontend||[]).push([[0],{18:function(e){e.exports=JSON.parse('{"b":["en","fr-FR","nl-NL","pt-BR"],"c":1000,"a":2500,"d":3000}')},42:function(e,t,a){e.exports=a.p+"static/media/logo.91554ce9.svg"},46:function(e,t,a){e.exports=a(95)},60:function(e,t,a){},81:function(e,t,a){},82:function(e,t,a){},83:function(e,t,a){},84:function(e,t,a){},85:function(e,t,a){},86:function(e,t,a){},87:function(e,t,a){},88:function(e,t,a){},90:function(e,t,a){},95:function(e,t,a){"use strict";a.r(t);var n=a(0),r=a.n(n),l=a(19),o=a.n(l),c=a(6),s=a.n(c),i=a(3),u=a(4),m=a(8),d=a(10),p=a(11),b=a(2),v=(a(60),a(42)),f=a.n(v),h=Object(b.c)()((function(e){var t=e.t;return r.a.createElement("nav",{className:"navbar navbar-expand-sm navbar-dark bg-primary "},r.a.createElement("a",{className:"navbar-brand",href:"/"},r.a.createElement("img",{src:f.a,width:"30",height:"30",className:"d-inline-block align-text-bottom mr-2",alt:""}),"PiFi Radio"),r.a.createElement("button",{className:"navbar-toggler",type:"button","data-toggle":"collapse","data-target":"#navbarNavAltMarkup","aria-controls":"navbarNavAltMarkup","aria-expanded":"false","aria-label":"Toggle navigation"},r.a.createElement("span",{className:"navbar-toggler-icon"})),r.a.createElement("div",{className:"collapse navbar-collapse",id:"navbarNavAltMarkup"},r.a.createElement("div",{className:"navbar-nav pt-1 ml-2"},r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#url-dialog"},t("playURL")),r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#settings"},t("settings")))))})),g=Object(b.c)()((function(e){var t=e.playerStatus,a=e.t,n=t.playing,l=t.title;return r.a.createElement("div",{className:"text-center"},r.a.createElement("h5",{className:"small text-uppercase"},a(n?"playing":"stopped")),r.a.createElement("h3",{className:"ellipsis"},l))})),y=a(15),E=a(14),k=a(27),N=a.n(k),w=a(5);N.a.interceptors.response.use(null,(function(e){var t=e.response&&e.response.status>=400&&e.response.status<500,a=!e.response;return t||a||w.b.error(r.a.createElement(b.a,null,(function(e,t){t.i18n;return r.a.createElement("p",null,e("errorUnexpected"))}))),e.response&&403===e.response.status&&w.b.error(r.a.createElement(b.a,null,(function(e,t){t.i18n;return r.a.createElement("p",null,e("errorForbidden"))}))),Promise.reject(e)}));var S={get:N.a.get,post:N.a.post},C="".concat("/api","/player");function O(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,a=new FormData;return a.set("method",e),t&&a.set("params",t),a}function x(){return S.post(C,O("play"))}function j(){return S.post(C,O("stop"))}function _(e){return S.post(C,O("change_vol",e))}function L(e){return S.post(C,O("play_radios",e))}function P(e){return S.post(C,O("play_urls",e))}var R=function(e){return e.playing?r.a.createElement("button",{className:"btn btn-danger",onClick:j,"aria-label":"Stop"},r.a.createElement(y.a,{icon:E.d})):r.a.createElement("button",{className:"btn btn-dark",onClick:x,"aria-label":"Play"},r.a.createElement(y.a,{icon:E.c}))},T=a(18),A=(a(81),Object(b.c)()((function(e){var t=e.playerStatus,a=e.t,n=t.vol<0,l=function(e,t,l){return r.a.createElement("button",{className:"btn btn-dark p-3",disabled:n,onClick:function(){return function(e){var t,n,r,l;return s.a.async((function(o){for(;;)switch(o.prev=o.next){case 0:return o.next=2,s.a.awrap(_(e));case 2:t=o.sent,n=t.data,"vol",r="".concat(a("volume"),": ").concat(n,"%"),l={toastId:"vol",autoClose:T.d},w.b.isActive("vol")?w.b.update("vol",{render:r}):w.b.info(r,l);case 8:case"end":return o.stop()}}))}(e)},"aria-label":l},r.a.createElement(y.a,{icon:t}))};return r.a.createElement("div",{className:"player-controls btn-group w-100"},l("-5",E.e,"Volume down"),l("+5",E.f,"Volume up"),r.a.createElement(R,{playing:t.playing}))}))),I=(a(82),function(){var e=document.querySelector("nav").clientHeight;document.querySelector(".player").style.top=e+24+"px"}),B=function(e){var t=e.playerStatus;return Object(n.useEffect)(I,[t]),r.a.createElement("div",{className:"player p-2"},r.a.createElement(g,{playerStatus:t}),r.a.createElement(A,{playerStatus:t}))},F=Object(b.c)()((function(e){var t=e.value,a=e.onChange,n=e.t;return r.a.createElement("input",{className:"form-control mb-4",type:"text",id:"query",placeholder:n("search"),autoComplete:"off",value:t,onChange:function(e){return a(e.target.value)},onFocus:function(){return a("")}})})),U=(a(83),function(){return r.a.createElement("div",{className:"loader"},r.a.createElement("div",{className:"spinner-border",role:"status"},r.a.createElement("span",{className:"sr-only"},"Loading...")))}),q="".concat("/api","/streams");a(84);var M=function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={streams:{},loading:!0,query:""},a.isPlaying=function(e){return e===a.props.playerStatus.title&&a.props.playerStatus.playing},a.handleItemClick=function(e){var t,n,r;return s.a.async((function(l){for(;;)switch(l.prev=l.next){case 0:if(t=a.props,n=t.t,r=t.onBackdrop,!a.isPlaying(e)){l.next=3;break}return l.abrupt("return");case 3:return r(n("tunning"),e),l.prev=4,l.next=7,s.a.awrap(L(e));case 7:l.next=12;break;case 9:l.prev=9,l.t0=l.catch(4),l.t0.response&&400===l.t0.response.status&&w.b.error(n("errorNotFound"));case 12:case"end":return l.stop()}}),null,null,[[4,9]])},a.getItemClasses=function(e){var t="streams__item ellipsis list-group-item ";return a.isPlaying(e)?t+"active":t+"list-group-item-action"},a.handleSearch=function(e){a.setState({query:e})},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){var e,t;return s.a.async((function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,s.a.awrap(S.get(q));case 2:e=a.sent,t=e.data,this.setState({streams:t,loading:!1});case 5:case"end":return a.stop()}}),null,this)}},{key:"filteredStreams",value:function(){var e=this.state,t=e.streams,a=e.query;if(""===a)return t;var n={};for(var r in t)r.toLowerCase().includes(a.toLowerCase())&&!t[r]&&(n[r]=t[r]);return n}},{key:"renderList",value:function(){var e=this,t=this.filteredStreams();return 0===Object.keys(t).length?r.a.createElement("h4",{className:"p-4"},this.props.t("noStreams")):r.a.createElement("ul",{className:"list-group list-group-flush"},Object.keys(t).map((function(a){return t[a]?r.a.createElement("li",{className:"streams__header ellipsis",key:a},a):r.a.createElement("li",{className:e.getItemClasses(a),key:a,onClick:function(){return e.handleItemClick(a)}},a)})))}},{key:"render",value:function(){return this.state.loading?r.a.createElement(U,null):r.a.createElement("div",{className:"streams p-2"},r.a.createElement(F,{value:this.state.query,onChange:this.handleSearch}),this.renderList())}}]),t}(n.Component),D=Object(b.c)()(M),Y=(a(85),function(e){var t=e.playerStatus,a=t.title,n=t.playing;return r.a.createElement("div",{className:"mini-player p-3"},r.a.createElement("div",{className:"mini-player__left ellipsis pr-3"},r.a.createElement(y.a,{icon:E.b,className:"drawer__toggler mr-3"}),r.a.createElement("span",null,a)),r.a.createElement(R,{playing:n}))}),V=(a(86),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={open:!1},a.toggle=function(){a.setState({open:!a.state.open})},a.handleClick=function(e){var t=e.target;"BUTTON"===t.tagName||"path"===t.tagName||a.toggle()},a.handleTouchStart=function(e){a.setState({touchStartY:e.touches[0].clientY})},a.handleTouchMove=function(e){var t=a.state,n=t.open,r=t.touchStartY,l=e.changedTouches[0].clientY,o=l<r;l>r&&n?a.toggle():o&&!n&&a.toggle()},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"render",value:function(){var e=this.props.playerStatus,t="drawer fixed-bottom bg-secondary border-top border-secondary";return this.state.open?(t+=" drawer--open",document.body.classList.add("body--drawer")):document.body.classList.remove("body--drawer"),r.a.createElement("div",{className:t,onClick:this.handleClick,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},this.state.open?r.a.createElement(r.a.Fragment,null,r.a.createElement(y.a,{icon:E.a,className:"drawer__toggler fa-lg mb-3"}),r.a.createElement(B,{playerStatus:e})," "):r.a.createElement(Y,{playerStatus:e}))}}]),t}(n.Component)),W=function(e){var t=e.id,a=e.title,n=e.footer,l=e.children;return r.a.createElement("div",{className:"modal fade",id:t,tabIndex:"-1",role:"dialog","aria-labelledby":"staticBackdropLabel","aria-hidden":"true"},r.a.createElement("div",{className:"modal-dialog",role:"document"},r.a.createElement("div",{className:"modal-content"},r.a.createElement("div",{className:"modal-header"},r.a.createElement("h5",{className:"modal-title",id:"staticBackdropLabel"},a),r.a.createElement("button",{type:"button",className:"close","data-dismiss":"modal","aria-label":"Close"},r.a.createElement("span",{"aria-hidden":"true"},"\xd7"))),r.a.createElement("div",{className:"modal-body"},l),r.a.createElement("div",{className:"modal-footer"},n))))},J=a(43),K=function(e){var t=e.id,a=e.label,n=e.row,l=e.data,o=Object(J.a)(e,["id","label","row","data"]);return r.a.createElement("div",{className:n?"form-group row":"form-group"},r.a.createElement("label",{htmlFor:t,className:"col-sm-2 col-form-label"},a),r.a.createElement("div",{className:"col-sm-10"},r.a.createElement("select",Object.assign({id:t,className:"form-control"},o),l.map((function(e){return r.a.createElement("option",{key:e.id||e,value:e.id||e},e.name||e)})))))},G="darkly",H="theme",$=function(e){return"https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/".concat(e,"/bootstrap.min.css")},z=[{id:"darkly",name:"Darkly",themeColor:"#375a7f"},{id:"lux",name:"Lux",themeColor:"#1a1a1a"},{id:"litera",name:"Litera",themeColor:"#4582ec"}];function Q(){var e=X(),t=$(e),a=z.find((function(t){return t.id===e})).themeColor;document.querySelector('link[title="theme"]').setAttribute("href",t),document.querySelector('meta[name="theme-color"]').setAttribute("content",a)}function X(){var e=localStorage.getItem(H);return""===e?G:0===z.filter((function(t){return t.id===e})).length?(localStorage.removeItem(H),G):e}var Z=a(21),ee=Object(b.c)()((function(e){var t=e.t;return r.a.createElement(W,{id:"settings",title:t("settings"),footer:r.a.createElement("button",{className:"btn btn-secondary","data-dismiss":"modal"},t("close"))},r.a.createElement(K,{id:"theme-select",label:t("theme"),data:z,row:!0,value:X(),onChange:function(e){var t;t=e.target.value,localStorage.setItem(H,t),Q()}}),r.a.createElement(K,{id:"language-select",label:t("language"),data:T.b,row:!0,value:Z.a.language,onChange:function(e){Z.a.changeLanguage(e.target.value)}}),r.a.createElement("hr",{className:"mt-5"}),r.a.createElement("p",{className:"small"},"Copyright \xa9 2017-2019\xa0",r.a.createElement("a",{href:"https://rafaelc.org/",target:"_blank",rel:"noopener noreferrer",style:{color:"inherit"}},"Rafael Cavalcanti")))})),te=a(22),ae=Object(b.c)()((function(e){var t=e.t,a=Object(n.useState)(""),l=Object(te.a)(a,2),o=l[0],c=l[1],i=function(){""!==o&&(u(o),c(""))},u=function(e){var a,n;return s.a.async((function(r){for(;;)switch(r.prev=r.next){case 0:return a={type:w.b.TYPE.ERROR,render:t("errorNotFound")},n=Object(w.b)(t("tryingURL")),r.prev=2,r.next=5,s.a.awrap(P(e));case 5:r.next=10;break;case 7:r.prev=7,r.t0=r.catch(2),r.t0.response&&400===r.t0.response.status&&w.b.update(n,a);case 10:case"end":return r.stop()}}),null,null,[[2,7]])};return r.a.createElement(W,{id:"url-dialog",title:t("playURL"),footer:r.a.createElement(r.a.Fragment,null,r.a.createElement("button",{className:"btn btn-primary","data-dismiss":"modal",onClick:i},"OK"),r.a.createElement("button",{className:"btn btn-secondary",onClick:function(){return c("")},"data-dismiss":"modal"},"Cancel"))},r.a.createElement("input",{className:"form-control mb-4",type:"text",placeholder:"URL",value:o,onChange:function(e){var t=e.target;c(t.value)},onKeyDown:function(e){"Enter"===e.key&&i()}}))})),ne=(a(87),function(e){var t=e.title,a=e.body,n=document.body.classList.contains("body--backdrop");t&&!n?(document.body.style.paddingRight=window.innerWidth-document.documentElement.clientWidth+"px",document.body.classList.add("body--backdrop")):!t&&n&&(document.body.style.paddingRight=0,document.body.classList.remove("body--backdrop"));var l="backdrop p-2 text-white"+(t?" backdrop--visible":"");return r.a.createElement("div",{className:l},r.a.createElement("h3",{className:"text-white"},t),r.a.createElement("h5",{className:"text-white"},a))}),re=(a(88),a(89),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={playerStatus:{},loading:!0,networkError:!1,backdrop:{}},a.handleBackdrop=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";a.setState({backdrop:{title:e,body:t}}),setTimeout((function(){return a.setState({backdrop:{}})}),T.a)},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){this.updatePlayerStatus()}},{key:"updatePlayerStatus",value:function(){var e,t,a=this;return s.a.async((function(n){for(;;)switch(n.prev=n.next){case 0:return n.prev=0,n.next=3,s.a.awrap(S.get(C));case 3:e=n.sent,t=e.data,this.setState({playerStatus:t,loading:!1,networkError:!1}),n.next=11;break;case 8:n.prev=8,n.t0=n.catch(0),this.setState({loading:!1,networkError:!0});case 11:setTimeout((function(){return a.updatePlayerStatus()}),T.c);case 12:case"end":return n.stop()}}),null,this,[[0,8]])}},{key:"render",value:function(){var e=this.state,t=e.loading,a=e.networkError,n=e.backdrop,l=e.playerStatus,o=this.props.t;return a?r.a.createElement(ne,{title:o("errorNetwork")}):t?r.a.createElement(U,null):l.con_mpd?r.a.createElement("div",{className:"app"},r.a.createElement(w.a,null),r.a.createElement(ne,{title:n.title,body:n.body}),r.a.createElement(h,null),r.a.createElement(V,{playerStatus:l}),r.a.createElement("main",{className:"app-main p-4"},r.a.createElement(B,{playerStatus:l}),r.a.createElement(D,{onBackdrop:this.handleBackdrop,playerStatus:l}),r.a.createElement(ee,null),r.a.createElement(ae,null))):r.a.createElement(ne,{title:o("disconnectedMPD")})}}]),t}(n.Component)),le=Object(b.c)()(re);Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));var oe=a(44),ce=a(45);Z.a.use(oe.a).use(ce.a).use(b.b).init({fallbackLng:"en",debug:Object({NODE_ENV:"production",PUBLIC_URL:"",REACT_APP_API_URL:"/api"}).REACT_APP_I18N_DEBUG,interpolation:{escapeValue:!1}});Z.a,a(90),a(91),a(92);Q(),o.a.render(r.a.createElement(n.Suspense,{fallback:r.a.createElement(U,null)},r.a.createElement(le,null)),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((function(e){e.unregister()}))}},[[46,1,2]]]);
|
2
|
+
//# sourceMappingURL=main.5341431d.chunk.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["logo.svg","components/navBar.jsx","components/playerStatus.jsx","services/httpService.js","services/playerService.js","components/playStopControl.jsx","components/playerControls.jsx","components/player.jsx","components/common/searchBox.jsx","components/loader.jsx","services/streamsService.js","components/streams.jsx","components/miniPlayer.jsx","components/drawer.jsx","components/common/modal.jsx","components/common/select.jsx","theme.js","components/settings.jsx","components/urlDialog.jsx","components/backdrop.jsx","utils/style.js","App.js","serviceWorker.js","i18n.js","index.js"],"names":["module","exports","withTranslation","t","className","href","src","logo","width","height","alt","type","data-toggle","data-target","aria-controls","aria-expanded","aria-label","id","playerStatus","playing","title","axios","interceptors","response","use","error","expectedError","status","networkError","toast","i18n","Promise","reject","get","post","apiEndpoint","process","body","method","params","FormData","set","play","http","stop","changeVol","delta","playRadio","name","playURL","url","PlayStopControl","onClick","icon","faStop","faPlay","volDisabled","vol","renderVolButton","label","disabled","a","async","data","toastMsg","toastOpts","toastId","autoClose","volTimeout","isActive","update","render","info","handleVolChange","faVolumeDown","faVolumeUp","setPlayerTop","navHeight","document","querySelector","clientHeight","style","top","Player","useEffect","value","onChange","placeholder","autoComplete","e","target","onFocus","Loader","role","Streams","state","streams","loading","query","isPlaying","props","handleItemClick","onBackdrop","getItemClasses","classes","handleSearch","setState","this","filtered","k","toLowerCase","includes","filteredStreams","Object","keys","length","map","key","renderList","Component","MiniPlayer","faChevronUp","Drawer","open","toggle","handleClick","tagName","handleTouchStart","touchStartY","touches","clientY","handleTouchMove","touchEndY","changedTouches","touchUp","classList","add","remove","onTouchStart","onTouchMove","Fragment","faChevronDown","Modal","footer","children","tabIndex","aria-labelledby","aria-hidden","data-dismiss","Select","row","rest","htmlFor","d","DEFAULT_THEME","STORAGE_KEY","getThemePath","themeId","themes","themeColor","applyTheme","getThemeId","themePath","find","setAttribute","localId","localStorage","getItem","filter","removeItem","setItem","languages","i18next","language","changeLanguage","rel","color","useState","setURL","handleOK","doPlayURL","errorToastOpts","TYPE","ERROR","input","onKeyDown","Backdrop","bodyStyled","contains","paddingRight","window","innerWidth","documentElement","clientWidth","App","backdrop","handleBackdrop","setTimeout","backdropTimeout","updatePlayerStatus","updateInterval","con_mpd","Boolean","location","hostname","match","Backend","LanguageDetector","initReactI18next","init","fallbackLng","debug","REACT_APP_I18N_DEBUG","interpolation","escapeValue","ReactDOM","fallback","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.5341431d.chunk.js","sourcesContent":["module.exports = __webpack_public_path__ + \"static/media/logo.91554ce9.svg\";","import React from 'react';\nimport { withTranslation } from 'react-i18next';\nimport './navBar.scss';\nimport logo from '../logo.svg';\n\nconst NavBar = ({ t }) => (\n <nav className=\"navbar navbar-expand-sm navbar-dark bg-primary \">\n <a className=\"navbar-brand\" href=\"/\">\n <img\n src={logo}\n width=\"30\"\n height=\"30\"\n className=\"d-inline-block align-text-bottom mr-2\"\n alt=\"\"\n />\n PiFi Radio\n </a>\n <button\n className=\"navbar-toggler\"\n type=\"button\"\n data-toggle=\"collapse\"\n data-target=\"#navbarNavAltMarkup\"\n aria-controls=\"navbarNavAltMarkup\"\n aria-expanded=\"false\"\n aria-label=\"Toggle navigation\"\n >\n <span className=\"navbar-toggler-icon\"></span>\n </button>\n <div className=\"collapse navbar-collapse\" id=\"navbarNavAltMarkup\">\n <div className=\"navbar-nav pt-1 ml-2\">\n {/* We use buttons and different class names instead of Bootstrap's\n anchor, so we don't get a warning for href=\"#\". */}\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#url-dialog\"\n >\n {t('playURL')}\n </button>\n\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#settings\"\n >\n {t('settings')}\n </button>\n </div>\n </div>\n </nav>\n);\nexport default withTranslation()(NavBar);\n","import React from 'react';\nimport { withTranslation } from 'react-i18next';\n\nconst PlayerStatus = ({ playerStatus, t }) => {\n const { playing, title } = playerStatus;\n\n return (\n <div className=\"text-center\">\n <h5 className=\"small text-uppercase\">\n {playing ? t('playing') : t('stopped')}\n </h5>\n <h3 className=\"ellipsis\">{title}</h3>\n </div>\n );\n};\n\nexport default withTranslation()(PlayerStatus);\n","import React from 'react';\nimport axios from 'axios';\nimport { toast } from 'react-toastify';\nimport { Translation } from 'react-i18next';\n\naxios.interceptors.response.use(null, error => {\n const expectedError =\n error.response &&\n error.response.status >= 400 &&\n error.response.status < 500;\n\n // We already alert about these with a backdrop on App.js\n const networkError = !error.response;\n\n // Ugly, but i18n only worked this way\n if (!expectedError && !networkError) {\n toast.error(\n <Translation>\n {(t, { i18n }) => <p>{t('errorUnexpected')}</p>}\n </Translation>\n );\n }\n // This expected error is universal in our app, so we'll place it here\n if (error.response && error.response.status === 403)\n toast.error(\n <Translation>{(t, { i18n }) => <p>{t('errorForbidden')}</p>}</Translation>\n );\n\n return Promise.reject(error);\n});\n\nexport default {\n get: axios.get,\n post: axios.post\n};\n","import http from './httpService.js';\n\nconst apiEndpoint = `${process.env.REACT_APP_API_URL}/player`;\n\nfunction body(method, params = null) {\n const body = new FormData();\n body.set('method', method);\n if (params) body.set('params', params);\n return body;\n}\n\nexport function getStatus() {\n return http.get(apiEndpoint);\n}\n\nexport function play() {\n return http.post(apiEndpoint, body('play'));\n}\n\nexport function stop() {\n return http.post(apiEndpoint, body('stop'));\n}\n\nexport function changeVol(delta) {\n return http.post(apiEndpoint, body('change_vol', delta));\n}\n\nexport function playRadio(name) {\n return http.post(apiEndpoint, body('play_radios', name));\n}\n\nexport function playURL(url) {\n return http.post(apiEndpoint, body('play_urls', url));\n}\n","import React from 'react';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faStop, faPlay } from '@fortawesome/free-solid-svg-icons';\nimport { play, stop } from '../services/playerService';\n\nconst PlayStopControl = ({ playing }) =>\n playing ? (\n <button className=\"btn btn-danger\" onClick={stop} aria-label=\"Stop\">\n <FontAwesomeIcon icon={faStop} />\n </button>\n ) : (\n <button className=\"btn btn-dark\" onClick={play} aria-label=\"Play\">\n <FontAwesomeIcon icon={faPlay} />\n </button>\n );\n\nexport default PlayStopControl;\n","import React from 'react';\nimport PlayStopControl from './playStopControl';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faVolumeDown, faVolumeUp } from '@fortawesome/free-solid-svg-icons';\nimport { toast } from 'react-toastify';\nimport { withTranslation } from 'react-i18next';\nimport { changeVol } from '../services/playerService';\nimport { volTimeout } from '../config.json';\nimport './playerControls.scss';\n\nconst PlayerControls = ({ playerStatus, t }) => {\n const volDisabled = playerStatus.vol < 0;\n\n const handleVolChange = async delta => {\n const { data: vol } = await changeVol(delta);\n\n const toastId = 'vol';\n const toastMsg = `${t('volume')}: ${vol}%`;\n const toastOpts = { toastId, autoClose: volTimeout };\n if (toast.isActive(toastId)) toast.update(toastId, { render: toastMsg });\n else toast.info(toastMsg, toastOpts);\n };\n\n const renderVolButton = (delta, icon, label) => (\n <button\n className=\"btn btn-dark p-3\"\n disabled={volDisabled}\n onClick={() => handleVolChange(delta)}\n aria-label={label}\n >\n <FontAwesomeIcon icon={icon} />\n </button>\n );\n\n return (\n <div className=\"player-controls btn-group w-100\">\n {renderVolButton('-5', faVolumeDown, 'Volume down')}\n {renderVolButton('+5', faVolumeUp, 'Volume up')}\n <PlayStopControl playing={playerStatus.playing} />\n </div>\n );\n};\n\nexport default withTranslation()(PlayerControls);\n","import React, { useEffect } from 'react';\nimport PlayerStatus from './playerStatus';\nimport PlayerControls from './playerControls';\nimport './player.scss';\n\nconst Player = ({ playerStatus }) => {\n useEffect(setPlayerTop, [playerStatus]);\n\n return (\n <div className=\"player p-2\">\n <PlayerStatus playerStatus={playerStatus} />\n <PlayerControls playerStatus={playerStatus} />\n </div>\n );\n};\n\n// Calculate and set top for Player\n// Needed for desktop view with sticky\nexport const setPlayerTop = () => {\n const navHeight = document.querySelector('nav').clientHeight;\n const topMargin = 24;\n document.querySelector('.player').style.top = navHeight + topMargin + 'px';\n};\n\nexport default Player;\n","import React from 'react';\nimport { withTranslation } from 'react-i18next';\n\nconst SearchBox = ({ value, onChange, t }) => {\n return (\n <input\n className=\"form-control mb-4\"\n type=\"text\"\n id=\"query\"\n placeholder={t('search')}\n autoComplete=\"off\"\n value={value}\n onChange={e => onChange(e.target.value)}\n onFocus={() => onChange('')}\n ></input>\n );\n};\n\nexport default withTranslation()(SearchBox);\n","import React from 'react';\nimport './loader.scss';\n\nconst Loader = () => (\n <div className=\"loader\">\n <div className=\"spinner-border\" role=\"status\">\n <span className=\"sr-only\">Loading...</span>\n </div>\n </div>\n);\n\nexport default Loader;\n","import http from './httpService';\n\nconst apiEndpoint = `${process.env.REACT_APP_API_URL}/streams`;\n\nexport function getStreams() {\n return http.get(apiEndpoint);\n}\n","import React, { Component } from 'react';\nimport SearchBox from './common/searchBox';\nimport Loader from './loader';\nimport { withTranslation } from 'react-i18next';\nimport { toast } from 'react-toastify';\nimport { playRadio } from '../services/playerService';\nimport { getStreams } from '../services/streamsService';\nimport './streams.scss';\n\nclass Streams extends Component {\n state = { streams: {}, loading: true, query: '' };\n\n async componentDidMount() {\n const { data: streams } = await getStreams();\n this.setState({ streams, loading: false });\n }\n\n isPlaying = name => {\n return (\n name === this.props.playerStatus.title && this.props.playerStatus.playing\n );\n };\n\n handleItemClick = async name => {\n const { t, onBackdrop } = this.props;\n\n if (this.isPlaying(name)) return;\n\n onBackdrop(t('tunning'), name);\n try {\n await playRadio(name);\n } catch (ex) {\n if (ex.response && ex.response.status === 400)\n toast.error(t('errorNotFound'));\n }\n };\n\n getItemClasses = name => {\n const classes = 'streams__item ellipsis list-group-item ';\n return this.isPlaying(name)\n ? classes + 'active'\n : classes + 'list-group-item-action';\n };\n\n handleSearch = query => {\n this.setState({ query });\n };\n\n filteredStreams() {\n const { streams, query } = this.state;\n\n if (query === '') return streams;\n\n let filtered = {};\n for (let k in streams) {\n if (k.toLowerCase().includes(query.toLowerCase()) && !streams[k]) {\n filtered[k] = streams[k];\n }\n }\n return filtered;\n }\n\n renderList() {\n const streams = this.filteredStreams();\n\n if (Object.keys(streams).length === 0)\n return <h4 className=\"p-4\">{this.props.t('noStreams')}</h4>;\n\n return (\n <ul className=\"list-group list-group-flush\">\n {Object.keys(streams).map(name =>\n streams[name] ? (\n <li className=\"streams__header ellipsis\" key={name}>\n {name}\n </li>\n ) : (\n <li\n className={this.getItemClasses(name)}\n key={name}\n onClick={() => this.handleItemClick(name)}\n >\n {name}\n </li>\n )\n )}\n </ul>\n );\n }\n\n render() {\n if (this.state.loading) return <Loader />;\n\n return (\n <div className=\"streams p-2\">\n <SearchBox value={this.state.query} onChange={this.handleSearch} />\n {this.renderList()}\n </div>\n );\n }\n}\n\nexport default withTranslation()(Streams);\n","import React from 'react';\nimport PlayStopControl from './playStopControl';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp } from '@fortawesome/free-solid-svg-icons';\nimport './miniPlayer.scss';\n\nconst MiniPlayer = ({ playerStatus }) => {\n const { title, playing } = playerStatus;\n return (\n <div className=\"mini-player p-3\">\n <div className=\"mini-player__left ellipsis pr-3\">\n <FontAwesomeIcon icon={faChevronUp} className=\"drawer__toggler mr-3\" />\n <span>{title}</span>\n </div>\n <PlayStopControl playing={playing} />\n </div>\n );\n};\n\nexport default MiniPlayer;\n","import React, { Component } from 'react';\nimport MiniPlayer from './miniPlayer';\nimport Player from './player';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown } from '@fortawesome/free-solid-svg-icons';\nimport './drawer.scss';\n\nclass Drawer extends Component {\n state = { open: false };\n\n toggle = () => {\n this.setState({ open: !this.state.open });\n };\n\n handleClick = ({ target }) => {\n const blackList = target.tagName === 'BUTTON' || target.tagName === 'path';\n if (blackList) return;\n\n this.toggle();\n };\n\n handleTouchStart = e => {\n this.setState({ touchStartY: e.touches[0].clientY });\n };\n\n handleTouchMove = e => {\n const { open, touchStartY } = this.state;\n const touchEndY = e.changedTouches[0].clientY;\n const touchUp = touchEndY < touchStartY;\n const touchDown = touchEndY > touchStartY;\n\n if (touchDown && open) this.toggle();\n else if (touchUp && !open) this.toggle();\n };\n\n render() {\n const { playerStatus } = this.props;\n let classes =\n 'drawer fixed-bottom bg-secondary border-top border-secondary';\n\n if (this.state.open) {\n classes += ' drawer--open';\n document.body.classList.add('body--drawer');\n } else document.body.classList.remove('body--drawer');\n\n return (\n <div\n className={classes}\n onClick={this.handleClick}\n onTouchStart={this.handleTouchStart}\n onTouchMove={this.handleTouchMove}\n >\n {this.state.open ? (\n <React.Fragment>\n <FontAwesomeIcon\n icon={faChevronDown}\n className=\"drawer__toggler fa-lg mb-3\"\n />\n <Player playerStatus={playerStatus} />{' '}\n </React.Fragment>\n ) : (\n <MiniPlayer playerStatus={playerStatus} />\n )}\n </div>\n );\n }\n}\n\nexport default Drawer;\n","import React from 'react';\n\nconst Modal = ({ id, title, footer, children }) => {\n return (\n <div\n className=\"modal fade\"\n id={id}\n tabIndex=\"-1\"\n role=\"dialog\"\n aria-labelledby=\"staticBackdropLabel\"\n aria-hidden=\"true\"\n >\n <div className=\"modal-dialog\" role=\"document\">\n <div className=\"modal-content\">\n <div className=\"modal-header\">\n <h5 className=\"modal-title\" id=\"staticBackdropLabel\">\n {title}\n </h5>\n <button\n type=\"button\"\n className=\"close\"\n data-dismiss=\"modal\"\n aria-label=\"Close\"\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n <div className=\"modal-body\">{children}</div>\n <div className=\"modal-footer\">{footer}</div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default Modal;\n","import React from 'react';\n\nconst Select = ({ id, label, row, data, ...rest }) => (\n <div className={row ? 'form-group row' : 'form-group'}>\n <label htmlFor={id} className=\"col-sm-2 col-form-label\">\n {label}\n </label>\n <div className=\"col-sm-10\">\n <select id={id} className=\"form-control\" {...rest}>\n {data.map(d => (\n <option key={d.id || d} value={d.id || d}>\n {d.name || d}\n </option>\n ))}\n </select>\n </div>\n </div>\n);\n\nexport default Select;\n","const DEFAULT_THEME = 'darkly';\n\nconst STORAGE_KEY = 'theme';\n\nconst getThemePath = themeId =>\n `https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/${themeId}/bootstrap.min.css`;\n\nexport const themes = [\n { id: 'darkly', name: 'Darkly', themeColor: '#375a7f' },\n { id: 'lux', name: 'Lux', themeColor: '#1a1a1a' },\n { id: 'litera', name: 'Litera', themeColor: '#4582ec' }\n];\n\nexport function changeTheme(themeId) {\n localStorage.setItem(STORAGE_KEY, themeId);\n applyTheme();\n}\n\nexport function applyTheme() {\n const themeId = getThemeId();\n const themePath = getThemePath(themeId);\n const themeColor = themes.find(t => t.id === themeId).themeColor;\n\n const linkEl = document.querySelector('link[title=\"theme\"]');\n linkEl.setAttribute('href', themePath);\n\n const metaEl = document.querySelector('meta[name=\"theme-color\"]');\n metaEl.setAttribute('content', themeColor);\n}\n\nexport function getThemeId() {\n const localId = localStorage.getItem(STORAGE_KEY);\n\n if (localId === '') return DEFAULT_THEME;\n if (themes.filter(t => t.id === localId).length === 0) {\n localStorage.removeItem(STORAGE_KEY);\n return DEFAULT_THEME;\n }\n\n return localId;\n}\n","import React from 'react';\nimport Modal from './common/modal';\nimport Select from './common/select';\nimport { getThemeId, changeTheme, themes } from '../theme';\nimport i18next from 'i18next';\nimport { withTranslation } from 'react-i18next';\nimport { languages } from '../config.json';\n\nconst Settings = ({ t }) => {\n const renderFooter = () => (\n <button className=\"btn btn-secondary\" data-dismiss=\"modal\">\n {t('close')}\n </button>\n );\n\n return (\n <Modal id=\"settings\" title={t('settings')} footer={renderFooter()}>\n <Select\n id=\"theme-select\"\n label={t('theme')}\n data={themes}\n row\n value={getThemeId()}\n onChange={e => {\n changeTheme(e.target.value);\n }}\n />\n <Select\n id=\"language-select\"\n label={t('language')}\n data={languages}\n row\n value={i18next.language}\n onChange={e => {\n i18next.changeLanguage(e.target.value);\n }}\n />\n <hr className=\"mt-5\" />\n <p className=\"small\">\n Copyright © 2017-2019 \n {/* Use the default color, as some themes give a different color for links. */}\n <a\n href=\"https://rafaelc.org/\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{ color: 'inherit' }}\n >\n Rafael Cavalcanti\n </a>\n </p>\n </Modal>\n );\n};\n\nexport default withTranslation()(Settings);\n","import React, { useState } from 'react';\nimport Modal from './common/modal';\nimport { toast } from 'react-toastify';\nimport { playURL } from '../services/playerService';\nimport { withTranslation } from 'react-i18next';\n\nconst URLDialog = ({ t }) => {\n const [url, setURL] = useState('');\n\n const handleOK = () => {\n if (url === '') return;\n doPlayURL(url);\n setURL('');\n };\n\n const doPlayURL = async url => {\n const errorToastOpts = {\n type: toast.TYPE.ERROR,\n render: t('errorNotFound')\n };\n\n const toastId = toast(t('tryingURL'));\n\n try {\n await playURL(url);\n } catch (ex) {\n if (ex.response && ex.response.status === 400)\n toast.update(toastId, errorToastOpts);\n }\n };\n\n const handleChange = ({ target: input }) => {\n setURL(input.value);\n };\n\n const handleKeyDown = e => {\n if (e.key === 'Enter') handleOK();\n };\n\n const renderFooter = () => (\n <React.Fragment>\n <button\n className=\"btn btn-primary\"\n data-dismiss=\"modal\"\n onClick={handleOK}\n >\n OK\n </button>\n <button\n className=\"btn btn-secondary\"\n onClick={() => setURL('')}\n data-dismiss=\"modal\"\n >\n Cancel\n </button>\n </React.Fragment>\n );\n\n return (\n <Modal id=\"url-dialog\" title={t('playURL')} footer={renderFooter()}>\n <input\n className=\"form-control mb-4\"\n type=\"text\"\n placeholder=\"URL\"\n value={url}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n />\n </Modal>\n );\n};\n\nexport default withTranslation()(URLDialog);\n","import React from 'react';\nimport { getScrollbarWidth } from '../utils/style';\nimport './backdrop.scss';\n\nconst Backdrop = ({ title, body }) => {\n const bodyStyled = document.body.classList.contains('body--backdrop');\n\n if (title && !bodyStyled) {\n // Calculate this BEFORE adding the class.\n document.body.style.paddingRight = getScrollbarWidth() + 'px';\n document.body.classList.add('body--backdrop');\n // We need this check because Bootstrap modals also style paddingRight\n } else if (!title && bodyStyled) {\n document.body.style.paddingRight = 0;\n document.body.classList.remove('body--backdrop');\n }\n\n const classes =\n 'backdrop p-2 text-white' + (title ? ' backdrop--visible' : '');\n\n // We need text-white on h* tags because some themes override it\n return (\n <div className={classes}>\n <h3 className=\"text-white\">{title}</h3>\n <h5 className=\"text-white\">{body}</h5>\n </div>\n );\n};\n\nexport default Backdrop;\n","export const getScrollbarWidth = () =>\n window.innerWidth - document.documentElement.clientWidth;\n","import React, { Component } from 'react';\nimport NavBar from './components/navBar';\nimport Player from './components/player';\nimport Streams from './components/streams';\nimport Drawer from './components/drawer';\nimport Settings from './components/settings';\nimport URLDialog from './components/urlDialog';\nimport Loader from './components/loader';\nimport Backdrop from './components/backdrop';\nimport { ToastContainer } from 'react-toastify';\nimport { getStatus } from './services/playerService';\nimport { withTranslation } from 'react-i18next';\nimport { updateInterval, backdropTimeout } from './config.json';\nimport './App.scss';\nimport 'react-toastify/dist/ReactToastify.css';\n\nclass App extends Component {\n state = {\n playerStatus: {},\n loading: true,\n networkError: false,\n backdrop: {}\n };\n\n componentDidMount() {\n this.updatePlayerStatus();\n }\n\n async updatePlayerStatus() {\n try {\n const { data: playerStatus } = await getStatus();\n this.setState({ playerStatus, loading: false, networkError: false });\n } catch (ex) {\n this.setState({ loading: false, networkError: true });\n }\n\n setTimeout(() => this.updatePlayerStatus(), updateInterval);\n }\n\n handleBackdrop = (title, body = '') => {\n this.setState({ backdrop: { title, body } });\n setTimeout(() => this.setState({ backdrop: {} }), backdropTimeout);\n };\n\n render() {\n const { loading, networkError, backdrop, playerStatus } = this.state;\n const { t } = this.props;\n\n if (networkError) return <Backdrop title={t('errorNetwork')} />;\n if (loading) return <Loader />;\n if (!playerStatus.con_mpd) return <Backdrop title={t('disconnectedMPD')} />;\n\n return (\n <div className=\"app\">\n <ToastContainer />\n <Backdrop title={backdrop.title} body={backdrop.body} />\n <NavBar />\n <Drawer playerStatus={playerStatus} />\n\n <main className=\"app-main p-4\">\n <Player playerStatus={playerStatus} />\n <Streams\n onBackdrop={this.handleBackdrop}\n playerStatus={playerStatus}\n />\n <Settings />\n <URLDialog />\n </main>\n </div>\n );\n }\n}\n\nexport default withTranslation()(App);\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' }\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\n\nimport Backend from 'i18next-xhr-backend';\nimport LanguageDetector from 'i18next-browser-languagedetector';\n// not like to use this?\n// have a look at the Quick start guide\n// for passing in lng and translations on init\n\ni18n\n // load translation using xhr -> see /public/locales\n // learn more: https://github.com/i18next/i18next-xhr-backend\n .use(Backend)\n // detect user language\n // learn more: https://github.com/i18next/i18next-browser-languageDetector\n .use(LanguageDetector)\n // pass the i18n instance to react-i18next.\n .use(initReactI18next)\n // init i18next\n // for all options read: https://www.i18next.com/overview/configuration-options\n .init({\n fallbackLng: 'en',\n debug: process.env.REACT_APP_I18N_DEBUG,\n\n interpolation: {\n escapeValue: false // not needed for react as it escapes by default\n }\n });\n\nexport default i18n;\n","import React, { Suspense } from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\nimport Loader from './components/loader';\nimport './i18n';\nimport { applyTheme } from './theme';\nimport './index.scss';\n\n// Bootstrap\n//import 'bootstrap/dist/css/bootstrap.css';\n//import 'bootswatch/dist/flatly/bootstrap.min.css'; // This replaces default Bootstrap file\nimport 'jquery/dist/jquery.min.js';\nimport 'bootstrap/dist/js/bootstrap.js';\n\napplyTheme();\n\nReactDOM.render(\n // Suspense needed for i18n\n <Suspense fallback={<Loader />}>\n <App />\n </Suspense>,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""}
|
data/lib/pifi/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pifi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rafael Cavalcanti
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
@@ -122,19 +122,19 @@ files:
|
|
122
122
|
- lib/pifi/public/mstile-310x150.png
|
123
123
|
- lib/pifi/public/mstile-310x310.png
|
124
124
|
- lib/pifi/public/mstile-70x70.png
|
125
|
-
- lib/pifi/public/precache-manifest.
|
125
|
+
- lib/pifi/public/precache-manifest.b2ad228b5bcabcdf0fb713c92d5fd6ef.js
|
126
126
|
- lib/pifi/public/robots.txt
|
127
127
|
- lib/pifi/public/safari-pinned-tab.svg
|
128
128
|
- lib/pifi/public/service-worker.js
|
129
129
|
- lib/pifi/public/static/css/2.5dbdccff.chunk.css
|
130
130
|
- lib/pifi/public/static/css/2.5dbdccff.chunk.css.map
|
131
|
-
- lib/pifi/public/static/css/main.
|
132
|
-
- lib/pifi/public/static/css/main.
|
131
|
+
- lib/pifi/public/static/css/main.b630c694.chunk.css
|
132
|
+
- lib/pifi/public/static/css/main.b630c694.chunk.css.map
|
133
133
|
- lib/pifi/public/static/js/2.edc56e6f.chunk.js
|
134
134
|
- lib/pifi/public/static/js/2.edc56e6f.chunk.js.LICENSE
|
135
135
|
- lib/pifi/public/static/js/2.edc56e6f.chunk.js.map
|
136
|
-
- lib/pifi/public/static/js/main.
|
137
|
-
- lib/pifi/public/static/js/main.
|
136
|
+
- lib/pifi/public/static/js/main.5341431d.chunk.js
|
137
|
+
- lib/pifi/public/static/js/main.5341431d.chunk.js.map
|
138
138
|
- lib/pifi/public/static/js/runtime-main.f04a0f25.js
|
139
139
|
- lib/pifi/public/static/js/runtime-main.f04a0f25.js.map
|
140
140
|
- lib/pifi/public/static/media/logo.91554ce9.svg
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["navBar.scss","playerControls.scss","player.scss","loader.scss","streams.scss","miniPlayer.scss","drawer.scss","../variables.scss","backdrop.scss","App.scss","index.scss"],"names":[],"mappings":"AAAA,QACE,iCAA2B,CAA3B,yBAA2B,CAC3B,KAAM,CACN,SAAU,CCHZ,iBACE,2BAA6B,CAC7B,QAAY,CACZ,OAAW,CACX,YAAa,CCJf,QACE,uBAAgB,CAAhB,eAAgB,CAChB,QAAS,CACT,WAAY,CACZ,gBAAiB,CACjB,WAAY,CACZ,eAAgB,CAChB,6DACA,CAAA,qBAAsB,CCRxB,QACE,UAAW,CACX,YAAa,CACb,sBAAuB,CACvB,kBAAmB,CACnB,YAAa,CCHf,SACE,eAAgB,CAChB,WAAY,CACb,eAGC,cAAe,CADjB,sBAII,cAAe,CAChB,iBAID,gBAAiB,CACjB,cAAe,CACf,qBAAuB,CACxB,YAGC,4BAA8B,CAE9B,yBAHF,YAII,4BAA+C,CAElD,CC3BD,aACE,eAAgB,CAChB,WAAY,CACZ,YAAa,CACb,kBAAmB,CACnB,6BAA8B,CAC9B,WAAY,CCJd,QACE,mBAAqB,CACrB,WCHkB,CDIlB,cAAe,CACf,iBAAkB,CAClB,oCAAA,CAAA,4BAA8B,CAE9B,cACE,WAAY,CACZ,YAAa,CACb,4BAA6B,CAC7B,qBAAsB,CACtB,kBAAmB,CACpB,cAID,eAAgB,CEnBlB,UACE,UAAW,CACX,cAAe,CACf,UAAW,CACX,WAAY,CACZ,qBAAyB,CACzB,SAAU,CACV,iBAAkB,CAClB,+BAAyB,CAAzB,uBAAyB,CACzB,cAAe,CACf,YAAa,CACb,qBAAsB,CACtB,sBAAuB,CACvB,kBAAmB,CACnB,iBAAkB,CAElB,mBACE,WAAa,CACb,kBAAmB,CACpB,gBAID,eAAgB,CCrBlB,UACE,YAAa,CACb,4BAA6B,CAC9B,QAGC,iBAAkB,CAElB,yBAHF,QAII,kBAAmB,CAEtB,CAGC,yBADF,kBAEI,YAAa,CAEhB,CCnBD,KACE,QAAS,CACT,qBAAsB,CACtB,iBAAkB,CACnB,iBAGC,cAAe,CAChB,UAGC,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB","file":"main.a50e54d2.chunk.css","sourcesContent":[".navbar {\n position: sticky !important;\n top: 0;\n z-index: 5;\n}\n",".player-controls {\n position: absolute !important;\n bottom: 0rem;\n right: 0rem;\n padding: 2rem;\n}\n",".player {\n position: sticky;\n top: 4rem; /* we set this dinamically */\n height: 70vh;\n min-height: 500px;\n width: 400px;\n min-width: 300px;\n background: url('../logo.svg') center no-repeat;\n background-size: 120px;\n}\n",".loader {\n z-index: 10;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n}\n","@import '../variables.scss';\n\n.streams {\n min-width: 300px;\n width: 450px;\n}\n\n.streams__item {\n cursor: pointer;\n\n &.active {\n cursor: default;\n }\n}\n\n.streams__header {\n font-size: 1.3rem;\n margin-top: 1em;\n padding: 0.5rem 1.25rem;\n}\n\n.list-group {\n margin-bottom: 1rem !important;\n\n @media (max-width: $mobile-width) {\n margin-bottom: 1rem + $drawer-height !important; /* drawer */\n }\n}\n",".mini-player {\n max-width: 600px;\n margin: auto;\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: 100%;\n}\n","@import '../variables.scss';\n\n.drawer {\n z-index: 5 !important;\n height: $drawer-height;\n cursor: pointer;\n touch-action: none; // don't scroll on touch\n transition: all 0.25s ease-out;\n\n &--open {\n height: 100%;\n display: flex;\n justify-content: space-evenly;\n flex-direction: column;\n align-items: center;\n }\n}\n\n.body--drawer {\n overflow: hidden;\n}\n","$mobile-width: 750px;\n$drawer-height: 5rem;\n",".backdrop {\n z-index: 10;\n position: fixed;\n width: 100%;\n height: 100%;\n background-color: #222222;\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n cursor: default;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n text-align: center;\n\n &--visible {\n opacity: 0.94;\n visibility: visible;\n }\n}\n\n.body--backdrop {\n overflow: hidden;\n}\n","@import './variables.scss';\n\n.app-main {\n display: flex;\n justify-content: space-evenly;\n}\n\n.drawer {\n visibility: hidden;\n\n @media (max-width: $mobile-width) {\n visibility: visible;\n }\n}\n\n.app-main > .player {\n @media (max-width: $mobile-width) {\n display: none;\n }\n}\n","body {\n margin: 0;\n box-sizing: border-box;\n overflow-y: scroll; // so elements don't move if scroll is off\n}\n\nbutton[disabled] {\n cursor: default;\n}\n\n.ellipsis {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n"]}
|
@@ -1,2 +0,0 @@
|
|
1
|
-
(this.webpackJsonppifi_frontend=this.webpackJsonppifi_frontend||[]).push([[0],{18:function(e){e.exports=JSON.parse('{"b":["en","fr-FR","nl-NL","pt-BR"],"c":1000,"a":2500,"d":3000}')},42:function(e,t,a){e.exports=a.p+"static/media/logo.91554ce9.svg"},46:function(e,t,a){e.exports=a(95)},60:function(e,t,a){},81:function(e,t,a){},82:function(e,t,a){},83:function(e,t,a){},84:function(e,t,a){},85:function(e,t,a){},86:function(e,t,a){},87:function(e,t,a){},88:function(e,t,a){},90:function(e,t,a){},95:function(e,t,a){"use strict";a.r(t);var n=a(0),r=a.n(n),l=a(19),o=a.n(l),c=a(6),s=a.n(c),i=a(3),u=a(4),m=a(8),d=a(10),p=a(11),b=a(2),v=(a(60),a(42)),f=a.n(v),h=Object(b.c)()((function(e){var t=e.t;return r.a.createElement("nav",{className:"navbar navbar-expand-sm navbar-dark bg-primary "},r.a.createElement("a",{className:"navbar-brand",href:"/"},r.a.createElement("img",{src:f.a,width:"30",height:"30",className:"d-inline-block align-text-bottom mr-2",alt:""}),"PiFi Radio"),r.a.createElement("button",{className:"navbar-toggler",type:"button","data-toggle":"collapse","data-target":"#navbarNavAltMarkup","aria-controls":"navbarNavAltMarkup","aria-expanded":"false","aria-label":"Toggle navigation"},r.a.createElement("span",{className:"navbar-toggler-icon"})),r.a.createElement("div",{className:"collapse navbar-collapse",id:"navbarNavAltMarkup"},r.a.createElement("div",{className:"navbar-nav pt-1 ml-2"},r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#url-dialog"},t("playURL")),r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#settings"},t("settings")))))})),g=Object(b.c)()((function(e){var t=e.playerStatus,a=e.t,n=t.playing,l=t.title;return r.a.createElement("div",{className:"text-center"},r.a.createElement("h5",{className:"small text-uppercase"},a(n?"playing":"stopped")),r.a.createElement("h3",{className:"ellipsis"},l))})),y=a(15),E=a(14),k=a(27),N=a.n(k),w=a(5);N.a.interceptors.response.use(null,(function(e){var t=e.response&&e.response.status>=400&&e.response.status<500,a=!e.response;return t||a||w.b.error(r.a.createElement(b.a,null,(function(e,t){t.i18n;return r.a.createElement("p",null,e("errorUnexpected"))}))),e.response&&403===e.response.status&&w.b.error(r.a.createElement(b.a,null,(function(e,t){t.i18n;return r.a.createElement("p",null,e("errorForbidden"))}))),Promise.reject(e)}));var S={get:N.a.get,post:N.a.post},C="".concat("/api","/player");function O(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,a=new FormData;return a.set("method",e),t&&a.set("params",t),a}function x(){return S.post(C,O("play"))}function j(){return S.post(C,O("stop"))}function _(e){return S.post(C,O("change_vol",e))}function L(e){return S.post(C,O("play_radios",e))}function R(e){return S.post(C,O("play_urls",e))}var P=function(e){return e.playing?r.a.createElement("button",{className:"btn btn-danger",onClick:j},r.a.createElement(y.a,{icon:E.d})):r.a.createElement("button",{className:"btn btn-dark",onClick:x},r.a.createElement(y.a,{icon:E.c}))},T=a(18),A=(a(81),Object(b.c)()((function(e){var t=e.playerStatus,a=e.t,n=t.vol<0,l=function(e,t){return r.a.createElement("button",{className:"btn btn-dark p-3",disabled:n,onClick:function(){return function(e){var t,n,r,l;return s.a.async((function(o){for(;;)switch(o.prev=o.next){case 0:return o.next=2,s.a.awrap(_(e));case 2:t=o.sent,n=t.data,"vol",r="".concat(a("volume"),": ").concat(n,"%"),l={toastId:"vol",autoClose:T.d},w.b.isActive("vol")?w.b.update("vol",{render:r}):w.b.info(r,l);case 8:case"end":return o.stop()}}))}(e)}},r.a.createElement(y.a,{icon:t}))};return r.a.createElement("div",{className:"player-controls btn-group w-100"},l("-5",E.e),l("+5",E.f),r.a.createElement(P,{playing:t.playing}))}))),I=(a(82),function(){var e=document.querySelector("nav").clientHeight;document.querySelector(".player").style.top=e+24+"px"}),B=function(e){var t=e.playerStatus;return Object(n.useEffect)(I,[t]),r.a.createElement("div",{className:"player p-2"},r.a.createElement(g,{playerStatus:t}),r.a.createElement(A,{playerStatus:t}))},F=Object(b.c)()((function(e){var t=e.value,a=e.onChange,n=e.t;return r.a.createElement("input",{className:"form-control mb-4",type:"text",id:"query",placeholder:n("search"),autoComplete:"off",value:t,onChange:function(e){return a(e.target.value)},onFocus:function(){return a("")}})})),U=(a(83),function(){return r.a.createElement("div",{className:"loader"},r.a.createElement("div",{className:"spinner-border",role:"status"},r.a.createElement("span",{className:"sr-only"},"Loading...")))}),q="".concat("/api","/streams");a(84);var M=function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={streams:{},loading:!0,query:""},a.isPlaying=function(e){return e===a.props.playerStatus.title&&a.props.playerStatus.playing},a.handleItemClick=function(e){var t,n,r;return s.a.async((function(l){for(;;)switch(l.prev=l.next){case 0:if(t=a.props,n=t.t,r=t.onBackdrop,!a.isPlaying(e)){l.next=3;break}return l.abrupt("return");case 3:return r(n("tunning"),e),l.prev=4,l.next=7,s.a.awrap(L(e));case 7:l.next=12;break;case 9:l.prev=9,l.t0=l.catch(4),l.t0.response&&400===l.t0.response.status&&w.b.error(n("errorNotFound"));case 12:case"end":return l.stop()}}),null,null,[[4,9]])},a.getItemClasses=function(e){var t="streams__item ellipsis list-group-item ";return a.isPlaying(e)?t+"active":t+"list-group-item-action"},a.handleSearch=function(e){a.setState({query:e})},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){var e,t;return s.a.async((function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,s.a.awrap(S.get(q));case 2:e=a.sent,t=e.data,this.setState({streams:t,loading:!1});case 5:case"end":return a.stop()}}),null,this)}},{key:"filteredStreams",value:function(){var e=this.state,t=e.streams,a=e.query;if(""===a)return t;var n={};for(var r in t)r.toLowerCase().includes(a.toLowerCase())&&!t[r]&&(n[r]=t[r]);return n}},{key:"renderList",value:function(){var e=this,t=this.filteredStreams();return 0===Object.keys(t).length?r.a.createElement("h4",{className:"p-4"},this.props.t("noStreams")):r.a.createElement("ul",{className:"list-group list-group-flush"},Object.keys(t).map((function(a){return t[a]?r.a.createElement("li",{className:"streams__header ellipsis",key:a},a):r.a.createElement("li",{className:e.getItemClasses(a),key:a,onClick:function(){return e.handleItemClick(a)}},a)})))}},{key:"render",value:function(){return this.state.loading?r.a.createElement(U,null):r.a.createElement("div",{className:"streams p-2"},r.a.createElement(F,{value:this.state.query,onChange:this.handleSearch}),this.renderList())}}]),t}(n.Component),D=Object(b.c)()(M),Y=(a(85),function(e){var t=e.playerStatus,a=t.title,n=t.playing;return r.a.createElement("div",{className:"mini-player p-3"},r.a.createElement("div",{className:"mini-player__left ellipsis pr-3"},r.a.createElement(y.a,{icon:E.b,className:"drawer__toggler mr-3"}),r.a.createElement("span",null,a)),r.a.createElement(P,{playing:n}))}),W=(a(86),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={open:!1},a.toggle=function(){a.setState({open:!a.state.open})},a.handleClick=function(e){var t=e.target;"BUTTON"===t.tagName||"path"===t.tagName||a.toggle()},a.handleTouchStart=function(e){a.setState({touchStartY:e.touches[0].clientY})},a.handleTouchMove=function(e){var t=a.state,n=t.open,r=t.touchStartY,l=e.changedTouches[0].clientY,o=l<r;l>r&&n?a.toggle():o&&!n&&a.toggle()},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"render",value:function(){var e=this.props.playerStatus,t="drawer fixed-bottom bg-secondary border-top border-secondary";return this.state.open?(t+=" drawer--open",document.body.classList.add("body--drawer")):document.body.classList.remove("body--drawer"),r.a.createElement("div",{className:t,onClick:this.handleClick,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},this.state.open?r.a.createElement(r.a.Fragment,null,r.a.createElement(y.a,{icon:E.a,className:"drawer__toggler fa-lg mb-3"}),r.a.createElement(B,{playerStatus:e})," "):r.a.createElement(Y,{playerStatus:e}))}}]),t}(n.Component)),J=function(e){var t=e.id,a=e.title,n=e.footer,l=e.children;return r.a.createElement("div",{className:"modal fade",id:t,tabIndex:"-1",role:"dialog","aria-labelledby":"staticBackdropLabel","aria-hidden":"true"},r.a.createElement("div",{className:"modal-dialog",role:"document"},r.a.createElement("div",{className:"modal-content"},r.a.createElement("div",{className:"modal-header"},r.a.createElement("h5",{className:"modal-title",id:"staticBackdropLabel"},a),r.a.createElement("button",{type:"button",className:"close","data-dismiss":"modal","aria-label":"Close"},r.a.createElement("span",{"aria-hidden":"true"},"\xd7"))),r.a.createElement("div",{className:"modal-body"},l),r.a.createElement("div",{className:"modal-footer"},n))))},K=a(43),V=function(e){var t=e.id,a=e.label,n=e.row,l=e.data,o=Object(K.a)(e,["id","label","row","data"]);return r.a.createElement("div",{className:n?"form-group row":"form-group"},r.a.createElement("label",{htmlFor:t,className:"col-sm-2 col-form-label"},a),r.a.createElement("div",{className:"col-sm-10"},r.a.createElement("select",Object.assign({id:t,className:"form-control"},o),l.map((function(e){return r.a.createElement("option",{key:e.id||e,value:e.id||e},e.name||e)})))))},G="darkly",H="theme",$=function(e){return"https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/".concat(e,"/bootstrap.min.css")},z=[{id:"darkly",name:"Darkly",themeColor:"#375a7f"},{id:"lux",name:"Lux",themeColor:"#1a1a1a"},{id:"litera",name:"Litera",themeColor:"#4582ec"}];function Q(){var e=X(),t=$(e),a=z.find((function(t){return t.id===e})).themeColor;document.querySelector('link[title="theme"]').setAttribute("href",t),document.querySelector('meta[name="theme-color"]').setAttribute("content",a)}function X(){var e=localStorage.getItem(H);return""===e?G:0===z.filter((function(t){return t.id===e})).length?(localStorage.removeItem(H),G):e}var Z=a(21),ee=Object(b.c)()((function(e){var t=e.t;return r.a.createElement(J,{id:"settings",title:t("settings"),footer:r.a.createElement("button",{className:"btn btn-secondary","data-dismiss":"modal"},t("close"))},r.a.createElement(V,{id:"theme-select",label:t("theme"),data:z,row:!0,value:X(),onChange:function(e){var t;t=e.target.value,localStorage.setItem(H,t),Q()}}),r.a.createElement(V,{id:"language-select",label:t("language"),data:T.b,row:!0,value:Z.a.language,onChange:function(e){Z.a.changeLanguage(e.target.value)}}),r.a.createElement("hr",{className:"mt-5"}),r.a.createElement("p",{className:"small"},"Copyright \xa9 2017-2019\xa0",r.a.createElement("a",{href:"https://rafaelc.org/",target:"_blank",rel:"noopener noreferrer",style:{color:"inherit"}},"Rafael Cavalcanti")))})),te=a(22),ae=Object(b.c)()((function(e){var t=e.t,a=Object(n.useState)(""),l=Object(te.a)(a,2),o=l[0],c=l[1],i=function(){""!==o&&(u(o),c(""))},u=function(e){var a,n;return s.a.async((function(r){for(;;)switch(r.prev=r.next){case 0:return a={type:w.b.TYPE.ERROR,render:t("errorNotFound")},n=Object(w.b)(t("tryingURL")),r.prev=2,r.next=5,s.a.awrap(R(e));case 5:r.next=10;break;case 7:r.prev=7,r.t0=r.catch(2),r.t0.response&&400===r.t0.response.status&&w.b.update(n,a);case 10:case"end":return r.stop()}}),null,null,[[2,7]])};return r.a.createElement(J,{id:"url-dialog",title:t("playURL"),footer:r.a.createElement(r.a.Fragment,null,r.a.createElement("button",{className:"btn btn-primary","data-dismiss":"modal",onClick:i},"OK"),r.a.createElement("button",{className:"btn btn-secondary",onClick:function(){return c("")},"data-dismiss":"modal"},"Cancel"))},r.a.createElement("input",{className:"form-control mb-4",type:"text",placeholder:"URL",value:o,onChange:function(e){var t=e.target;c(t.value)},onKeyDown:function(e){"Enter"===e.key&&i()}}))})),ne=(a(87),function(e){var t=e.title,a=e.body,n=document.body.classList.contains("body--backdrop");t&&!n?(document.body.style.paddingRight=window.innerWidth-document.documentElement.clientWidth+"px",document.body.classList.add("body--backdrop")):!t&&n&&(document.body.style.paddingRight=0,document.body.classList.remove("body--backdrop"));var l="backdrop p-2 text-white"+(t?" backdrop--visible":"");return r.a.createElement("div",{className:l},r.a.createElement("h3",{className:"text-white"},t),r.a.createElement("h5",{className:"text-white"},a))}),re=(a(88),a(89),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,r=new Array(n),l=0;l<n;l++)r[l]=arguments[l];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(r)))).state={playerStatus:{},loading:!0,networkError:!1,backdrop:{}},a.handleBackdrop=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";a.setState({backdrop:{title:e,body:t}}),setTimeout((function(){return a.setState({backdrop:{}})}),T.a)},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){this.updatePlayerStatus()}},{key:"updatePlayerStatus",value:function(){var e,t,a=this;return s.a.async((function(n){for(;;)switch(n.prev=n.next){case 0:return n.prev=0,n.next=3,s.a.awrap(S.get(C));case 3:e=n.sent,t=e.data,this.setState({playerStatus:t,loading:!1,networkError:!1}),n.next=11;break;case 8:n.prev=8,n.t0=n.catch(0),this.setState({loading:!1,networkError:!0});case 11:setTimeout((function(){return a.updatePlayerStatus()}),T.c);case 12:case"end":return n.stop()}}),null,this,[[0,8]])}},{key:"render",value:function(){var e=this.state,t=e.loading,a=e.networkError,n=e.backdrop,l=e.playerStatus,o=this.props.t;return a?r.a.createElement(ne,{title:o("errorNetwork")}):t?r.a.createElement(U,null):l.con_mpd?r.a.createElement("div",{className:"app"},r.a.createElement(w.a,null),r.a.createElement(ne,{title:n.title,body:n.body}),r.a.createElement(h,null),r.a.createElement(W,{playerStatus:l}),r.a.createElement("main",{className:"app-main p-4"},r.a.createElement(B,{playerStatus:l}),r.a.createElement(D,{onBackdrop:this.handleBackdrop,playerStatus:l}),r.a.createElement(ee,null),r.a.createElement(ae,null))):r.a.createElement(ne,{title:o("disconnectedMPD")})}}]),t}(n.Component)),le=Object(b.c)()(re);Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));var oe=a(44),ce=a(45);Z.a.use(oe.a).use(ce.a).use(b.b).init({fallbackLng:"en",debug:Object({NODE_ENV:"production",PUBLIC_URL:"",REACT_APP_API_URL:"/api"}).REACT_APP_I18N_DEBUG,interpolation:{escapeValue:!1}});Z.a,a(90),a(91),a(92);Q(),o.a.render(r.a.createElement(n.Suspense,{fallback:r.a.createElement(U,null)},r.a.createElement(le,null)),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((function(e){e.unregister()}))}},[[46,1,2]]]);
|
2
|
-
//# sourceMappingURL=main.9b725bd5.chunk.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["logo.svg","components/navBar.jsx","components/playerStatus.jsx","services/httpService.js","services/playerService.js","components/playStopControl.jsx","components/playerControls.jsx","components/player.jsx","components/common/searchBox.jsx","components/loader.jsx","services/streamsService.js","components/streams.jsx","components/miniPlayer.jsx","components/drawer.jsx","components/common/modal.jsx","components/common/select.jsx","theme.js","components/settings.jsx","components/urlDialog.jsx","components/backdrop.jsx","utils/style.js","App.js","serviceWorker.js","i18n.js","index.js"],"names":["module","exports","withTranslation","t","className","href","src","logo","width","height","alt","type","data-toggle","data-target","aria-controls","aria-expanded","aria-label","id","playerStatus","playing","title","axios","interceptors","response","use","error","expectedError","status","networkError","toast","i18n","Promise","reject","get","post","apiEndpoint","process","body","method","params","FormData","set","play","http","stop","changeVol","delta","playRadio","name","playURL","url","PlayStopControl","onClick","icon","faStop","faPlay","volDisabled","vol","renderVolButton","disabled","a","async","data","toastMsg","toastOpts","toastId","autoClose","volTimeout","isActive","update","render","info","handleVolChange","faVolumeDown","faVolumeUp","setPlayerTop","navHeight","document","querySelector","clientHeight","style","top","Player","useEffect","value","onChange","placeholder","autoComplete","e","target","onFocus","Loader","role","Streams","state","streams","loading","query","isPlaying","props","handleItemClick","onBackdrop","getItemClasses","classes","handleSearch","setState","this","filtered","k","toLowerCase","includes","filteredStreams","Object","keys","length","map","key","renderList","Component","MiniPlayer","faChevronUp","Drawer","open","toggle","handleClick","tagName","handleTouchStart","touchStartY","touches","clientY","handleTouchMove","touchEndY","changedTouches","touchUp","classList","add","remove","onTouchStart","onTouchMove","Fragment","faChevronDown","Modal","footer","children","tabIndex","aria-labelledby","aria-hidden","data-dismiss","Select","label","row","rest","htmlFor","d","DEFAULT_THEME","STORAGE_KEY","getThemePath","themeId","themes","themeColor","applyTheme","getThemeId","themePath","find","setAttribute","localId","localStorage","getItem","filter","removeItem","setItem","languages","i18next","language","changeLanguage","rel","color","useState","setURL","handleOK","doPlayURL","errorToastOpts","TYPE","ERROR","input","onKeyDown","Backdrop","bodyStyled","contains","paddingRight","window","innerWidth","documentElement","clientWidth","App","backdrop","handleBackdrop","setTimeout","backdropTimeout","updatePlayerStatus","updateInterval","con_mpd","Boolean","location","hostname","match","Backend","LanguageDetector","initReactI18next","init","fallbackLng","debug","REACT_APP_I18N_DEBUG","interpolation","escapeValue","ReactDOM","fallback","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.9b725bd5.chunk.js","sourcesContent":["module.exports = __webpack_public_path__ + \"static/media/logo.91554ce9.svg\";","import React from 'react';\nimport { withTranslation } from 'react-i18next';\nimport './navBar.scss';\nimport logo from '../logo.svg';\n\nconst NavBar = ({ t }) => (\n <nav className=\"navbar navbar-expand-sm navbar-dark bg-primary \">\n <a className=\"navbar-brand\" href=\"/\">\n <img\n src={logo}\n width=\"30\"\n height=\"30\"\n className=\"d-inline-block align-text-bottom mr-2\"\n alt=\"\"\n />\n PiFi Radio\n </a>\n <button\n className=\"navbar-toggler\"\n type=\"button\"\n data-toggle=\"collapse\"\n data-target=\"#navbarNavAltMarkup\"\n aria-controls=\"navbarNavAltMarkup\"\n aria-expanded=\"false\"\n aria-label=\"Toggle navigation\"\n >\n <span className=\"navbar-toggler-icon\"></span>\n </button>\n <div className=\"collapse navbar-collapse\" id=\"navbarNavAltMarkup\">\n <div className=\"navbar-nav pt-1 ml-2\">\n {/* We use buttons and different class names instead of Bootstrap's\n anchor, so we don't get a warning for href=\"#\". */}\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#url-dialog\"\n >\n {t('playURL')}\n </button>\n\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#settings\"\n >\n {t('settings')}\n </button>\n </div>\n </div>\n </nav>\n);\nexport default withTranslation()(NavBar);\n","import React from 'react';\nimport { withTranslation } from 'react-i18next';\n\nconst PlayerStatus = ({ playerStatus, t }) => {\n const { playing, title } = playerStatus;\n\n return (\n <div className=\"text-center\">\n <h5 className=\"small text-uppercase\">\n {playing ? t('playing') : t('stopped')}\n </h5>\n <h3 className=\"ellipsis\">{title}</h3>\n </div>\n );\n};\n\nexport default withTranslation()(PlayerStatus);\n","import React from 'react';\nimport axios from 'axios';\nimport { toast } from 'react-toastify';\nimport { Translation } from 'react-i18next';\n\naxios.interceptors.response.use(null, error => {\n const expectedError =\n error.response &&\n error.response.status >= 400 &&\n error.response.status < 500;\n\n // We already alert about these with a backdrop on App.js\n const networkError = !error.response;\n\n // Ugly, but i18n only worked this way\n if (!expectedError && !networkError) {\n toast.error(\n <Translation>\n {(t, { i18n }) => <p>{t('errorUnexpected')}</p>}\n </Translation>\n );\n }\n // This expected error is universal in our app, so we'll place it here\n if (error.response && error.response.status === 403)\n toast.error(\n <Translation>{(t, { i18n }) => <p>{t('errorForbidden')}</p>}</Translation>\n );\n\n return Promise.reject(error);\n});\n\nexport default {\n get: axios.get,\n post: axios.post\n};\n","import http from './httpService.js';\n\nconst apiEndpoint = `${process.env.REACT_APP_API_URL}/player`;\n\nfunction body(method, params = null) {\n const body = new FormData();\n body.set('method', method);\n if (params) body.set('params', params);\n return body;\n}\n\nexport function getStatus() {\n return http.get(apiEndpoint);\n}\n\nexport function play() {\n return http.post(apiEndpoint, body('play'));\n}\n\nexport function stop() {\n return http.post(apiEndpoint, body('stop'));\n}\n\nexport function changeVol(delta) {\n return http.post(apiEndpoint, body('change_vol', delta));\n}\n\nexport function playRadio(name) {\n return http.post(apiEndpoint, body('play_radios', name));\n}\n\nexport function playURL(url) {\n return http.post(apiEndpoint, body('play_urls', url));\n}\n","import React from 'react';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faStop, faPlay } from '@fortawesome/free-solid-svg-icons';\nimport { play, stop } from '../services/playerService';\n\nconst PlayStopControl = ({ playing }) =>\n playing ? (\n <button className=\"btn btn-danger\" onClick={stop}>\n <FontAwesomeIcon icon={faStop} />\n </button>\n ) : (\n <button className=\"btn btn-dark\" onClick={play}>\n <FontAwesomeIcon icon={faPlay} />\n </button>\n );\n\nexport default PlayStopControl;\n","import React from 'react';\nimport PlayStopControl from './playStopControl';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faVolumeDown, faVolumeUp } from '@fortawesome/free-solid-svg-icons';\nimport { toast } from 'react-toastify';\nimport { withTranslation } from 'react-i18next';\nimport { changeVol } from '../services/playerService';\nimport { volTimeout } from '../config.json';\nimport './playerControls.scss';\n\nconst PlayerControls = ({ playerStatus, t }) => {\n const volDisabled = playerStatus.vol < 0;\n\n const handleVolChange = async delta => {\n const { data: vol } = await changeVol(delta);\n\n const toastId = 'vol';\n const toastMsg = `${t('volume')}: ${vol}%`;\n const toastOpts = { toastId, autoClose: volTimeout };\n if (toast.isActive(toastId)) toast.update(toastId, { render: toastMsg });\n else toast.info(toastMsg, toastOpts);\n };\n\n const renderVolButton = (delta, icon) => (\n <button\n className=\"btn btn-dark p-3\"\n disabled={volDisabled}\n onClick={() => handleVolChange(delta)}\n >\n <FontAwesomeIcon icon={icon} />\n </button>\n );\n\n return (\n <div className=\"player-controls btn-group w-100\">\n {renderVolButton('-5', faVolumeDown)}\n {renderVolButton('+5', faVolumeUp)}\n <PlayStopControl playing={playerStatus.playing} />\n </div>\n );\n};\n\nexport default withTranslation()(PlayerControls);\n","import React, { useEffect } from 'react';\nimport PlayerStatus from './playerStatus';\nimport PlayerControls from './playerControls';\nimport './player.scss';\n\nconst Player = ({ playerStatus }) => {\n useEffect(setPlayerTop, [playerStatus]);\n\n return (\n <div className=\"player p-2\">\n <PlayerStatus playerStatus={playerStatus} />\n <PlayerControls playerStatus={playerStatus} />\n </div>\n );\n};\n\n// Calculate and set top for Player\n// Needed for desktop view with sticky\nexport const setPlayerTop = () => {\n const navHeight = document.querySelector('nav').clientHeight;\n const topMargin = 24;\n document.querySelector('.player').style.top = navHeight + topMargin + 'px';\n};\n\nexport default Player;\n","import React from 'react';\nimport { withTranslation } from 'react-i18next';\n\nconst SearchBox = ({ value, onChange, t }) => {\n return (\n <input\n className=\"form-control mb-4\"\n type=\"text\"\n id=\"query\"\n placeholder={t('search')}\n autoComplete=\"off\"\n value={value}\n onChange={e => onChange(e.target.value)}\n onFocus={() => onChange('')}\n ></input>\n );\n};\n\nexport default withTranslation()(SearchBox);\n","import React from 'react';\nimport './loader.scss';\n\nconst Loader = () => (\n <div className=\"loader\">\n <div className=\"spinner-border\" role=\"status\">\n <span className=\"sr-only\">Loading...</span>\n </div>\n </div>\n);\n\nexport default Loader;\n","import http from './httpService';\n\nconst apiEndpoint = `${process.env.REACT_APP_API_URL}/streams`;\n\nexport function getStreams() {\n return http.get(apiEndpoint);\n}\n","import React, { Component } from 'react';\nimport SearchBox from './common/searchBox';\nimport Loader from './loader';\nimport { withTranslation } from 'react-i18next';\nimport { toast } from 'react-toastify';\nimport { playRadio } from '../services/playerService';\nimport { getStreams } from '../services/streamsService';\nimport './streams.scss';\n\nclass Streams extends Component {\n state = { streams: {}, loading: true, query: '' };\n\n async componentDidMount() {\n const { data: streams } = await getStreams();\n this.setState({ streams, loading: false });\n }\n\n isPlaying = name => {\n return (\n name === this.props.playerStatus.title && this.props.playerStatus.playing\n );\n };\n\n handleItemClick = async name => {\n const { t, onBackdrop } = this.props;\n\n if (this.isPlaying(name)) return;\n\n onBackdrop(t('tunning'), name);\n try {\n await playRadio(name);\n } catch (ex) {\n if (ex.response && ex.response.status === 400)\n toast.error(t('errorNotFound'));\n }\n };\n\n getItemClasses = name => {\n const classes = 'streams__item ellipsis list-group-item ';\n return this.isPlaying(name)\n ? classes + 'active'\n : classes + 'list-group-item-action';\n };\n\n handleSearch = query => {\n this.setState({ query });\n };\n\n filteredStreams() {\n const { streams, query } = this.state;\n\n if (query === '') return streams;\n\n let filtered = {};\n for (let k in streams) {\n if (k.toLowerCase().includes(query.toLowerCase()) && !streams[k]) {\n filtered[k] = streams[k];\n }\n }\n return filtered;\n }\n\n renderList() {\n const streams = this.filteredStreams();\n\n if (Object.keys(streams).length === 0)\n return <h4 className=\"p-4\">{this.props.t('noStreams')}</h4>;\n\n return (\n <ul className=\"list-group list-group-flush\">\n {Object.keys(streams).map(name =>\n streams[name] ? (\n <li className=\"streams__header ellipsis\" key={name}>\n {name}\n </li>\n ) : (\n <li\n className={this.getItemClasses(name)}\n key={name}\n onClick={() => this.handleItemClick(name)}\n >\n {name}\n </li>\n )\n )}\n </ul>\n );\n }\n\n render() {\n if (this.state.loading) return <Loader />;\n\n return (\n <div className=\"streams p-2\">\n <SearchBox value={this.state.query} onChange={this.handleSearch} />\n {this.renderList()}\n </div>\n );\n }\n}\n\nexport default withTranslation()(Streams);\n","import React from 'react';\nimport PlayStopControl from './playStopControl';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp } from '@fortawesome/free-solid-svg-icons';\nimport './miniPlayer.scss';\n\nconst MiniPlayer = ({ playerStatus }) => {\n const { title, playing } = playerStatus;\n return (\n <div className=\"mini-player p-3\">\n <div className=\"mini-player__left ellipsis pr-3\">\n <FontAwesomeIcon icon={faChevronUp} className=\"drawer__toggler mr-3\" />\n <span>{title}</span>\n </div>\n <PlayStopControl playing={playing} />\n </div>\n );\n};\n\nexport default MiniPlayer;\n","import React, { Component } from 'react';\nimport MiniPlayer from './miniPlayer';\nimport Player from './player';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown } from '@fortawesome/free-solid-svg-icons';\nimport './drawer.scss';\n\nclass Drawer extends Component {\n state = { open: false };\n\n toggle = () => {\n this.setState({ open: !this.state.open });\n };\n\n handleClick = ({ target }) => {\n const blackList = target.tagName === 'BUTTON' || target.tagName === 'path';\n if (blackList) return;\n\n this.toggle();\n };\n\n handleTouchStart = e => {\n this.setState({ touchStartY: e.touches[0].clientY });\n };\n\n handleTouchMove = e => {\n const { open, touchStartY } = this.state;\n const touchEndY = e.changedTouches[0].clientY;\n const touchUp = touchEndY < touchStartY;\n const touchDown = touchEndY > touchStartY;\n\n if (touchDown && open) this.toggle();\n else if (touchUp && !open) this.toggle();\n };\n\n render() {\n const { playerStatus } = this.props;\n let classes =\n 'drawer fixed-bottom bg-secondary border-top border-secondary';\n\n if (this.state.open) {\n classes += ' drawer--open';\n document.body.classList.add('body--drawer');\n } else document.body.classList.remove('body--drawer');\n\n return (\n <div\n className={classes}\n onClick={this.handleClick}\n onTouchStart={this.handleTouchStart}\n onTouchMove={this.handleTouchMove}\n >\n {this.state.open ? (\n <React.Fragment>\n <FontAwesomeIcon\n icon={faChevronDown}\n className=\"drawer__toggler fa-lg mb-3\"\n />\n <Player playerStatus={playerStatus} />{' '}\n </React.Fragment>\n ) : (\n <MiniPlayer playerStatus={playerStatus} />\n )}\n </div>\n );\n }\n}\n\nexport default Drawer;\n","import React from 'react';\n\nconst Modal = ({ id, title, footer, children }) => {\n return (\n <div\n className=\"modal fade\"\n id={id}\n tabIndex=\"-1\"\n role=\"dialog\"\n aria-labelledby=\"staticBackdropLabel\"\n aria-hidden=\"true\"\n >\n <div className=\"modal-dialog\" role=\"document\">\n <div className=\"modal-content\">\n <div className=\"modal-header\">\n <h5 className=\"modal-title\" id=\"staticBackdropLabel\">\n {title}\n </h5>\n <button\n type=\"button\"\n className=\"close\"\n data-dismiss=\"modal\"\n aria-label=\"Close\"\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n <div className=\"modal-body\">{children}</div>\n <div className=\"modal-footer\">{footer}</div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default Modal;\n","import React from 'react';\n\nconst Select = ({ id, label, row, data, ...rest }) => (\n <div className={row ? 'form-group row' : 'form-group'}>\n <label htmlFor={id} className=\"col-sm-2 col-form-label\">\n {label}\n </label>\n <div className=\"col-sm-10\">\n <select id={id} className=\"form-control\" {...rest}>\n {data.map(d => (\n <option key={d.id || d} value={d.id || d}>\n {d.name || d}\n </option>\n ))}\n </select>\n </div>\n </div>\n);\n\nexport default Select;\n","const DEFAULT_THEME = 'darkly';\n\nconst STORAGE_KEY = 'theme';\n\nconst getThemePath = themeId =>\n `https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/${themeId}/bootstrap.min.css`;\n\nexport const themes = [\n { id: 'darkly', name: 'Darkly', themeColor: '#375a7f' },\n { id: 'lux', name: 'Lux', themeColor: '#1a1a1a' },\n { id: 'litera', name: 'Litera', themeColor: '#4582ec' }\n];\n\nexport function changeTheme(themeId) {\n localStorage.setItem(STORAGE_KEY, themeId);\n applyTheme();\n}\n\nexport function applyTheme() {\n const themeId = getThemeId();\n const themePath = getThemePath(themeId);\n const themeColor = themes.find(t => t.id === themeId).themeColor;\n\n const linkEl = document.querySelector('link[title=\"theme\"]');\n linkEl.setAttribute('href', themePath);\n\n const metaEl = document.querySelector('meta[name=\"theme-color\"]');\n metaEl.setAttribute('content', themeColor);\n}\n\nexport function getThemeId() {\n const localId = localStorage.getItem(STORAGE_KEY);\n\n if (localId === '') return DEFAULT_THEME;\n if (themes.filter(t => t.id === localId).length === 0) {\n localStorage.removeItem(STORAGE_KEY);\n return DEFAULT_THEME;\n }\n\n return localId;\n}\n","import React from 'react';\nimport Modal from './common/modal';\nimport Select from './common/select';\nimport { getThemeId, changeTheme, themes } from '../theme';\nimport i18next from 'i18next';\nimport { withTranslation } from 'react-i18next';\nimport { languages } from '../config.json';\n\nconst Settings = ({ t }) => {\n const renderFooter = () => (\n <button className=\"btn btn-secondary\" data-dismiss=\"modal\">\n {t('close')}\n </button>\n );\n\n return (\n <Modal id=\"settings\" title={t('settings')} footer={renderFooter()}>\n <Select\n id=\"theme-select\"\n label={t('theme')}\n data={themes}\n row\n value={getThemeId()}\n onChange={e => {\n changeTheme(e.target.value);\n }}\n />\n <Select\n id=\"language-select\"\n label={t('language')}\n data={languages}\n row\n value={i18next.language}\n onChange={e => {\n i18next.changeLanguage(e.target.value);\n }}\n />\n <hr className=\"mt-5\" />\n <p className=\"small\">\n Copyright © 2017-2019 \n {/* Use the default color, as some themes give a different color for links. */}\n <a\n href=\"https://rafaelc.org/\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{ color: 'inherit' }}\n >\n Rafael Cavalcanti\n </a>\n </p>\n </Modal>\n );\n};\n\nexport default withTranslation()(Settings);\n","import React, { useState } from 'react';\nimport Modal from './common/modal';\nimport { toast } from 'react-toastify';\nimport { playURL } from '../services/playerService';\nimport { withTranslation } from 'react-i18next';\n\nconst URLDialog = ({ t }) => {\n const [url, setURL] = useState('');\n\n const handleOK = () => {\n if (url === '') return;\n doPlayURL(url);\n setURL('');\n };\n\n const doPlayURL = async url => {\n const errorToastOpts = {\n type: toast.TYPE.ERROR,\n render: t('errorNotFound')\n };\n\n const toastId = toast(t('tryingURL'));\n\n try {\n await playURL(url);\n } catch (ex) {\n if (ex.response && ex.response.status === 400)\n toast.update(toastId, errorToastOpts);\n }\n };\n\n const handleChange = ({ target: input }) => {\n setURL(input.value);\n };\n\n const handleKeyDown = e => {\n if (e.key === 'Enter') handleOK();\n };\n\n const renderFooter = () => (\n <React.Fragment>\n <button\n className=\"btn btn-primary\"\n data-dismiss=\"modal\"\n onClick={handleOK}\n >\n OK\n </button>\n <button\n className=\"btn btn-secondary\"\n onClick={() => setURL('')}\n data-dismiss=\"modal\"\n >\n Cancel\n </button>\n </React.Fragment>\n );\n\n return (\n <Modal id=\"url-dialog\" title={t('playURL')} footer={renderFooter()}>\n <input\n className=\"form-control mb-4\"\n type=\"text\"\n placeholder=\"URL\"\n value={url}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n />\n </Modal>\n );\n};\n\nexport default withTranslation()(URLDialog);\n","import React from 'react';\nimport { getScrollbarWidth } from '../utils/style';\nimport './backdrop.scss';\n\nconst Backdrop = ({ title, body }) => {\n const bodyStyled = document.body.classList.contains('body--backdrop');\n\n if (title && !bodyStyled) {\n // Calculate this BEFORE adding the class.\n document.body.style.paddingRight = getScrollbarWidth() + 'px';\n document.body.classList.add('body--backdrop');\n // We need this check because Bootstrap modals also style paddingRight\n } else if (!title && bodyStyled) {\n document.body.style.paddingRight = 0;\n document.body.classList.remove('body--backdrop');\n }\n\n const classes =\n 'backdrop p-2 text-white' + (title ? ' backdrop--visible' : '');\n\n // We need text-white on h* tags because some themes override it\n return (\n <div className={classes}>\n <h3 className=\"text-white\">{title}</h3>\n <h5 className=\"text-white\">{body}</h5>\n </div>\n );\n};\n\nexport default Backdrop;\n","export const getScrollbarWidth = () =>\n window.innerWidth - document.documentElement.clientWidth;\n","import React, { Component } from 'react';\nimport NavBar from './components/navBar';\nimport Player from './components/player';\nimport Streams from './components/streams';\nimport Drawer from './components/drawer';\nimport Settings from './components/settings';\nimport URLDialog from './components/urlDialog';\nimport Loader from './components/loader';\nimport Backdrop from './components/backdrop';\nimport { ToastContainer } from 'react-toastify';\nimport { getStatus } from './services/playerService';\nimport { withTranslation } from 'react-i18next';\nimport { updateInterval, backdropTimeout } from './config.json';\nimport './App.scss';\nimport 'react-toastify/dist/ReactToastify.css';\n\nclass App extends Component {\n state = {\n playerStatus: {},\n loading: true,\n networkError: false,\n backdrop: {}\n };\n\n componentDidMount() {\n this.updatePlayerStatus();\n }\n\n async updatePlayerStatus() {\n try {\n const { data: playerStatus } = await getStatus();\n this.setState({ playerStatus, loading: false, networkError: false });\n } catch (ex) {\n this.setState({ loading: false, networkError: true });\n }\n\n setTimeout(() => this.updatePlayerStatus(), updateInterval);\n }\n\n handleBackdrop = (title, body = '') => {\n this.setState({ backdrop: { title, body } });\n setTimeout(() => this.setState({ backdrop: {} }), backdropTimeout);\n };\n\n render() {\n const { loading, networkError, backdrop, playerStatus } = this.state;\n const { t } = this.props;\n\n if (networkError) return <Backdrop title={t('errorNetwork')} />;\n if (loading) return <Loader />;\n if (!playerStatus.con_mpd) return <Backdrop title={t('disconnectedMPD')} />;\n\n return (\n <div className=\"app\">\n <ToastContainer />\n <Backdrop title={backdrop.title} body={backdrop.body} />\n <NavBar />\n <Drawer playerStatus={playerStatus} />\n\n <main className=\"app-main p-4\">\n <Player playerStatus={playerStatus} />\n <Streams\n onBackdrop={this.handleBackdrop}\n playerStatus={playerStatus}\n />\n <Settings />\n <URLDialog />\n </main>\n </div>\n );\n }\n}\n\nexport default withTranslation()(App);\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' }\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\n\nimport Backend from 'i18next-xhr-backend';\nimport LanguageDetector from 'i18next-browser-languagedetector';\n// not like to use this?\n// have a look at the Quick start guide\n// for passing in lng and translations on init\n\ni18n\n // load translation using xhr -> see /public/locales\n // learn more: https://github.com/i18next/i18next-xhr-backend\n .use(Backend)\n // detect user language\n // learn more: https://github.com/i18next/i18next-browser-languageDetector\n .use(LanguageDetector)\n // pass the i18n instance to react-i18next.\n .use(initReactI18next)\n // init i18next\n // for all options read: https://www.i18next.com/overview/configuration-options\n .init({\n fallbackLng: 'en',\n debug: process.env.REACT_APP_I18N_DEBUG,\n\n interpolation: {\n escapeValue: false // not needed for react as it escapes by default\n }\n });\n\nexport default i18n;\n","import React, { Suspense } from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\nimport Loader from './components/loader';\nimport './i18n';\nimport { applyTheme } from './theme';\nimport './index.scss';\n\n// Bootstrap\n//import 'bootstrap/dist/css/bootstrap.css';\n//import 'bootswatch/dist/flatly/bootstrap.min.css'; // This replaces default Bootstrap file\nimport 'jquery/dist/jquery.min.js';\nimport 'bootstrap/dist/js/bootstrap.js';\n\napplyTheme();\n\nReactDOM.render(\n // Suspense needed for i18n\n <Suspense fallback={<Loader />}>\n <App />\n </Suspense>,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""}
|