Add tabs for chronological filtering (#69)

This commit is contained in:
Ben Halpern 2018-03-13 11:13:19 -04:00 committed by GitHub
parent b8a51b3947
commit e052e63d34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 463 additions and 179 deletions

View file

@ -1,18 +1,27 @@
function initScrolling() {
client = algoliasearch('<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>', '<%= ENV["ALGOLIASEARCH_SEARCH_ONLY_KEY"] %>');
articlesIndex = client.initIndex('ordered_articles_<%= Rails.env %>');
checkIfNearBottomOfPage();
}
function checkIfNearBottomOfPage() {
var elCheck = document.getElementById("index-container");
if (elCheck) {
client = algoliasearch('<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>', '<%= ENV["ALGOLIASEARCH_SEARCH_ONLY_KEY"] %>');
var homeEl = document.getElementById("index-container");
if (homeEl.dataset.feed === "base-feed") {
articlesIndex = client.initIndex('ordered_articles_<%= Rails.env %>');
} else if (homeEl.dataset.feed === "latest") {
articlesIndex = client.initIndex('ordered_articles_by_published_at_<%= Rails.env %>');
} else {
articlesIndex = client.initIndex('ordered_articles_by_positive_reactions_count_<%= Rails.env %>');
}
initScrolling.called = true;
if (document.getElementsByClassName("single-article").length < 2 || location.search.indexOf("q=") > -1 ) {
document.getElementById("loading-articles").style.display = "none"
done = true;
}
else {
} else {
document.getElementById("loading-articles").style.display = "block"
}
fetchNextPageIfNearBottom();
@ -97,7 +106,7 @@ function algoliaPaginate(tag){
filters.push(tag);
}
articlesIndex.search("*", {
hitsPerPage: 30,
hitsPerPage: 15,
page: nextPage,
attributesToHighlight: [],
tagFilters: filters,

View file

@ -34,6 +34,21 @@ function initializeDrawerSliders() {
slideSidebar("right","outOfView");
slideSidebar("left","outOfView");
});
listenForNarrowMenuClick();
}
}
function listenForNarrowMenuClick(event) {
var navLinks = document.getElementsByClassName("narrow-nav-menu");
for (var i = 0; i < navLinks.length; i++) {
document.getElementById("narrow-nav-menu").classList.remove("showing");
}
document.getElementById("narrow-feed-butt").onclick = function(){
document.getElementById("narrow-nav-menu").classList.add("showing");
}
for (var i = 0; i < navLinks.length; i++) {
navLinks[i].onclick = function(event){
document.getElementById("narrow-nav-menu").classList.remove("showing");
}
}
}

View file

@ -22,6 +22,16 @@ function algoliaFollowedArticles(){
}
var client = algoliasearch('<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>', '<%= ENV["ALGOLIASEARCH_SEARCH_ONLY_KEY"] %>');
var index = client.initIndex('ordered_articles_<%= Rails.env %>');
var homeEl = document.getElementById("index-container");
if (homeEl.dataset.feed === "base-feed") {
articlesIndex = client.initIndex('ordered_articles_<%= Rails.env %>');
} else if (homeEl.dataset.feed === "latest") {
articlesIndex = client.initIndex('ordered_articles_by_published_at_<%= Rails.env %>');
} else {
articlesIndex = client.initIndex('ordered_articles_by_positive_reactions_count_<%= Rails.env %>');
}
index.search("*", {
hitsPerPage: 20,
page: "0",

View file

@ -1,7 +1,7 @@
function initializeReadingListPage(){
if ( getCurrentPage("reading_list_items-index") && checkUserLoggedIn() ) {
client = algoliasearch('<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>', '<%= ENV["ALGOLIASEARCH_SEARCH_ONLY_KEY"] %>');
articlesIndex = client.initIndex('ordered_articles_<%= Rails.env %>');
readinglistIndex = client.initIndex('ordered_articles_<%= Rails.env %>');
commentsIndex = client.initIndex('ordered_comments_<%= Rails.env %>');
var params = getQueryParams(document.location.search);
document.getElementById('substories').dataset.tabs = '{"v": "'+(params.v || '')+'", "t": "'+(params.t || '')+'" }'
@ -22,7 +22,7 @@ function getArticles(tag, num){
var algoliaIds = user.reading_list_ids.map(function(id){return "articles-"+id});
var resultDivs = []
var tags = {}
articlesIndex.getObjects(algoliaIds, function(err, content) {
readinglistIndex.getObjects(algoliaIds, function(err, content) {
var results = num ? content.results.slice(0, num) : content.results ;
results.forEach(function(story, i){
if (story) {

View file

@ -104,7 +104,7 @@ function buildArticleHTML(article) {
'+searchSnippetHTML+'\
</div>\
</a>\
<h4><a href="/'+article.user.username+'">'+filterXSS(article.user.name)+'</a></h4>\
<h4><a href="/'+article.user.username+'">'+filterXSS(article.user.name)+'・'+article.readable_publish_date+'</a></h4>\
<div class="tags">'+tagString+'</div>\
'+commentsCountHTML+reactionsCountHTML+'\
'+saveButton+'</div>';

View file

@ -13,7 +13,7 @@
margin-top:22px;
}
&.sub-home{
margin-top:25px;
margin-top:39px;
.side-bar{
@media screen and ( min-width: 950px ){
margin-top:12px;
@ -83,6 +83,9 @@
&.on-page-nav-butt-right{
right: 3%;
}
@media screen and ( min-width: 950px ){
display:none;
}
}
.on-page-nav-label{
position:absolute;
@ -92,10 +95,55 @@
font-stretch:condensed;
top:18px;
font-weight:600;
&.sub-home-nav{
top:12px;
}
.wide-nav-links{
display: none;
}
@media screen and ( min-width: 650px ){
.narrow-nav-select{
display: none;
}
.wide-nav-links{
display: block;
}
}
@media screen and ( min-width: 950px ){
top:9px;
&.sub-home-nav{
top:8px;
}
}
}
@media screen and ( min-width: 950px ){
display:none;
.separator{
border-left: 2px solid $light-medium-gray;
display: inline-block;
margin: 0px 7px;
height:15px;
}
.nav-chronofiter-link{
display: inline-block;
padding: 2px 8px;
font-size:0.95em;
&.selected{
background: $purple;
box-shadow: 3px 4px 0px darken($purple, 6%);
}
&:hover{
opacity:1;
}
}
.narrow-nav-select{
button {
all: unset;
background: transparent;
border:0;
font-family: $helvetica-condensed;
font-stretch:condensed;
font-weight:600;
cursor: pointer;
}
}
}
a{
@ -1203,6 +1251,35 @@
}
}
.narrow-nav-menu{
position:absolute;
top:40px;
left: 0;
right:0;
background: white;
z-index:500;
font-family: $helvetica-condensed;
font-stretch:condensed;
font-weight:600;
box-shadow: 0px 20px 50px rgba(0,0,0,0.3);
padding: 5px 0px 15px;
display: none;
font-size: 1.1em;
a{
display: block;
text-align: center;
color: $black;
padding: 20px 0px;
&.selected {
background: $purple;
}
}
&.showing {
display: block;
}
}
.load-more-cta{
margin: 35px auto;
border: 0px;

View file

@ -82,25 +82,28 @@ class StoriesController < ApplicationController
redirect_to "/t/#{@tag_model.alias_for}"
return
end
@stories = Article.where(published: true).
includes(:user).
limited_column_select.
page(@page).
per(8).
filter_excluded_tags(@tag)
if @tag_model && @tag_model.requires_approval
@stories = ArticleDecorator.decorate_collection(Article.
where(published: true, approved: true).
order("#{@tag_model.requires_approval ? "featured_number" : "hotness_score"} DESC").
includes(:user).
limited_column_select.
page(@page).
per(8).
filter_excluded_tags(@tag))
else
@stories = ArticleDecorator.decorate_collection(Article.
where(published: true).
order("hotness_score DESC").
includes(:user).
limited_column_select.
page(@page).
per(user_signed_in? ? 4 : 10).
filter_excluded_tags(@tag))
@stories = @stories.where(approved: true)
end
if ["week", "month", "year", "infinity"].include?(params[:timeframe])
@stories = @stories.where("published_at > ?", Timeframer.new(params[:timeframe]).datetime).
order("positive_reactions_count DESC")
elsif params[:timeframe] == "latest"
@stories = @stories.order("published_at DESC")
else
@stories = @stories.order("hotness_score DESC")
end
@stories = @stories.decorate
@featured_story = Article.new
@article_index = true
set_surrogate_key_header "articles-#{@tag}", @stories.map(&:record_key)
@ -112,17 +115,25 @@ class StoriesController < ApplicationController
@home_page = true
@page = (params[:page] || 1).to_i
num_articles = user_signed_in? ? 3 : 15
@stories = ArticleDecorator.decorate_collection(Article.where(published: true, featured: true).
order("hotness_score DESC").
@stories = Article.where(published: true).
includes(:user).
limited_column_select.
page(@page).
per(num_articles).
filter_excluded_tags(params[:tag]))
@featured_story = Article.where(published: true, featured: true).
where.not(main_image: nil).
limited_column_select.
order("hotness_score DESC").first&.decorate || Article.new
filter_excluded_tags(params[:tag])
if ["week", "month", "year", "infinity"].include?(params[:timeframe])
@stories = @stories.where("published_at > ?", Timeframer.new(params[:timeframe]).datetime).
order("positive_reactions_count DESC")
@featured_story = @stories.where.not(main_image: nil).first&.decorate || Article.new
elsif params[:timeframe] == "latest"
@stories = @stories.order("published_at DESC").
where("featured_number > ?", 1449999999)
@featured_story = Article.new
else
@stories = @stories.where(featured: true).order("hotness_score DESC")
@featured_story = @stories.where.not(main_image: nil).first&.decorate || Article.new
end
@stories = @stories.decorate
@podcast_episodes = PodcastEpisode.
includes(:podcast).
order("published_at desc").
@ -203,7 +214,6 @@ class StoriesController < ApplicationController
end
end
def handle_article_show
@article_show = true
@comment = Comment.new
@ -261,4 +271,5 @@ class StoriesController < ApplicationController
find_by_slug(params[:slug])&.
decorate
end
end

View file

@ -216,5 +216,17 @@ module ApplicationHelper
},
]
colors[user.id % 10]
end
end
def timeframe_check(given_timeframe)
params[:timeframe] == given_timeframe
end
def list_path
if params[:tag].present?
"/t/#{params[:tag]}"
else
""
end
end
end

View file

@ -45,23 +45,61 @@ class CacheBuster
if article.organization.present?
bust("/#{article.organization.slug}")
end
bust_home_pages(article)
bust_tag_pages(article)
if article.collection
article.collection.articles.each do |a|
bust(a.path)
end
end
end
def bust_home_pages(article)
if article.featured_number.to_i > Time.now.to_i
bust("/")
bust("?i=i")
end
begin
[[1.week.ago, "week"],[1.month.ago, "month"],[1.year.ago, "year"],[5.years.ago, "infinity"]].each do |timeframe|
if Article.where(published: true).where("published_at > ?", timeframe[0]).
order("positive_reactions_count DESC").limit(4).pluck(:id).include?(article.id)
bust("/top/#{timeframe[1]}")
bust("/top/#{timeframe[1]}?i=i")
bust("/top/#{timeframe[1]}/?i=i")
end
if Article.where(published: true).where("published_at > ?", timeframe[0]).
order("hotness_score DESC").limit(3).pluck(:id).include?(article.id)
bust("/")
bust("?i=i")
end
end
if article.published && article.published_at > 1.hour.ago
bust("/latest")
bust("/latest?i=i")
end
end
def bust_tag_pages(article)
return unless article.published
article.tag_list.each do |tag|
if article.published_at.to_i > 3.minutes.ago.to_i
article.tag_list.each do |tag|
bust("/t/#{tag}")
bust("/t/#{tag}/latest")
bust("/t/#{tag}/latest?i=i")
end
[[1.week.ago, "week"],[1.month.ago, "month"],[1.year.ago, "year"],[5.years.ago, "infinity"]].
each do |timeframe|
if Article.where(published: true).where("published_at > ?", timeframe[0]).tagged_with(tag).
order("positive_reactions_count DESC").limit(3).pluck(:id).include?(article.id)
bust("/top/#{timeframe[1]}")
bust("/top/#{timeframe[1]}?i=i")
bust("/top/#{timeframe[1]}/?i=i")
end
if Article.where(published: true).where("published_at > ?", timeframe[0]).tagged_with(tag).
order("hotness_score DESC").limit(3).pluck(:id).include?(article.id)
bust("/")
bust("?i=i")
end
end
if article.collection
article.collection.articles.each do |a|
bust(a.path)
end
end
rescue
puts "Tag issue"
end
end
end

View file

@ -1,8 +1,9 @@
class HexComparer
attr_accessor :hexes
def initialize(hexes)
attr_accessor :hexes, :amount
def initialize(hexes, amount=1)
@hexes = hexes
@amount = amount
end
def order
@ -23,26 +24,26 @@ class HexComparer
"#%02x%02x%02x" % rgb
rescue
smallest
end
end
end
def accent
if brightness(1.14).size == 7
brightness(1.14)
elsif brightness(1.08).size == 7
brightness(1.08)
elsif brightness(1.06).size == 7
brightness(1.06)
elsif brightness(0.96).size == 7
brightness(0.96)
elsif brightness(0.9).size == 7
brightness(0.9)
elsif brightness(0.8).size == 7
brightness(0.8)
elsif brightness(0.7).size == 7
brightness(0.7)
elsif brightness(0.6).size == 7
brightness(0.6)
def accent()
if brightness(1.14 ** amount).size == 7
brightness(1.14 ** amount)
elsif brightness(1.08 ** amount).size == 7
brightness(1.08 ** amount)
elsif brightness(1.06 ** amount).size == 7
brightness(1.06 ** amount)
elsif brightness(0.96 ** amount).size == 7
brightness(0.96 ** amount)
elsif brightness(0.9 ** amount).size == 7
brightness(0.9 ** amount)
elsif brightness(0.8 ** amount).size == 7
brightness(0.8 ** amount)
elsif brightness(0.7 ** amount).size == 7
brightness(0.7 ** amount)
elsif brightness(0.6 ** amount).size == 7
brightness(0.6 ** amount)
end
end

22
app/labor/timeframer.rb Normal file
View file

@ -0,0 +1,22 @@
class Timeframer
attr_accessor :timeframe
def initialize(timeframe)
@timeframe = timeframe
end
def datetime
if timeframe == "infinity"
5.years.ago
elsif timeframe == "week"
1.week.ago
elsif timeframe == "month"
1.month.ago
elsif timeframe == "year"
1.year.ago
elsif timeframe == "latest"
"latest"
end
end
end

View file

@ -57,7 +57,7 @@ class Article < ApplicationRecord
:comments_count, :positive_reactions_count, :cached_tag_list,
:main_image, :main_image_background_hex_color, :updated_at,
:video, :user_id, :organization_id, :video_source_url, :video_code,
:video_thumbnail_url, :video_closed_caption_track_url)
:video_thumbnail_url, :video_closed_caption_track_url, :published_at)
}
scope :limited_columns_internal_select, -> {
@ -80,7 +80,7 @@ class Article < ApplicationRecord
:featured, :published, :published_at, :featured_number,
:comments_count, :reactions_count, :positive_reactions_count,
:path, :class_name, :user_name, :user_username, :comments_blob,
:body_text, :tag_keywords_for_search, :search_score
:body_text, :tag_keywords_for_search, :search_score, :readable_publish_date
attribute :user do
{ username: user.username,
name: user.name,
@ -112,7 +112,8 @@ class Article < ApplicationRecord
per_environment: true,
enqueue: :trigger_delayed_index do
attributes :title, :path, :class_name, :comments_count,
:tag_list, :positive_reactions_count, :id, :hotness_score
:tag_list, :positive_reactions_count, :id, :hotness_score,
:readable_publish_date
attribute :user do
{ username: user.username,
name: user.name,
@ -129,6 +130,12 @@ class Article < ApplicationRecord
("organization_#{organization_id}" if organization)].flatten.compact
end
ranking ["desc(hotness_score)"]
add_replica "ordered_articles_by_positive_reactions_count", inherit: true, per_environment: true do
ranking ["desc(positive_reactions_count)"]
end
add_replica "ordered_articles_by_published_at", inherit: true, per_environment: true do
ranking ["desc(published_at)"]
end
end
end
@ -143,13 +150,23 @@ class Article < ApplicationRecord
end
end
def self.active_discuss_threads(tag)
where(published:true).
where("published_at > ?", 7.days.ago).
tagged_with(["discuss", tag]).
order("last_comment_at DESC").
limit(8).
pluck(:path, :title, :comments_count, :created_at)
def self.active_discuss_threads(tag=nil, time_ago=nil)
stories = where(published:true).
limit(8)
if time_ago == "latest"
stories = stories.order("published_at DESC")
elsif time_ago
stories = stories.order("comments_count DESC").
where("published_at > ?", time_ago)
else
stories = stories.order("last_comment_at DESC").
where("published_at > ?", (tag.present? ? 5 : 2).days.ago)
end
stories = stories.tagged_with(["discuss", tag]) if tag
stories = stories.tagged_with("discuss") unless tag
stories.pluck(:path, :title, :comments_count, :created_at)
end
def body_text
@ -264,9 +281,9 @@ class Article < ApplicationRecord
def readable_publish_date
if published_at && published_at.year == Time.now.year
published_at.strftime("%b %e")
published_at&.strftime("%b %e")
else
published_at.strftime("%b %e '%y")
published_at&.strftime("%b %e '%y")
end
end

View file

@ -257,81 +257,83 @@
</div>
<% elsif @podcast %>
<% else %>
<%= render "articles/sidebar_nav" %>
<div class="widget sponsorship-widget <%= "showing" unless user_signed_in? %>" id="sponsorship-widget">
<header>
&lt;COMMUNITY SPONSORS<span class="emoji">
<img src="<%= asset_path "emoji/emoji-one-heart.png" %>" />
</span>&gt;
</header>
<div class="widget-body">
<div class="sidebar-sponsor">
<a class="partner-link" href="https://www.manifold.co/?utm_campaign=dev.to_march&utm_source=dev.to&utm_medium=sponsorship" target="_blank" data-details="manifold__image">
<img src="<%= cloudinary("https://thepracticaldev.s3.amazonaws.com/i/hsdm15wphp83rh0o5ees.png", 300) %>" style="margin-bottom:8px;" />
</a>
<div class="sponsor-tagline" style="display:inline;">
The marketplace for independent developer services
<a class="partner-link sponsor-learn-more" href="https://www.manifold.co/?utm_campaign=dev.to_march&utm_source=dev.to&utm_medium=sponsorship" target="_blank" data-details="manifold__learn-more">
Learn More
<% cache("main-sidebar-nav--#{user_signed_in?}", :expires_in => 5.days) do %>
<%= render "articles/sidebar_nav" %>
<div class="widget sponsorship-widget <%= "showing" unless user_signed_in? %>" id="sponsorship-widget">
<header>
&lt;COMMUNITY SPONSORS<span class="emoji">
<img src="<%= asset_path "emoji/emoji-one-heart.png" %>" />
</span>&gt;
</header>
<div class="widget-body">
<div class="sidebar-sponsor">
<a class="partner-link" href="https://www.manifold.co/?utm_campaign=dev.to_march&utm_source=dev.to&utm_medium=sponsorship" target="_blank" data-details="manifold__image">
<img src="<%= cloudinary("https://thepracticaldev.s3.amazonaws.com/i/hsdm15wphp83rh0o5ees.png", 300) %>" style="margin-bottom:8px;" />
</a>
<div class="sponsor-tagline" style="display:inline;">
The marketplace for independent developer services
<a class="partner-link sponsor-learn-more" href="https://www.manifold.co/?utm_campaign=dev.to_march&utm_source=dev.to&utm_medium=sponsorship" target="_blank" data-details="manifold__learn-more">
Learn More
</a>
</div>
</div>
<div class="sidebar-sponsor">
<a class="partner-link" href="https://triplebyte.com/?ref=devto_mar18_hp" target="_blank" data-details="triplebyte__image">
<img src="<%= cloudinary("https://thepracticaldev.s3.amazonaws.com/i/r50i600z08ty2sjwey4o.png", 300) %>" />
</a>
<div class="sponsor-tagline">
Do one coding quiz, get multiple job offers
<a class="partner-link sponsor-learn-more" href="https://triplebyte.com/?ref=devto_mar18_hp" target="_blank" data-details="triplebyte__learn-more">
Learn More
</a>
</div>
</div>
</div>
<div class="sidebar-sponsor">
<a class="partner-link" href="https://triplebyte.com/?ref=devto_mar18_hp" target="_blank" data-details="triplebyte__image">
<img src="<%= cloudinary("https://thepracticaldev.s3.amazonaws.com/i/r50i600z08ty2sjwey4o.png", 300) %>" />
</a>
<div class="sponsor-tagline">
Do one coding quiz, get multiple job offers
<a class="partner-link sponsor-learn-more" href="https://triplebyte.com/?ref=devto_mar18_hp" target="_blank" data-details="triplebyte__learn-more">
Learn More
<div class="sidebar-sponsor">
<a class="partner-link" href="http://goto.algolia.com/l/139121/2018-01-31/287fnc" target="_blank" data-details="algolia__image">
<img src="<%= cloudinary("https://thepracticaldev.s3.amazonaws.com/i/ehfv4m28il0uevcgxj47.png", 300) %>" />
</a>
<div class="sponsor-tagline">
Fast & relevant search API, now with free read operations
<a class="partner-link sponsor-learn-more" href="http://goto.algolia.com/l/139121/2018-01-31/287fnc" target="_blank" data-details="algolia__learn-more">
Learn More
</a>
</div>
</div>
<div class="sponsors-love-message">
We are grateful for wonderful sponsors who help sustain the dev community.
</div>
<div class="sponsor-footer">
<p>
<a href="/sponsorship-info">Sponsorship Info/Policy</a>
</p>
</div>
</div>
<div class="sidebar-sponsor">
<a class="partner-link" href="http://goto.algolia.com/l/139121/2018-01-31/287fnc" target="_blank" data-details="algolia__image">
<img src="<%= cloudinary("https://thepracticaldev.s3.amazonaws.com/i/ehfv4m28il0uevcgxj47.png", 300) %>" />
</a>
<div class="sponsor-tagline">
Fast & relevant search API, now with free read operations
<a class="partner-link sponsor-learn-more" href="http://goto.algolia.com/l/139121/2018-01-31/287fnc" target="_blank" data-details="algolia__learn-more">
Learn More
</a>
</div>
</div>
<div class="widget">
<header>
&lt;KEY LINKS&gt;
</header>
<div class="widget-body">
<a href="https://twitter.com/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("twitter-logo.svg") %>" class="social-icon" /></a>
<a href="https://github.com/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("github-logo.svg") %>" class="social-icon" /></a>
<a href="https://instagram.com/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("instagram-logo.svg") %>" class="social-icon" /></a>
<a href="https://facebook.com/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("facebook-logo.svg") %>" class="social-icon" /></a>
<a href="https://twitch.tv/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("twitch-logo.svg") %>" class="social-icon" /></a>
</div>
<div class="sponsors-love-message">
We are grateful for wonderful sponsors who help sustain the dev community.
</div>
<div class="sponsor-footer">
<p>
<a href="/sponsorship-info">Sponsorship Info/Policy</a>
</p>
<div class="side-footer">
<a href="/about">About</a>
<a href="/membership">Sustaining Membership</a>
<a href="/events">Events</a>
<a href="/sponsors">Sponsors</a>
<a href="https://shop.dev.to/">DEV Shop</a>
<a href="/privacy">Privacy Policy</a>
<a href="/terms">Terms of Use</a>
<a href="/contact">Contact</a>
<a href="/faq">FAQ</a>
<a href="/code-of-conduct">Code of Conduct</a>
</div>
</div>
</div>
<div class="widget">
<header>
&lt;KEY LINKS&gt;
</header>
<div class="widget-body">
<a href="https://twitter.com/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("twitter-logo.svg") %>" class="social-icon" /></a>
<a href="https://github.com/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("github-logo.svg") %>" class="social-icon" /></a>
<a href="https://instagram.com/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("instagram-logo.svg") %>" class="social-icon" /></a>
<a href="https://facebook.com/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("facebook-logo.svg") %>" class="social-icon" /></a>
<a href="https://twitch.tv/thepracticaldev" target="_blank" rel="noopener"><img src="<%= asset_path("twitch-logo.svg") %>" class="social-icon" /></a>
</div>
<div class="side-footer">
<a href="/about">About</a>
<a href="/membership">Sustaining Membership</a>
<a href="/events">Events</a>
<a href="/sponsors">Sponsors</a>
<a href="https://shop.dev.to/">DEV Shop</a>
<a href="/privacy">Privacy Policy</a>
<a href="/terms">Terms of Use</a>
<a href="/contact">Contact</a>
<a href="/faq">FAQ</a>
<a href="/code-of-conduct">Code of Conduct</a>
</div>
</div>
<% end %>
<% end %>
</div>
</div>

View file

@ -42,14 +42,14 @@
<% end %>
<% elsif @user && @user.class.name == "Organization" %>
<% elsif @tag %>
<% if Article.active_discuss_threads(@tag).any? %>
<% if Article.active_discuss_threads(@tag, Timeframer.new(params[:timeframe]).datetime).any? %>
<div class="widget">
<header>
<#DISCUSS>
</header>
<div class="widget-body">
<div class="widget-link-list">
<% Article.active_discuss_threads(@tag).
<% Article.active_discuss_threads(@tag, Timeframer.new(params[:timeframe]).datetime).
each do |plucked_article| %>
<%= render "articles/widget_list_item", plucked_article: plucked_article, show_comment_count: true %>
<% end %>
@ -132,13 +132,7 @@
</header>
<div class="widget-body">
<div class="widget-link-list">
<% Article.
where(published:true).
where("published_at > ?", 2.days.ago).
tagged_with("discuss").
order("last_comment_at DESC").
limit(8).
pluck(:path, :title, :comments_count, :created_at).
<% Article.active_discuss_threads(nil, Timeframer.new(params[:timeframe]).datetime).
each do |plucked_article| %>
<%= render "articles/widget_list_item", plucked_article: plucked_article, show_comment_count: true %>
<% end %>

View file

@ -22,7 +22,8 @@
</div>
</a>
<h4>
<a href="<%=story.user.path%>"><%= story.user.name %></a>
<a href="<%=story.user.path%>"><%= story.user.name %>・<%= story.readable_publish_date %></a>
</h4>
<div class="tags">
<% story.cached_tag_list_array.each do |tag| %>

View file

@ -11,7 +11,7 @@
<meta property="og:image" content="<%= @user.profile_image_url %>" >
<meta property="og:description" content="<%= @user.summary %>" />
<meta property="og:site_name" content="The Practical Dev" />
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@ThePracticalDev">
<meta name="twitter:creator" content="@<%=@user.twitter_username %>">
@ -20,20 +20,20 @@
<meta name="twitter:image:src" content="<%= @user.profile_image_url %>" >
<%= auto_discovery_link_tag(:rss, "https://dev.to/feed/#{@user.username}", {title: "The Practical Dev RSS Feed"}) %>
<% elsif @tag.present? %>
<% title "#{@tag} - DEV" %>
<% title "#{@tag}#{ " - top this #{params[:timeframe]}" if params[:timeframe].present? } - The DEV Community" %>
<link rel="canonical" href="https://dev.to/<%= @tag %>"/>
<meta name="description" content="<%= @tag %> content on dev.to">
<meta name="keywords" content="software development,engineering,rails,javascript,ruby">
<meta property="og:type" content="website" />
<meta property="og:url" content="https://dev.to/<%= @tag %>" />
<meta property="og:title" content="<%= @tag %> — DEV" />
<meta property="og:title" content="<%= @tag %><%= " - top this #{params[:timeframe]}" if params[:timeframe].present? %> - The DEV Community" />
<meta property="og:description" content="<%= @tag %> content on dev.to" />
<meta property="og:site_name" content="The Practical Dev" />
<meta name="twitter:site" content="@ThePracticalDev">
<meta name="twitter:creator" content="@<%=@tag %>">
<meta name="twitter:title" content="<%= @tag %> — DEV">
<meta name="twitter:title" content="<%= @tag %><%= " - top this #{params[:timeframe]}" if params[:timeframe].present? %> - The DEV Community">
<meta name="twitter:description" content="<%= @tag %> content on dev.to">
<% if @tag_model && @tag_model.social_image.present? %>
@ -67,20 +67,20 @@
<% elsif @podcast_index %>
<%= render "podcast_episodes/meta" %>
<% else %>
<% title "The DEV Community" %>
<link rel="canonical" href="https://dev.to"/>
<% title "The DEV Community#{ " - top this #{params[:timeframe]}" if params[:timeframe].present? }" %>
<link rel="canonical" href="https://dev.to<%= request.path %>"/>
<meta name="description" content="Where programmers share ideas and help each other grow.">
<meta name="keywords" content="software development,engineering,rails,javascript,ruby">
<meta property="og:type" content="website" />
<meta property="og:url" content="https://dev.to/" />
<meta property="og:title" content="The DEV Community" />
<meta property="og:url" content="https://dev.to<%= request.path %>" />
<meta property="og:title" content="The DEV Community<%= " - top this #{params[:timeframe]}" if params[:timeframe].present? %>" />
<meta property="og:image" content="https://thepracticaldev.s3.amazonaws.com/i/g355ol6qsrg0j2mhngz9.png" >
<meta property="og:description" content="Where programmers share ideas and help each other grow." />
<meta property="og:site_name" content="The DEV Community" />
<meta name="twitter:site" content="@ThePracticalDev">
<meta name="twitter:title" content="The DEV Community">
<meta name="twitter:title" content="The DEV Community<%= " - top this #{params[:timeframe]}" if params[:timeframe].present? %>">
<meta name="twitter:description" content="Where programmers share ideas, experiences, and help each other grow.">
<meta name="twitter:image:src" content="https://thepracticaldev.s3.amazonaws.com/i/g355ol6qsrg0j2mhngz9.png" >
<meta name="twitter:card" content="summary_large_image">
@ -95,7 +95,7 @@
.slanty-accent{
background-color: <%= HexComparer.new([user_colors(@user)[:bg]]).accent %>;
}
</style>
</style>
<div class="user-profile-header" style="<%= user_colors_style(@user) %>" itemscope itemtype="http://schema.org/Person" itemprop="mainEntityOfPage">
<meta itemprop="url" content="https://dev.to/<%=@user.username%>">
<div class="user-profile-header-container">
@ -193,20 +193,78 @@
<% end %>
<div class="home <%= "sub-home" if (@user || @tag || @query)%>" id="index-container"
data-params="<%= params.to_json(only: [:tag,:username,:q]) %>" data-which="<%=@list_of%>"
data-algolia-tag="<%= @user ? "#{@user.class.name.downcase}_#{@user.id}" : nil || @tag %>">
data-algolia-tag="<%= @user ? "#{@user.class.name.downcase}_#{@user.id}" : nil || @tag %>"
data-feed="<%= params[:timeframe] || "base-feed"%>">
<%= render "articles/sidebar" %>
<div class="articles-list" id="articles-list">
<% if @home_page %>
<% if @home_page || @tag %>
<% if @tag_model && @tag_model.supported %>
<style>
.nav-chronofiter-link,.narrow-nav-select button {
color: <%= @tag_model.text_color_hex %> !important;
}
.nav-chronofiter-link.selected {
background: <%= @tag_model.text_color_hex %> !important;
color: <%= @tag_model.bg_color_hex %> !important;
box-shadow: 3px 4px 0px <%= HexComparer.new([@tag_model.bg_color_hex || "#ffffff"], 4).accent %> !important;
}
.narrow-nav-menu a.selected {
color: <%= @tag_model.text_color_hex %> !important;
background: <%= @tag_model.bg_color_hex %> !important;
}
.on-page-nav-controls .separator {
border-color: <%= HexComparer.new([@tag_model.bg_color_hex || "#ffffff"], 4).accent %> !important;
}
</style>
<% end %>
<div class="on-page-nav-controls" id="on-page-nav-controls">
<div class="on-page-nav-label">
&lt;MY DEV FEED&gt;
<div class="on-page-nav-label <%= "sub-home-nav" if (@user || @tag || @query)%>">
<div class="wide-nav-links">
<a class="nav-chronofiter-link <%= 'selected' if ["week", "month", "year", "infinity", "latest"].exclude?(params[:timeframe]) %>" href="<%= list_path %>/">
FEED
</a>
<span class="separator"></span>
<a class="nav-chronofiter-link <%= 'selected' if timeframe_check('week') %>" href="<%= list_path %>/top/week">
WEEK
</a>
<a class="nav-chronofiter-link <%= 'selected' if timeframe_check('month') %>" href="<%= list_path %>/top/month">
MONTH
</a>
<a class="nav-chronofiter-link <%= 'selected' if timeframe_check('year') %>" href="<%= list_path %>/top/year">
YEAR
</a>
<a class="nav-chronofiter-link <%= 'selected' if timeframe_check('infinity') %>" href="<%= list_path %>/top/infinity">
INIFINITY
</a>
<span class="separator"></span>
<a class="nav-chronofiter-link <%= 'selected' if timeframe_check('latest') %>" href="<%= list_path %>/latest">
LATEST
</a>
</div>
<div class="narrow-nav-select" aria-label="feed-filter-select">
<% if ["week", "month", "year", "infinity", "latest"].exclude?(params[:timeframe]) %>
<button id="narrow-feed-butt">&lt;MY DEV FEED&gt;</button>
<% elsif timeframe_check('week') %>
<button id="narrow-feed-butt">&lt;PAST WEEK&gt;</button>
<% elsif timeframe_check('month') %>
<button id="narrow-feed-butt">&lt;PAST MONTH&gt;</button>
<% elsif timeframe_check('year') %>
<button id="narrow-feed-butt">&lt;PAST YEAR&gt;</button>
<% elsif timeframe_check('infinity') %>
<button id="narrow-feed-butt">&lt;INFINITY&gt;</button>
<% elsif timeframe_check('latest') %>
<button id="narrow-feed-butt">&lt;LATEST&gt;</button>
<% end %>
</div>
</div>
<button class="on-page-nav-butt on-page-nav-butt-left" id="on-page-nav-butt-left" aria-label="nav-button-left">
<img src="<%= asset_path 'stack.svg' %>" alt="left-sidebar-nav">
</button>
<button class="on-page-nav-butt on-page-nav-butt-right" id="on-page-nav-butt-right" aria-label="nav-button-right">
<img src="<%= asset_path 'lightning.svg' %>" alt="right-sidebar-nav">
</button>
<% if @home_page %>
<button class="on-page-nav-butt on-page-nav-butt-left" id="on-page-nav-butt-left" aria-label="nav-button-left">
<img src="<%= asset_path 'stack.svg' %>" alt="left-sidebar-nav">
</button>
<button class="on-page-nav-butt on-page-nav-butt-right" id="on-page-nav-butt-right" aria-label="nav-button-right">
<img src="<%= asset_path 'lightning.svg' %>" alt="right-sidebar-nav">
</button>
<% end %>
</div>
<% end %>
<% if @user.blank? && @tag.blank? && @featured_story.id %>
@ -223,7 +281,7 @@
<a href="<%= @featured_story.user.path %>" class="featured-profile-button">
<img class="featured-profile-pic" src="<%= ProfileImage.new(@featured_story.user).get(90) %>" alt="<%= @featured_story.title %>" />
</a>
<div class="featured-user-name"><a href="<%= @featured_story.user.path %>"><%= @featured_story.user.name %></a></div>
<div class="featured-user-name"><a href="<%= @featured_story.user.path %>"><%= @featured_story.user.name %>・<%= @featured_story.readable_publish_date %></a></div>
<div class="featured-tags tags">
<% @featured_story.cached_tag_list_array.each do |tag| %>
<a href="/t/<%= tag %>"><span class="tag">#<%= tag %></span></a>
@ -280,6 +338,16 @@
<%= render "articles/algolia_search" %>
<% end %>
<div class="narrow-nav-menu" id="narrow-nav-menu" aria-label="feed-nav-menu">
<a href="<%= list_path %>/" class="<%= 'selected' if ["week", "month", "year", "infinity", "latest"].exclude?(params[:timeframe]) %>">&lt;MY DEV FEED&gt;</a>
<a href="<%= list_path %>/top/week" class="<%= 'selected' if timeframe_check('week') %>">&lt;PAST WEEK&gt;</a>
<a href="<%= list_path %>/top/month" class="<%= 'selected' if timeframe_check('month') %>">&lt;PAST MONTH&gt;</a>
<a href="<%= list_path %>/top/year" class="<%= 'selected' if timeframe_check('year') %>">&lt;PAST YEAR&gt;</a>
<a href="<%= list_path %>/top/infinity" class="<%= 'selected' if timeframe_check('infinity') %>">&lt;INFINITY&gt;</a>
<a href="<%= list_path %>/latest" class="<%= 'selected' if timeframe_check('latest') %>">&lt;LATEST&gt;</a>
</div>
<script async>
var isTouchDevice = ('ontouchstart' in window || navigator.maxTouchPoints > 0);
if (isTouchDevice) {

View file

@ -13,6 +13,7 @@
"features",
"privacy",
"terms",
"latest",
"contact",
"merch",
"rlygenerator",
@ -91,6 +92,7 @@
"programming",
"programmer",
"links",
"top",
"gigs",
"start",
"startups",

View file

@ -192,9 +192,14 @@ Rails.application.routes.draw do
get "/tag/:tag" => 'stories#index'
get "/t/:tag" => 'stories#index'
get "/t/:tag/top/:timeframe" => 'stories#index'
get "/t/:tag/:timeframe" => 'stories#index', constraints: { timeframe: /latest/}
get "/getting-started" => "onboarding#index"
get "/top/:timeframe" => 'stories#index'
get "/:timeframe" => 'stories#index', constraints: { timeframe: /latest/}
get "/:username/:slug/comments/new/:parent_id_code" => 'comments#new'
get "/:username/:slug/comments/new" => 'comments#new'