Videos UI update (#15177)
* videos ui updates * move cheese around * . * . * thumbnail yolo * thumbnail yolo * object fit * aspect ratio yolo * . * loading videos * Make Travis and asset pipeline happy with .mp4 extension * Use Cloudinary video url, testing for Travis * Try weird things * Try a real image url * Use a real image instead of 'video' string * Update views.scss Co-authored-by: Andy Z <17884966+Zhao-Andy@users.noreply.github.com>
This commit is contained in:
parent
accb3c55ba
commit
ca108aa9c8
10 changed files with 80 additions and 141 deletions
|
|
@ -160,27 +160,18 @@ function fetchNextFollowersPage(el) {
|
|||
}
|
||||
|
||||
function buildVideoArticleHTML(videoArticle) {
|
||||
return (
|
||||
'<a class="single-video-article single-article" href="' +
|
||||
videoArticle.path +
|
||||
'" id="video-article-' +
|
||||
videoArticle.id +
|
||||
'">\n' +
|
||||
' <div class="video-image" style="background-image: url(' +
|
||||
videoArticle.cloudinary_video_url +
|
||||
')">\n' +
|
||||
' <span class="video-timestamp">' +
|
||||
videoArticle.video_duration_in_minutes +
|
||||
'</span>\n' +
|
||||
' </div>\n' +
|
||||
' <p><strong>' +
|
||||
videoArticle.title +
|
||||
'</strong></p>\n' +
|
||||
' <p>' +
|
||||
videoArticle.user.name +
|
||||
'</p>\n' +
|
||||
'</a>'
|
||||
);
|
||||
return `<a href="${videoArticle.path}" id="video-article-${videoArticle.id}" class="crayons-card media-card">
|
||||
<div class="media-card__artwork">
|
||||
<img src="${videoArticle.cloudinary_video_url}" class="w-100 object-cover block aspect-16-9 h-auto" width="320" height="180" alt="${videoArticle.title}">
|
||||
<span class="media-card__artwork__badge">${videoArticle.video_duration_in_minutes}</span>
|
||||
</div>
|
||||
<div class="media-card__content">
|
||||
<h2 class="fs-base mb-2 fw-medium">${videoArticle.title}</h2>
|
||||
<small class="fs-s">
|
||||
${videoArticle.user.name}
|
||||
</small>
|
||||
</div>
|
||||
</a>`;
|
||||
}
|
||||
|
||||
function insertVideos(videoArticles) {
|
||||
|
|
@ -198,14 +189,11 @@ function insertVideos(videoArticles) {
|
|||
|
||||
var distanceFromBottom =
|
||||
document.documentElement.scrollHeight - document.body.scrollTop;
|
||||
var newNode = document.createElement('div');
|
||||
newNode.innerHTML = newVideosHTML;
|
||||
newNode.className += 'video-collection';
|
||||
var singleArticles = document.querySelectorAll(
|
||||
'.single-article, .crayons-story',
|
||||
);
|
||||
var lastElement = singleArticles[singleArticles.length - 1];
|
||||
insertAfter(newNode, lastElement);
|
||||
|
||||
var parentNode = document.querySelector('.js-video-collection');
|
||||
var frag = document.createRange().createContextualFragment(newVideosHTML);
|
||||
parentNode.appendChild(frag);
|
||||
|
||||
if (nextPage > 0) {
|
||||
fetching = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import '../config/import';
|
||||
|
||||
.podcast-episode-card {
|
||||
.media-card {
|
||||
background: var(--card-bg);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
|
|
@ -8,24 +8,31 @@
|
|||
color: var(--card-color);
|
||||
transition: all var(--transition-props);
|
||||
|
||||
&__logo {
|
||||
&__artwork {
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
border-radius: var(--radius) var(--radius) 0 0;
|
||||
}
|
||||
|
||||
&__badge {
|
||||
position: absolute;
|
||||
right: var(--su-2);
|
||||
bottom: var(--su-2);
|
||||
border-radius: var(--radius);
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
color: #fff;
|
||||
padding: var(--su-1);
|
||||
line-height: var(--lh-tight);
|
||||
font-size: var(--fs-s);
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: var(--su-4);
|
||||
line-height: var(--lh-tight);
|
||||
|
||||
h3 {
|
||||
font-size: var(--fs-base);
|
||||
margin-bottom: var(--su-2);
|
||||
font-weight: var(--fw-medium);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: var(--fs-s);
|
||||
small {
|
||||
opacity: 0.6;
|
||||
transition: all var(--transition-props);
|
||||
}
|
||||
|
|
@ -36,7 +43,7 @@
|
|||
0 4px 6px -2px rgba(0, 0, 0, 0.05), 0 0 0 1px var(--base-a10);
|
||||
color: var(--link-color-hover);
|
||||
|
||||
p {
|
||||
small {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
|
@ -633,6 +633,20 @@
|
|||
true,
|
||||
null
|
||||
),
|
||||
(
|
||||
'object',
|
||||
'object-fit',
|
||||
(
|
||||
'contain': contain,
|
||||
'cover': cover,
|
||||
'fill': fill,
|
||||
'none': none,
|
||||
'scale-down': scale-down
|
||||
),
|
||||
(),
|
||||
false
|
||||
),
|
||||
('aspect', 'aspect-ratio', ('1-1': 1 / 1, '16-9': 16 / 9), (), false),
|
||||
(null, 'all', ('reset': unset), (), false, null),
|
||||
(
|
||||
'break',
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
@import 'components/headers';
|
||||
@import 'components/indicators';
|
||||
@import 'components/loaders';
|
||||
@import 'components/media-cards';
|
||||
@import 'components/modals';
|
||||
@import 'components/navigation';
|
||||
@import 'components/notices';
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
@import 'preact/sidebar-widget';
|
||||
@import 'preact/article-form';
|
||||
@import 'sidebar-data';
|
||||
@import 'video-collection';
|
||||
@import 'video-player';
|
||||
@import 'listings';
|
||||
@import 'credits';
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
.home.video-page-title {
|
||||
text-align: center;
|
||||
header {
|
||||
font-size: calc(0.9vw + 10px);
|
||||
}
|
||||
}
|
||||
.video-collection {
|
||||
background: var(--body-bg);
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
text-align: left;
|
||||
@media screen and (min-width: 950px) {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.single-video-article {
|
||||
border: solid 1px $light-medium-gray;
|
||||
margin: 5px;
|
||||
width: 100%;
|
||||
padding-bottom: 8px;
|
||||
border-radius: 3px;
|
||||
@media screen and (min-width: 550px) {
|
||||
width: 47%;
|
||||
}
|
||||
@media screen and (min-width: 739px) {
|
||||
width: 31%;
|
||||
}
|
||||
.video-image {
|
||||
position: relative;
|
||||
padding-top: 56%;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
background: $black no-repeat center center;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;
|
||||
}
|
||||
.video-timestamp {
|
||||
position: absolute;
|
||||
font-size: 0.7em;
|
||||
bottom: 11px;
|
||||
right: 4px;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
color: $white;
|
||||
padding: 2px 5px 3px;
|
||||
font-weight: 500;
|
||||
border-radius: 3px;
|
||||
}
|
||||
img {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
p {
|
||||
margin: 0px;
|
||||
padding: 2px 8px;
|
||||
max-height: 100%;
|
||||
max-width: 90%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--body-color);
|
||||
font-size: 0.95em;
|
||||
&.video-username {
|
||||
color: var(--card-color-tertiary);
|
||||
font-size: 0.88em;
|
||||
}
|
||||
}
|
||||
img {
|
||||
max-width: 350px;
|
||||
@media screen and (max-width: 739px) {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,3 @@
|
|||
@import 'views/signin';
|
||||
@import 'views/signup-modal';
|
||||
@import 'views/sponsors';
|
||||
@import 'views/podcasts';
|
||||
|
|
|
|||
|
|
@ -43,15 +43,15 @@
|
|||
<div class="grid gap-2 m:gap-4 grid-cols-2 s:grid-cols-3 m:grid-cols-5 px-2 m:px-0 mb-6">
|
||||
<% @podcast_episodes.each do |episode| %>
|
||||
<% episode = episode.decorate %>
|
||||
<a href="/<%= episode.podcast.slug %>/<%= episode.slug %>" class="crayons-card podcast-episode-card <% unless @podcast %>podcast-episode-card--all<% end %>">
|
||||
<div class="podcast-episode-card__logo">
|
||||
<a href="/<%= episode.podcast.slug %>/<%= episode.slug %>" class="crayons-card media-card <% unless @podcast %>media-card--all<% end %>">
|
||||
<div class="media-card__artwork">
|
||||
<%= optimized_image_tag(episode.image_url || episode.podcast.image_url,
|
||||
optimizer_options: { crop: "imagga_scale", width: 240, height: 240 },
|
||||
image_options: { alt: episode.title, loading: "lazy", class: "w-100 h-100" }) %>
|
||||
</div>
|
||||
<div class="podcast-episode-card__content">
|
||||
<h3><%= episode.title %></h3>
|
||||
<p>
|
||||
<div class="media-card__content">
|
||||
<h3 class="fs-base mb-2 fw-medium"><%= episode.title %></h3>
|
||||
<small class="fs-s">
|
||||
<%= episode.podcast.title %>,
|
||||
<% if episode.published_at? %>
|
||||
<time class="published-at" datetime="<%= episode.published_timestamp %>">
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
</time>
|
||||
<span class="time-ago-indicator-initial-placeholder" data-seconds="<%= episode.published_at_int %>"></span>
|
||||
<% end %>
|
||||
</p>
|
||||
</small>
|
||||
</div>
|
||||
</a>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -19,20 +19,28 @@
|
|||
<meta name="twitter:card" content="summary_large_image">
|
||||
<% end %>
|
||||
|
||||
<div class="home video-page-title" id="index-container" data-which="videos" data-params="<%= params.to_json(only: %i[tag username q]) %>">
|
||||
<div id="index-container" data-which="videos" data-params="<%= params.to_json(only: %i[tag username q]) %>">
|
||||
|
||||
<main id="main-content">
|
||||
<header class="video-page-title">
|
||||
<h1><%= t("views.videos.heading", community: community_name) %></h1>
|
||||
<main id="main-content" class="crayons-layout crayons-layout--header-inside">
|
||||
<header class="crayons-page-header">
|
||||
<h1 class="crayons-title"><%= t("views.videos.heading", community: community_name) %></h1>
|
||||
<a class="crayons-btn crayons-btn--ghost" href="<%= new_video_path %>" data-no-instant> <%= t("dashboard.upload_a_video") %></a>
|
||||
</header>
|
||||
<div class="video-collection" id="video-collection">
|
||||
<div class="js-video-collection grid gap-2 m:gap-4 s:grid-cols-2 m:grid-cols-3 mb-6">
|
||||
<% @video_articles.each do |video_article| %>
|
||||
<a class="single-video-article single-article" href="<%= video_article.path %>" id="video-article-<%= video_article.id %>">
|
||||
<div class="video-image" style="background-image: url(<%= video_article.cloudinary_video_url %>)">
|
||||
<span class="video-timestamp"><%= video_article.video_duration_in_minutes %></span>
|
||||
</div>
|
||||
<p><strong><%= video_article.title %></strong></p>
|
||||
<p class="video-username"><%= video_article.user.name %></p>
|
||||
<a href="<%= video_article.path %>" id="video-article-<%= video_article.id %>" class="crayons-card media-card">
|
||||
<div class="media-card__artwork">
|
||||
<%= optimized_image_tag(video_article.video_thumbnail_url,
|
||||
optimizer_options: { crop: "imagga_scale", width: 320, height: 180 },
|
||||
image_options: { alt: video_article.title, loading: "lazy", class: "w-100 object-cover block aspect-16-9 h-auto" }) %>
|
||||
<span class="media-card__artwork__badge"><%= video_article.video_duration_in_minutes %></span>
|
||||
</div>
|
||||
<div class="media-card__content">
|
||||
<h2 class="fs-base mb-2 fw-medium"><%= video_article.title %></h2>
|
||||
<small class="fs-s">
|
||||
<%= video_article.user.name %>
|
||||
</small>
|
||||
</div>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ RSpec.describe "Videos", type: :request do
|
|||
it "shows articles with video" do
|
||||
not_video_article = create(:article)
|
||||
video_article = create(:article)
|
||||
video_article.update_columns(video: "video", video_thumbnail_url: "video", title: "this video")
|
||||
video_article.update_columns(
|
||||
video: "video",
|
||||
video_thumbnail_url: "https://dummyimage.com/240x180.jpg",
|
||||
title: "this video",
|
||||
)
|
||||
get "/videos"
|
||||
expect(response.body).to include video_article.title
|
||||
expect(response.body).not_to include not_video_article.title
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue