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:
ludwiczakpawel 2021-10-28 09:34:44 +02:00 committed by GitHub
parent accb3c55ba
commit ca108aa9c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 80 additions and 141 deletions

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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',

View file

@ -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';

View file

@ -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';

View file

@ -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%;
}
}
}
}

View file

@ -12,4 +12,3 @@
@import 'views/signin';
@import 'views/signup-modal';
@import 'views/sponsors';
@import 'views/podcasts';

View file

@ -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 %>

View file

@ -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>

View file

@ -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