kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
				
				
				
			Podcast search capabilities
							rodzic
							
								
									6ad4ad2b5c
								
							
						
					
					
						commit
						f477ba1b4f
					
				|  | @ -103,6 +103,7 @@ class ArtistFilter( | |||
|     playable = filters.BooleanFilter(field_name="_", method="filter_playable") | ||||
|     has_albums = filters.BooleanFilter(field_name="_", method="filter_has_albums") | ||||
|     tag = TAG_FILTER | ||||
|     content_category = filters.CharFilter("content_category") | ||||
|     scope = common_filters.ActorScopeFilter( | ||||
|         actor_field="tracks__uploads__library__actor", | ||||
|         distinct=True, | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| Added new search functions to allow users to more easily search for podcasts in the UI. | ||||
|  | @ -118,10 +118,9 @@ Scope: | |||
|     - "actor:alice@example.com" | ||||
|     - "domain:example.com" | ||||
| 
 | ||||
| ContentType: | ||||
|   name: "content_type" | ||||
| ContentCategory: | ||||
|   name: "content_category" | ||||
|   in: "query" | ||||
|   default: "all" | ||||
|   description: | | ||||
|     Limits the results to those whose artist content type matches the query. | ||||
|    | ||||
|  |  | |||
|  | @ -407,6 +407,7 @@ paths: | |||
|         - $ref: "./api/parameters.yml#/PageSize" | ||||
|         - $ref: "./api/parameters.yml#/Related" | ||||
|         - $ref: "./api/parameters.yml#/Scope" | ||||
|         - $ref: "./api/parameters.yml#/ContentCategory" | ||||
|       responses: | ||||
|         200: | ||||
|           content: | ||||
|  | @ -505,7 +506,7 @@ paths: | |||
|         - $ref: "./api/parameters.yml#/PageSize" | ||||
|         - $ref: "./api/parameters.yml#/Related" | ||||
|         - $ref: "./api/parameters.yml#/Scope" | ||||
|         - $ref: "./api/parameters.yml#/ContentType" | ||||
|         - $ref: "./api/parameters.yml#/ContentCategory" | ||||
| 
 | ||||
|       responses: | ||||
|         200: | ||||
|  |  | |||
|  | @ -114,20 +114,22 @@ | |||
|     <div class="ui small hidden divider"></div> | ||||
|     <section :class="['ui', 'bottom', 'attached', {active: selectedTab === 'library'}, 'tab']" :aria-label="labels.mainMenu"> | ||||
|       <nav class="ui vertical large fluid inverted menu" role="navigation" :aria-label="labels.mainMenu"> | ||||
|         <div :class="[{collapsed: !exploreExpanded}, 'collaspable item']"> | ||||
|         <div :class="[{collapsed: !exploreExpanded}, 'collapsible item']"> | ||||
|           <h2 class="header" role="button" @click="exploreExpanded = true" tabindex="0" @focus="exploreExpanded = true"> | ||||
|             <translate translate-context="*/*/*/Verb">Explore</translate> | ||||
|             <i class="angle right icon" v-if="!exploreExpanded"></i> | ||||
|           </h2> | ||||
|           <div class="menu"> | ||||
|             <router-link class="item" :to="{name: 'search'}"><i class="search icon"></i><translate translate-context="Sidebar/Navigation/List item.Link/Verb">Search</translate></router-link> | ||||
|             <router-link class="item" :exact="true" :to="{name: 'library.index'}"><i class="music icon"></i><translate translate-context="Sidebar/Navigation/List item.Link/Verb">Browse</translate></router-link> | ||||
|             <router-link class="item" :to="{name: 'library.podcasts.browse'}"><i class="podcast icon"></i><translate translate-context="*/*/*">Podcasts</translate></router-link> | ||||
|             <router-link class="item" :to="{name: 'library.albums.browse'}"><i class="compact disc icon"></i><translate translate-context="*/*/*">Albums</translate></router-link> | ||||
|             <router-link class="item" :to="{name: 'library.artists.browse'}"><i class="user icon"></i><translate translate-context="*/*/*">Artists</translate></router-link> | ||||
|             <router-link class="item" :to="{name: 'library.playlists.browse'}"><i class="list icon"></i><translate translate-context="*/*/*">Playlists</translate></router-link> | ||||
|             <router-link class="item" :to="{name: 'library.radios.browse'}"><i class="feed icon"></i><translate translate-context="*/*/*">Radios</translate></router-link> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div :class="[{collapsed: !myLibraryExpanded}, 'collaspable item']" v-if="$store.state.auth.authenticated"> | ||||
|         <div :class="[{collapsed: !myLibraryExpanded}, 'collapsible item']" v-if="$store.state.auth.authenticated"> | ||||
|           <h3 class="header" role="button" @click="myLibraryExpanded = true" tabindex="0" @focus="myLibraryExpanded = true"> | ||||
|             <translate translate-context="*/*/*/Noun">My Library</translate> | ||||
|             <i class="angle right icon" v-if="!myLibraryExpanded"></i> | ||||
|  | @ -225,7 +227,9 @@ export default { | |||
|     }, | ||||
|     focusedMenu () { | ||||
|       let mapping = { | ||||
|         "search": 'exploreExpanded', | ||||
|         "library.index": 'exploreExpanded', | ||||
|         "library.podcasts.browse": 'exploreExpanded', | ||||
|         "library.albums.browse": 'exploreExpanded', | ||||
|         "library.albums.detail": 'exploreExpanded', | ||||
|         "library.artists.browse": 'exploreExpanded', | ||||
|  |  | |||
|  | @ -157,7 +157,9 @@ export default { | |||
|           page: this.page, | ||||
|           tag: this.tags, | ||||
|           paginateBy: this.paginateBy, | ||||
|           ordering: this.getOrderingAsString() | ||||
|           ordering: this.getOrderingAsString(), | ||||
|           content_category: 'music', | ||||
|           include_channels: true, | ||||
|         }).toString() | ||||
|       ) | ||||
|     }, | ||||
|  | @ -175,6 +177,7 @@ export default { | |||
|         playable: "true", | ||||
|         tag: this.tags, | ||||
|         include_channels: "true", | ||||
|         content_category: 'music', | ||||
|       } | ||||
|       logger.default.debug("Fetching artists") | ||||
|       axios.get( | ||||
|  |  | |||
|  | @ -0,0 +1,247 @@ | |||
| <template> | ||||
|   <main v-title="labels.title"> | ||||
|     <section class="ui vertical stripe segment"> | ||||
|       <h2 class="ui header"> | ||||
|         <translate translate-context="Content/Podcasts/Title">Browsing Podcasts</translate> | ||||
|       </h2> | ||||
|       <form :class="['ui', {'loading': isLoading}, 'form']" @submit.prevent="updatePage();updateQueryString();fetchData()"> | ||||
|         <div class="fields"> | ||||
|           <div class="field"> | ||||
|             <label for="artist-search"> | ||||
|               <translate translate-context="Content/Search/Input.Label/Noun">Podcast Title</translate> | ||||
|             </label> | ||||
|             <div class="ui action input"> | ||||
|               <input id="artist-search" type="text" name="search" v-model="query" :placeholder="labels.searchPlaceholder"/> | ||||
|               <button class="ui icon button" type="submit" :aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"> | ||||
|                 <i class="search icon"></i> | ||||
|               </button> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="field"> | ||||
|             <label for="tags-search"><translate translate-context="*/*/*/Noun">Tags</translate></label> | ||||
|             <tags-selector v-model="tags"></tags-selector> | ||||
|           </div> | ||||
|           <div class="field"> | ||||
|             <label for="artist-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> | ||||
|             <select id="artist-ordering" class="ui dropdown" v-model="ordering"> | ||||
|               <option v-for="option in orderingOptions" :value="option[0]"> | ||||
|                 {{ sharedLabels.filters[option[1]] }} | ||||
|               </option> | ||||
|             </select> | ||||
|           </div> | ||||
|           <div class="field"> | ||||
|             <label for="artist-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label> | ||||
|             <select id="artist-ordering-direction" class="ui dropdown" v-model="orderingDirection"> | ||||
|               <option value="+"><translate translate-context="Content/Search/Dropdown">Ascending</translate></option> | ||||
|               <option value="-"><translate translate-context="Content/Search/Dropdown">Descending</translate></option> | ||||
|             </select> | ||||
|           </div> | ||||
|           <div class="field"> | ||||
|             <label for="artist-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label> | ||||
|             <select id="artist-results" class="ui dropdown" v-model="paginateBy"> | ||||
|               <option :value="parseInt(12)">12</option> | ||||
|               <option :value="parseInt(30)">30</option> | ||||
|               <option :value="parseInt(50)">50</option> | ||||
|             </select> | ||||
|           </div> | ||||
|         </div> | ||||
|       </form> | ||||
|       <div class="ui hidden divider"></div> | ||||
|       <div v-if="result && result.results.length > 0" class="ui five app-cards cards"> | ||||
|         <div v-if="isLoading" class="ui inverted active dimmer"> | ||||
|           <div class="ui loader"></div> | ||||
|         </div> | ||||
|         <artist-card :artist="artist" v-for="artist in result.results" :key="artist.id"></artist-card> | ||||
|       </div> | ||||
|       <div v-else-if="!isLoading" class="ui placeholder segment sixteen wide column" style="text-align: center; display: flex; align-items: center"> | ||||
|         <div class="ui icon header"> | ||||
|           <i class="podcast icon"></i> | ||||
|           <translate translate-context="Content/Artists/Placeholder"> | ||||
|             No results matching your query | ||||
|           </translate> | ||||
|         </div> | ||||
|         <router-link | ||||
|           v-if="$store.state.auth.authenticated" | ||||
|           :to="{name: 'content.index'}" | ||||
|           class="ui success button labeled icon"> | ||||
|           <i class="upload icon"></i> | ||||
|           <translate translate-context="Content/*/Verb"> | ||||
|             Create a Channel | ||||
|           </translate> | ||||
|         </router-link> | ||||
|         <h1 v-if ="$store.state.auth.authenticated" class="ui with-actions header"> | ||||
|         <div class="actions"> | ||||
|           <a @click.stop.prevent="showSubscribeModal = true"> | ||||
|             <i class="plus icon"></i> | ||||
|             <translate translate-context="Content/Profile/Button">Subscribe to feed</translate> | ||||
|           </a> | ||||
|         </div> | ||||
|       </h1> | ||||
|       </div> | ||||
|       <div class="ui center aligned basic segment"> | ||||
|         <pagination | ||||
|           v-if="result && result.count > paginateBy" | ||||
|           @page-changed="selectPage" | ||||
|           :current="page" | ||||
|           :paginate-by="paginateBy" | ||||
|           :total="result.count" | ||||
|           ></pagination> | ||||
|       </div> | ||||
|     </section> | ||||
|     <modal class="tiny" :show.sync="showSubscribeModal" :fullscreen="false"> | ||||
|         <h2 class="header"> | ||||
|           <translate translate-context="*/*/*/Noun">Subscription</translate> | ||||
|         </h2> | ||||
|         <div class="scrolling content" ref="modalContent"> | ||||
|           <remote-search-form | ||||
|             type="rss" | ||||
|             :show-submit="false" | ||||
|             :standalone="false" | ||||
|             @subscribed="showSubscribeModal = false; fetchData()" | ||||
|             :redirect="false"></remote-search-form> | ||||
|         </div> | ||||
|         <div class="actions"> | ||||
|           <button class="ui basic deny button"> | ||||
|             <translate translate-context="*/*/Button.Label/Verb">Cancel</translate> | ||||
|           </button> | ||||
|           <button form="remote-search" type="submit" class="ui primary button"> | ||||
|             <i class="bookmark icon"></i> | ||||
|             <translate translate-context="*/*/*/Verb">Subscribe</translate> | ||||
|           </button> | ||||
|         </div> | ||||
|       </modal> | ||||
| 
 | ||||
|   </main> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import qs from 'qs' | ||||
| import axios from "axios" | ||||
| import _ from "@/lodash" | ||||
| import $ from "jquery" | ||||
| 
 | ||||
| import logger from "@/logging" | ||||
| 
 | ||||
| import OrderingMixin from "@/components/mixins/Ordering" | ||||
| import PaginationMixin from "@/components/mixins/Pagination" | ||||
| import TranslationsMixin from "@/components/mixins/Translations" | ||||
| import ArtistCard from "@/components/audio/artist/Card" | ||||
| import Pagination from "@/components/Pagination" | ||||
| import TagsSelector from '@/components/library/TagsSelector' | ||||
| import Modal from '@/components/semantic/Modal' | ||||
| import RemoteSearchForm from "@/components/RemoteSearchForm" | ||||
| 
 | ||||
| const FETCH_URL = "artists/" | ||||
| 
 | ||||
| export default { | ||||
|   mixins: [OrderingMixin, PaginationMixin, TranslationsMixin], | ||||
|   props: { | ||||
|     defaultQuery: { type: String, required: false, default: "" }, | ||||
|     defaultTags: { type: Array, required: false, default: () => { return [] } }, | ||||
|     scope: { type: String, required: false, default: "all" }, | ||||
|   }, | ||||
|   components: { | ||||
|     ArtistCard, | ||||
|     Pagination, | ||||
|     TagsSelector, | ||||
|     RemoteSearchForm, | ||||
|     Modal, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       isLoading: true, | ||||
|       result: null, | ||||
|       page: parseInt(this.defaultPage), | ||||
|       query: this.defaultQuery, | ||||
|       tags: (this.defaultTags || []).filter((t) => { return t.length > 0 }), | ||||
|       orderingOptions: [["creation_date", "creation_date"], ["name", "name"]], | ||||
|       showSubscribeModal: false, | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.fetchData() | ||||
|   }, | ||||
|   mounted() { | ||||
|     $(".ui.dropdown").dropdown() | ||||
|   }, | ||||
|   computed: { | ||||
|     labels() { | ||||
|       let searchPlaceholder = this.$pgettext('Content/Search/Input.Placeholder', "Search…") | ||||
|       let title = this.$pgettext('*/*/*/Noun', "Podcasts") | ||||
|       return { | ||||
|         searchPlaceholder, | ||||
|         title | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     updateQueryString: function() { | ||||
|       history.pushState( | ||||
|         {}, | ||||
|         null, | ||||
|         this.$route.path + '?' + new URLSearchParams( | ||||
|           { | ||||
|           query: this.query, | ||||
|           page: this.page, | ||||
|           tag: this.tags, | ||||
|           paginateBy: this.paginateBy, | ||||
|           ordering: this.getOrderingAsString(), | ||||
|           include_channels: true, | ||||
|           content_category: 'podcast', | ||||
|         }).toString() | ||||
|       ) | ||||
|     }, | ||||
|     fetchData: function() { | ||||
|       var self = this | ||||
|       this.isLoading = true | ||||
|       let url = FETCH_URL | ||||
|       let params = { | ||||
|         scope: this.scope, | ||||
|         page: this.page, | ||||
|         page_size: this.paginateBy, | ||||
|         has_albums: this.excludeCompilation, | ||||
|         q: this.query, | ||||
|         ordering: this.getOrderingAsString(), | ||||
|         playable: "true", | ||||
|         tag: this.tags, | ||||
|         include_channels: "true", | ||||
|         content_category: 'podcast', | ||||
|       } | ||||
|       logger.default.debug("Fetching artists") | ||||
|       axios.get( | ||||
|         url, | ||||
|         { | ||||
|           params: params, | ||||
|           paramsSerializer: function(params) { | ||||
|             return qs.stringify(params, { indices: false }) | ||||
|           } | ||||
|         } | ||||
|       ).then(response => { | ||||
|         self.result = response.data | ||||
|         self.isLoading = false | ||||
|       }, error => { | ||||
|         self.result = null | ||||
|         self.isLoading = false | ||||
|       }) | ||||
|     }, | ||||
|     selectPage: function(page) { | ||||
|       this.page = page | ||||
|     }, | ||||
|     updatePage() { | ||||
|       this.page = this.defaultPage | ||||
|     }, | ||||
|   }, | ||||
|   watch: { | ||||
|     page() { | ||||
|       this.updateQueryString() | ||||
|       this.fetchData() | ||||
|     }, | ||||
|     "$store.state.moderation.lastUpdate": function () { | ||||
|       this.fetchData() | ||||
|     }, | ||||
|     excludeCompilation() { | ||||
|       this.fetchData() | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | @ -637,6 +637,23 @@ export default new Router({ | |||
|             defaultPage: route.query.page | ||||
|           }) | ||||
|         }, | ||||
|         { | ||||
|           path: "podcasts/", | ||||
|           name: "library.podcasts.browse", | ||||
|           component: () => | ||||
|             import( | ||||
|               /* webpackChunkName: "podcasts" */ "@/components/library/Podcasts" | ||||
|             ), | ||||
|           props: route => ({ | ||||
|             defaultOrdering: route.query.ordering, | ||||
|             defaultQuery: route.query.query, | ||||
|             defaultTags: Array.isArray(route.query.tag || []) | ||||
|               ? route.query.tag | ||||
|               : [route.query.tag], | ||||
|             defaultPaginateBy: route.query.paginateBy, | ||||
|             defaultPage: route.query.page | ||||
|           }) | ||||
|         }, | ||||
|         { | ||||
|           path: "me/albums", | ||||
|           name: "library.albums.me", | ||||
|  |  | |||
|  | @ -45,6 +45,11 @@ export default { | |||
|         orderingDirection: "-", | ||||
|         ordering: "creation_date", | ||||
|       }, | ||||
|       "library.podcasts.browse": { | ||||
|         paginateBy: 30, | ||||
|         orderingDirection: "-", | ||||
|         ordering: "creation_date", | ||||
|       }, | ||||
|       "library.radios.browse": { | ||||
|         paginateBy: 12, | ||||
|         orderingDirection: "-", | ||||
|  |  | |||
|  | @ -1,244 +1,237 @@ | |||
| 
 | ||||
| .ui.wide.left.sidebar { | ||||
|   @include media(">desktop") { | ||||
|     width: $desktop-sidebar-width; | ||||
|   } | ||||
| 
 | ||||
|   @include media(">widedesktop") { | ||||
|     width: $widedesktop-sidebar-width; | ||||
|   } | ||||
|     @include media(">desktop") { | ||||
|         width: $desktop-sidebar-width; | ||||
|     } | ||||
|     @include media(">widedesktop") { | ||||
|         width: $widedesktop-sidebar-width; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| .sidebar { | ||||
|   .logo { | ||||
|     &.bordered.icon { | ||||
|       padding: .5em .41em !important; | ||||
|     .logo { | ||||
|         &.bordered.icon { | ||||
|             padding: .5em .41em !important; | ||||
|         } | ||||
|         path { | ||||
|             fill: white; | ||||
|         } | ||||
|     } | ||||
|     path { | ||||
|       fill: white; | ||||
|     .tab { | ||||
|         flex-direction: column; | ||||
|     } | ||||
|   } | ||||
|   .tab { | ||||
|     flex-direction: column; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .component-sidebar { | ||||
|   .ui.search .input { | ||||
|     flex: 1; | ||||
|     .prompt { | ||||
|       border-radius: 0; | ||||
|     .ui.search .input { | ||||
|         flex: 1; | ||||
|         .prompt { | ||||
|             border-radius: 0; | ||||
|         } | ||||
|     } | ||||
|   } | ||||
|   .ui.search .results { | ||||
|     vertical-align: middle; | ||||
|   } | ||||
|   .ui.search .name { | ||||
|     vertical-align: middle; | ||||
|   } | ||||
| 
 | ||||
|   &.sidebar { | ||||
|     overflow-y: visible !important; | ||||
|     background: var(--sidebar-background); | ||||
|     z-index: 1; | ||||
|     @include media(">desktop") { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       justify-content: space-between; | ||||
|       padding-bottom: 4em; | ||||
|     .ui.search .results { | ||||
|         vertical-align: middle; | ||||
|     } | ||||
|     > nav { | ||||
|       flex-grow: 1; | ||||
|       overflow-y: auto; | ||||
|     .ui.search .name { | ||||
|         vertical-align: middle; | ||||
|     } | ||||
|     @include media(">desktop") { | ||||
|       .menu .item.collapse-button-wrapper { | ||||
|     &.sidebar { | ||||
|         overflow-y: visible !important; | ||||
|         background: var(--sidebar-background); | ||||
|         z-index: 1; | ||||
|         @include media(">desktop") { | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|             justify-content: space-between; | ||||
|             padding-bottom: 4em; | ||||
|         } | ||||
|         >nav { | ||||
|             flex-grow: 1; | ||||
|             overflow-y: auto; | ||||
|         } | ||||
|         @include media(">desktop") { | ||||
|             .menu .item.collapse-button-wrapper { | ||||
|                 padding: 0; | ||||
|             } | ||||
|             .collapse.button { | ||||
|                 display: none !important; | ||||
|             } | ||||
|         } | ||||
|         @include media("<=desktop") { | ||||
|             position: static !important; | ||||
|             width: 100% !important; | ||||
|             &.collapsed { | ||||
|                 .player-wrapper, | ||||
|                 .search, | ||||
|                 .signup.segment, | ||||
|                 nav.secondary { | ||||
|                     display: none; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         >div { | ||||
|             margin: 0; | ||||
|             background-color: var(--sidebar-background); | ||||
|         } | ||||
|         .menu.vertical { | ||||
|             background: transparent; | ||||
|         } | ||||
|     } | ||||
|     .ui.vertical.menu { | ||||
|         .item .item { | ||||
|             font-size: 1em; | ||||
|             >i.icon { | ||||
|                 float: none; | ||||
|                 margin: 0 0.5em 0 0; | ||||
|             } | ||||
|         } | ||||
|         .item.active { | ||||
|             border-right: 5px solid var(--vibrant-color); | ||||
|             border-radius: 0 !important; | ||||
|             background: var(--sidebar-active-item-background) !important; | ||||
|         } | ||||
|         .item.collapsed { | ||||
|             &:not(:focus)>.menu { | ||||
|                 display: none; | ||||
|             } | ||||
|             .header { | ||||
|                 margin-bottom: 0; | ||||
|             } | ||||
|         } | ||||
|         .collapsible.item .header { | ||||
|             cursor: pointer; | ||||
|         } | ||||
|     } | ||||
|     .ui.secondary.menu { | ||||
|         margin-left: 0; | ||||
|         margin-right: 0; | ||||
|     } | ||||
|     .tabs { | ||||
|         flex: 1; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         overflow-y: auto; | ||||
|         justify-content: space-between; | ||||
|         @include media("<=desktop") { | ||||
|             max-height: 500px; | ||||
|         } | ||||
|     } | ||||
|     .ui.tab.active { | ||||
|         display: flex; | ||||
|     } | ||||
|     .tab[data-tab="queue"] { | ||||
|         flex-direction: column; | ||||
|         tr { | ||||
|             cursor: pointer; | ||||
|         } | ||||
|         td:nth-child(2) { | ||||
|             width: 55px; | ||||
|         } | ||||
|     } | ||||
|     .item .header .angle.icon { | ||||
|         float: right; | ||||
|         margin: 0; | ||||
|     } | ||||
|     .tab[data-tab="library"] { | ||||
|         flex-direction: column; | ||||
|         flex: 1 1 auto; | ||||
|         >.menu { | ||||
|             flex: 1; | ||||
|             flex-grow: 1; | ||||
|         } | ||||
|         >.player-wrapper { | ||||
|             width: 100%; | ||||
|         } | ||||
|     } | ||||
|     .sidebar .segment { | ||||
|         margin: 0; | ||||
|         border-radius: 0; | ||||
|     } | ||||
|     .ui.menu .item.inline.admin-dropdown.dropdown>.menu { | ||||
|         left: 0; | ||||
|         right: auto; | ||||
|     } | ||||
|     .ui.segment.header-wrapper { | ||||
|         background: var(--sidebar-header-background); | ||||
|         color: var(--sidebar-header-color); | ||||
|         box-shadow: var(--sidebar-header-box-shadow); | ||||
|         padding: 0; | ||||
|       } | ||||
|       .collapse.button { | ||||
|         display: none !important; | ||||
|       } | ||||
|     } | ||||
|     @include media("<=desktop") { | ||||
|       position: static !important; | ||||
|       width: 100% !important; | ||||
|       &.collapsed { | ||||
|         .player-wrapper, | ||||
|         .search, | ||||
|         .signup.segment, | ||||
|         nav.secondary { | ||||
|           display: none; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     > div { | ||||
|       margin: 0; | ||||
|       background-color: var(--sidebar-background); | ||||
|     } | ||||
|     .menu.vertical { | ||||
|       background: transparent; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .ui.vertical.menu { | ||||
|     .item .item { | ||||
|       font-size: 1em; | ||||
|       > i.icon { | ||||
|         float: none; | ||||
|         margin: 0 0.5em 0 0; | ||||
|       } | ||||
|     } | ||||
|     .item.active { | ||||
|       border-right: 5px solid var(--vibrant-color); | ||||
|       border-radius: 0 !important; | ||||
|       background: var(--sidebar-active-item-background) !important; | ||||
|     } | ||||
|     .item.collapsed { | ||||
|       &:not(:focus) > .menu { | ||||
|         display: none; | ||||
|       } | ||||
|       .header { | ||||
|         display: flex; | ||||
|         justify-content: space-between; | ||||
|         align-items: center; | ||||
|         height: 4em; | ||||
|         margin-bottom: 0; | ||||
|       } | ||||
|     } | ||||
|     .collaspable.item .header { | ||||
|       cursor: pointer; | ||||
|     } | ||||
|   } | ||||
|   .ui.secondary.menu { | ||||
|     margin-left: 0; | ||||
|     margin-right: 0; | ||||
|   } | ||||
|   .tabs { | ||||
|     flex: 1; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     overflow-y: auto; | ||||
|     justify-content: space-between; | ||||
|     @include media("<=desktop") { | ||||
|       max-height: 500px; | ||||
|     } | ||||
|   } | ||||
|   .ui.tab.active { | ||||
|     display: flex; | ||||
|   } | ||||
|   .tab[data-tab="queue"] { | ||||
|     flex-direction: column; | ||||
|     tr { | ||||
|       cursor: pointer; | ||||
|     } | ||||
|     td:nth-child(2) { | ||||
|       width: 55px; | ||||
|     } | ||||
|   } | ||||
|   .item .header .angle.icon { | ||||
|     float: right; | ||||
|     margin: 0; | ||||
|   } | ||||
|   .tab[data-tab="library"] { | ||||
|     flex-direction: column; | ||||
|     flex: 1 1 auto; | ||||
|     > .menu { | ||||
|       flex: 1; | ||||
|       flex-grow: 1; | ||||
|     } | ||||
|     > .player-wrapper { | ||||
|       width: 100%; | ||||
|     } | ||||
|   } | ||||
|   .sidebar .segment { | ||||
|     margin: 0; | ||||
|     border-radius: 0; | ||||
|   } | ||||
| 
 | ||||
|   .ui.menu .item.inline.admin-dropdown.dropdown > .menu { | ||||
|     left: 0; | ||||
|     right: auto; | ||||
|   } | ||||
|   .ui.segment.header-wrapper { | ||||
|     background: var(--sidebar-header-background); | ||||
|     color: var(--sidebar-header-color); | ||||
|     box-shadow: var(--sidebar-header-box-shadow); | ||||
|     padding: 0; | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     align-items: center; | ||||
|     height: 4em; | ||||
|     margin-bottom: 0; | ||||
|     nav { | ||||
|       > .item, > .menu > .item > .item { | ||||
|         &:hover { | ||||
|           background-color: transparent; | ||||
|         nav { | ||||
|             >.item, | ||||
|             >.menu>.item>.item { | ||||
|                 &:hover { | ||||
|                     background-color: transparent; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   nav.top.title-menu { | ||||
|     flex-grow: 1; | ||||
|     .item { | ||||
|       font-size: 1.5em; | ||||
|     nav.top.title-menu { | ||||
|         flex-grow: 1; | ||||
|         .item { | ||||
|             font-size: 1.5em; | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .logo { | ||||
|     cursor: pointer; | ||||
|     display: inline-block; | ||||
|     margin: 0px; | ||||
|   } | ||||
| 
 | ||||
|   &.collapsed .search-wrapper { | ||||
|     @include media("<desktop") { | ||||
|       padding: 0; | ||||
|     .logo { | ||||
|         cursor: pointer; | ||||
|         display: inline-block; | ||||
|         margin: 0px; | ||||
|     } | ||||
|   } | ||||
|   .ui.search { | ||||
|     display: flex; | ||||
|   } | ||||
|   .ui.message.black { | ||||
|     background: var(--sidebar-background); | ||||
|   } | ||||
| 
 | ||||
|   .ui.mini.image { | ||||
|     width: 100%; | ||||
|   } | ||||
|   nav.top { | ||||
|     align-items: self-end; | ||||
|     padding: 0.5em 0; | ||||
|     > .item, > .right.menu > .item { | ||||
|       // color: rgba(255, 255, 255, 0.9) !important; | ||||
|       font-size: 1.2em; | ||||
|       &:hover, > .dropdown > .icon { | ||||
|         // color: rgba(255, 255, 255, 0.9) !important; | ||||
|       } | ||||
|       > .label, > .dropdown > .label { | ||||
|         font-size: 0.5em; | ||||
|         right: 1.7em; | ||||
|         bottom: -0.5em; | ||||
|         z-index: 0 !important; | ||||
|       } | ||||
|     &.collapsed .search-wrapper { | ||||
|         @include media("<desktop") { | ||||
|             padding: 0; | ||||
|         } | ||||
|     } | ||||
|   } | ||||
|   .ui.user-dropdown > .text > .label { | ||||
|     margin-right: 0; | ||||
|   } | ||||
|   .logo-wrapper { | ||||
|     display: inline-block; | ||||
|     margin: 0 auto; | ||||
|     @include media("<desktop") { | ||||
|       margin: 0; | ||||
|     .ui.search { | ||||
|         display: flex; | ||||
|     } | ||||
|     img { | ||||
|       height: 1em; | ||||
|       display: inline-block; | ||||
|       margin: 0 auto; | ||||
|     .ui.message.black { | ||||
|         background: var(--sidebar-background); | ||||
|     } | ||||
|     @include media(">tablet") { | ||||
|       img { | ||||
|         height: 1.5em; | ||||
|       } | ||||
|     .ui.mini.image { | ||||
|         width: 100%; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|     nav.top { | ||||
|         align-items: self-end; | ||||
|         padding: 0.5em 0; | ||||
|         >.item, | ||||
|         >.right.menu>.item { | ||||
|             // color: rgba(255, 255, 255, 0.9) !important; | ||||
|             font-size: 1.2em; | ||||
|             &:hover, | ||||
|             >.dropdown>.icon { | ||||
|                 // color: rgba(255, 255, 255, 0.9) !important; | ||||
|             } | ||||
|             >.label, | ||||
|             >.dropdown>.label { | ||||
|                 font-size: 0.5em; | ||||
|                 right: 1.7em; | ||||
|                 bottom: -0.5em; | ||||
|                 z-index: 0 !important; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     .ui.user-dropdown>.text>.label { | ||||
|         margin-right: 0; | ||||
|     } | ||||
|     .logo-wrapper { | ||||
|         display: inline-block; | ||||
|         margin: 0 auto; | ||||
|         @include media("<desktop") { | ||||
|             margin: 0; | ||||
|         } | ||||
|         img { | ||||
|             height: 1em; | ||||
|             display: inline-block; | ||||
|             margin: 0 auto; | ||||
|         } | ||||
|         @include media(">tablet") { | ||||
|             img { | ||||
|                 height: 1.5em; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -43,11 +43,11 @@ | |||
|      | ||||
|         <empty-state v-else-if="!currentResults || currentResults.count === 0" @refresh="search" :refresh="true"></empty-state> | ||||
|          | ||||
|         <div v-else-if="type === 'artists'" class="ui five app-cards cards"> | ||||
|         <div v-else-if="type === 'artists' || type === 'podcasts'" class="ui five app-cards cards"> | ||||
|           <artist-card :artist="artist" v-for="artist in currentResults.results" :key="artist.id"></artist-card> | ||||
|         </div> | ||||
|          | ||||
|         <div v-else-if="type === 'albums'" class="ui five app-cards cards"> | ||||
|         <div v-else-if="type === 'albums' || type === 'series'" class="ui five app-cards cards"> | ||||
|           <album-card | ||||
|             v-for="album in currentResults.results" | ||||
|             :key="album.id" | ||||
|  | @ -124,6 +124,8 @@ export default { | |||
|         playlists: null, | ||||
|         radios: null, | ||||
|         tags: null, | ||||
|         podcasts: null, | ||||
|         series: null, | ||||
|       }, | ||||
|       isLoading: false, | ||||
|       paginateBy: 25, | ||||
|  | @ -147,15 +149,28 @@ export default { | |||
|         submitSearch | ||||
|       } | ||||
|     }, | ||||
|     axiosParams() { | ||||
|       const params = new URLSearchParams(); | ||||
|       params.append('q', this.query); | ||||
|       params.append('page', this.page); | ||||
|       params.append('page_size', this.paginateBy); | ||||
|       if(this.currentType.contentCategory != undefined) {params.append('content_category', this.currentType.contentCategory)}; | ||||
|       if(this.currentType.includeChannels != undefined) {params.append('include_channels', this.currentType.includeChannels)}; | ||||
|       return params; | ||||
|     }, | ||||
|     types () { | ||||
|       return [ | ||||
|         { | ||||
|           id: 'artists', | ||||
|           label: this.$pgettext("*/*/*/Noun", "Artists"), | ||||
|           includeChannels: true, | ||||
|           contentCategory: 'music', | ||||
|         }, | ||||
|         { | ||||
|           id: 'albums', | ||||
|           label: this.$pgettext("*/*/*", "Albums"), | ||||
|           includeChannels: true, | ||||
|           contentCategory: 'music', | ||||
|         }, | ||||
|         { | ||||
|           id: 'tracks', | ||||
|  | @ -174,6 +189,20 @@ export default { | |||
|           id: 'tags', | ||||
|           label: this.$pgettext("*/*/*", "Tags"), | ||||
|         }, | ||||
|         { | ||||
|           id: 'podcasts', | ||||
|           label: this.$pgettext("*/*/*", "Podcasts"), | ||||
|           endpoint: '/artists', | ||||
|           contentCategory: 'podcast', | ||||
|           includeChannels: true, | ||||
|         }, | ||||
|         { | ||||
|           id: 'series', | ||||
|           label: this.$pgettext("*/*/*", "Series"), | ||||
|           endpoint: '/albums', | ||||
|           includeChannels: true, | ||||
|           contentCategory: 'podcast', | ||||
|         }, | ||||
|       ] | ||||
|     }, | ||||
|     currentType () { | ||||
|  | @ -197,13 +226,18 @@ export default { | |||
|       this.isLoading = true | ||||
|       let response = await axios.get( | ||||
|         this.currentType.endpoint || this.currentType.id, | ||||
|         {params: {q: this.query, page: this.page, page_size: this.paginateBy}} | ||||
|         {params: this.axiosParams} | ||||
|       ) | ||||
|       this.results[this.currentType.id] = response.data | ||||
|       this.isLoading = false | ||||
|       this.types.forEach(t => { | ||||
|         if (t.id != this.currentType.id) { | ||||
|           axios.get(t.endpoint || t.id, {params: {q: this.query, page_size: 1}}).then(response => { | ||||
|           axios.get(t.endpoint || t.id, {params: { | ||||
|               q: this.query,  | ||||
|               page_size: 1, | ||||
|               content_category: t.contentCategory, | ||||
|               include_channels: t.includeChannels, | ||||
|             }}).then(response => { | ||||
|             this.results[t.id] = response.data | ||||
|           }) | ||||
|         } | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Ciarán Ainsworth
						Ciarán Ainsworth