New loading bar and track filters

This commit is contained in:
Josef Citrine 2016-08-04 03:42:46 +01:00
parent 1fb70bbfa5
commit eb0dd46648
16 changed files with 169 additions and 285 deletions

View file

@ -1,44 +1,49 @@
<form ng-submit="updateAccount()" class="pfm-form account-settings-form">
<ul class="toolbar">
<li>
<button type="submit" class="btn btn-default" ng-class="{disabled: !isDirty || isSaving, 'btn-primary': isDirty}">
Save Changes
<i ng-show="isSaving" class="fa fa-cog fa-spin fa-lg"></i>
</button>
</li>
</ul>
<div class="stretch-to-bottom">
<div class="form-row" ng-class="{'has-error': errors.display_name != null}">
<label class="strong" for="display_name">Display Name</label>
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" placeholder="Display Name" id="display_name" ng-model="settings.display_name" />
<div class="error">{{errors.display_name}}</div>
</div>
<form ng-submit="updateAccount()" class="pfm-form account-settings-form thin">
<div layout="row" class="button-bar">
<md-button class="md-raised md-primary" type="submit" ng-class="{disabled: !isDirty || isSaving, 'btn-primary': isDirty}">
Save Changes
</button>
</div>
<div class="form-row" ng-class="{'has-error': errors.slug != null}">
<label class="strong" for="slug">Slug (your profile URL: https://pony.fm/{{settings.slug}})</label>
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" placeholder="slug" id="slug" ng-model="settings.slug" />
<div class="error">{{errors.slug}}</div>
</div>
<div layout="row" layout-xs="column">
<md-input-container flex-gt-sm>
<label>Display Name</label>
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" id="display_name" ng-model="settings.display_name" />
</md-input-container>
<div class="form-row">
<label for="can_see_explicit_content" class="strong"><input ng-change="touchModel()" ng-disabled="isLoading" id="can_see_explicit_content" type="checkbox" ng-model="settings.can_see_explicit_content" /> Can See Explicit Content</label>
</div>
<md-input-container flex-gt-sm>
<label>Profile URL</label>
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" id="slug" ng-model="settings.slug" />
</md-input-container>
</div>
<div class="form-row" ng-class="{'has-error': errors.bio != null}">
<label class="strong" for="bio">Bio</label>
<textarea id="bio" placeholder="bio (optional)" ng-model="settings.bio" ng-disabled="isLoading" ng-change="touchModel()"></textarea>
<div class="error">{{errors.description}}</div>
</div>
<div layout="row" layout-xs="column">
<md-input-container class="md-block" flex-gt-sm>
<label>Bio</label>
<textarea id="bio" ng-model="settings.bio" ng-disabled="isLoading" ng-change="touchModel()"></textarea>
</md-input-container>
</div>
<div class="form-row" ng-class="{'has-error': errors.avatar != null || errors.gravatar != null}">
<label for="uses_gravatar" class="strong">
<input ng-change="touchModel()" ng-disabled="isLoading" id="uses_gravatar" type="checkbox" ng-model="settings.uses_gravatar" /> Use Gravatar
</label>
<div ng-show="!settings.uses_gravatar">
<pfm-image-upload set-image="setAvatar" image="settings.avatar_url" user-id="settings.id"></pfm-image-upload>
</div>
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" ng-show="settings.uses_gravatar" placeholder="Gravatar Email" ng-model="settings.gravatar" />
<div class="error" ng-show="errors.avatar != null">{{errors.avatar}}</div>
<div class="error" ng-show="errors.gravatar != null">{{errors.gravatar}}</div>
</div>
<div layout="row" layout-xs="column">
<md-input-container flex-gt-sm>
<md-checkbox ng-disabled="isLoading" ng-model="settings.can_see_explicit_content"> Can See Explicit Content</md-checkbox>
</md-input-container>
</div>
<div layout="row" layout-xs="column">
<md-input-container flex-gt-sm>
<md-checkbox ng-change="touchModel()" ng-disabled="isLoading" id="uses_gravatar" type="checkbox" ng-model="settings.uses_gravatar">Use Gravatar</md-checkbox>
</md-input-container>
</div>
<div layout="row" layout-xs="column" ng-show="settings.uses_gravatar">
<md-input-container flex-gt-sm>
<label>Gravatar email</label>
<input type="text" ng-disabled="isSaving" ng-model="settings.gravatar" />
</md-input-container>
</div>
<div layout="row" layout-xs="column" ng-show="!settings.uses_gravatar">
<md-input-container flex-gt-sm>
<pfm-image-upload set-image="setAvatar" image="settings.avatar_url" user-id="settings.id"></pfm-image-upload>
</md-input-container>
</div>
</form>

View file

@ -1,40 +1,17 @@
<div><!--<ul class="albums-listing {{::class}}">
<li ng-repeat="album in albums track by album.id">
<a ng-href="{{::album.url}}">
<img class="image" pfm-src-loader="::album.covers.normal" pfm-src-size="normal" />
<span class="info">
<span class="title">{{::album.title}}</span>
<span class="published">
by <span>{{::album.user.name}}</span>
</span>
<span class="stats">
<strong>{{::album.stats.favourites}}</strong> <i class="material-icons">favorite</i>
<strong>{{::album.stats.comments}}</strong> <i class="material-icons">comment</i>
<strong>{{::album.stats.downloads}}</strong> <i class="material-icons">file_download</i>
</span>
</span>
</a>
</li>
<li ng-if="!albums.length" class="empty">
No albums found&hellip;
</li>
</ul>-->
<div layout="row" layout-xs="column" layout-wrap>
<a ng-repeat="album in albums track by album.id" ng-href="{{::album.url}}" class="clickable" flex-gt-xs="50" flex-gt-sm="25" flex-gt-md="20">
<md-card class="album">
<img class="md-card-image" pfm-src-loader="::album.covers.normal" pfm-src-size="normal">
<div flex-xs="50" flex-gt-xs="25" layout="column">
<a ng-repeat="album in albums track by album.id" ng-href="{{::album.url}}" class="clickable">
<md-card class="album">
<img class="md-card-image" pfm-src-loader="::album.covers.normal" pfm-src-size="normal">
<md-card-content>
<span>{{::album.title}}</span>
</md-card-content>
<md-card-content>
<span>{{::album.title}}</span>
</md-card-content>
<md-card-actions layout="row" layout-align="end center" class="album-stats">
<span><md-icon>favorite</md-icon> {{::album.stats.favourites}}</span>
<span><md-icon>comment</md-icon> {{::album.stats.comments}}</span>
<span><md-icon>file_download</md-icon> {{::album.stats.downloads}}</span>
</md-card-actions>
</md-card>
<md-card-actions layout="row" layout-align="end center" class="album-stats">
<span><md-icon>favorite</md-icon> {{::album.stats.favourites}}</span>
<span><md-icon>comment</md-icon> {{::album.stats.comments}}</span>
<span><md-icon>file_download</md-icon> {{::album.stats.downloads}}</span>
</md-card-actions>
</md-card>
</a>
</div>
</div>

View file

@ -7,7 +7,6 @@
<span ng-hide="track.is_published">
Publish Track
</span>
<i ng-show="isSaving" class="fa fa-cog fa-spin fa-lg"></i>
</md-button>
<div flex="5" hide-xs hide-sm>

View file

@ -1,69 +1,42 @@
<ul class="dropdowns">
<li class="dropdown filterable" ng-class="{'has-filter': query.filters.trackTypes.selectedArray.length}">
<a class="dropdown-toggle btn btn-default" bs-dropdown>
Type: <strong>{{query.filters.trackTypes.title}}</strong>
</a>
<a class="btn btn-default" pfm-eat-click ng-click="clearFilter('trackTypes')"><i class="material-icons">remove</i></a>
<ul class="dropdown-menu">
<li ng-repeat="type in ::filters.trackTypes.values track by type.id"
ng-class="{selected: query.isIdSelected('trackTypes', type.id)}"
>
<a class="dont-close" pfm-eat-click href="#" ng-click="toggleListFilter('trackTypes', type.id); $event.stopPropagation();"><i class="material-icons">add</i></a>
<a pfm-eat-click href="#" ng-click="setListFilter('trackTypes', type.id);">{{::type.title}}</a>
</li>
</ul>
</li>
<li class="dropdown filterable" ng-class="{'has-filter': query.filters.showSongs.selectedArray.length}">
<a class="dropdown-toggle btn btn-default" bs-dropdown>
Show Songs: <strong>{{query.filters.showSongs.title}}</strong>
</a>
<a class="btn btn-default" pfm-eat-click ng-click="clearFilter('showSongs')"><i class="material-icons">remove</i></a>
<ul class="dropdown-menu">
<li ng-repeat="song in ::filters.showSongs.values track by song.id"
ng-class="{selected: query.isIdSelected('showSongs', song.id)}"
>
<a class="dont-close" pfm-eat-click href="#" ng-click="toggleListFilter('showSongs', song.id); $event.stopPropagation();"><i class="material-icons">add</i></a>
<a pfm-eat-click href="#" ng-click="setListFilter('showSongs', song.id);">{{::song.title}}</a>
</li>
</ul>
</li>
<li class="dropdown filterable" ng-class="{'has-filter': query.filters.genres.selectedArray.length}">
<a class="dropdown-toggle btn btn-default" bs-dropdown>
Genre: <strong>{{query.filters.genres.title}}</strong>
</a>
<a class="btn btn-default" pfm-eat-click ng-click="clearFilter('genres')"><i class="material-icons">remove</i></a>
<ul class="dropdown-menu">
<li ng-repeat="genre in ::filters.genres.values track by genre.id" ng-class="{selected: query.isIdSelected('genres', genre.id)}">
<a class="dont-close" pfm-eat-click href="#" ng-click="toggleListFilter('genres', genre.id); $event.stopPropagation();"><i class="material-icons">add</i></a>
<a pfm-eat-click href="#" ng-click="setListFilter('genres', genre.id);">{{::genre.title}}</a>
</li>
</ul>
</li>
<li class="dropdown filterable" ng-class="{'has-filter': !query.filters.isVocal.isDefault}">
<a class="dropdown-toggle btn btn-default" bs-dropdown>
Is Vocal: <strong>{{query.filters.isVocal.title}}</strong>
</a>
<a class="btn btn-default" pfm-eat-click ng-click="clearFilter('isVocal')"><i class="material-icons">remove</i></a>
<ul class="dropdown-menu">
<li ng-repeat="item in ::filters.isVocal.values track by $index"
ng-class="{selected: item == query.filters.isVocal}"
>
<a pfm-eat-click href="#" ng-click="setFilter('isVocal', item);">{{::item.title}}</a>
</li>
</ul>
</li>
<li class="dropdown filterable" ng-class="{'has-filter': !query.filters.sort.isDefault}">
<a class="dropdown-toggle btn btn-default" bs-dropdown>
Order: <strong>{{query.filters.sort.title}}</strong>
</a>
<a class="btn btn-default" pfm-eat-click ng-click="clearFilter('sort')"><i class="material-icons">remove</i></a>
<ul class="dropdown-menu">
<li ng-repeat="filter in ::filters.sort.values track by $index" ng-class="{selected: filter == query.filters.sort}">
<a pfm-eat-click href="#" ng-click="setFilter('sort', filter)">{{::filter.title}}</a>
</li>
</ul>
</li>
</ul>
<div layout="row" layout-wrap flex flex-gt-md="50">
<md-input-container flex flex-xs="50">
<label>Type</label>
<md-select ng-model="type" md-on-close="filterDropdownAfterClose(type, 'trackTypes')" multiple>
<md-option value="any" selected>Any</md-option>
<md-option ng-repeat="type in ::filters.trackTypes.values track by type.id" value="{{type.id}}">{{::type.title}}</md-option>
</md-select>
</md-input-container>
<md-input-container flex flex-xs="50">
<label>Show songs</label>
<md-select ng-model="showSongs" md-on-close="filterDropdownAfterClose(showSongs, 'showSongs')" multiple>
<md-option value="any" selected>Any</md-option>
<md-option ng-repeat="song in ::filters.showSongs.values track by song.id" value="{{song.id}}">{{::song.title}}</md-option>
</md-select>
</md-input-container>
<md-input-container flex flex-xs="50">
<label>Genre</label>
<md-select ng-model="genres" md-on-close="filterDropdownAfterClose(genres, 'genres')" multiple>
<md-option value="any" selected>Any</md-option>
<md-option ng-repeat="genre in ::filters.genres.values track by genre.id" value="{{genre.id}}">{{::genre.title}}</md-option>
</md-select>
</md-input-container>
<md-input-container flex flex-xs="50">
<label>Has vocals</label>
<md-select ng-model="isVocal" md-on-close="handleSingularDropdown('isVocal', isVocal)">
<md-option ng-repeat="item in ::filters.isVocal.values track by $index" value="{{item}}">{{::item.title}}</md-option>
</md-select>
</md-input-container>
<md-input-container flex>
<label>Order by</label>
<md-select ng-model="order" md-on-close="handleSingularDropdown('sort', order)">
<md-option ng-repeat="filter in ::filters.sort.values track by $index" value="{{filter}}">{{::filter.title}}</md-option>
</md-select>
</md-input-container>
</div>
<div class="pagination" ng-if="totalPages > 1">
<ul>

View file

@ -83,9 +83,7 @@ module.exports = angular.module('ponyfm').controller "application", [
$scope.$on '$viewContentLoaded', () ->
window.setTimeout (-> window.handleResize()), 0
if $loadingElement
$loadingElement.removeClass 'loading'
$loadingElement = null
$scope.loading = false
$scope.stateIncludes = (state) ->
if $loadingElement
@ -129,14 +127,8 @@ module.exports = angular.module('ponyfm').controller "application", [
newParts = newState.name.split '.'
oldParts = oldState.name.split '.'
zipped = _.zip(newParts, oldParts)
for i in [0..zipped.length]
break if !zipped[i] || zipped[i][0] != zipped[i][1]
selector += ' ui-view '
selector += ' ui-view ' if newState.name != oldState.name
$loadingElement = $ selector
$loadingElement.addClass 'loading'
$scope.loading = true;
stateToInject = angular.copy newState
stateToInject.params = newParams

View file

@ -28,6 +28,45 @@ module.exports = angular.module('ponyfm').controller "tracks", [
$scope.recentTracks = null
$scope.query = tracks.mainQuery
$scope.filters = tracks.filters
$scope.isVocal = JSON.stringify($scope.query.filters.isVocal)
$scope.order = JSON.stringify($scope.query.filters.sort)
filterDropdown = (newVar, oldVar) ->
if newVar instanceof Array && oldVar instanceof Array
if 'any' in newVar
if !('any' in oldVar)
newVar = ['any']
else if newVar.length > oldVar.length
anyIndex = newVar.indexOf('any')
newVar.splice(anyIndex, 1)
$scope.handleSingularDropdown = (filter, value) ->
$scope.clearFilter filter
$scope.setFilter filter, JSON.parse(value)
$scope.filterDropdownAfterClose = (values, filter) ->
$scope.clearFilter filter
anyOption = 'any'
if anyOption in values && values.length > 1
anyIndex = values.indexOf(anyOption)
values.splice(anyIndex, 1)
for id in values
if id != anyOption
if filter == 'isVocal'
$scope.setFilter filter, id
else
$scope.toggleListFilter filter, id
$scope.$watch 'type', (newVar, oldVar) ->
filterDropdown newVar, oldVar
$scope.$watch 'showSongs', (newVar, oldVar) ->
filterDropdown newVar, oldVar
$scope.$watch 'genres', (newVar, oldVar) ->
filterDropdown newVar, oldVar
$scope.toggleListFilter = (filter, id) ->
$scope.query.toggleListFilter filter, id

View file

@ -64,3 +64,6 @@ window.handleResize()
$(window).resize window.handleResize
$('.site-content').empty()
$(document).ready () ->
$('body').removeClass('loading');

View file

@ -27,7 +27,7 @@
@import 'account-albums';
@import 'admin';
@import 'components/components';
//@import 'forms';
@import 'forms';
@import 'animations';
@import 'body';
@import 'components/player';

View file

@ -27,6 +27,12 @@ a {
}
}
.loading {
md-menu-content {
display: none;
}
}
.site-content{
.box-sizing(border-box);
padding: 10px;

View file

@ -694,6 +694,10 @@ canvas {
text-decoration: none !important;
}
form.thin md-input-container {
margin: 15px 0 0 0;
}
md-card.album {
md-card-content {
padding: 4px 12px;
@ -715,3 +719,7 @@ md-card.album {
}
}
}
.button-bar {
margin-bottom: 10px;
}

View file

@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@import 'https://fonts.googleapis.com/css?family=Material+Icons';
@import 'base/bootstrap/bootstrap';
@import 'variables';
@import 'mixins';

View file

@ -18,44 +18,6 @@
@import "base/bootstrap/bootstrap";
.form-row {
margin-bottom: 10px;
.error {
.alert();
.alert-danger();
display: none;
padding: 3px;
font-size: 8pt;
}
.alert {
padding: 3px;
font-size: 8pt;
}
}
.track-editor {
.row [class*=col]:first-child {
margin-left: 0;
}
.row {
margin: 0;
}
.row .col-sm-12 {
padding: 0;
}
.row .col-sm-6 {
padding: 0;
width: 48.93617021%;
margin-left: 2.12765957%;
}
}
.has-error {
label {
color: @brand-danger;
@ -66,29 +28,7 @@
}
}
label.strong {
font-size: 8pt;
font-weight: bold;
}
input[type="text"], input[type="password"], input[type="date"], input[type="number"], textarea {
padding: 3px;
border: 1px solid;
border-color: #9c9c9c #9c9c9c #ccc #ccc;
}
input[type="text"], input[type="password"], input[type="date"], input[type="number"], textarea, select {
.border-radius(0px);
.box-sizing(border-box);
display: block;
font-size: 12px;
width: 100%;
&:focus {
.box-shadow(none);
}
&.x-large {
font-size: 12pt;
padding: 0.5em;
@ -103,73 +43,3 @@ input[type="text"], input[type="password"], input[type="date"], input[type="numb
background: @btn-danger-bg;
}
}
select {
height: auto;
line-height: normal;
padding: 0px;
}
label {
input[type="checkbox"] {
margin: 0px;
margin-top: -2px;
vertical-align: middle;
}
}
textarea {
height: 60px;
}
.switch {
display: inline-block;
position: relative;
width: 40px;
height: 16px;
border-radius: 8px;
background: rgba(0,0,0,0.26);
-webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1);
transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1);
vertical-align: middle;
cursor: pointer;
&::before {
content: '';
position: absolute;
top: -4px;
left: -4px;
width: 24px;
height: 24px;
background: #fafafa;
box-shadow: 0 2px 8px rgba(0,0,0,0.28);
border-radius: 50%;
transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
&:active::before {
box-shadow: 0 2px 8px rgba(0,0,0,0.28), 0 0 0 20px rgba(128,128,128,0.1);
}
}
input:checked + .switch {
background: rgba(132,82,138,0.5);
}
input:checked + .switch::before {
left: 20px;
background: #84528a;
}
input:checked + .switch:active::before {
box-shadow: 0 2px 8px rgba(0,0,0,0.28), 0 0 0 20px rgba(0,150,136,0.2);
}
.switch.disabled {
background: #bfbfbf;
}
.switch.disabled::before {
background: #dadada;
}

View file

@ -69,6 +69,12 @@ header {
}
}
.loader {
position: fixed;
top: 59px;
left: 0;
}
.now-playing {
height: 64px;
background: #fff;
@ -360,3 +366,7 @@ header {
md-input-container#title {
margin-bottom: 0;
}
._md-select-menu-container {
z-index: 100;
}

View file

@ -68,6 +68,7 @@
</div>
@endif
<a href="#" ng-click="toggleSearchBar()" class="search-button"><i class="material-icons">search</i></a>
<md-progress-linear md-mode="indeterminate" class="loader" ng-disabled="!loading"></md-progress-linear>
</div>
<div class="now-playing" ng-class="{'playing': isPlaying}">
<pfm-player></pfm-player>
@ -127,6 +128,7 @@
</a>
</li>
</ul>
<ui-view class="site-content">
@yield('app_content')
</ui-view>

View file

@ -28,7 +28,7 @@
@yield('styles')
</head>
<body ng-controller="application" class="{{Auth::check() ? 'is-logged' : ''}}">
<body ng-controller="application" class="{{Auth::check() ? 'is-logged' : ''}} loading">
@yield('content')
@yield('scripts')
</body>

View file

@ -28,6 +28,7 @@
<meta property="fb:admins" content="1165335382" />
<base href="/" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
{!! Assets::styleIncludes('embed') !!}
</head>
<body>