ProgramPageListView + article list view

This commit is contained in:
bkfox 2019-08-17 17:51:12 +02:00
parent abaccf9ded
commit 595af5a69d
12 changed files with 62 additions and 36 deletions

View File

@ -7220,7 +7220,8 @@ aside .media .content {
font-size: 1.5rem !important; font-size: 1.5rem !important;
height: 2.5em !important; } height: 2.5em !important; }
.player .media-content { .player .media-content {
padding: 0.2em; } padding-top: 0.4em;
padding-left: 0.4em; }
.player .button { .player .button {
font-size: 1.5rem !important; font-size: 1.5rem !important;
height: 2.5em; height: 2.5em;

View File

@ -393,7 +393,7 @@ eval("//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __w
/***/ (function(module, __webpack_exports__, __webpack_require__) { /***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict"; "use strict";
eval("/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"media\" }, [\n _c(\"div\", { staticClass: \"media-left\" }, [\n _c(\n \"div\",\n {\n staticClass: \"button\",\n attrs: { title: _vm.buttonTitle, \"aria-label\": _vm.buttonTitle },\n on: {\n click: function($event) {\n return _vm.toggle()\n }\n }\n },\n [\n _vm.playing\n ? _c(\"span\", { staticClass: \"fas fa-pause\" })\n : _c(\"span\", { staticClass: \"fas fa-play\" })\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"audio\",\n {\n ref: \"audio\",\n on: {\n playing: _vm.onChange,\n ended: _vm.onChange,\n pause: _vm.onChange\n }\n },\n [_vm._t(\"sources\")],\n 2\n )\n ]),\n _vm._v(\" \"),\n _vm.onAir && _vm.onAir.cover\n ? _c(\"div\", { staticClass: \"media-left media-cover\" }, [\n _c(\"img\", { staticClass: \"cover\", attrs: { src: _vm.onAir.cover } })\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm.onAir && _vm.onAir.type == \"track\"\n ? _c(\n \"div\",\n { staticClass: \"media-content\" },\n [_vm._t(\"track\", null, { onAir: _vm.onAir, liveInfo: _vm.liveInfo })],\n 2\n )\n : _vm.onAir && _vm.onAir.type == \"diffusion\"\n ? _c(\n \"div\",\n { staticClass: \"media-content\" },\n [\n _vm._t(\"diffusion\", null, {\n onAir: _vm.onAir,\n liveInfo: _vm.liveInfo\n })\n ],\n 2\n )\n : _c(\"div\", [_vm._t(\"empty\")], 2)\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/vue/player.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options"); eval("/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"media\" }, [\n _c(\"div\", { staticClass: \"media-left\" }, [\n _c(\n \"div\",\n {\n staticClass: \"button\",\n attrs: { title: _vm.buttonTitle, \"aria-label\": _vm.buttonTitle },\n on: {\n click: function($event) {\n return _vm.toggle()\n }\n }\n },\n [\n _vm.playing\n ? _c(\"span\", { staticClass: \"fas fa-pause\" })\n : _c(\"span\", { staticClass: \"fas fa-play\" })\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"audio\",\n {\n ref: \"audio\",\n on: {\n playing: _vm.onChange,\n ended: _vm.onChange,\n pause: _vm.onChange\n }\n },\n [_vm._t(\"sources\")],\n 2\n )\n ]),\n _vm._v(\" \"),\n _vm.onAir && _vm.onAir.cover\n ? _c(\"div\", { staticClass: \"media-left media-cover\" }, [\n _c(\"img\", { staticClass: \"cover\", attrs: { src: _vm.onAir.cover } })\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm.onAir && _vm.onAir.type == \"track\"\n ? _c(\n \"div\",\n { staticClass: \"media-content\" },\n [_vm._t(\"track\", null, { onAir: _vm.onAir, liveInfo: _vm.liveInfo })],\n 2\n )\n : _vm.onAir && _vm.onAir.type == \"diffusion\"\n ? _c(\n \"div\",\n { staticClass: \"media-content\" },\n [\n _vm._t(\"diffusion\", null, {\n onAir: _vm.onAir,\n liveInfo: _vm.liveInfo\n })\n ],\n 2\n )\n : _c(\"div\", { staticClass: \"media-content\" }, [_vm._t(\"empty\")], 2)\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/vue/player.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
/***/ }), /***/ }),

View File

@ -42,6 +42,10 @@
</h4> </h4>
<div class="">[[ onAir.info ]]</div> <div class="">[[ onAir.info ]]</div>
</template> </template>
<template v-slot:empty>
<h4 class="title is-4">{{ station.name }}</h4>
</template>
</a-player> </a-player>
</div> </div>
{% endif %} {% endif %}

View File

@ -39,6 +39,8 @@ urls = [
views.ProgramDetailView.as_view(), name='program-detail'), views.ProgramDetailView.as_view(), name='program-detail'),
path(_('programs/<slug:program_slug>/episodes/'), path(_('programs/<slug:program_slug>/episodes/'),
views.EpisodeListView.as_view(), name='diffusion-list'), views.EpisodeListView.as_view(), name='diffusion-list'),
path(_('programs/<slug:program_slug>/articles/'),
views.ArticleListView.as_view(), name='article-list'),
path(_('episodes/'), path(_('episodes/'),
views.EpisodeListView.as_view(), name='diffusion-list'), views.EpisodeListView.as_view(), name='diffusion-list'),

View File

@ -25,7 +25,7 @@ class LiveAPIView(BaseLogListView, BaseAPIView, ListAPIView):
queryset = Log.objects.all() queryset = Log.objects.all()
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
self.min_date = tz.now() - tz.timedelta(minutes=5) self.min_date = tz.now() - tz.timedelta(minutes=20)
return super().get(*args, **kwargs) return super().get(*args, **kwargs)
def get_serializer(self, queryset, *args, **kwargs): def get_serializer(self, queryset, *args, **kwargs):

View File

@ -1,12 +1,14 @@
from ..models import Article from ..models import Article
from .page import PageListView from .program import ProgramPageListView
__all__ = ['ArticleListView'] __all__ = ['ArticleListView']
class ArticleListView(PageListView): class ArticleListView(ProgramPageListView):
model = Article model = Article
template_name = 'aircox/article_list.html'
show_headline = True
is_static = False is_static = False
def get_queryset(self): def get_queryset(self):

View File

@ -7,8 +7,7 @@ from django.views.generic import ListView
from ..models import Diffusion, Episode, Page, Program, Sound from ..models import Diffusion, Episode, Page, Program, Sound
from .base import BaseView from .base import BaseView
from .page import PageListView from .program import ProgramPageDetailView, ProgramPageListView
from .program import ProgramPageDetailView
__all__ = ['EpisodeDetailView', 'DiffusionListView', 'TimetableView'] __all__ = ['EpisodeDetailView', 'DiffusionListView', 'TimetableView']
@ -30,32 +29,11 @@ class EpisodeDetailView(ProgramPageDetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
# TODO: pagination: in template, only a limited number of pages displayed class EpisodeListView(ProgramPageListView):
class EpisodeListView(PageListView):
model = Episode model = Episode
template_name = 'aircox/diffusion_list.html'
item_template_name = 'aircox/episode_item.html' item_template_name = 'aircox/episode_item.html'
show_headline = True show_headline = True
template_name = 'aircox/diffusion_list.html'
program = None
def get(self, request, *args, **kwargs):
program_slug = kwargs.get('program_slug')
if program_slug:
self.program = get_object_or_404(Program, slug=program_slug)
return super().get(request, *args, **kwargs)
def get_queryset(self):
qs = super().get_queryset()
if self.program:
qs = qs.filter(program=self.program)
return qs
def get_context_data(self, **kwargs):
program = kwargs.setdefault('program', self.program)
if program is not None:
kwargs.setdefault('cover', program.cover)
kwargs.setdefault('parent', program)
return super().get_context_data(**kwargs)
class TimetableView(BaseView, ListView): class TimetableView(BaseView, ListView):

View File

@ -1,4 +1,5 @@
from django.core.exceptions import FieldDoesNotExist
from django.http import Http404 from django.http import Http404
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
@ -45,10 +46,11 @@ class PageDetailView(BaseView, DetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
# TODO: pagination: in template, only a limited number of pages displayed
class PageListView(BaseView, ListView): class PageListView(BaseView, ListView):
template_name = 'aircox/page_list.html' template_name = 'aircox/page_list.html'
item_template_name = 'aircox/page_item.html' item_template_name = 'aircox/page_item.html'
paginate_by = 10 paginate_by = 20
show_headline = True show_headline = True
show_side_nav = True show_side_nav = True
categories = None categories = None
@ -82,3 +84,4 @@ class PageListView(BaseView, ListView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

View File

@ -1,15 +1,19 @@
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import get_object_or_404
from aircox.models import Episode, Program from aircox.models import Episode, Program
from .page import PageDetailView from .page import PageDetailView, PageListView
__all__ = ['ProgramPageDetailView', 'ProgramDetailView'] __all__ = ['ProgramPageDetailView', 'ProgramDetailView']
class ProgramPageDetailView(PageDetailView): class ProgramPageDetailView(PageDetailView):
""" Base view class for rendering content of a specific programs. """ """
Base view class for a page that is displayed as a program's child page.
"""
show_side_nav = True show_side_nav = True
list_count=5 list_count = 5
def get_episodes_queryset(self, program): def get_episodes_queryset(self, program):
return program.episode_set.published().order_by('-date') return program.episode_set.published().order_by('-date')
@ -21,9 +25,40 @@ class ProgramPageDetailView(PageDetailView):
program=program, episodes=episodes[:self.list_count], **kwargs) program=program, episodes=episodes[:self.list_count], **kwargs)
class ProgramPageListView(PageListView):
"""
Base list view class rendering pages as a program's child page.
Retrieved program from it slug provided by `kwargs['program_slug']`.
This view class can be used with or without providing a program.
"""
program = None
def get(self, request, *args, **kwargs):
slug = kwargs.get('program_slug', None)
if slug is not None:
self.program = get_object_or_404(
Program.objects.select_related('cover'), slug=slug)
return super().get(request, *args, **kwargs)
def get_queryset(self):
return super().get_queryset().filter(program=self.program) \
if self.program else super().get_queryset()
def get_context_data(self, **kwargs):
program = kwargs.setdefault('program', self.program)
if program is not None:
kwargs.setdefault('cover', program.cover)
kwargs.setdefault('parent', program)
return super().get_context_data(**kwargs)
class ProgramDetailView(ProgramPageDetailView): class ProgramDetailView(ProgramPageDetailView):
model = Program model = Program
def get_articles_queryset(self, program):
return program.article_set.published().order_by('-date')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs.setdefault('program', self.object) kwargs.setdefault('program', self.object)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

View File

@ -105,7 +105,8 @@ aside {
} }
.media-content { .media-content {
padding: 0.2em; padding-top: 0.4em;
padding-left: 0.4em;
} }
.button { .button {

View File

@ -19,7 +19,7 @@
<div class="media-content" v-else-if="onAir && onAir.type == 'diffusion'"> <div class="media-content" v-else-if="onAir && onAir.type == 'diffusion'">
<slot name="diffusion" :onAir="onAir" :liveInfo="liveInfo"></slot> <slot name="diffusion" :onAir="onAir" :liveInfo="liveInfo"></slot>
</div> </div>
<div v-else><slot name="empty"></slot></div> <div class="media-content" v-else><slot name="empty"></slot></div>
</div> </div>
</div> </div>
</template> </template>