pifi 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ (this.webpackJsonppifi_frontend=this.webpackJsonppifi_frontend||[]).push([[0],{19:function(e){e.exports=JSON.parse('{"b":["en","fr-FR","nl-NL","pl-PL","pt-BR"],"c":1000,"a":2500,"d":3000}')},28:function(e,t,a){e.exports=a.p+"static/media/logo.91554ce9.svg"},46:function(e,t,a){e.exports=a(94)},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){},89:function(e,t,a){},94:function(e,t,a){"use strict";a.r(t);var n=a(0),r=a.n(n),l=a(20),o=a.n(l),c=a(5),s=a.n(c),i=a(3),u=a(4),m=a(8),d=a(10),p=a(11),b=a(6),v=a(2),f=(a(60),a(28)),g=a.n(f),h=function(){var e=Object(v.c)().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:g.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"},e("playURL")),r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#settings"},e("settings")),r.a.createElement("button",{className:"btn btn-link nav-link","data-toggle":"modal","data-target":"#about"},e("about")))))},y=function(e){var t=e.playerStatus,a=t.playing,n=t.title,l=Object(v.c)().t;return r.a.createElement("div",{className:"text-center w-100"},r.a.createElement("h5",{className:"small text-uppercase"},l(a?"playing":"stopped")),r.a.createElement("h3",{className:"ellipsis"},n))},E=a(18),k=a(16),N=a(23),S=a.n(N);S.a.defaults.baseURL="/api",S.a.interceptors.response.use(null,(function(e){var t=e.response&&e.response.status>=400&&e.response.status<500;return document.querySelector(".Toastify")&&(t||b.b.error(r.a.createElement(v.a,null,(function(e){return e("errorUnexpected")}))),e.response&&403===e.response.status&&b.b.error(r.a.createElement(v.a,null,(function(e){return e("errorForbidden")})))),Promise.reject(e)}));var w={get:S.a.get,post:S.a.post},O="/player";function j(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 C(){return w.post(O,j("play"))}function x(){return w.post(O,j("stop"))}function _(e){return w.post(O,j("change_vol",e))}function L(e){return w.post(O,j("play_radios",e))}function P(e){return w.post(O,j("play_urls",e))}var T=function(e){return e.playing?r.a.createElement("button",{className:"btn btn-danger",onClick:x,"aria-label":"Stop"},r.a.createElement(E.a,{icon:k.d})):r.a.createElement("button",{className:"btn btn-dark",onClick:C,"aria-label":"Play"},r.a.createElement(E.a,{icon:k.c}))},R=a(19),A=function(e){var t=e.playerStatus,a=Object(v.c)().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:R.d},b.b.isActive("vol")?b.b.update("vol",{render:r}):b.b.info(r,l);case 8:case"end":return o.stop()}}))}(e)},"aria-label":l},r.a.createElement(E.a,{icon:t}))};return r.a.createElement("div",{className:"player-controls btn-group w-100 m-2",onClick:function(e){return e.stopPropagation()}},l("-5",k.e,"Volume down"),l("+5",k.f,"Volume up"),r.a.createElement(T,{playing:t.playing}))},I=(a(81),function(e){var t=e.playerStatus;return r.a.createElement("div",{className:"player p-2"},r.a.createElement(y,{playerStatus:t}),r.a.createElement("img",{src:a(28),alt:"Logo",className:"player-logo m-4"}),r.a.createElement(A,{playerStatus:t}))}),B=(a(82),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...")))}),D=function(e){var t=e.value,a=e.onChange,n=Object(v.c)().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("")}})},F="/streams";a(83);var U=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&&b.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(w.get(F));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(B,null):r.a.createElement("div",{className:"streams p-2"},r.a.createElement(D,{value:this.state.query,onChange:this.handleSearch}),this.renderList())}}]),t}(n.Component),q=Object(v.d)()(U),M=(a(84),function(e){var t=e.playerStatus,a=t.title,n=t.playing;return r.a.createElement("div",{className:"mini-player p-2"},r.a.createElement("div",{className:"mini-player__left ellipsis"},r.a.createElement("span",null,a)),r.a.createElement("div",{className:"mini-player__right",onClick:function(e){return e.stopPropagation()}},r.a.createElement(T,{playing:n})))}),Y=(a(85),function(e){function t(){var e,a;Object(i.a)(this,t);for(var n=arguments.length,l=new Array(n),o=0;o<n;o++)l[o]=arguments[o];return(a=Object(m.a)(this,(e=Object(d.a)(t)).call.apply(e,[this].concat(l)))).state={open:!1},a.toggle=function(){a.setState({open:!a.state.open})},a.handleClick=function(){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.renderToggleButton=function(){return r.a.createElement("button",{className:"btn btn-primary-outline","aria-label":"Toggle player"},r.a.createElement(E.a,{icon:a.state.open?k.a:k.b,className:"drawer__toggler fa-lg"}))},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"render",value:function(){var e=this.props.playerStatus,t=this.state.open,a="drawer fixed-bottom bg-secondary border-top border-secondary p-2";return t?(a+=" drawer--open",document.body.classList.add("body--drawer")):document.body.classList.remove("body--drawer"),r.a.createElement("div",{className:a,onClick:this.handleClick,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},this.renderToggleButton(),t?r.a.createElement(I,{playerStatus:e}):r.a.createElement(M,{playerStatus:e}))}}]),t}(n.Component)),V=a(14),W=function(e){var t=e.id,a=e.title,n=e.footer,l=e.children,o=Object(v.c)().t,c=r.a.createElement("button",{className:"btn btn-secondary","data-dismiss":"modal"},o("close"));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||c))))},J=a(43),H=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",K="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"}];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(K);return""===e?G:0===z.filter((function(t){return t.id===e})).length?(localStorage.removeItem(K),G):e}var Z={availableThemes:z,getCurrentId:X,change:function(e){localStorage.setItem(K,e),Q()},apply:Q},ee=function(){var e=Object(v.c)(),t=e.t,a=e.i18n,l=Object(n.useState)(""),o=Object(V.a)(l,2),c=o[0],s=o[1];Object(n.useEffect)((function(){s(Z.getCurrentId())}),[]);return r.a.createElement(W,{id:"settings",title:t("settings")},r.a.createElement(H,{id:"theme-select",label:t("theme"),data:Z.availableThemes,row:!0,value:c,onChange:function(e){return function(e){var t=e.target.value;Z.change(t),s(t)}(e)}}),r.a.createElement(H,{id:"language-select",label:t("language"),data:R.b,row:!0,value:a.language,onChange:function(e){return a.changeLanguage(e.target.value)}}))},te=function(){var e=Object(n.useState)(""),t=Object(V.a)(e,2),a=t[0],l=t[1],o=Object(v.c)().t,c=function(){""!==a&&(i(a),l(""))},i=function(e){var t,a;return s.a.async((function(n){for(;;)switch(n.prev=n.next){case 0:return t={type:b.b.TYPE.ERROR,render:o("errorNotFound")},a=Object(b.b)(o("tryingURL")),n.prev=2,n.next=5,s.a.awrap(P(e));case 5:n.next=10;break;case 7:n.prev=7,n.t0=n.catch(2),n.t0.response&&400===n.t0.response.status&&b.b.update(a,t);case 10:case"end":return n.stop()}}),null,null,[[2,7]])};return r.a.createElement(W,{id:"url-dialog",title:o("playURL"),footer:r.a.createElement(r.a.Fragment,null,r.a.createElement("button",{className:"btn btn-primary","data-dismiss":"modal",onClick:c},o("ok")),r.a.createElement("button",{className:"btn btn-secondary",onClick:function(){return l("")},"data-dismiss":"modal"},o("cancel")))},r.a.createElement("input",{className:"form-control mb-4",type:"text",placeholder:"URL",value:a,onChange:function(e){var t=e.target;l(t.value)},onKeyDown:function(e){"Enter"===e.key&&c()}}))},ae="/config";var ne=function(){var e=Object(v.c)().t,t=Object(n.useState)({}),a=Object(V.a)(t,2),l=a[0],o=a[1],c=Object(n.useState)(!0),i=Object(V.a)(c,2),u=i[0],m=i[1];Object(n.useEffect)((function(){!function(){var e,t;s.a.async((function(a){for(;;)switch(a.prev=a.next){case 0:return a.next=2,s.a.awrap(w.get(ae));case 2:e=a.sent,t=e.data,o(t),m(!1);case 6:case"end":return a.stop()}}))}()}),[]);var d=[{label:e("mpdHost"),value:l.mpd_host},{label:e("mpdPort"),value:l.mpd_port},{label:e("environment"),value:l.environment},{label:e("version"),value:l.version}],p=r.a.createElement("p",{className:"small mt-5"},"Copyright \xa9 2017-2020\xa0",r.a.createElement("a",{href:"https://rafaelc.org/",target:"_blank",rel:"noopener noreferrer",style:{color:"inherit"}},"Rafael Cavalcanti"));return r.a.createElement(W,{id:"about",title:e("about")},u?r.a.createElement(B,null):r.a.createElement("table",{className:"table"},r.a.createElement("tbody",null,d.map((function(e){return r.a.createElement("tr",{key:e.label},r.a.createElement("th",{scope:"row"},e.label),r.a.createElement("td",null,e.value))})))),p)},re=(a(86),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))}),le=(a(87),a(88),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:{}})}),R.a)},a.setDesktopPlayerTop=function(){var e=document.querySelector("nav"),t=document.querySelector(".app-main > .player");if(e&&t){var a=e.clientHeight;t.style.top=a+24+"px"}},a.setDocumentTitle=function(){var e=a.state,t=e.playerStatus;!e.networkError&&t.playing?document.title="PiFi \ud83d\udd0a "+t.title:document.title="PiFi Radio"},a}return Object(p.a)(t,e),Object(u.a)(t,[{key:"componentDidMount",value:function(){this.updatePlayerStatus()}},{key:"componentDidUpdate",value:function(){this.setDesktopPlayerTop(),this.setDocumentTitle()}},{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(w.get(O));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()}),R.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(re,{title:o("errorNetwork")}):t?r.a.createElement(B,null):l.con_mpd?r.a.createElement("div",{className:"app"},r.a.createElement(b.a,null),r.a.createElement(re,{title:n.title,body:n.body}),r.a.createElement(h,null),r.a.createElement(Y,{playerStatus:l}),r.a.createElement("main",{className:"app-main p-4"},r.a.createElement(I,{playerStatus:l}),r.a.createElement(q,{onBackdrop:this.handleBackdrop,playerStatus:l}),r.a.createElement(te,null),r.a.createElement(ee,null),r.a.createElement(ne,null))):r.a.createElement(re,{title:o("disconnectedMPD")})}}]),t}(n.Component)),oe=Object(v.d)()(le);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 ce=a(27),se=a(44),ie=a(45);ce.a.use(se.a).use(ie.a).use(v.b).init({fallbackLng:"en",debug:Object({NODE_ENV:"production",PUBLIC_URL:"",REACT_APP_API_URL:"/api"}).REACT_APP_I18N_DEBUG,load:"currentOnly",returnEmptyString:!1,interpolation:{escapeValue:!1}}),ce.a.on("languageChanged",(function(e){document.documentElement.setAttribute("lang",e)}));ce.a,a(89),a(90),a(91);Z.apply(),o.a.render(r.a.createElement(n.Suspense,{fallback:r.a.createElement(B,null)},r.a.createElement(oe,null)),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((function(e){e.unregister()}))}},[[46,1,2]]]);
2
+ //# sourceMappingURL=main.f1b7c0d0.chunk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["assets/logo.svg","components/navBar.jsx","components/player/playerStatus.jsx","services/httpService.js","services/playerService.js","components/player/playStopControl.jsx","components/player/playerControls.jsx","components/player/player.jsx","components/common/loader.jsx","components/common/searchBox.jsx","services/streamsService.js","components/streams.jsx","components/player/miniPlayer.jsx","components/drawer.jsx","components/common/modal.jsx","components/common/select.jsx","theme.js","components/modals/settings.jsx","components/modals/urlDialog.jsx","services/configService.js","components/modals/about.jsx","components/common/backdrop.jsx","utils/style.js","App.js","serviceWorker.js","i18n.js","index.js"],"names":["module","exports","NavBar","t","useTranslation","className","href","src","logo","width","height","alt","type","data-toggle","data-target","aria-controls","aria-expanded","aria-label","id","PlayerStatus","playerStatus","playing","title","axios","defaults","baseURL","process","interceptors","response","use","error","expectedError","status","document","querySelector","toast","Promise","reject","get","post","apiEndpoint","body","method","params","FormData","set","play","http","stop","changeVol","delta","playRadio","name","playURL","url","PlayStopControl","onClick","icon","faStop","faPlay","PlayerControls","volDisabled","vol","renderVolButton","label","disabled","a","async","data","toastMsg","toastOpts","toastId","autoClose","volTimeout","isActive","update","render","info","handleVolChange","e","stopPropagation","faVolumeDown","faVolumeUp","Player","require","Loader","role","SearchBox","value","onChange","placeholder","autoComplete","target","onFocus","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","withTranslation","MiniPlayer","Drawer","open","toggle","handleClick","handleTouchStart","touchStartY","touches","clientY","handleTouchMove","touchEndY","changedTouches","touchUp","renderToggleButton","faChevronDown","faChevronUp","classList","add","remove","onTouchStart","onTouchMove","Modal","footer","children","defaultFooter","data-dismiss","tabIndex","aria-labelledby","aria-hidden","Select","row","rest","htmlFor","d","DEFAULT_THEME_ID","STORAGE_KEY","getThemePath","themeId","availableThemes","themeColor","apply","getCurrentId","themePath","find","setAttribute","localId","localStorage","getItem","filter","removeItem","change","setItem","Settings","i18n","useState","setThemeId","useEffect","theme","newThemeId","handleThemeChange","languages","language","changeLanguage","URLDialog","setURL","handleOK","doPlayURL","errorToastOpts","TYPE","ERROR","Fragment","input","onKeyDown","About","config","setConfig","setLoading","fetchData","tableData","mpd_host","mpd_port","environment","version","copyright","rel","style","color","item","scope","Backdrop","bodyStyled","contains","paddingRight","window","innerWidth","documentElement","clientWidth","App","networkError","backdrop","handleBackdrop","setTimeout","backdropTimeout","setDesktopPlayerTop","nav","desktopPlayer","navHeight","clientHeight","top","setDocumentTitle","updatePlayerStatus","updateInterval","con_mpd","Boolean","location","hostname","match","Backend","LanguageDetector","initReactI18next","init","fallbackLng","debug","REACT_APP_I18N_DEBUG","load","returnEmptyString","interpolation","escapeValue","on","lang","ReactDOM","fallback","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.f1b7c0d0.chunk.js","sourcesContent":["module.exports = __webpack_public_path__ + \"static/media/logo.91554ce9.svg\";","import React from 'react';\nimport { useTranslation } from 'react-i18next';\nimport './navBar.scss';\nimport logo from '../assets/logo.svg';\n\nconst NavBar = () => {\n const { t } = useTranslation();\n\n return (\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\n <button\n className=\"btn btn-link nav-link\"\n data-toggle=\"modal\"\n data-target=\"#about\"\n >\n {t('about')}\n </button>\n </div>\n </div>\n </nav>\n );\n};\nexport default NavBar;\n","import React from 'react';\nimport { useTranslation } from 'react-i18next';\n\nconst PlayerStatus = ({ playerStatus }) => {\n const { playing, title } = playerStatus;\n\n const { t } = useTranslation();\n\n return (\n <div className=\"text-center w-100\">\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 PlayerStatus;\n","import React from 'react';\nimport axios from 'axios';\nimport { toast } from 'react-toastify';\nimport { Translation } from 'react-i18next';\n\naxios.defaults.baseURL = process.env.REACT_APP_API_URL;\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 // Some errors cause our app to only render a backdrop warning.\n // Queuing toasts would be redundant.\n const hasToastify = document.querySelector('.Toastify');\n\n if (hasToastify) {\n if (!expectedError)\n toast.error(<Translation>{t => t('errorUnexpected')}</Translation>);\n\n // Universal expected error\n if (error.response && error.response.status === 403)\n toast.error(<Translation>{t => t('errorForbidden')}</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 = '/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 { useTranslation } from 'react-i18next';\nimport { changeVol } from '../../services/playerService';\nimport { volTimeout } from '../../config.json';\n\nconst PlayerControls = ({ playerStatus }) => {\n const { t } = useTranslation();\n\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\n className=\"player-controls btn-group w-100 m-2\"\n onClick={e => e.stopPropagation()}\n >\n {renderVolButton('-5', faVolumeDown, 'Volume down')}\n {renderVolButton('+5', faVolumeUp, 'Volume up')}\n <PlayStopControl playing={playerStatus.playing} />\n </div>\n );\n};\n\nexport default PlayerControls;\n","import React from 'react';\nimport PlayerStatus from './playerStatus';\nimport PlayerControls from './playerControls';\nimport './player.scss';\n\nconst Player = ({ playerStatus }) => {\n const renderLogo = () => (\n <img\n src={require('../../assets/logo.svg')}\n alt=\"Logo\"\n className=\"player-logo m-4\"\n />\n );\n\n return (\n <div className=\"player p-2\">\n <PlayerStatus playerStatus={playerStatus} />\n {renderLogo()}\n <PlayerControls playerStatus={playerStatus} />\n </div>\n );\n};\n\nexport default Player;\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 React from 'react';\nimport { useTranslation } from 'react-i18next';\n\nconst SearchBox = ({ value, onChange }) => {\n const { t } = useTranslation();\n\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 SearchBox;\n","import http from './httpService';\n\nconst apiEndpoint = '/streams';\n\nexport function getStreams() {\n return http.get(apiEndpoint);\n}\n","import React, { Component } from 'react';\nimport { withTranslation } from 'react-i18next';\nimport { toast } from 'react-toastify';\nimport Loader from './common/loader';\nimport SearchBox from './common/searchBox';\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 './miniPlayer.scss';\n\nconst MiniPlayer = ({ playerStatus }) => {\n const { title, playing } = playerStatus;\n\n return (\n <div className=\"mini-player p-2\">\n <div className=\"mini-player__left ellipsis\">\n <span>{title}</span>\n </div>\n <div className=\"mini-player__right\" onClick={e => e.stopPropagation()}>\n <PlayStopControl playing={playing} />\n </div>\n </div>\n );\n};\n\nexport default MiniPlayer;\n","import React, { Component } from 'react';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';\nimport MiniPlayer from './player/miniPlayer';\nimport Player from './player/player';\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 = () => {\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 renderToggleButton = () => (\n <button className=\"btn btn-primary-outline\" aria-label=\"Toggle player\">\n <FontAwesomeIcon\n icon={this.state.open ? faChevronDown : faChevronUp}\n className=\"drawer__toggler fa-lg\"\n />\n </button>\n );\n\n render() {\n const { playerStatus } = this.props;\n const { open } = this.state;\n\n let classes =\n 'drawer fixed-bottom bg-secondary border-top border-secondary p-2';\n\n if (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.renderToggleButton()}\n {open ? (\n <Player playerStatus={playerStatus} />\n ) : (\n <MiniPlayer playerStatus={playerStatus} />\n )}\n </div>\n );\n }\n}\n\nexport default Drawer;\n","import React from 'react';\nimport { useTranslation } from 'react-i18next';\n\nconst Modal = ({ id, title, footer, children }) => {\n const { t } = useTranslation();\n\n const defaultFooter = (\n <button className=\"btn btn-secondary\" data-dismiss=\"modal\">\n {t('close')}\n </button>\n );\n\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\">&times;</span>\n </button>\n </div>\n <div className=\"modal-body\">{children}</div>\n <div className=\"modal-footer\">{footer ? footer : defaultFooter}</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_ID = 'darkly';\n\nconst STORAGE_KEY = 'theme';\n\nconst getThemePath = themeId =>\n `https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/${themeId}/bootstrap.min.css`;\n\nconst availableThemes = [\n { id: 'darkly', name: 'Darkly', themeColor: '#375a7f' },\n { id: 'lux', name: 'Lux', themeColor: '#1a1a1a' }\n];\n\nfunction change(themeId) {\n localStorage.setItem(STORAGE_KEY, themeId);\n apply();\n}\n\nfunction apply() {\n const themeId = getCurrentId();\n const themePath = getThemePath(themeId);\n const themeColor = availableThemes.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\nfunction getCurrentId() {\n const localId = localStorage.getItem(STORAGE_KEY);\n\n if (localId === '') return DEFAULT_THEME_ID;\n if (availableThemes.filter(t => t.id === localId).length === 0) {\n localStorage.removeItem(STORAGE_KEY);\n return DEFAULT_THEME_ID;\n }\n\n return localId;\n}\n\nexport default {\n availableThemes,\n getCurrentId,\n change,\n apply\n};\n","import React, { useState, useEffect } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport Modal from '../common/modal';\nimport Select from '../common/select';\nimport theme from '../../theme';\nimport { languages } from '../../config.json';\n\nconst Settings = () => {\n const { t, i18n } = useTranslation();\n\n const [themeId, setThemeId] = useState('');\n\n useEffect(() => {\n setThemeId(theme.getCurrentId());\n }, []);\n\n const handleThemeChange = ({ target }) => {\n const newThemeId = target.value;\n theme.change(newThemeId);\n setThemeId(newThemeId);\n };\n\n return (\n <Modal id=\"settings\" title={t('settings')}>\n <Select\n id=\"theme-select\"\n label={t('theme')}\n data={theme.availableThemes}\n row\n value={themeId}\n onChange={e => handleThemeChange(e)}\n />\n <Select\n id=\"language-select\"\n label={t('language')}\n data={languages}\n row\n value={i18n.language}\n onChange={e => i18n.changeLanguage(e.target.value)}\n />\n </Modal>\n );\n};\n\nexport default Settings;\n","import React, { useState } from 'react';\nimport { toast } from 'react-toastify';\nimport { useTranslation } from 'react-i18next';\nimport Modal from '../common/modal';\nimport { playURL } from '../../services/playerService';\n\nconst URLDialog = () => {\n const [url, setURL] = useState('');\n\n const { t } = useTranslation();\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 {t('ok')}\n </button>\n <button\n className=\"btn btn-secondary\"\n onClick={() => setURL('')}\n data-dismiss=\"modal\"\n >\n {t('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 URLDialog;\n","import http from './httpService';\n\nconst apiEndpoint = '/config';\n\nexport function getConfig() {\n return http.get(apiEndpoint);\n}\n","import React from 'react';\nimport { useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport Modal from '../common/modal';\nimport { getConfig } from '../../services/configService';\nimport Loader from '../common/loader';\n\nconst About = () => {\n const { t } = useTranslation();\n\n const [config, setConfig] = useState({});\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n async function fetchData() {\n const { data: config } = await getConfig();\n setConfig(config);\n setLoading(false);\n }\n fetchData();\n }, []);\n\n const tableData = [\n { label: t('mpdHost'), value: config.mpd_host },\n { label: t('mpdPort'), value: config.mpd_port },\n { label: t('environment'), value: config.environment },\n { label: t('version'), value: config.version }\n ];\n\n const renderTable = () => {\n if (loading) return <Loader />;\n\n return (\n <table className=\"table\">\n <tbody>\n {tableData.map(item => (\n <tr key={item.label}>\n <th scope=\"row\">{item.label}</th>\n <td>{item.value}</td>\n </tr>\n ))}\n </tbody>\n </table>\n );\n };\n\n const copyright = (\n <p className=\"small mt-5\">\n Copyright &copy; 2017-2020&nbsp;\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 );\n \n return (\n <Modal id=\"about\" title={t('about')}>\n {renderTable()}\n {copyright}\n </Modal>\n );\n};\n\nexport default About;\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 { ToastContainer } from 'react-toastify';\nimport { withTranslation } from 'react-i18next';\nimport NavBar from './components/navBar';\nimport Player from './components/player/player';\nimport Streams from './components/streams';\nimport Drawer from './components/drawer';\nimport Settings from './components/modals/settings';\nimport URLDialog from './components/modals/urlDialog';\nimport About from './components/modals/about';\nimport Loader from './components/common/loader';\nimport Backdrop from './components/common/backdrop';\nimport { getStatus } from './services/playerService';\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 componentDidUpdate() {\n this.setDesktopPlayerTop();\n this.setDocumentTitle();\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 // Calculate and set top for Player on desktop view\n setDesktopPlayerTop = () => {\n const nav = document.querySelector('nav');\n const desktopPlayer = document.querySelector('.app-main > .player');\n if (!nav || !desktopPlayer) return;\n\n const navHeight = nav.clientHeight;\n const topMargin = 24;\n\n desktopPlayer.style.top = navHeight + topMargin + 'px';\n };\n\n setDocumentTitle = () => {\n const { playerStatus: status, networkError } = this.state;\n\n if (!networkError && status.playing)\n document.title = 'PiFi 🔊 ' + status.title;\n else document.title = 'PiFi Radio';\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 <URLDialog />\n <Settings />\n <About />\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 load: 'currentOnly',\n returnEmptyString: false,\n\n interpolation: {\n escapeValue: false // not needed for react as it escapes by default\n }\n });\n\n// Set language on html tag\ni18n.on('languageChanged', lang => {\n document.documentElement.setAttribute('lang', lang);\n});\n\nexport default i18n;\n","import React, { Suspense } from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\nimport Loader from './components/common/loader';\nimport * as serviceWorker from './serviceWorker';\nimport './i18n';\nimport theme from './theme';\nimport './index.scss';\n// Bootstrap dependencies\nimport 'jquery/dist/jquery.min.js';\nimport 'bootstrap/dist/js/bootstrap.js';\n\ntheme.apply();\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
@@ -1,3 +1,3 @@
1
1
  module PiFi
2
- VERSION = "0.4.1".freeze
2
+ VERSION = "0.4.2".freeze
3
3
  end
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.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Cavalcanti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-23 00:00:00.000000000 Z
11
+ date: 2020-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
@@ -117,6 +117,7 @@ files:
117
117
  - lib/pifi/public/locales/en/translation.json
118
118
  - lib/pifi/public/locales/fr-FR/translation.json
119
119
  - lib/pifi/public/locales/nl-NL/translation.json
120
+ - lib/pifi/public/locales/pl-PL/translation.json
120
121
  - lib/pifi/public/locales/pt-BR/translation.json
121
122
  - lib/pifi/public/manifest.json
122
123
  - lib/pifi/public/mstile-144x144.png
@@ -124,19 +125,19 @@ files:
124
125
  - lib/pifi/public/mstile-310x150.png
125
126
  - lib/pifi/public/mstile-310x310.png
126
127
  - lib/pifi/public/mstile-70x70.png
127
- - lib/pifi/public/precache-manifest.91762315d3372889e680b8497d590c91.js
128
+ - lib/pifi/public/precache-manifest.5dbdf662d076c5f2d13288a700dab9d6.js
128
129
  - lib/pifi/public/robots.txt
129
130
  - lib/pifi/public/safari-pinned-tab.svg
130
131
  - lib/pifi/public/service-worker.js
131
132
  - lib/pifi/public/static/css/2.5dbdccff.chunk.css
132
133
  - lib/pifi/public/static/css/2.5dbdccff.chunk.css.map
133
- - lib/pifi/public/static/css/main.75888152.chunk.css
134
- - lib/pifi/public/static/css/main.75888152.chunk.css.map
135
- - lib/pifi/public/static/js/2.9b0b7c5f.chunk.js
136
- - lib/pifi/public/static/js/2.9b0b7c5f.chunk.js.LICENSE
137
- - lib/pifi/public/static/js/2.9b0b7c5f.chunk.js.map
138
- - lib/pifi/public/static/js/main.7d7142ee.chunk.js
139
- - lib/pifi/public/static/js/main.7d7142ee.chunk.js.map
134
+ - lib/pifi/public/static/css/main.93c9cd50.chunk.css
135
+ - lib/pifi/public/static/css/main.93c9cd50.chunk.css.map
136
+ - lib/pifi/public/static/js/2.89cb5725.chunk.js
137
+ - lib/pifi/public/static/js/2.89cb5725.chunk.js.LICENSE
138
+ - lib/pifi/public/static/js/2.89cb5725.chunk.js.map
139
+ - lib/pifi/public/static/js/main.f1b7c0d0.chunk.js
140
+ - lib/pifi/public/static/js/main.f1b7c0d0.chunk.js.map
140
141
  - lib/pifi/public/static/js/runtime-main.f04a0f25.js
141
142
  - lib/pifi/public/static/js/runtime-main.f04a0f25.js.map
142
143
  - lib/pifi/public/static/media/logo.91554ce9.svg
@@ -1,2 +0,0 @@
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{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:100%}.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;flex-direction:column;align-items:center;justify-content:space-around}@supports not (-ms-ime-align:auto){.drawer--open{justify-content:space-evenly}}@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-around}@supports not (-ms-ime-align:auto){.app-main{justify-content:space-evenly}}.app-main>.player{position:-webkit-sticky;position:sticky;top:4rem}.drawer{visibility:hidden}@media (max-width:750px){.drawer{visibility:visible}}@media (max-width:750px){.app-main>.player{display:none}}body{height:100vh;overflow-y:scroll}#root{height:100%}button[disabled]{cursor:default}.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
2
- /*# sourceMappingURL=main.75888152.chunk.css.map */
@@ -1 +0,0 @@
1
- {"version":3,"sources":["navBar.scss","playerControls.scss","player.scss","loader.scss","streams.scss","miniPlayer.scss","drawer.scss","../styles/_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,WAAY,CACZ,gBAAiB,CACjB,WAAY,CACZ,eAAgB,CAChB,6DACA,CAAA,qBAAsB,CCNxB,QACE,UAAW,CACX,YAAa,CACb,sBAAuB,CACvB,kBAAmB,CACnB,WAAY,CCHd,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,qBAAsB,CACtB,kBAAmB,CACnB,4BAA6B,CAGM,mCARrC,cASI,4BAA6B,CAEhC,CAMD,yBADF,cAEI,eAAgB,CAEnB,CE7BD,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,CAGM,mCALrC,UAMI,4BAA6B,CAEhC,CAED,kBACE,uBAAgB,CAAhB,eAAgB,CAChB,QAAS,CACV,QAGC,iBAAkB,CAElB,yBAHF,QAII,kBAAmB,CAEtB,CAGC,yBADF,kBAEI,YAAa,CAEhB,CC7BD,KACE,YAAa,CACb,iBAAkB,CACnB,MAGC,WAAY,CACb,iBAGC,cAAe,CAChB,UAGC,kBAAmB,CACnB,eAAgB,CAChB,sBAAuB","file":"main.75888152.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 height: 70vh;\n min-height: 500px;\n width: 400px;\n min-width: 300px;\n background: url('../../assets/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: 100%;\n}\n","@import '../styles/variables';\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 '../styles/variables';\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 flex-direction: column;\n align-items: center;\n justify-content: space-around;\n\n // Not Edge 18-. It partially supports space-evenly.\n @supports not (-ms-ime-align: auto) {\n justify-content: space-evenly;\n }\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 './styles/variables';\n\n.app-main {\n display: flex;\n justify-content: space-around;\n\n // Not Edge 18-. It partially supports space-evenly.\n @supports not (-ms-ime-align: auto) {\n justify-content: space-evenly;\n }\n}\n\n.app-main > .player {\n position: sticky;\n top: 4rem; // we set this dynamically\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 height: 100vh;\n overflow-y: scroll; // so elements don't move if page fits view\n}\n\n#root {\n height: 100%;\n}\n\nbutton[disabled] {\n cursor: default;\n}\n\n.ellipsis {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n"]}