mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-22 13:07:59 +01:00
Basic mobile support (#81)
Mobile specific view. Basically Pony.fm desktop squashed into a mobile view
This commit is contained in:
parent
87d25bb8b8
commit
ca397f09fc
12 changed files with 311 additions and 40 deletions
BIN
public/images/icons/app-icon-144.png
Normal file
BIN
public/images/icons/app-icon-144.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
public/images/icons/app-icon-192.png
Normal file
BIN
public/images/icons/app-icon-192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
public/images/icons/app-icon-96.png
Normal file
BIN
public/images/icons/app-icon-96.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
24
public/manifest.json
Normal file
24
public/manifest.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"short_name": "Pony.fm",
|
||||||
|
"name": "Pony.fm",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "images/icons/app-icon-96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "images/icons/app-icon-144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "images/icons/app-icon-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "portrait"
|
||||||
|
}
|
47
public/service-worker.js
Normal file
47
public/service-worker.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// Pony.fm - A community for pony fan music.
|
||||||
|
// Copyright (C) 2016 Josef Citrine
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
var urlsToCache = [
|
||||||
|
'/',
|
||||||
|
'/build/styles/app.css',
|
||||||
|
'/build/scripts/app.js',
|
||||||
|
'/build/scripts/templates.js',
|
||||||
|
'/templates/directives/player.html',
|
||||||
|
'/templates/directives/search.html',
|
||||||
|
'/templates/directives/tracks-list.html',
|
||||||
|
'/templates/directives/users-list.html',
|
||||||
|
'/templates/directives/albums-list.html',
|
||||||
|
'/templates/directives/playlists-list.html',
|
||||||
|
'/templates/home/index.html',
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
var CACHE_NAME = 'pfm-cache-v1';
|
||||||
|
|
||||||
|
// Set the callback for the install step
|
||||||
|
self.addEventListener('install', function(event) {
|
||||||
|
// Doesn't do anything right now
|
||||||
|
// Could never get offline to fully
|
||||||
|
// work without bugs :(
|
||||||
|
|
||||||
|
// Perform install steps
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then(function(cache) {
|
||||||
|
console.log('Opened cache');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
|
@ -21,9 +21,20 @@ module.exports = angular.module('ponyfm').controller "application", [
|
||||||
$scope.$state = $state
|
$scope.$state = $state
|
||||||
$scope.$stateParams = $stateParams
|
$scope.$stateParams = $stateParams
|
||||||
$scope.isPinnedPlaylistSelected = false
|
$scope.isPinnedPlaylistSelected = false
|
||||||
|
$scope.menuActive = false
|
||||||
$loadingElement = null
|
$loadingElement = null
|
||||||
loadingStateName = null
|
loadingStateName = null
|
||||||
|
|
||||||
|
if 'serviceWorker' of navigator
|
||||||
|
console.log 'Service Worker is supported'
|
||||||
|
navigator.serviceWorker.register('service-worker.js').then((reg) ->
|
||||||
|
console.log 'SW registered', reg
|
||||||
|
).catch (err) ->
|
||||||
|
console.log 'SW register failed', err
|
||||||
|
|
||||||
|
$scope.menuToggle = () ->
|
||||||
|
$scope.menuActive = !$scope.menuActive
|
||||||
|
|
||||||
if window.pfm.error
|
if window.pfm.error
|
||||||
$state.transitionTo 'errors-' + window.pfm.error
|
$state.transitionTo 'errors-' + window.pfm.error
|
||||||
|
|
||||||
|
@ -60,6 +71,7 @@ module.exports = angular.module('ponyfm').controller "application", [
|
||||||
|
|
||||||
statesPreloaded = {}
|
statesPreloaded = {}
|
||||||
$scope.$on '$stateChangeStart', (e, newState, newParams, oldState, oldParams) ->
|
$scope.$on '$stateChangeStart', (e, newState, newParams, oldState, oldParams) ->
|
||||||
|
$scope.menuActive = false
|
||||||
$scope.isPinnedPlaylistSelected = false
|
$scope.isPinnedPlaylistSelected = false
|
||||||
|
|
||||||
if newState.name == 'content.playlist'
|
if newState.name == 'content.playlist'
|
||||||
|
|
|
@ -16,8 +16,14 @@
|
||||||
|
|
||||||
window.handleResize = () ->
|
window.handleResize = () ->
|
||||||
windowHeight = $(window).height()
|
windowHeight = $(window).height()
|
||||||
|
windowWidth = $(window).width()
|
||||||
|
isMobile = windowWidth <= 480
|
||||||
$siteBody = $ '.site-body'
|
$siteBody = $ '.site-body'
|
||||||
$siteBody.height windowHeight - $('header').height()
|
|
||||||
|
if isMobile
|
||||||
|
$siteBody.height windowHeight - $('.now-playing').height() * 2
|
||||||
|
else
|
||||||
|
$siteBody.height windowHeight - $('header').height()
|
||||||
|
|
||||||
$('.dropdown-menu').each () ->
|
$('.dropdown-menu').each () ->
|
||||||
$this = $ this
|
$this = $ this
|
||||||
|
@ -26,10 +32,11 @@ window.handleResize = () ->
|
||||||
'max-height': newMaxHeight
|
'max-height': newMaxHeight
|
||||||
|
|
||||||
$('.stretch-to-bottom').each () ->
|
$('.stretch-to-bottom').each () ->
|
||||||
$this = $ this
|
if !isMobile
|
||||||
newHeight = windowHeight - $this.offset().top
|
$this = $ this
|
||||||
if newHeight > 0
|
newHeight = windowHeight - $this.offset().top
|
||||||
$this.height newHeight
|
if newHeight > 0
|
||||||
|
$this.height newHeight
|
||||||
|
|
||||||
$('.revealable').each () ->
|
$('.revealable').each () ->
|
||||||
$this = $ this
|
$this = $ this
|
||||||
|
|
1
resources/assets/styles/app.less
vendored
1
resources/assets/styles/app.less
vendored
|
@ -36,3 +36,4 @@
|
||||||
@import 'components/uploader';
|
@import 'components/uploader';
|
||||||
@import 'components/search';
|
@import 'components/search';
|
||||||
@import 'components/track-editor';
|
@import 'components/track-editor';
|
||||||
|
@import 'mobile';
|
||||||
|
|
101
resources/assets/styles/layout.less
vendored
101
resources/assets/styles/layout.less
vendored
|
@ -31,35 +31,7 @@ html body {
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
> a {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
width: (@pfm-sidebar-size);
|
|
||||||
height: 64px;
|
|
||||||
line-height: 42px;
|
|
||||||
background: #84528A;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 24pt;
|
|
||||||
font-weight: lighter;
|
|
||||||
position: relative;
|
|
||||||
z-index: 600;
|
|
||||||
font-family: 'Josefin Sans', sans-serif;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: darken(#84528A, 25%);
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.logo {
|
|
||||||
padding-left: 6px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 90%;
|
|
||||||
padding-top: 13px;
|
|
||||||
max-height: 45%
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-details {
|
.user-details {
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -116,15 +88,46 @@ header {
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
> a {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
width: (@pfm-sidebar-size);
|
||||||
|
height: 64px;
|
||||||
|
line-height: 42px;
|
||||||
|
background: #84528A;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 24pt;
|
||||||
|
font-weight: lighter;
|
||||||
|
position: relative;
|
||||||
|
z-index: 600;
|
||||||
|
font-family: 'Josefin Sans', sans-serif;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: darken(#84528A, 25%);
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.logo {
|
||||||
|
padding-left: 6px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 90%;
|
||||||
|
padding-top: 13px;
|
||||||
|
max-height: 45%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
background: #515151;
|
background: #515151;
|
||||||
width: @pfm-sidebar-size;
|
width: @pfm-sidebar-size;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
float: left;
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
@ -251,3 +254,41 @@ header {
|
||||||
.chart-btn-container {
|
.chart-btn-container {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.burger-wrapper {
|
||||||
|
top:20px;
|
||||||
|
left: 20px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.burger {
|
||||||
|
width: 25px;
|
||||||
|
height: 20px;
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bun-top,.meat,.bun-bottom {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: white;
|
||||||
|
width: 25px;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bun-top {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meat {
|
||||||
|
top: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bun-bottom {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
127
resources/assets/styles/mobile.less
vendored
Normal file
127
resources/assets/styles/mobile.less
vendored
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
@media only screen and (max-width: 480px) {
|
||||||
|
html, body {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header {
|
||||||
|
margin-top: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-content {
|
||||||
|
margin-left: 0px;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.now-playing {
|
||||||
|
margin-left: 0px;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1010;
|
||||||
|
box-shadow: 0 0 8px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-details {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1002;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-header {
|
||||||
|
display: block !important;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1001;
|
||||||
|
background: #84528A;
|
||||||
|
height: 64px;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
height: 40px;
|
||||||
|
width: 160px;
|
||||||
|
padding-top: 16px;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
max-height: 75%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
left: -170px;
|
||||||
|
transition: transform 0.5s ease;
|
||||||
|
transform: translateX(0px) translateZ(0);
|
||||||
|
perspective: 1000;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
will-change: transform;
|
||||||
|
z-index: 1011;
|
||||||
|
height: ~"calc(100% - 64px)";
|
||||||
|
box-shadow: 0 8px 8px rgba(0,0,0,0.5);
|
||||||
|
|
||||||
|
> a {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.active {
|
||||||
|
transform: translateX(170px) translateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-details > .avatar {
|
||||||
|
border: 0 !important;
|
||||||
|
box-shadow: initial !important;
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-player {
|
||||||
|
margin-right: 0px !important;
|
||||||
|
.buttons {
|
||||||
|
li.volume, li.status {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.search-input {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdowns {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
> li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
> .btn {
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.details-columns {
|
||||||
|
.cboxElement {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
float: none;
|
||||||
|
padding: 0px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
margin-right: 0px;
|
||||||
|
margin-left: 0px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,9 +42,16 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<a href="/">
|
<div class="mobile-header">
|
||||||
<img src="/images/ponyfm-logo-white.svg" class="logo">
|
<div class="burger-wrapper" ng-click="menuToggle()">
|
||||||
</a>
|
<div class="burger">
|
||||||
|
<div class="bun-top"></div>
|
||||||
|
<div class="meat"></div>
|
||||||
|
<div class="bun-bottom"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="/" class="logo"><img src="/images/ponyfm-logo-white.svg"></a>
|
||||||
|
</div>
|
||||||
<div class="now-playing">
|
<div class="now-playing">
|
||||||
@if (Auth::check())
|
@if (Auth::check())
|
||||||
<div class="user-details dropdown">
|
<div class="user-details dropdown">
|
||||||
|
@ -65,7 +72,10 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="site-body">
|
<div class="site-body">
|
||||||
<ul class="sidebar" ng-controller="sidebar">
|
<ul class="sidebar" ng-controller="sidebar" ng-class="{'active': menuActive}">
|
||||||
|
<a href="/">
|
||||||
|
<img src="/images/ponyfm-logo-white.svg" class="logo">
|
||||||
|
</a>
|
||||||
<li><pfm-search></pfm-search></li>
|
<li><pfm-search></pfm-search></li>
|
||||||
<li ng-class="{selected: stateIncludes('content.tracks') || stateIncludes('content.track')}"><a href="/tracks">Tracks</a></li>
|
<li ng-class="{selected: stateIncludes('content.tracks') || stateIncludes('content.track')}"><a href="/tracks">Tracks</a></li>
|
||||||
<li ng-class="{selected: stateIncludes('content.albums') || stateIncludes('content.album')}"><a href="/albums">Albums</a></li>
|
<li ng-class="{selected: stateIncludes('content.albums') || stateIncludes('content.album')}"><a href="/albums">Albums</a></li>
|
||||||
|
|
|
@ -21,7 +21,9 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
<title ng-bind="title">Pony.fm</title>
|
<title ng-bind="title">Pony.fm</title>
|
||||||
<meta name="description" content="@{{ description }}" />
|
<meta name="description" content="@{{ description }}" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||||
|
<meta name="theme-color" content="#84528A" />
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
<base href="/" />
|
<base href="/" />
|
||||||
|
|
||||||
@yield('styles')
|
@yield('styles')
|
||||||
|
|
Loading…
Reference in a new issue