[IMP] website_event_track[_live]: add visual & usability improvements to event.tracks
PURPOSE This commit adds several minor visual improvements to the event.tracks frontend and backend views as well as a few quality of life changes to ease usability. SPECS - Add "hide and show" feature for tracks In the event homepage, all the tracks of the selected event will be displayed. As the tracks are grouped by day, some sections will only contain tracks that have already taken place. If there are a lot of tracks, it might be better to fold these sections to reduce the payload of the page. That way, the user will be able to see the upcoming tracks more easily. With these changes, a new chevron icon will be added above each section. When the user clicks on it, the user can fold and unfold a section. By default, the section containing tracks that have already taken place will be initially folded. - Add partner tag line The user may need to know more about the speaker before attending a talk. The user may wonder: Who will give the talk ? What is his/her field of expertise ? Where the speaker comes from ? To answer these questions, we will display: the name, the job position and the company name of the speaker. - Add replay suggestion when no suggestion can be provided When a video ends and no suggestion can be provided, the player will display an empty cover to hide the Youtube suggestions. With these new changes, the cover will now suggest the user to replay the video when no suggestion can be provided. - Dynamic count down for incoming tracks When the user accesses a track that will start in several hours, an alert will indicate the remaining time before the beginning of the track. Unfortunately, this alert is not dynamic: If the user keeps the page in a tab and come back later, the alert will not be updated and will display an incorrect estimation. The changes address this issue: The timer will now be updated dynamically as the time goes on. When the countdown reaches 0, the alert will automatically be removed from the dom. - Fix links of the agenda view In the agenda, the title of a track can be a link. When the user hover it, the cursor of the user will now turn into a clickable hand only if the track is accessible. - Remove the default date for the new tracks. - Set a default duration of half an hour for the new tracks. LINKS Task-2347597 COM PR odoo/odoo#69102 ENT PR odoo/enterprise#17612 X-original-commit: 6804b8a92c03db09c2694150f968d8056492a1d4
This commit is contained in:
committed by
Thibault Delavallée
parent
98f46907c2
commit
0f50285cf6
@@ -78,4 +78,13 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="display_timer_alert_widget" name="Display Countdown widget">
|
||||
<div class="o_we_track_timer alert alert-warning alert-dismissible fade show d-none mx-3 mt-3 mb-0" role="alert" t-att-data-time-to-live="time_to_live">
|
||||
Starts <span />
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<i class="fa fa-times"/>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
'website_event_track/static/src/js/website_event_track_proposal_form.js',
|
||||
'website_event_track/static/src/js/website_event_track_proposal_form_tags.js',
|
||||
'website_event_track/static/src/js/event_track_reminder.js',
|
||||
'website_event_track/static/src/js/event_track_timer.js',
|
||||
'website_event_track/static/src/js/website_event_pwa_widget.js',
|
||||
'website_event_track/static/lib/idb-keyval/idb-keyval.js',
|
||||
],
|
||||
|
||||
@@ -121,6 +121,13 @@ class EventTrackController(http.Controller):
|
||||
tracks_announced = tracks_announced.sorted('wishlisted_by_default', reverse=True)
|
||||
tracks_by_day.append({'date': False, 'name': _('Coming soon'), 'tracks': tracks_announced})
|
||||
|
||||
for tracks_group in tracks_by_day:
|
||||
# the tracks group is folded if all tracks are done (and if it's not "today")
|
||||
tracks_group['default_collapsed'] = (today_tz != tracks_group['date']) and all(
|
||||
track.is_track_done and not track.is_track_live
|
||||
for track in tracks_group['tracks']
|
||||
)
|
||||
|
||||
# return rendering values
|
||||
return {
|
||||
# event information
|
||||
|
||||
@@ -74,8 +74,11 @@ class Track(models.Model):
|
||||
partner_company_name = fields.Char(
|
||||
'Company Name', compute='_compute_partner_company_name',
|
||||
readonly=False, store=True)
|
||||
partner_tag_line = fields.Char(
|
||||
'Tag Line', compute='_compute_partner_tag_line',
|
||||
help='Description of the partner (name, function and company name)')
|
||||
image = fields.Image(
|
||||
string="Speaker Photo", compute="_compute_speaker_image",
|
||||
string="Speaker Photo", compute="_compute_partner_image",
|
||||
readonly=False, store=True,
|
||||
max_width=256, max_height=256)
|
||||
# contact information
|
||||
@@ -91,7 +94,7 @@ class Track(models.Model):
|
||||
# time information
|
||||
date = fields.Datetime('Track Date')
|
||||
date_end = fields.Datetime('Track End Date', compute='_compute_end_date', store=True)
|
||||
duration = fields.Float('Duration', default=1.5, help="Track duration in hours.")
|
||||
duration = fields.Float('Duration', default=0.5, help="Track duration in hours.")
|
||||
is_track_live = fields.Boolean(
|
||||
'Is Track Live', compute='_compute_track_time_data',
|
||||
help="Track has started and is ongoing")
|
||||
@@ -199,8 +202,32 @@ class Track(models.Model):
|
||||
elif not track.partner_company_name:
|
||||
track.partner_company_name = track.partner_id.parent_id.name
|
||||
|
||||
@api.depends('partner_name', 'partner_function', 'partner_company_name')
|
||||
def _compute_partner_tag_line(self):
|
||||
for track in self:
|
||||
if not track.partner_name:
|
||||
track.partner_tag_line = False
|
||||
continue
|
||||
|
||||
tag_line = track.partner_name
|
||||
if track.partner_function:
|
||||
if track.partner_company_name:
|
||||
tag_line = _('%(name)s, %(function)s at %(company)s',
|
||||
name=track.partner_name,
|
||||
function=track.partner_function,
|
||||
company=track.partner_company_name
|
||||
)
|
||||
else:
|
||||
tag_line = '%s, %s' % (track.partner_name, track.partner_function)
|
||||
elif track.partner_company_name:
|
||||
tag_line = _('%(name)s from %(company)s',
|
||||
name=tag_line,
|
||||
company=track.partner_company_name
|
||||
)
|
||||
track.partner_tag_line = tag_line
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_speaker_image(self):
|
||||
def _compute_partner_image(self):
|
||||
for track in self:
|
||||
if not track.image:
|
||||
track.image = track.partner_id.image_256
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
odoo.define('website_event_track.website_event_track_timer', function (require) {
|
||||
|
||||
'use strict';
|
||||
|
||||
const publicWidget = require('web.public.widget');
|
||||
|
||||
/*
|
||||
* Simple implementation of a timer widget that uses a "time to live" configuration
|
||||
* value to countdown seconds on a target element.
|
||||
* Will be used to visually countdown the time before a talk starts.
|
||||
* When the timer reaches 0, the element destroys itself.
|
||||
*/
|
||||
publicWidget.registry.websiteEventTrackTimer = publicWidget.Widget.extend({
|
||||
|
||||
selector: '.o_we_track_timer',
|
||||
events: {
|
||||
'click .close': '_onCloseClick',
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
return this._super.apply(this, arguments).then(() => {
|
||||
let timeToLive = this.$el.data('time-to-live');
|
||||
let deadline = moment().add(timeToLive, 'seconds');
|
||||
let remainingMs = deadline.diff(moment());
|
||||
if (remainingMs > 0) {
|
||||
this._updateTimerDisplay(remainingMs);
|
||||
this.$el.removeClass('d-none');
|
||||
this.deadline = deadline;
|
||||
this.timer = setInterval(this._refreshTimer.bind(this), 1000);
|
||||
} else {
|
||||
this.destroy();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy: function() {
|
||||
this.$el.parent().remove();
|
||||
clearInterval(this.timer);
|
||||
this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
_onCloseClick: function () {
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
/**
|
||||
* The function will trigger an update if the timer has not yet expired.
|
||||
* Otherwise, the component will be destroyed.
|
||||
*/
|
||||
_refreshTimer: function () {
|
||||
let remainingMs = this.deadline.diff(moment());
|
||||
if (remainingMs > 0) {
|
||||
this._updateTimerDisplay(remainingMs);
|
||||
} else {
|
||||
this.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The function will have the responsibility to update the text indicating
|
||||
* the time remaining before the counter expires. The function will use
|
||||
* MomentJS to transform the remaining time in more a human friendly format
|
||||
* Example: "in 32 minutes", "in 17 hours", etc.
|
||||
* @param {integer} remainingMs - Time remaining before the counter expires (in ms).
|
||||
*/
|
||||
_updateTimerDisplay: function (remainingMs) {
|
||||
let $timerTextEl = this.$el.find('span');
|
||||
let str = moment.duration(remainingMs, 'ms').humanize(true);
|
||||
if (str !== $timerTextEl.text()) {
|
||||
$timerTextEl.text(str);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return publicWidget.registry.websiteEventTrackTimer;
|
||||
|
||||
});
|
||||
@@ -81,12 +81,12 @@
|
||||
}
|
||||
div.o_we_agenda_card_content {
|
||||
height: 100%;
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
.o_we_agenda_card_title, small {
|
||||
word-break: break-word;
|
||||
}
|
||||
.badge:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.badge {
|
||||
height: fit-content;
|
||||
@@ -180,6 +180,13 @@
|
||||
/*
|
||||
* EVENT TOOL: DATE
|
||||
*/
|
||||
.o_wevent_event .o_we_track_day_header > div > span.h1 {
|
||||
font-size: xx-large;
|
||||
.o_wevent_event {
|
||||
.o_we_track_day_header {
|
||||
& > div > span.h1 {
|
||||
font-size: xx-large;
|
||||
}
|
||||
a.collapsed {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
<button name="%(action_event_track_from_event)d"
|
||||
type="action"
|
||||
class="oe_stat_button"
|
||||
icon="fa-microphone"
|
||||
context="{'default_date': date_begin}">
|
||||
icon="fa-microphone">
|
||||
<field name="track_count" string="Tracks" widget="statinfo"/>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
@@ -174,14 +174,20 @@
|
||||
</div>
|
||||
|
||||
<div class="o_we_agenda_card_content d-flex flex-column justify-content-center my-1">
|
||||
<div t-att-class="'text-black' if track.website_published or is_event_user else 'text-muted'"
|
||||
t-att-onclick="'window.location=\'/event/%s/track/%s\'' % (slug(event), slug(track))
|
||||
if track.website_published or is_event_user else ''">
|
||||
<span class="o_we_agenda_card_title text-bold" t-esc="track.name"/>
|
||||
<div class="o_we_agenda_card_title">
|
||||
<t t-if="track.website_published or is_event_user">
|
||||
<a t-att-href="'/event/%s/track/%s' % (slug(event), slug(track))" class="text-black text-bold">
|
||||
<t t-esc="track.name"/>
|
||||
</a>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span class="text-muted text-bold">
|
||||
<t t-esc="track.name"/>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="text-muted text-center">
|
||||
<small t-if="track.partner_id or track.partner_name">
|
||||
<t t-esc="track.partner_id.sudo().name or track.partner_name"/></small>
|
||||
<div class="text-muted text-center" t-if="track.partner_tag_line">
|
||||
<small t-esc="track.partner_tag_line"/>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center flex-wrap">
|
||||
<t t-foreach="track.tag_ids" t-as="tag">
|
||||
|
||||
@@ -170,8 +170,8 @@
|
||||
<t t-set="tracks_header_name" t-value="tracks_info['name']"/>
|
||||
<t t-set="tracks" t-value="tracks_info['tracks']"/>
|
||||
<!-- DAY HEADER -->
|
||||
<div class="o_we_track_day_header w-100">
|
||||
<div t-if="tracks_date" class="d-flex">
|
||||
<div class="o_we_track_day_header d-flex">
|
||||
<div class="d-flex flex-grow-1" t-if="tracks_date">
|
||||
<span class="h1 m-0 font-weight-bold" t-esc="tracks_date"
|
||||
t-options="{'widget': 'date', 'format': 'EEEE dd'}"/>
|
||||
<div class="d-flex flex-column ml-2">
|
||||
@@ -181,102 +181,112 @@
|
||||
t-options="{'widget': 'date', 'format': 'YYYY'}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div t-elif="tracks_header_name">
|
||||
<div class="d-flex flex-grow-1" t-elif="tracks_header_name">
|
||||
<span class="h1 m-0 font-weight-bold"
|
||||
t-esc="tracks_header_name"/>
|
||||
</div>
|
||||
<a t-attf-class="ml-auto align-self-start text-black {{ 'collapsed' if tracks_info['default_collapsed'] else '' }}"
|
||||
t-attf-href="#collapse_session_list_{{ tracks_info_index }}"
|
||||
t-attf-aria-controls="collapse_session_list_{{ tracks_info_index }}"
|
||||
t-att-aria-expanded="'false' if tracks_info['default_collapsed'] else 'true'"
|
||||
data-toggle="collapse">
|
||||
<i class="fa fa-2x fa-chevron-down"/>
|
||||
</a>
|
||||
<hr class="mt-2 pb-1 mb-1"/>
|
||||
</div>
|
||||
<!-- DAY TRACKS LIST -->
|
||||
<div t-foreach="tracks" t-as="track"
|
||||
t-att-class="'o_wesession_list_item px-2 py-2 event_color_%d' % (track.color)">
|
||||
<!-- Side information in a floating div (desktop only) -->
|
||||
<div t-if="not event.is_done and (not track.date or today_tz <= tracks_date) and option_track_wishlist"
|
||||
class="float-right d-none d-md-block ml-2">
|
||||
<t t-call="website_event_track.track_widget_reminder">
|
||||
<t t-set="reminder_small" t-value="False"/>
|
||||
<t t-set="reminder_light" t-value="False"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="row no-gutters">
|
||||
<!-- Main column: name, speaker -->
|
||||
<div class="col-md-7">
|
||||
<!-- Reminder widget: directly in line to gain space, mobile only -->
|
||||
<div t-if="not event.is_done and (not track.date or today_tz <= tracks_date) and option_track_wishlist"
|
||||
class="float-right d-block d-md-none ml-2">
|
||||
<t t-call="website_event_track.track_widget_reminder">
|
||||
<t t-set="reminder_small" t-value="True"/>
|
||||
<t t-set="reminder_light" t-value="False"/>
|
||||
</t>
|
||||
</div>
|
||||
<span class="h5 mb0">
|
||||
<a t-if="track.is_published or is_event_user"
|
||||
class="mr-2"
|
||||
t-att-href="track.website_url">
|
||||
<span t-field="track.name"/>
|
||||
</a>
|
||||
<t t-else="">
|
||||
<span class="mr-2" t-field="track.name"/>
|
||||
</t>
|
||||
<span t-if="not track.is_published and is_event_user"
|
||||
class="badge badge-danger o_wevent_online_badge_unpublished">
|
||||
Unpublished
|
||||
</span>
|
||||
</span>
|
||||
<div class="text-muted d-flex align-items-center">
|
||||
<span class="text-muted" t-esc="track.partner_name"/>
|
||||
<t t-if="tracks_date and today_tz <= tracks_date">
|
||||
<!-- Hour: Live > Remaining > Hour: mobile only -->
|
||||
<div class="d-block d-md-none">
|
||||
<span t-if="track.partner_name" class="ml-2">&bull;</span>
|
||||
<span t-if="track.is_track_live and not track.is_track_done"
|
||||
class="badge badge-danger ml-2">Live</span>
|
||||
<span t-elif="not track.is_track_done and track.is_track_soon"
|
||||
class="ml-2">
|
||||
<span t-esc="track.track_start_remaining"
|
||||
t-options="{'widget': 'duration', 'digital': False, 'format': 'narrow',
|
||||
'add_direction': True, 'unit': 'second', 'round': 'minute'}"/>
|
||||
</span>
|
||||
<span t-elif="not track.is_track_done and not track.is_track_soon"
|
||||
class="ml-2"
|
||||
t-esc="track.date"
|
||||
t-options="{'widget': 'datetime', 'time_only': True, 'format': 'short'}"/>
|
||||
<span t-else="" class="badge badge-info ml-2">Finished</span>
|
||||
</div>
|
||||
<!-- Duration (desktop only) -->
|
||||
<t t-if="track.duration and not track.is_track_done and not track.is_track_done">
|
||||
<span class="d-none d-md-block ml-2">&bull;</span>
|
||||
<span class="d-none d-md-block ml-2"
|
||||
t-esc="track.duration"
|
||||
t-options="{'widget': 'duration', 'digital': False, 'format': 'short', 'unit': 'hour', 'round': 'minute'}"/>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
<div t-attf-class="collapse {{ '' if tracks_info['default_collapsed'] else 'show' }}"
|
||||
t-attf-id="collapse_session_list_{{ tracks_info_index }}">
|
||||
<div t-foreach="tracks" t-as="track"
|
||||
t-att-class="'o_wesession_list_item px-2 py-2 event_color_%d' % (track.color)">
|
||||
<!-- Side information in a floating div (desktop only) -->
|
||||
<div t-if="not event.is_done and (not track.date or today_tz <= tracks_date) and option_track_wishlist"
|
||||
class="float-right d-none d-md-block ml-2">
|
||||
<t t-call="website_event_track.track_widget_reminder">
|
||||
<t t-set="reminder_small" t-value="False"/>
|
||||
<t t-set="reminder_light" t-value="False"/>
|
||||
</t>
|
||||
</div>
|
||||
<!-- Aside column: date, tags -->
|
||||
<div class="col-md-5">
|
||||
<!-- Hour: Live > Remaining > Hour: desktop only -->
|
||||
<div t-if="tracks_date and today_tz <= tracks_date"
|
||||
class="d-none d-md-block float-right">
|
||||
<span t-if="track.is_track_live and not track.is_track_done"
|
||||
class="badge badge-danger ml-2">Live</span>
|
||||
<span t-elif="not track.is_track_done and track.is_track_soon"
|
||||
class="ml-2">
|
||||
<span t-esc="track.track_start_remaining"
|
||||
t-options="{'widget': 'duration', 'digital': False, 'format': 'narrow',
|
||||
'add_direction': True, 'unit': 'second', 'round': 'minute'}"/>
|
||||
<div class="row no-gutters">
|
||||
<!-- Main column: name, speaker -->
|
||||
<div class="col-md-7">
|
||||
<!-- Reminder widget: directly in line to gain space, mobile only -->
|
||||
<div t-if="not event.is_done and (not track.date or today_tz <= tracks_date) and option_track_wishlist"
|
||||
class="float-right d-block d-md-none ml-2">
|
||||
<t t-call="website_event_track.track_widget_reminder">
|
||||
<t t-set="reminder_small" t-value="True"/>
|
||||
<t t-set="reminder_light" t-value="False"/>
|
||||
</t>
|
||||
</div>
|
||||
<span class="h5 mb0">
|
||||
<a t-if="track.is_published or is_event_user"
|
||||
class="mr-2"
|
||||
t-att-href="track.website_url">
|
||||
<span t-field="track.name"/>
|
||||
</a>
|
||||
<t t-else="">
|
||||
<span class="mr-2" t-field="track.name"/>
|
||||
</t>
|
||||
<span t-if="not track.is_published and is_event_user"
|
||||
class="badge badge-danger o_wevent_online_badge_unpublished">
|
||||
Unpublished
|
||||
</span>
|
||||
</span>
|
||||
<span t-elif="not track.is_track_done and not track.is_track_soon"
|
||||
class="ml-2"
|
||||
t-esc="track.date"
|
||||
t-options="{'widget': 'datetime', 'time_only': True, 'format': 'short'}"/>
|
||||
<span t-else="" class="badge badge-info ml-2">Finished</span>
|
||||
<div class="text-muted d-flex align-items-center">
|
||||
<span class="text-muted" t-esc="track.partner_tag_line"/>
|
||||
<t t-if="tracks_date and today_tz <= tracks_date">
|
||||
<!-- Hour: Live > Remaining > Hour: mobile only -->
|
||||
<div class="d-block d-md-none">
|
||||
<span t-if="track.partner_tag_line" class="ml-2">&bull;</span>
|
||||
<span t-if="track.is_track_live and not track.is_track_done"
|
||||
class="badge badge-danger ml-2">Live</span>
|
||||
<span t-elif="not track.is_track_done and track.is_track_soon"
|
||||
class="ml-2">
|
||||
<span t-esc="track.track_start_remaining"
|
||||
t-options="{'widget': 'duration', 'digital': False, 'format': 'narrow',
|
||||
'add_direction': True, 'unit': 'second', 'round': 'minute'}"/>
|
||||
</span>
|
||||
<span t-elif="not track.is_track_done and not track.is_track_soon"
|
||||
class="ml-2"
|
||||
t-esc="track.date"
|
||||
t-options="{'widget': 'datetime', 'time_only': True, 'format': 'short'}"/>
|
||||
<span t-else="" class="badge badge-info ml-2">Finished</span>
|
||||
</div>
|
||||
<!-- Duration (desktop only) -->
|
||||
<t t-if="track.duration and not track.is_track_done and not track.is_track_done">
|
||||
<span class="d-none d-md-block ml-2">&bull;</span>
|
||||
<span class="d-none d-md-block ml-2"
|
||||
t-esc="track.duration"
|
||||
t-options="{'widget': 'duration', 'digital': False, 'format': 'short', 'unit': 'hour', 'round': 'minute'}"/>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Tags: desktop only -->
|
||||
<div class="d-none d-md-block">
|
||||
<t t-foreach="track.tag_ids" t-as="tag">
|
||||
<t t-if="tag.color" t-call="website_event_track.track_tag_badge_link"/>
|
||||
</t>
|
||||
<!-- Aside column: date, tags -->
|
||||
<div class="col-md-5">
|
||||
<!-- Hour: Live > Remaining > Hour: desktop only -->
|
||||
<div t-if="tracks_date and today_tz <= tracks_date"
|
||||
class="d-none d-md-block float-right">
|
||||
<span t-if="track.is_track_live and not track.is_track_done"
|
||||
class="badge badge-danger ml-2">Live</span>
|
||||
<span t-elif="not track.is_track_done and track.is_track_soon"
|
||||
class="ml-2">
|
||||
<span t-esc="track.track_start_remaining"
|
||||
t-options="{'widget': 'duration', 'digital': False, 'format': 'narrow',
|
||||
'add_direction': True, 'unit': 'second', 'round': 'minute'}"/>
|
||||
</span>
|
||||
<span t-elif="not track.is_track_done and not track.is_track_soon"
|
||||
class="ml-2"
|
||||
t-esc="track.date"
|
||||
t-options="{'widget': 'datetime', 'time_only': True, 'format': 'short'}"/>
|
||||
<span t-else="" class="badge badge-info ml-2">Finished</span>
|
||||
</div>
|
||||
<!-- Tags: desktop only -->
|
||||
<div class="d-none d-md-block">
|
||||
<t t-foreach="track.tag_ids" t-as="tag">
|
||||
<t t-if="tag.color" t-call="website_event_track.track_tag_badge_link"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,12 +45,10 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div t-elif="track.track_start_remaining" class="pt-3">
|
||||
<div class="mx-3 alert alert-warning">
|
||||
Starting in
|
||||
<span t-esc="track.track_start_remaining"
|
||||
t-options="{'widget': 'duration', 'digital': False, 'unit': 'second', 'round': 'minute'}"/>
|
||||
</div>
|
||||
<div t-elif="track.track_start_remaining">
|
||||
<t t-call="website_event.display_timer_alert_widget">
|
||||
<t t-set="time_to_live" t-value="track.track_start_remaining"/>
|
||||
</t>
|
||||
</div>
|
||||
<!-- TRACK DESCRIPTION -->
|
||||
<div class="o_wesession_track_main_description overflow-auto">
|
||||
@@ -135,9 +133,9 @@
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<template id="event_track_aside" name="Track: Aside">
|
||||
<div t-att-class="'col-12 pl-0 pr-0 pr-md-3 o_wevent_online_page_aside o_wesession_track_aside %s' % ('col-md-3 col-lg-2' if option_widescreen else 'col-md-4 col-lg-3')">
|
||||
<div t-att-class="'col-12 pl-0 pr-0 o_wevent_online_page_aside o_wesession_track_aside %s' % ('col-md-3 col-lg-2' if option_widescreen else 'col-md-4 col-lg-3')">
|
||||
<div class="bg-white pt-0 pt-md-3 o_wevent_online_page_aside_content">
|
||||
<div class="mx-2" t-if="track.website_cta">
|
||||
<div class="mx-2" t-if="track.date and track.website_cta">
|
||||
<t t-set="cta_coundown" t-value="bool(track.website_cta_start_remaining)"/>
|
||||
<div t-if="cta_coundown" class="text-center mt-2 mt-md-0 w-100 btn btn-primary d-none">
|
||||
<t t-call="website_event.display_timer_widget">
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
'assets': {
|
||||
'web.assets_frontend': [
|
||||
'website_event_track_live/static/src/scss/website_event_track_live.scss',
|
||||
'website_event_track_live/static/src/js/website_event_track_replay_suggestion.js',
|
||||
'website_event_track_live/static/src/js/website_event_track_suggestion.js',
|
||||
'website_event_track_live/static/src/js/website_event_track_live.js',
|
||||
],
|
||||
|
||||
@@ -5,10 +5,7 @@ odoo.define('website_event_track_live.website_event_youtube_embed', function (re
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
var TrackSuggestionWidget = require('website_event_track_live.website_event_track_suggestion');
|
||||
|
||||
var YOUTUBE_VIDEO_ENDED = 0;
|
||||
var YOUTUBE_VIDEO_PLAYING = 1;
|
||||
var YOUTUBE_VIDEO_PAUSED = 2;
|
||||
var ReplaySuggestionWidget = require('website_event_track_live.website_event_track_replay_suggestion');
|
||||
|
||||
publicWidget.registry.websiteEventTrackLive = publicWidget.Widget.extend({
|
||||
selector: '.o_wevent_event_track_live',
|
||||
@@ -20,7 +17,6 @@ publicWidget.registry.websiteEventTrackLive = publicWidget.Widget.extend({
|
||||
var self = this;
|
||||
return this._super(...arguments).then(function () {
|
||||
self._setupYoutubePlayer();
|
||||
self.isFullScreen = !!document.fullscreenElement;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -29,30 +25,27 @@ publicWidget.registry.websiteEventTrackLive = publicWidget.Widget.extend({
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
_onPlayerReady: function () {
|
||||
$(window).on('resize', this._onResize.bind(this));
|
||||
this.$('.o_wevent_event_track_live_loading').remove();
|
||||
},
|
||||
|
||||
_onPlayerStateChange: function (event) {
|
||||
if (event.data === YOUTUBE_VIDEO_ENDED) {
|
||||
this.trigger('video-ended');
|
||||
} else if (event.data === YOUTUBE_VIDEO_PLAYING) {
|
||||
this.trigger('video-playing');
|
||||
} else if (event.data === YOUTUBE_VIDEO_PAUSED) {
|
||||
this.trigger('video-paused');
|
||||
}
|
||||
switch (event.data) {
|
||||
case YT.PlayerState.ENDED:
|
||||
this.trigger('video-ended');
|
||||
return;
|
||||
case YT.PlayerState.PLAYING:
|
||||
this.trigger('video-playing');
|
||||
return;
|
||||
case YT.PlayerState.PAUSED:
|
||||
this.trigger('video-paused');
|
||||
return;
|
||||
};
|
||||
},
|
||||
|
||||
_onVideoEnded: function () {
|
||||
if (this.$el.data('hasNextSuggestion')) {
|
||||
// if we have an upcoming suggestion, add a covering block to avoid
|
||||
// showing Youtube suggestions while we fetch the appropriate suggestion
|
||||
// using a rpc. This allows avoiding a 'flicker' effect.
|
||||
this.$el.append($('<div/>', {
|
||||
class:'owevent_track_suggestion_loading position-absolute w-100'
|
||||
}));
|
||||
}
|
||||
|
||||
this.$el.append($('<div/>', {
|
||||
class: 'owevent_track_suggestion_loading position-absolute w-100'
|
||||
}));
|
||||
var self = this;
|
||||
this._rpc({
|
||||
route: '/event_track/get_track_suggestion',
|
||||
@@ -69,21 +62,11 @@ publicWidget.registry.websiteEventTrackLive = publicWidget.Widget.extend({
|
||||
this.youtubePlayer.seekTo(0);
|
||||
this.youtubePlayer.playVideo();
|
||||
this.$('.owevent_track_suggestion_loading').remove();
|
||||
if (this.trackSuggestion) {
|
||||
delete this.trackSuggestion;
|
||||
if (this.outro) {
|
||||
delete this.outro;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The 'fullscreenchange' event is probably a better fit but it is unfortunately
|
||||
* not triggered when Youtube enters fullscreen mode.
|
||||
* However, the global window 'resize' is.
|
||||
*/
|
||||
_onResize: function () {
|
||||
this.isFullScreen = !!document.fullscreenElement;
|
||||
this._showSuggestion();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -116,14 +99,26 @@ publicWidget.registry.websiteEventTrackLive = publicWidget.Widget.extend({
|
||||
},
|
||||
|
||||
/**
|
||||
* Automatically launches the next suggested track when this one ends
|
||||
* and the user is not in fullscreen mode.
|
||||
* If a new suggestion has been found, a cover containing a replay button
|
||||
* as well as a suggestion will automatically be placed over the Youtube
|
||||
* player when the video ends (in non-full screen mode). If no suggestion
|
||||
* has been found, the cover will only contain a replay button.
|
||||
*/
|
||||
_showSuggestion: function () {
|
||||
if (this.nextSuggestion && !this.isFullScreen && !this.trackSuggestion) {
|
||||
this.trackSuggestion = new TrackSuggestionWidget(this, this.nextSuggestion);
|
||||
this.trackSuggestion.appendTo(this.$el);
|
||||
this.trackSuggestion.on('replay', null, this._onReplay.bind(this));
|
||||
if (!this.outro) {
|
||||
if (this.nextSuggestion) {
|
||||
this.outro = new TrackSuggestionWidget(this, this.nextSuggestion);
|
||||
} else {
|
||||
var data = this.$el.data();
|
||||
this.outro = new ReplaySuggestionWidget(this, {
|
||||
current_track: {
|
||||
name: data.trackName,
|
||||
website_image_url: data.trackWebsiteImageUrl
|
||||
}
|
||||
});
|
||||
}
|
||||
this.outro.appendTo(this.$el);
|
||||
this.outro.on('replay', null, this._onReplay.bind(this));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
odoo.define('website_event_track_live.website_event_track_replay_suggestion', function (require) {
|
||||
'use strict';
|
||||
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
/**
|
||||
* The widget will have the responsibility to manage the interactions between the
|
||||
* Youtube player and the cover containing a replay button. This widget will
|
||||
* be used when no suggestion can be found in order to hide the Youtube suggestions.
|
||||
*/
|
||||
var WebsiteEventReplaySuggestion = Widget.extend({
|
||||
template: 'website_event_track_live.website_event_track_replay_suggestion',
|
||||
xmlDependencies: ['/website_event_track_live/static/src/xml/website_event_track_live_templates.xml'],
|
||||
events: {
|
||||
'click .owevent_track_suggestion_replay': '_onReplayClick'
|
||||
},
|
||||
|
||||
init: function (parent, options) {
|
||||
this._super(...arguments);
|
||||
this.currentTrack = {
|
||||
'name': options.current_track.name,
|
||||
'imageSrc': options.current_track.website_image_url,
|
||||
};
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* If the user clicks on the replay button, the function will remove the
|
||||
* cover and send a new event to the parent to replay the video from the
|
||||
* beginning.
|
||||
*/
|
||||
_onReplayClick: function () {
|
||||
this.trigger_up('replay');
|
||||
this.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
return WebsiteEventReplaySuggestion;
|
||||
});
|
||||
@@ -1,6 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<div t-name="website_event_track_live.website_event_track_replay_suggestion"
|
||||
class="owevent_track_suggestion position-absolute w-100"
|
||||
t-attf-style="background-image: url('#{widget.currentTrack.imageSrc}');">
|
||||
<div class="h-100 w-100 p-4 d-flex flex-column align-items-center owevent_track_suggestion_content">
|
||||
<div class="flex-grow-1 pt-5 d-flex flex-column align-items-center justify-content-center">
|
||||
<div class="text-white">You just watched:</div>
|
||||
<div class="h1 text-white" t-esc="widget.currentTrack.name"/>
|
||||
<div>
|
||||
<a href="#" class="owevent_track_suggestion_replay font-weight-bold">
|
||||
<span>Replay Video</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div t-name="website_event_track_live.website_event_track_suggestion"
|
||||
class="owevent_track_suggestion position-absolute w-100"
|
||||
t-attf-style="background-image: url('#{widget.currentTrack.imageSrc}');">
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
<t t-set="show_youtube_frame" t-value="track.youtube_video_url and (track.is_youtube_replay or track.is_track_soon or track.is_track_live or track.is_track_done)" />
|
||||
<div t-if="show_youtube_frame" class="flex-grow-1 o_wevent_event_track_live"
|
||||
t-att-data-track-id="track.id"
|
||||
t-att-data-youtube-video-id="track.youtube_video_id"
|
||||
t-att-data-has-next-suggestion="bool(track._get_track_suggestions(restrict_domain=[('youtube_video_url', '!=', False), ('is_published', '=', True)], limit=1))">
|
||||
t-att-data-track-name="track.name"
|
||||
t-att-data-track-website-image-url="track.website_image_url"
|
||||
t-att-data-youtube-video-id="track.youtube_video_id">
|
||||
<div class="position-absolute o_wevent_event_track_live_loading w-100 d-flex align-items-center justify-content-center text-white h4">
|
||||
<i class="fa fa-spin fa-spinner position-relative"/>
|
||||
<span class="pl-2">Loading Video...</span>
|
||||
|
||||
Reference in New Issue
Block a user