From e30d1b54ef14518e728ac4165d0f5c8b311ec4d0 Mon Sep 17 00:00:00 2001 From: bkfox Date: Tue, 10 Sep 2019 17:57:24 +0200 Subject: [PATCH] work on home page, fix views & templates issues --- .../__pycache__/__init__.cpython-37.pyc | Bin 577 -> 577 bytes .../models/__pycache__/episode.cpython-37.pyc | Bin 10253 -> 10560 bytes aircox/models/__pycache__/log.cpython-37.pyc | Bin 8961 -> 8949 bytes aircox/models/__pycache__/page.cpython-37.pyc | Bin 6545 -> 6553 bytes .../models/__pycache__/program.cpython-37.pyc | Bin 17012 -> 17012 bytes .../models/__pycache__/sound.cpython-37.pyc | Bin 8981 -> 8981 bytes .../models/__pycache__/station.cpython-37.pyc | Bin 4691 -> 4691 bytes aircox/models/episode.py | 21 ++- aircox/models/log.py | 21 +-- aircox/models/page.py | 2 +- aircox/static/aircox/admin.css | 106 +++++++++---- aircox/static/aircox/admin.js | 4 +- aircox/static/aircox/main.css | 103 +++++++++---- aircox/static/aircox/main.js | 2 +- aircox/templates/admin/aircox/statistics.html | 32 ++-- aircox/templates/admin/base.html | 5 +- aircox/templates/admin/change_list.html | 18 +-- aircox/templates/aircox/article_detail.html | 6 +- aircox/templates/aircox/base.html | 63 ++++---- aircox/templates/aircox/diffusion_list.html | 6 +- aircox/templates/aircox/episode_detail.html | 2 +- aircox/templates/aircox/home.html | 83 ++++++++++ aircox/templates/aircox/log_list.html | 23 +-- aircox/templates/aircox/page_list.html | 7 +- aircox/templates/aircox/program_base.html | 8 +- aircox/templates/aircox/program_detail.html | 5 +- .../aircox/{ => widgets}/diffusion_item.html | 2 +- .../aircox/{ => widgets}/episode_item.html | 2 +- .../aircox/{ => widgets}/log_item.html | 4 +- aircox/templates/aircox/widgets/log_list.html | 29 ++++ .../aircox/{ => widgets}/page_item.html | 32 +++- .../templates/aircox/widgets/page_list.html | 28 ++++ .../aircox/{ => widgets}/podcast_item.html | 0 .../aircox/{ => widgets}/track_item.html | 0 aircox/urls.py | 9 +- aircox/views/__init__.py | 7 +- aircox/views/admin.py | 3 +- aircox/views/api.py | 1 - aircox/views/article.py | 2 +- aircox/views/base.py | 17 ++- aircox/views/episode.py | 7 +- aircox/views/home.py | 44 ++++++ aircox/views/log.py | 7 +- aircox/views/mixins.py | 6 +- aircox/views/page.py | 10 +- aircox/views/program.py | 49 ++++-- assets/admin/admin.scss | 6 - assets/admin/statistics.vue | 4 + assets/public/player.vue | 1 + assets/public/styles.scss | 144 +++++++++++++----- instance/urls.py | 3 +- 51 files changed, 658 insertions(+), 276 deletions(-) create mode 100644 aircox/templates/aircox/home.html rename aircox/templates/aircox/{ => widgets}/diffusion_item.html (81%) rename aircox/templates/aircox/{ => widgets}/episode_item.html (95%) rename aircox/templates/aircox/{ => widgets}/log_item.html (79%) create mode 100644 aircox/templates/aircox/widgets/log_list.html rename aircox/templates/aircox/{ => widgets}/page_item.html (52%) create mode 100644 aircox/templates/aircox/widgets/page_list.html rename aircox/templates/aircox/{ => widgets}/podcast_item.html (100%) rename aircox/templates/aircox/{ => widgets}/track_item.html (100%) create mode 100644 aircox/views/home.py diff --git a/aircox/models/__pycache__/__init__.cpython-37.pyc b/aircox/models/__pycache__/__init__.cpython-37.pyc index cfebf4589f8fe46688fe920d8efcc3e05df60534..4631e3661a782f3baa1037a02f0ce27f9fd1dc46 100644 GIT binary patch delta 20 acmX@ea*&1FiI7%Q6yCQ#j-kVKaK#*v^c{B6o z{e1K0dFj}v$D_IKZd1X(JD*)XyO{ncdQ$DNiViQ;uS{NcSlRa6Rz;16VO3l=cf+)} zZ4M#cGpCv%Jk^QcKK$G>2tHhlcuED9)fHs{JguTE>ppnOvazD9s0-TPR~RrA|Fsnz zcXVH=gnZ4{XH}*z!Ghtd{IqW@cQ9iGW)$C;QL>@=L2KHXoo%>Iz2;i=l9=m?rtinm z@BOC`oF6nr$wEHjdPVLP3cOe=+dPLm*|2!CD_){7S364exfh(8y{dc8oXvOR8b3ff zA>K>!VS;A~=;W$q*BC!YQ}Po)A4#Nl2tQYHr$%8TBp@dK=z1eYemL#%X@bL|+LO}N zqBkSHicFS?G)dSg6p$M5VFi#Z6^YU+`VEy&`AC=-@`1q;$WdK_oPt_vEz#c7*7lID zV9!_A=|ZZi;)~%SQSLrB#h*bxNK0&u7FP7^LZR&0xE96i3WfH{I$8I+<6DnW=npcH zJZZEh$sksRJ6CZeH@r*RtaVVDqsK{fHtfhg>hSJ1)3qZRmw3 z#J92UPul}*m|2U;4ag^1l8r1X`4KkCQaFyXF_y+L#dfd^%PvBGjBR0CaV5>RvF$jH zv+;c9rZNj`f=w=l^Vyq<}K87bp%C>_e~z2`u8z1qB7`L zE@*rLWh^c-O9q*`q=F(1YDYLdo~$YdzKLW*-0nMMeuAP))vAcz{^z=nq7W1a@LtJ! zv;P&ywZe%kY@onbJFE3lu_~WcPUHr5z?qgiuo>dAie~A!v{6PWyKdv-!AF~vc{}?I zPUuxo1EB;c@C#}^jU%>_3m_!}C29#UpRrExhHW{s7F%x(mg8Eb2IqFotG;aUMy=-5 z$`-zF8HX&8JrvAd0=%G}ATDG;Hh^i$EfaFwLal!JJJbm{(85^J^RadVng|rc-NDb{ z$JW~qKM7D1cZa4ClfxGL+{z8_hprm=klyOzHLElz=dzQ~E&p!rt~#!V9qHm81;mDA zZu=!VAhphtR3V_Jv1&M;J?F}PmDPht8{%5BuSY7&mgJ(|uj2jWouC8BOff%tBsWKP zs0N5zBk#cpaeg#`cy-jWf?YD-VP7d#8_eblbYMS1`-;9CjY3p>Gg^eE*qa)FYx44I zsTlN$X6j04&BdRoM67L(KSXBEi}A6vaRbF6aeOQTTgBC}51J2@9w#M>;2c4LphU2f zfDy|HlM-*GqoD?_x=)K%Iy07%g5P0<+6#y}Te3W*%(m4XWsTL9ZOzl`jI%8!M|F!$C_8LTtE=KrUE4Oew!>zWx;CTa zblB|ZJD+3uj+$2#UZKE?o`ch%Q{}CR7)K8|Uo$`}p1a_PA^gxZyhUDoPmH20G{PjQ z>7twDlZ02`ffebJ@@bscC}c&oSsW&ou8Q!(;ErM;hC^rnOFrRZJsRvIjjB_trf|e^ zHBl?R)2CJ~x9WOsu_8T+7tvx)m4oOa*(dTiKu_$n^&JW|x?Kg`FU3y!=-6T$u0`YU zXY2z;Qmk=39%h|XJk4Nbu~e+c>K=!iJqOr@#xFg)Sy)!q9PO0`O45L}=Q%hW4MAVu zSM0CGuYE@ub0Ls8j>MXU%ZU)X+o&ccS%*tI#|UNkzQ5Ef=j|G~Z;n>nnpav}SQ`6J zftQjK`zxd?wJwt65U8=5hU+;CHQ8>DnrsQaNJgKM%2aWpiueb9PyW^`qn1V2zH&^I z?93qwu`BG>WTO|{YC+cNLj~>(e9ETaqf{JuE0u_~w>JIGN~yBU9kD`gj>{a}Pua{5 z!E}+W!qs#SyDpD@BaI1#&(kXh+PhoVSvZe>1m9veACN(sW7gtrI35*5TX1 v0bdQp+8F#jnB8Lpe3owcuo*i7M!*c%W)P|CYOYtDfW6r|j{P$jDHr70yt@oWhXJF@dp&sfHz(p@t;|C<0QqxmCu9 zkzbOLfuWcMXblSk8>1%Ij39L#3?MayTHy>eESp&R6k2ILnh0Ex-$a$$@HljG!6SeXSF`ItAi%H=XLDsBEO|C*6e KWAlAQF-8Ee!YMES diff --git a/aircox/models/__pycache__/page.cpython-37.pyc b/aircox/models/__pycache__/page.cpython-37.pyc index 1e1b87740006eb6433e214cb6545840cd3886c3a..e4ceab932ae22fff0843df514b297e07d2c03f19 100644 GIT binary patch delta 43 xcmbPeJkyxhiI$OJn^u@=oDm7fsA8O3ts?yn*W_9{`>d3R?gG diff --git a/aircox/models/__pycache__/program.cpython-37.pyc b/aircox/models/__pycache__/program.cpython-37.pyc index 07c00dd571dcd4a3720d0bb28d1346cb68e57c85..ae0e9b6535c231b8723c54c3ecc72e3578df2ad2 100644 GIT binary patch delta 22 ccmey;!uX|yk=u!vmx}=iE*vS}$erl~08(TI)&Kwi delta 22 ccmey;!uX|yk=u!vmx}=iw$Cix$erl~08pt0kN^Mx diff --git a/aircox/models/__pycache__/sound.cpython-37.pyc b/aircox/models/__pycache__/sound.cpython-37.pyc index db47a404fd5af591d2d58cdcfec5ae50e1bb93b7..bb3264a30808dc82250b63c9b3fdb7700ebf7e8a 100644 GIT binary patch delta 20 acmbR0Hr0*WiIw diff --git a/aircox/models/__pycache__/station.cpython-37.pyc b/aircox/models/__pycache__/station.cpython-37.pyc index fbb7e5887278e7afcd48ea1b8477b98bc53743e4..3b8c0b8d40cd4897205e3f02442e75788d734110 100644 GIT binary patch delta 20 acmcbta#@AjiI= now + # TODO: property? def is_live(self): """ True if Diffusion is live (False if there are sounds files). """ diff --git a/aircox/models/log.py b/aircox/models/log.py index 390d3f1..ac7765a 100644 --- a/aircox/models/log.py +++ b/aircox/models/log.py @@ -271,21 +271,22 @@ class Log(models.Model): object_list += logs[:index] logs = logs[index:] - # - last log while diff is running - if logs[0].date > diff.start: - object_list.append(logs[0]) + if len(logs): + # FIXME + # - last log while diff is running + #if logs[0].date > diff.start: + # object_list.append(logs[0]) - # - skips logs while diff is running - index = next((i for i, v in enumerate(logs) - if v.date < diff.start), len(logs)) - if index is not None and index > 0: - logs = logs[index:] + # - skips logs while diff is running + index = next((i for i, v in enumerate(logs) + if v.date < diff.start), len(logs)) + if index is not None and index > 0: + logs = logs[index:] # - add diff object_list.append(diff) - return object_list - + return object_list if count is None else object_list[:count] def print(self): r = [] diff --git a/aircox/models/page.py b/aircox/models/page.py index 6ccb5c8..b2dfc43 100644 --- a/aircox/models/page.py +++ b/aircox/models/page.py @@ -92,7 +92,7 @@ class Page(models.Model): objects = PageQuerySet.as_manager() detail_url_name = None - item_template_name = 'aircox/page_item.html' + item_template_name = 'aircox/widgets/page_item.html' def __str__(self): return '{}'.format(self.title or self.pk) diff --git a/aircox/static/aircox/admin.css b/aircox/static/aircox/admin.css index fa1696a..515d1ee 100644 --- a/aircox/static/aircox/admin.css +++ b/aircox/static/aircox/admin.css @@ -1,6 +1,3 @@ -.navbar { - box-shadow: 0em 0em 0.6em rgba(0, 0, 0, 0.4); } - .navbar .navbar-brand { padding-right: 1em; } @@ -7211,6 +7208,28 @@ a.navbar-item.is-active { margin: 0em; padding: 0em; } +.card .title { + padding: 0.2em; + font-size: 1.25rem; + font-weight: 500; } + .card .title a { + color: #363636; } + +.card.is-primary { + box-shadow: 0em 0em 0.5em #0a0a0a; } + +.card-super-title { + position: absolute; + z-index: 1000; + font-size: 1.2em; + font-weight: 700; + padding: 0.2em; + top: 1em; + background-color: white; } + .card-super-title .fas { + padding: 0.1em; + font-size: 0.8em; } + .filters { margin: 1em 0em; background-color: transparent; @@ -7223,16 +7242,6 @@ a.navbar-item.is-active { color: #7a7a7a; font-weight: 300; } -/* -.navbar-brand img { - min-height: 6em; -} - -.navbar-menu .navbar-item:not(:last-child) { - border-right: 1px $grey solid; -} -*/ -/** page **/ .page > .cover { float: right; max-width: 45%; } @@ -7247,26 +7256,23 @@ a.navbar-item.is-active { .page p { padding: 0.4em 0em; } -section > .toolbar { - background-color: rgba(0, 0, 0, 0.05); - padding: 1em; - margin-bottom: 1.5em; } - -main .cover { - margin: 1em 0em; - border: 0.2em black solid; } - -main .small-cover { - width: 10em; } - -aside .small-cover { - width: 4em; } - -aside .media .subtitle { - font-size: 1em; } - -aside .media .content { - display: none; } +.media.item .headline { + line-height: 1.2em; + max-height: calc(1.2em * 3); + overflow: hidden; } + .media.item .headline + .headline-overflow { + position: relative; + width: 100%; + height: 2em; + margin-top: -2em; } + .media.item .headline + .headline-overflow:before { + content: ''; + width: 100%; + height: 100%; + position: absolute; + left: 0; + bottom: 0; + background: linear-gradient(transparent 1em, whitesmoke); } .player { box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); } @@ -7287,3 +7293,37 @@ aside .media .content { .player .title { margin: 0em; } +.media .subtitle { + margin-bottom: 0.4em; } + +.media .media-content .headline { + font-size: 1em; + font-weight: 400; } + +body { + background-color: whitesmoke; } + +section > .toolbar { + background-color: rgba(0, 0, 0, 0.05); + padding: 1em; + margin-bottom: 1.5em; } + +main .cover { + margin: 1em 0em; + border: 0.2em black solid; } + +main .small-cover { + width: 10em; } + +aside > section { + margin-bottom: 2em; } + +aside .cover { + margin-bottom: 2em; } + +aside .small-cover { + width: 4em; } + +aside .media .subtitle { + font-size: 1em; } + diff --git a/aircox/static/aircox/admin.js b/aircox/static/aircox/admin.js index 81267d7..fbaf339 100644 --- a/aircox/static/aircox/admin.js +++ b/aircox/static/aircox/admin.js @@ -305,7 +305,7 @@ eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./asse /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n\n\nconst splitReg = new RegExp(`,\\s*`, 'g');\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n counts: {},\n }\n },\n\n methods: {\n update() {\n const items = this.$el.querySelectorAll('input[name=\"data\"]:checked')\n const counts = {};\n\n console.log(items)\n for(var item of items)\n if(item.value)\n for(var tag of item.value.split(splitReg))\n counts[tag.trim()] = (counts[tag.trim()] || 0) + 1;\n this.counts = counts;\n }\n },\n\n mounted() {\n this.$refs.form.addEventListener('change', () => this.update())\n this.update()\n }\n});\n\n\n//# sourceURL=webpack:///./assets/admin/statistics.vue?./node_modules/vue-loader/lib??vue-loader-options"); +eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n\n\nconst splitReg = new RegExp(`,\\s*`, 'g');\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n counts: {},\n }\n },\n\n methods: {\n update() {\n const items = this.$el.querySelectorAll('input[name=\"data\"]:checked')\n const counts = {};\n\n console.log(items)\n for(var item of items)\n if(item.value)\n for(var tag of item.value.split(splitReg))\n counts[tag.trim()] = (counts[tag.trim()] || 0) + 1;\n this.counts = counts;\n },\n\n onclick(event) {\n // TODO: row click => check checkbox\n }\n },\n\n mounted() {\n this.$refs.form.addEventListener('change', () => this.update())\n this.update()\n }\n});\n\n\n//# sourceURL=webpack:///./assets/admin/statistics.vue?./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), @@ -317,7 +317,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n\n\nc /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var _liveInfo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./liveInfo */ \"./assets/public/liveInfo.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n liveInfo: new _liveInfo__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this.liveInfoUrl, this.liveInfoTimeout),\n }\n },\n\n props: {\n buttonTitle: String,\n liveInfoUrl: String,\n liveInfoTimeout: { type: Number, default: 5},\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n onAir() {\n return this.liveInfo.items && this.liveInfo.items[0];\n },\n\n buttonStyle() {\n if(!this.onAir)\n return;\n return { backgroundImage: `url(${this.onAir.cover})` }\n }\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.liveInfo.refresh()\n },\n\n destroyed() {\n this.liveInfo.drop()\n },\n});\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var _liveInfo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./liveInfo */ \"./assets/public/liveInfo.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n liveInfo: new _liveInfo__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this.liveInfoUrl, this.liveInfoTimeout),\n }\n },\n\n props: {\n buttonTitle: String,\n liveInfoUrl: String,\n liveInfoTimeout: { type: Number, default: 5},\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n onAir() {\n return this.liveInfo.items && this.liveInfo.items[0];\n },\n\n buttonStyle() {\n if(!this.onAir)\n return;\n return { backgroundImage: `url(${this.onAir.cover})` }\n }\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n console.log('tooogle', this.paused, '-', this.$refs.audio.src)\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.liveInfo.refresh()\n },\n\n destroyed() {\n this.liveInfo.drop()\n },\n});\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), diff --git a/aircox/static/aircox/main.css b/aircox/static/aircox/main.css index 5368918..6e70140 100644 --- a/aircox/static/aircox/main.css +++ b/aircox/static/aircox/main.css @@ -7190,6 +7190,28 @@ a.navbar-item.is-active { margin: 0em; padding: 0em; } +.card .title { + padding: 0.2em; + font-size: 1.25rem; + font-weight: 500; } + .card .title a { + color: #363636; } + +.card.is-primary { + box-shadow: 0em 0em 0.5em #0a0a0a; } + +.card-super-title { + position: absolute; + z-index: 1000; + font-size: 1.2em; + font-weight: 700; + padding: 0.2em; + top: 1em; + background-color: white; } + .card-super-title .fas { + padding: 0.1em; + font-size: 0.8em; } + .filters { margin: 1em 0em; background-color: transparent; @@ -7202,16 +7224,6 @@ a.navbar-item.is-active { color: #7a7a7a; font-weight: 300; } -/* -.navbar-brand img { - min-height: 6em; -} - -.navbar-menu .navbar-item:not(:last-child) { - border-right: 1px $grey solid; -} -*/ -/** page **/ .page > .cover { float: right; max-width: 45%; } @@ -7226,26 +7238,23 @@ a.navbar-item.is-active { .page p { padding: 0.4em 0em; } -section > .toolbar { - background-color: rgba(0, 0, 0, 0.05); - padding: 1em; - margin-bottom: 1.5em; } - -main .cover { - margin: 1em 0em; - border: 0.2em black solid; } - -main .small-cover { - width: 10em; } - -aside .small-cover { - width: 4em; } - -aside .media .subtitle { - font-size: 1em; } - -aside .media .content { - display: none; } +.media.item .headline { + line-height: 1.2em; + max-height: calc(1.2em * 3); + overflow: hidden; } + .media.item .headline + .headline-overflow { + position: relative; + width: 100%; + height: 2em; + margin-top: -2em; } + .media.item .headline + .headline-overflow:before { + content: ''; + width: 100%; + height: 100%; + position: absolute; + left: 0; + bottom: 0; + background: linear-gradient(transparent 1em, whitesmoke); } .player { box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); } @@ -7266,3 +7275,37 @@ aside .media .content { .player .title { margin: 0em; } +.media .subtitle { + margin-bottom: 0.4em; } + +.media .media-content .headline { + font-size: 1em; + font-weight: 400; } + +body { + background-color: whitesmoke; } + +section > .toolbar { + background-color: rgba(0, 0, 0, 0.05); + padding: 1em; + margin-bottom: 1.5em; } + +main .cover { + margin: 1em 0em; + border: 0.2em black solid; } + +main .small-cover { + width: 10em; } + +aside > section { + margin-bottom: 2em; } + +aside .cover { + margin-bottom: 2em; } + +aside .small-cover { + width: 4em; } + +aside .media .subtitle { + font-size: 1em; } + diff --git a/aircox/static/aircox/main.js b/aircox/static/aircox/main.js index b514119..5af8f05 100644 --- a/aircox/static/aircox/main.js +++ b/aircox/static/aircox/main.js @@ -246,7 +246,7 @@ eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./asse /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var _liveInfo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./liveInfo */ \"./assets/public/liveInfo.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n liveInfo: new _liveInfo__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this.liveInfoUrl, this.liveInfoTimeout),\n }\n },\n\n props: {\n buttonTitle: String,\n liveInfoUrl: String,\n liveInfoTimeout: { type: Number, default: 5},\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n onAir() {\n return this.liveInfo.items && this.liveInfo.items[0];\n },\n\n buttonStyle() {\n if(!this.onAir)\n return;\n return { backgroundImage: `url(${this.onAir.cover})` }\n }\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.liveInfo.refresh()\n },\n\n destroyed() {\n this.liveInfo.drop()\n },\n});\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var _liveInfo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./liveInfo */ \"./assets/public/liveInfo.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n liveInfo: new _liveInfo__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this.liveInfoUrl, this.liveInfoTimeout),\n }\n },\n\n props: {\n buttonTitle: String,\n liveInfoUrl: String,\n liveInfoTimeout: { type: Number, default: 5},\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n onAir() {\n return this.liveInfo.items && this.liveInfo.items[0];\n },\n\n buttonStyle() {\n if(!this.onAir)\n return;\n return { backgroundImage: `url(${this.onAir.cover})` }\n }\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n console.log('tooogle', this.paused, '-', this.$refs.audio.src)\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.liveInfo.refresh()\n },\n\n destroyed() {\n this.liveInfo.drop()\n },\n});\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options"); /***/ }), diff --git a/aircox/templates/admin/aircox/statistics.html b/aircox/templates/admin/aircox/statistics.html index 6dfa9b2..5f0156e 100644 --- a/aircox/templates/admin/aircox/statistics.html +++ b/aircox/templates/admin/aircox/statistics.html @@ -3,8 +3,14 @@ {% block content %}{{ block.super }} +
+ {# TODO: date subtitle #} -
+ - - - -
{% endblock %} diff --git a/aircox/templates/admin/base.html b/aircox/templates/admin/base.html index e66b147..3eb3808 100644 --- a/aircox/templates/admin/base.html +++ b/aircox/templates/admin/base.html @@ -29,11 +29,10 @@ data-admin-utc-offset="{% now "Z" %}"> -
- +
{% if not is_popup %} -
-
+
{% endblock %} diff --git a/aircox/templates/aircox/article_detail.html b/aircox/templates/aircox/article_detail.html index dce7ec4..ee86496 100644 --- a/aircox/templates/aircox/article_detail.html +++ b/aircox/templates/aircox/article_detail.html @@ -4,12 +4,12 @@ {% block sidebar %} {{ block.super }} -{% if sidebar_items %} +{% if sidebar_object_list %}

{% trans "Latest news" %}

- {% for object in sidebar_items %} - {% include "aircox/page_item.html" %} + {% for object in sidebar_object_list %} + {% include "aircox/widgets/page_item.html" %} {% endfor %}
diff --git a/aircox/templates/aircox/base.html b/aircox/templates/aircox/base.html index e06477f..c427aa5 100644 --- a/aircox/templates/aircox/base.html +++ b/aircox/templates/aircox/base.html @@ -1,10 +1,29 @@ -{% load static i18n thumbnail aircox %} {% comment %} Context: - cover: image cover - site: current website - has_filters: display filter bar (using block "filters") +- sidebar_object_list: item to display in sidebar +- sidebar_url_name: url name sidebar item complete list +- sidebar_url_parent: parent page for sidebar items complete list + +Blocks: +- assets +- head_title +- head_extra +- top_nav +- header: + - title + - subtitle + - header_meta +- main: + - filters +- cover +- sidebar: + - sidebar_title {% endcomment %} +{% load static i18n thumbnail aircox %} + @@ -72,14 +91,14 @@ Context: {% endblock %} - {% if has_filters %} - - {% endif %} - - {% block main %}{% endblock main %} + {% block main %} + {% if has_filters %} + + {% endif %} + {% endblock main %} {% if has_sidebar %} @@ -92,28 +111,18 @@ Context: {% endblock %} {% block sidebar %} - {% if sidebar_items %} + {% if sidebar_object_list %} + {% with object_list=sidebar_object_list %} + {% with list_url=sidebar_list_url %} + {% with has_headline=False %}

{% block sidebar_title %}{% trans "Recently" %}{% endblock %}

- - {% for object in sidebar_items %} - {% include "aircox/episode_item.html" %} - {% endfor %} - -
- + {% include "aircox/widgets/page_list.html" %} + {% endwith %} + {% endwith %} + {% endwith %} {% endif %}
{% endblock %} diff --git a/aircox/templates/aircox/diffusion_list.html b/aircox/templates/aircox/diffusion_list.html index 78d60b4..47ebc29 100644 --- a/aircox/templates/aircox/diffusion_list.html +++ b/aircox/templates/aircox/diffusion_list.html @@ -16,7 +16,7 @@ {% endblock %} {% block main %}{{ block.super }} -{% with True as hide_schedule %} +{% with hide_schedule=True %}
{% for diffusion in object_list %} @@ -24,12 +24,12 @@
{% with diffusion.episode as object %} - {% include "aircox/episode_item.html" %} + {% include "aircox/widgets/episode_item.html" %} {% endwith %}
diff --git a/aircox/templates/aircox/episode_detail.html b/aircox/templates/aircox/episode_detail.html index 2d31754..43075f4 100644 --- a/aircox/templates/aircox/episode_detail.html +++ b/aircox/templates/aircox/episode_detail.html @@ -50,7 +50,7 @@

{% trans "Podcasts" %}

{% for object in podcasts %} - {% include "aircox/podcast_item.html" %} + {% include "aircox/widgets/podcast_item.html" %} {% endfor %}
{% endif %} diff --git a/aircox/templates/aircox/home.html b/aircox/templates/aircox/home.html new file mode 100644 index 0000000..3c76f5c --- /dev/null +++ b/aircox/templates/aircox/home.html @@ -0,0 +1,83 @@ +{% extends "aircox/page_list.html" %} +{% comment %} +Context: +- +- + +TODO: +- sidebar: + - logs + - diffusions +- main: + - focused + - nav to 'publications' view + - + +{% endcomment %} +{% load i18n %} + +{% block head_title %}{% block title %}{{ station.name }}{% endblock %}{% endblock %} + +{% block main %} +
+ {% with render_card=True %} + {% for object in top_diffs %} + {% with is_primary=object.is_now %} +
+

+ {% if is_primary %} + + {% trans "Currently" %} + {% else %}{{ object.start|date:"H:i" }}{% endif %} +

+ {% include object.item_template_name %} +
+ {% endwith %} + {% endfor %} + {% endwith %} +
+ +
+

{% trans "Last publications" %}

+{{ block.super }} +{% endblock %} + + +{% block pagination %} + +{% endblock %} + + +{% block sidebar %} +
+

{% trans "Previously on air" %}

+ {% with logs as object_list %} + {% include "aircox/widgets/log_list.html" %} + {% endwith %} +
+ +
+

{% trans "Today" %}

+ {% with hide_schedule=True %} + {% with has_headline=False %} + + {% for object in sidebar_object_list %} + + + + + {% endfor %} +
{{ object.start|date:"H:i" }}{% include "aircox/widgets/diffusion_item.html" %}
+ {% endwith %} + {% endwith %} +
+{% endblock %} + + diff --git a/aircox/templates/aircox/log_list.html b/aircox/templates/aircox/log_list.html index be03e6a..13d1772 100644 --- a/aircox/templates/aircox/log_list.html +++ b/aircox/templates/aircox/log_list.html @@ -16,29 +16,10 @@ {% endwith %} {% endblock %} -{% block main %} +{% block main %}{{ block.super }}
{#

{{ date }}

#} - {% with True as hide_schedule %} - - {% for object in object_list %} - - - - - {% endfor %} -
- {% if object|is_diffusion %} - - {% else %} - - {% endif %} - {% include "aircox/log_item.html" %}
- {% endwith %} + {% include "aircox/widgets/log_list.html" %}
{% endblock %} diff --git a/aircox/templates/aircox/page_list.html b/aircox/templates/aircox/page_list.html index 539a98c..ee5b01d 100644 --- a/aircox/templates/aircox/page_list.html +++ b/aircox/templates/aircox/page_list.html @@ -56,21 +56,23 @@ {% endblock %} -{% block main %} +{% block main %}{{ block.super }}
+{% with has_headline=True %} {% for object in object_list %} {% block list_object %} {% include object.item_template_name|default:item_template_name %} {% endblock %} {% endfor %} +{% endwith %}
{% if is_paginated %}
- {% update_query request.GET.copy page=None as GET %} {% with GET.urlencode as GET %} {% endwith %} {% endif %} diff --git a/aircox/templates/aircox/program_base.html b/aircox/templates/aircox/program_base.html index a3f5db2..d7a8e3e 100644 --- a/aircox/templates/aircox/program_base.html +++ b/aircox/templates/aircox/program_base.html @@ -3,13 +3,7 @@ {% block sidebar_title %} {% with program.title as program %} -{% blocktrans %} Recently on {{ program }}{% endblocktrans %} -{% endwith %} -{% endblock %} - -{% block sidebar %} -{% with program as parent %} -{{ block.super }} +{% blocktrans %}Recently on {{ program }}{% endblocktrans %} {% endwith %} {% endblock %} diff --git a/aircox/templates/aircox/program_detail.html b/aircox/templates/aircox/program_detail.html index 9692562..e91a330 100644 --- a/aircox/templates/aircox/program_detail.html +++ b/aircox/templates/aircox/program_detail.html @@ -10,15 +10,14 @@ {% block content %}{{ block.super }}
- -{% with show_headline=False %} +{% with has_headline=False %}
{% if articles %}

{% trans "Articles" %}

{% for object in articles %} - {% include "aircox/page_item.html" %} + {% include "aircox/widgets/page_item.html" %} {% endfor %}
diff --git a/aircox/templates/aircox/diffusion_item.html b/aircox/templates/aircox/widgets/diffusion_item.html similarity index 81% rename from aircox/templates/aircox/diffusion_item.html rename to aircox/templates/aircox/widgets/diffusion_item.html index 8b5d651..2971908 100644 --- a/aircox/templates/aircox/diffusion_item.html +++ b/aircox/templates/aircox/widgets/diffusion_item.html @@ -5,7 +5,7 @@ Context: {% endcomment %} {% with object as diffusion %} {% with object.episode as object %} -{% include "aircox/episode_item.html" %} +{% include "aircox/widgets/episode_item.html" %} {% endwith %} {% endwith %} diff --git a/aircox/templates/aircox/episode_item.html b/aircox/templates/aircox/widgets/episode_item.html similarity index 95% rename from aircox/templates/aircox/episode_item.html rename to aircox/templates/aircox/widgets/episode_item.html index a5d6911..d9c6a73 100644 --- a/aircox/templates/aircox/episode_item.html +++ b/aircox/templates/aircox/widgets/episode_item.html @@ -1,4 +1,4 @@ -{% extends "aircox/page_item.html" %} +{% extends "aircox/widgets/page_item.html" %} {% load i18n easy_thumbnails_tags aircox %} {% comment %} diff --git a/aircox/templates/aircox/log_item.html b/aircox/templates/aircox/widgets/log_item.html similarity index 79% rename from aircox/templates/aircox/log_item.html rename to aircox/templates/aircox/widgets/log_item.html index 9cbae9b..355f81c 100644 --- a/aircox/templates/aircox/log_item.html +++ b/aircox/templates/aircox/widgets/log_item.html @@ -11,11 +11,11 @@ for design review. {% if object|is_diffusion %} {% with object as diffusion %} - {% include "aircox/diffusion_item.html" %} + {% include "aircox/widgets/diffusion_item.html" %} {% endwith %} {% else %} {% with object.track as object %} - {% include "aircox/track_item.html" %} + {% include "aircox/widgets/track_item.html" %} {% endwith %} {% endif %} diff --git a/aircox/templates/aircox/widgets/log_list.html b/aircox/templates/aircox/widgets/log_list.html new file mode 100644 index 0000000..0b1cae3 --- /dev/null +++ b/aircox/templates/aircox/widgets/log_list.html @@ -0,0 +1,29 @@ +{% comment %} +Render list of logs (as widget). + +Context: +- object_list: list of logs to display +{% endcomment %} +{% load aircox %} + +{% with True as hide_schedule %} + + {% for object in object_list %} + + + + + {% endfor %} +
+ {% if object|is_diffusion %} + + {% else %} + + {% endif %} + {% include "aircox/widgets/log_item.html" %}
+{% endwith %} + diff --git a/aircox/templates/aircox/page_item.html b/aircox/templates/aircox/widgets/page_item.html similarity index 52% rename from aircox/templates/aircox/page_item.html rename to aircox/templates/aircox/widgets/page_item.html index 0d47cf8..67f422e 100644 --- a/aircox/templates/aircox/page_item.html +++ b/aircox/templates/aircox/widgets/page_item.html @@ -1,15 +1,36 @@ {% load i18n easy_thumbnails_tags aircox %} {% comment %} Context variables: -- object: the actual diffusion -- show_headline: if True, display headline +- object: the object to render +- render_card: render as card +- is-primary: render as primary +- has_headline (=False): if True, display headline +- has_cover (=True): hide page cover {% endcomment %} -
+{% if render_card %} + + + +{% else %} +
+ {% if has_cover|default_if_none:True %}
+ {% endif %}
{% block title %} @@ -26,8 +47,8 @@ Context variables: {% endblock %}
- {% if show_headline %} -
+ {% if has_headline|default_if_none:True %} +
{% block headline %} {{ object.headline|safe }} {% endblock %} @@ -35,4 +56,5 @@ Context variables: {% endif %}
+{% endif %} diff --git a/aircox/templates/aircox/widgets/page_list.html b/aircox/templates/aircox/widgets/page_list.html new file mode 100644 index 0000000..46a798b --- /dev/null +++ b/aircox/templates/aircox/widgets/page_list.html @@ -0,0 +1,28 @@ +{% comment %} +Display list of items as small list + +Context: +- object_list: object list +- list_url: url to complete list page +{% endcomment %} +{% load i18n %} + +{% for object in object_list %} +{% include object.item_template_name %} +{% endfor %} + +{% if list_url %} +
+ +{% endif %} + + diff --git a/aircox/templates/aircox/podcast_item.html b/aircox/templates/aircox/widgets/podcast_item.html similarity index 100% rename from aircox/templates/aircox/podcast_item.html rename to aircox/templates/aircox/widgets/podcast_item.html diff --git a/aircox/templates/aircox/track_item.html b/aircox/templates/aircox/widgets/track_item.html similarity index 100% rename from aircox/templates/aircox/track_item.html rename to aircox/templates/aircox/widgets/track_item.html diff --git a/aircox/urls.py b/aircox/urls.py index 6fbcf82..cb7c74e 100755 --- a/aircox/urls.py +++ b/aircox/urls.py @@ -24,8 +24,7 @@ api = [ urls = [ - path(_(''), - views.DiffusionListView.as_view(), name='home'), + path('', views.HomeView.as_view(), name='home'), path('api/', include(api)), # path('', views.PageDetailView.as_view(model=models.Article), @@ -51,9 +50,9 @@ urls = [ # path('', views.route_page, name='page'), path(_('publications/'), - views.ProgramPageListView.as_view(), name='page-list'), + views.PageListView.as_view(model=models.Page), name='page-list'), - path(_('programs/'), views.PageListView.as_view(model=models.Program), + path(_('programs/'), views.ProgramListView.as_view(model=models.Program), name='program-list'), path(_('programs//'), views.ProgramDetailView.as_view(), name='program-detail'), @@ -62,7 +61,7 @@ urls = [ path(_('programs//articles/'), views.ArticleListView.as_view(), name='article-list'), path(_('programs//publications/'), - views.ProgramPageListView.as_view(), name='page-list'), + views.ProgramPageListView.as_view(), name='program-page-list'), ] diff --git a/aircox/views/__init__.py b/aircox/views/__init__.py index ed44b56..e031491 100644 --- a/aircox/views/__init__.py +++ b/aircox/views/__init__.py @@ -1,9 +1,12 @@ from . import api, admin -from .article import ArticleDetailView, ArticleListView from .base import BaseView +from .home import HomeView + +from .article import ArticleDetailView, ArticleListView from .episode import EpisodeDetailView, EpisodeListView, DiffusionListView from .log import LogListView from .page import PageDetailView, PageListView -from .program import ProgramDetailView, ProgramPageListView +from .program import ProgramDetailView, ProgramListView, \ + ProgramPageDetailView, ProgramPageListView diff --git a/aircox/views/admin.py b/aircox/views/admin.py index 49ec58a..441d379 100644 --- a/aircox/views/admin.py +++ b/aircox/views/admin.py @@ -44,8 +44,7 @@ class AdminSite(admin.AdminSite): def get_urls(self): from django.urls import path, include - urls = super().get_urls() - urls += [ + urls = super().get_urls() + [ path('tools/statistics/', self.admin_view(StatisticsView.as_view()), name='tools-stats'), diff --git a/aircox/views/api.py b/aircox/views/api.py index e00e2eb..9060d52 100644 --- a/aircox/views/api.py +++ b/aircox/views/api.py @@ -4,7 +4,6 @@ from django.utils import timezone as tz from rest_framework.generics import ListAPIView -from ..utils import str_to_date from ..models import Log from ..serializers import LogInfo, LogInfoSerializer from .log import LogListMixin diff --git a/aircox/views/article.py b/aircox/views/article.py index e81cc1a..02dabbc 100644 --- a/aircox/views/article.py +++ b/aircox/views/article.py @@ -25,7 +25,7 @@ class ArticleDetailView(PageDetailView): class ArticleListView(ParentMixin, PageListView): model = Article template_name = 'aircox/article_list.html' - show_headline = True + has_headline = True is_static = False parent_model = Program diff --git a/aircox/views/base.py b/aircox/views/base.py index eb4c0f0..c3a807c 100644 --- a/aircox/views/base.py +++ b/aircox/views/base.py @@ -2,6 +2,7 @@ from django.http import Http404 from django.views.generic import DetailView, ListView from django.views.generic.base import TemplateResponseMixin, ContextMixin +from django.urls import reverse from ..models import Page from ..utils import Redirect @@ -35,21 +36,25 @@ class BaseView(TemplateResponseMixin, ContextMixin): return Page.objects.select_subclasses().published() \ .order_by('-pub_date') - def get_context_data(self, sidebar_items=None, **kwargs): + def get_sidebar_url(self): + return reverse('page-list') + + def get_context_data(self, **kwargs): kwargs.setdefault('station', self.station) kwargs.setdefault('cover', self.cover) kwargs.setdefault('has_filters', self.has_filters) has_sidebar = kwargs.setdefault('has_sidebar', self.has_sidebar) - if has_sidebar and sidebar_items is None: - sidebar_items = self.get_sidebar_queryset() - sidebar_items = None if sidebar_items is None else \ - sidebar_items[:self.list_count] + if has_sidebar and 'sidebar_object_list' not in kwargs: + sidebar_object_list = self.get_sidebar_queryset() + if sidebar_object_list is not None: + kwargs['sidebar_object_list'] = sidebar_object_list[:self.list_count] + kwargs['sidebar_list_url'] = self.get_sidebar_url() if not 'audio_streams' in kwargs: streams = self.station.audio_streams streams = streams and streams.split('\n') kwargs['audio_streams'] = streams - return super().get_context_data(sidebar_items=sidebar_items, **kwargs) + return super().get_context_data(**kwargs) diff --git a/aircox/views/episode.py b/aircox/views/episode.py index 8e1aa1a..36eb0fa 100644 --- a/aircox/views/episode.py +++ b/aircox/views/episode.py @@ -21,9 +21,6 @@ class EpisodeDetailView(ProgramPageDetailView): return Sound.objects.diffusion(diffusion).podcasts() def get_context_data(self, **kwargs): - self.program = kwargs.setdefault('program', self.object.program) - - kwargs.setdefault('parent', self.program) if not 'tracks' in kwargs: kwargs['tracks'] = self.object.track_set.order_by('position') if not 'podcasts' in kwargs: @@ -33,8 +30,8 @@ class EpisodeDetailView(ProgramPageDetailView): class EpisodeListView(ParentMixin, PageListView): model = Episode - item_template_name = 'aircox/episode_item.html' - show_headline = True + item_template_name = 'aircox/widgets/episode_item.html' + has_headline = True parent_model = Program diff --git a/aircox/views/home.py b/aircox/views/home.py new file mode 100644 index 0000000..71c8a4a --- /dev/null +++ b/aircox/views/home.py @@ -0,0 +1,44 @@ +import datetime + +from django.utils.translation import ugettext as _ +from django.utils import timezone as tz + +from ..models import Diffusion, Log, Page +from .page import PageListView + +class HomeView(PageListView): + template_name = 'aircox/home.html' + model = Page + queryset = Page.objects.select_subclasses() + paginate_by = 10 + list_count = 40 + logs_count = 5 + has_filters = False + + def get_logs(self): + today = datetime.date.today() + logs = Log.objects.on_air().today(today).filter(track__isnull=False) + diffs = Diffusion.objects.on_air().today(today) + return Log.merge_diffusions(logs, diffs, self.logs_count) + + def get_sidebar_queryset(self): + today = datetime.date.today() + return Diffusion.objects.on_air().today(today) + + def get_top_diffs(self): + now = tz.now() + current_diff = Diffusion.objects.on_air().now(now).first() + next_diffs = Diffusion.objects.on_air().after(now) + if current_diff: + diffs = [current_diff] + list(next_diffs.exclude(pk=current_diff.pk)[:2]) + else: + diffs = next_diffs[:3] + return diffs + + def get_context_data(self, **kwargs): + kwargs['logs'] = self.get_logs() + kwargs['top_diffs'] = self.get_top_diffs() + return super().get_context_data(**kwargs) + + + diff --git a/aircox/views/log.py b/aircox/views/log.py index f61670e..6f3de61 100644 --- a/aircox/views/log.py +++ b/aircox/views/log.py @@ -2,6 +2,7 @@ from collections import deque import datetime from django.views.generic import ListView +from django.utils import timezone as tz from ..models import Diffusion, Log from .base import BaseView @@ -23,12 +24,14 @@ class LogListMixin(GetDateMixin): def get_queryset(self): # only get logs for tracks: log for diffusion will be retrieved # by the diffusions' queryset. - qs = super().get_queryset().on_air().filter(track__isnull=False) + qs = super().get_queryset().on_air().filter(track__isnull=False) \ + .filter(date__lte=tz.now()) return qs.today(self.date) if self.date is not None else \ qs.after(self.min_date) if self.min_date is not None else qs def get_diffusions_queryset(self): - qs = Diffusion.objects.station(self.station).on_air() + qs = Diffusion.objects.station(self.station).on_air() \ + .filter(start__lte=tz.now()) return qs.today(self.date) if self.date is not None else \ qs.after(self.min_date) if self.min_date is not None else qs diff --git a/aircox/views/mixins.py b/aircox/views/mixins.py index 6b56260..28d3dc9 100644 --- a/aircox/views/mixins.py +++ b/aircox/views/mixins.py @@ -63,9 +63,9 @@ class ParentMixin: return super().get_queryset() def get_context_data(self, **kwargs): - parent = kwargs.setdefault('parent', self.parent) - if parent is not None: - kwargs.setdefault('cover', parent.cover) + self.parent = kwargs.setdefault('parent', self.parent) + if self.parent is not None: + kwargs.setdefault('cover', self.parent.cover) return super().get_context_data(**kwargs) diff --git a/aircox/views/page.py b/aircox/views/page.py index 1dcaa7f..9ba3f13 100644 --- a/aircox/views/page.py +++ b/aircox/views/page.py @@ -6,7 +6,7 @@ from django.views.generic import DetailView, ListView from honeypot.decorators import check_honeypot from ..forms import CommentForm -from ..models import Category, Comment +from ..models import Category, Comment, Page from ..utils import Redirect from .base import BaseView @@ -17,12 +17,12 @@ __all__ = ['PageDetailView', 'PageListView'] # TODO: pagination: in template, only a limited number of pages displayed class PageListView(BaseView, ListView): template_name = 'aircox/page_list.html' - item_template_name = 'aircox/page_item.html' + item_template_name = 'aircox/widgets/page_item.html' has_sidebar = True has_filters = True paginate_by = 20 - show_headline = True + has_headline = True categories = None def get(self, *args, **kwargs): @@ -30,7 +30,7 @@ class PageListView(BaseView, ListView): return super().get(*args, **kwargs) def get_queryset(self): - qs = super().get_queryset().published() \ + qs = super().get_queryset().select_subclasses().published() \ .select_related('cover', 'category') # category can be filtered based on request.GET['categories'] @@ -50,7 +50,7 @@ class PageListView(BaseView, ListView): kwargs.setdefault('item_template_name', self.item_template_name) kwargs.setdefault('filter_categories', self.get_categories_queryset()) kwargs.setdefault('categories', self.categories) - kwargs.setdefault('show_headline', self.show_headline) + kwargs.setdefault('has_headline', self.has_headline) return super().get_context_data(**kwargs) diff --git a/aircox/views/program.py b/aircox/views/program.py index e9698fc..4388148 100644 --- a/aircox/views/program.py +++ b/aircox/views/program.py @@ -1,6 +1,7 @@ from django.db.models import Q from django.core.exceptions import ObjectDoesNotExist from django.shortcuts import get_object_or_404 +from django.urls import reverse from ..models import Episode, Program, Page from .mixins import ParentMixin @@ -10,30 +11,54 @@ from .page import PageDetailView, PageListView __all__ = ['ProgramPageDetailView', 'ProgramDetailView', 'ProgramPageListView'] -class ProgramPageDetailView(PageDetailView): +class BaseProgramMixin: + def get_program(self): + return self.object + + def get_sidebar_url(self): + return reverse('program-page-list', + kwargs={"parent_slug": self.program.slug}) + + def get_context_data(self, **kwargs): + self.program = self.get_program() + kwargs['program'] = self.program + return super().get_context_data(**kwargs) + + +class ProgramDetailView(BaseProgramMixin, PageDetailView): + model = Program + + def get_sidebar_queryset(self): + return super().get_sidebar_queryset().filter(parent=self.program) + + +class ProgramListView(PageListView): + model = Program + + +class ProgramPageDetailView(BaseProgramMixin, ParentMixin, PageDetailView): """ Base view class for a page that is displayed as a program's child page. """ - program = None - has_sidebar = True - list_count = 5 + parent_model = Program + + def get_program(self): + self.parent = self.object.program + return self.object.program def get_sidebar_queryset(self): - return super().get_sidebar_queryset().filter(parent=self.object) + return super().get_sidebar_queryset().filter(parent=self.program) -class ProgramPageListView(ParentMixin, PageListView): +class ProgramPageListView(BaseProgramMixin, ParentMixin, PageListView): model = Page parent_model = Program queryset = Page.objects.select_subclasses() - -class ProgramDetailView(ProgramPageDetailView): - model = Program + def get_program(self): + return self.parent def get_context_data(self, **kwargs): - self.program = kwargs.setdefault('program', self.object) + kwargs.setdefault('sidebar_url_parent', None) return super().get_context_data(**kwargs) - - diff --git a/assets/admin/admin.scss b/assets/admin/admin.scss index 8b299d3..0cc5b67 100644 --- a/assets/admin/admin.scss +++ b/assets/admin/admin.scss @@ -1,10 +1,4 @@ -// @import './bulmacolors'; - -.navbar { - box-shadow: 0em 0em 0.6em rgba(0, 0, 0, 0.4); -} - .navbar .navbar-brand { padding-right: 1em; } diff --git a/assets/admin/statistics.vue b/assets/admin/statistics.vue index bebbc65..518c1fe 100644 --- a/assets/admin/statistics.vue +++ b/assets/admin/statistics.vue @@ -26,6 +26,10 @@ export default { for(var tag of item.value.split(splitReg)) counts[tag.trim()] = (counts[tag.trim()] || 0) + 1; this.counts = counts; + }, + + onclick(event) { + // TODO: row click => check checkbox } }, diff --git a/assets/public/player.vue b/assets/public/player.vue index 62dba06..5fcacdb 100644 --- a/assets/public/player.vue +++ b/assets/public/player.vue @@ -83,6 +83,7 @@ export default { }, toggle() { + console.log('tooogle', this.paused, '-', this.$refs.audio.src) if(this.paused) this.play() else diff --git a/assets/public/styles.scss b/assets/public/styles.scss index 1c4c987..e4e927c 100644 --- a/assets/public/styles.scss +++ b/assets/public/styles.scss @@ -3,7 +3,7 @@ $body-background-color: $light; -@import "~bulma/bulma"; +@import "~bulma"; //-- helpers/modifiers .is-fullwidth { width: 100%; } @@ -58,6 +58,38 @@ a.navbar-item.is-active { } } +//-- cards +.card { + .title { + a { + color: $dark; + } + + padding: 0.2em; + font-size: $size-5; + font-weight: $weight-medium; + } + + &.is-primary { + box-shadow: 0em 0em 0.5em $black + } +} + +.card-super-title { + position: absolute; + z-index: 1000; + font-size: 1.2em; + font-weight: $weight-bold; + padding: 0.2em; + top: 1em; + background-color: $white; + + .fas { + padding: 0.1em; + font-size: 0.8em; + } +} + //-- filters .filters { @@ -77,18 +109,7 @@ a.navbar-item.is-active { } -/* -.navbar-brand img { - min-height: 6em; -} - -.navbar-menu .navbar-item:not(:last-child) { - border-right: 1px $grey solid; -} -*/ - - -/** page **/ +//-- page .page { & > .cover { float: right; @@ -109,39 +130,33 @@ a.navbar-item.is-active { } } -section > .toolbar { - background-color: rgba(0,0,0,0.05); - padding: 1em; - margin-bottom: 1.5em; -} +.media.item .headline { + line-height: 1.2em; + max-height: calc(1.2em * 3); + overflow: hidden; -main { - .cover { - margin: 1em 0em; - border: 0.2em black solid; + & + .headline-overflow { + position: relative; + width: 100%; + height: 2em; + margin-top: -2em; } - .small-cover { - width: 10em; - } -} - -aside { - .small-cover { - width: 4em; - } - - .media .subtitle { - font-size: 1em; - } - - .media .content { - display: none; + & + .headline-overflow:before { + content:''; + width:100%; + height:100%; + position:absolute; + left:0; + bottom:0; + background:linear-gradient(transparent 1em, $body-background-color); } } + +//-- player .player { box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); @@ -175,4 +190,57 @@ aside { } +//-- media +.media { + .subtitle { + margin-bottom: 0.4em; + } + .media-content .headline { + font-size: 1em; + font-weight: 400; + } +} + +//-- general +body { + background-color: $body-background-color; +} + +section > .toolbar { + background-color: rgba(0,0,0,0.05); + padding: 1em; + margin-bottom: 1.5em; +} + + +main { + .cover { + margin: 1em 0em; + border: 0.2em black solid; + } + + .small-cover { + width: 10em; + } +} + +aside { + & > section { + margin-bottom: 2em; + } + + .cover { + margin-bottom: 2em; + } + + .small-cover { + width: 4em; + } + + .media .subtitle { + font-size: 1em; + } +} + + diff --git a/instance/urls.py b/instance/urls.py index 38ae87f..2046b53 100755 --- a/instance/urls.py +++ b/instance/urls.py @@ -22,9 +22,8 @@ import aircox.urls try: - urlpatterns = [ + urlpatterns = aircox.urls.urls + [ path('admin/', admin.site.urls), - path('aircox/', include(aircox.urls.urls)), ] if settings.DEBUG: