FEATURE: Basic support for threads.net onebox (#22471)
This commit is contained in:
parent
dc46acb851
commit
3fd327c458
|
@ -1010,3 +1010,81 @@ aside.onebox.preview-error .site-icon {
|
|||
height: 16px;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.onebox.threadsstatus .onebox-body {
|
||||
.thumbnail,
|
||||
.thumbnail.onebox-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.threads-screen-name {
|
||||
font-size: var(--font-down-1);
|
||||
}
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.thread-contents .thread-description {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
p,
|
||||
.thread-contents {
|
||||
clear: left;
|
||||
padding: 1em 0;
|
||||
|
||||
.quoted {
|
||||
border: 1px solid var(--primary-low);
|
||||
padding: 0.5em 1em;
|
||||
margin-top: 1em;
|
||||
white-space: normal;
|
||||
|
||||
.quoted-link {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.quoted-title {
|
||||
font-weight: bold;
|
||||
margin: 0.5em 0;
|
||||
padding: 0;
|
||||
|
||||
span {
|
||||
font-weight: lighter;
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date {
|
||||
display: flex;
|
||||
line-height: var(--line-height-small);
|
||||
|
||||
.timestamp {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
|
||||
.like,
|
||||
.replies {
|
||||
align-items: center;
|
||||
color: var(--primary-medium);
|
||||
display: flex;
|
||||
margin-left: 0.75em;
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.is-reply {
|
||||
color: var(--primary-medium);
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,6 +165,7 @@ require_relative "engine/google_play_app_onebox"
|
|||
require_relative "engine/image_onebox"
|
||||
require_relative "engine/video_onebox"
|
||||
require_relative "engine/audio_onebox"
|
||||
require_relative "engine/threads_status_onebox"
|
||||
require_relative "engine/stack_exchange_onebox"
|
||||
require_relative "engine/twitter_status_onebox"
|
||||
require_relative "engine/wikimedia_onebox"
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Onebox
|
||||
module Engine
|
||||
class ThreadsStatusOnebox
|
||||
include Engine
|
||||
include LayoutSupport
|
||||
include HTML
|
||||
|
||||
matches_regexp(%r{^https?://www\.threads\.net/t/(?<id>[\d\w_-]+)/?.*?$})
|
||||
always_https
|
||||
|
||||
def self.priority
|
||||
1
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def link
|
||||
raw.css("link[rel='canonical']").first["href"]
|
||||
end
|
||||
|
||||
def likes
|
||||
@og[:description].split(" ").first
|
||||
end
|
||||
|
||||
def replies
|
||||
@og[:description].split(", ").drop(1).join(", ").split(" repl").first
|
||||
end
|
||||
|
||||
def description
|
||||
text = @og[:description].split(". ").drop(1).join(". ")
|
||||
linkify_mentions(text)
|
||||
end
|
||||
|
||||
def title
|
||||
@og[:title].split(" (@").first
|
||||
end
|
||||
|
||||
def screen_name
|
||||
@og[:title].split(" (@").drop(1).join(" (@").split(") on Threads")[0]
|
||||
end
|
||||
|
||||
def avatar
|
||||
poster_response =
|
||||
begin
|
||||
Onebox::Helpers.fetch_response("https://www.threads.net/@#{screen_name}")
|
||||
rescue StandardError
|
||||
return nil
|
||||
end
|
||||
poster_html = Nokogiri.HTML(poster_response)
|
||||
poster_data = ::Onebox::OpenGraph.new(poster_html).data
|
||||
poster_data[:image]
|
||||
end
|
||||
|
||||
def image
|
||||
@og[:image]
|
||||
end
|
||||
|
||||
def favicon
|
||||
raw.css("link[rel='icon']").first["href"]
|
||||
end
|
||||
|
||||
def linkify_mentions(text)
|
||||
text.gsub(/@([\w\d]+)/, "<a href='https://www.threads.net/@\\1'>@\\1</a>")
|
||||
end
|
||||
|
||||
def data
|
||||
@og = ::Onebox::OpenGraph.new(raw).data
|
||||
|
||||
@data ||= {
|
||||
favicon: favicon,
|
||||
link: link,
|
||||
description: description,
|
||||
image: image,
|
||||
title: title,
|
||||
screen_name: screen_name,
|
||||
avatar: avatar,
|
||||
likes: likes,
|
||||
replies: replies,
|
||||
}
|
||||
|
||||
# if the image is the same as the avatar, don't show it
|
||||
# means it's a thread with no image
|
||||
@data[:image] = nil if @data[:image].split("?").first == @data[:avatar].split("?").first
|
||||
|
||||
@data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
{{#avatar}}<img src="{{avatar}}" class="thumbnail onebox-avatar">{{/avatar}}
|
||||
<h4><a href="{{link}}" target="_blank" rel="noopener">{{title}}</a></h4>
|
||||
<div class="threads-screen-name"><a href="{{link}}" target="_blank" rel="noopener">@{{screen_name}}</a></div>
|
||||
|
||||
<div class="thread-contents">
|
||||
<span class="thread-description">{{{description}}}</span>
|
||||
{{#image}}
|
||||
<div class="scale-images"><img src="{{image}}"></div>
|
||||
{{/image}}
|
||||
</div>
|
||||
|
||||
<div class="date">
|
||||
{{#likes}}
|
||||
<span class="like">
|
||||
<svg viewBox="0 0 512 512" width="14px" height="16px" aria-hidden="true">
|
||||
<path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"></path>
|
||||
</svg>
|
||||
{{likes}}
|
||||
</span>
|
||||
{{/likes}}
|
||||
|
||||
{{#replies}}
|
||||
<span class="replies">
|
||||
<svg viewBox="0 0 640 512" width="14px" height="16px" aria-hidden="true">
|
||||
<path d="M629.657 343.598L528.971 444.284c-9.373 9.372-24.568 9.372-33.941 0L394.343 343.598c-9.373-9.373-9.373-24.569 0-33.941l10.823-10.823c9.562-9.562 25.133-9.34 34.419.492L480 342.118V160H292.451a24.005 24.005 0 0 1-16.971-7.029l-16-16C244.361 121.851 255.069 96 276.451 96H520c13.255 0 24 10.745 24 24v222.118l40.416-42.792c9.285-9.831 24.856-10.054 34.419-.492l10.823 10.823c9.372 9.372 9.372 24.569-.001 33.941zm-265.138 15.431A23.999 23.999 0 0 0 347.548 352H160V169.881l40.416 42.792c9.286 9.831 24.856 10.054 34.419.491l10.822-10.822c9.373-9.373 9.373-24.569 0-33.941L144.971 67.716c-9.373-9.373-24.569-9.373-33.941 0L10.343 168.402c-9.373 9.373-9.373 24.569 0 33.941l10.822 10.822c9.562 9.562 25.133 9.34 34.419-.491L96 169.881V392c0 13.255 10.745 24 24 24h243.549c21.382 0 32.09-25.851 16.971-40.971l-16.001-16z"></path>
|
||||
</svg>
|
||||
{{replies}}
|
||||
</span>
|
||||
{{/replies}}
|
||||
</div>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,94 @@
|
|||
# frozen_string_literal: true
|
||||
RSpec.describe Onebox::Engine::ThreadsStatusOnebox do
|
||||
context "with a thread with only text" do
|
||||
let(:link) { "https://www.threads.net/t/CuVvRcttG57" }
|
||||
let(:html) { described_class.new(link).to_html }
|
||||
|
||||
before do
|
||||
stub_request(:get, link).to_return(
|
||||
status: 200,
|
||||
body: onebox_response("threadsstatus_without_image"),
|
||||
)
|
||||
stub_request(:get, "https://www.threads.net/@rafael_falco").to_return(
|
||||
status: 200,
|
||||
body: onebox_response("threadsstatus_without_image"),
|
||||
)
|
||||
end
|
||||
|
||||
it "includes threads content" do
|
||||
expect(html).to include("trazer a lista de follows")
|
||||
end
|
||||
|
||||
it "includes name" do
|
||||
expect(html).to include("Rafael Silva")
|
||||
end
|
||||
|
||||
it "includes username" do
|
||||
expect(html).to include("@rafael_falco")
|
||||
end
|
||||
|
||||
it "includes user avatar" do
|
||||
expect(html).to include(
|
||||
"https://scontent.cdninstagram.com/v/t51.2885-19/358195671_1485179698889636_5420020496346583344_n.jpg?stp=dst-jpg_s150x150&_nc_ht=scontent.cdninstagram.com&_nc_cat=108&_nc_ohc=UbFgg6blcOUAX8XVrUj&edm=APs17CUBAAAA&ccb=7-5&oh=00_AfDTSDE1W16bDEOUCofc_RLwOXbwfwL83BafmR_f4_ou6g&oe=64AB848C&_nc_sid=10d13b",
|
||||
)
|
||||
end
|
||||
|
||||
it "includes twitter link" do
|
||||
expect(html).to include("https://www.threads.net/t/CuVvRcttG57")
|
||||
end
|
||||
|
||||
it "includes twitter likes" do
|
||||
expect(html).to include("3")
|
||||
end
|
||||
|
||||
it "includes twitter retweets" do
|
||||
expect(html).to include("1")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a thread containing an image" do
|
||||
let(:link) { "https://www.threads.net/t/CuWRRrQuql9" }
|
||||
let(:html) { described_class.new(link).to_html }
|
||||
|
||||
before do
|
||||
stub_request(:get, link).to_return(
|
||||
status: 200,
|
||||
body: onebox_response("threadsstatus_featured_image"),
|
||||
)
|
||||
stub_request(:get, "https://www.threads.net/@joyqiuu").to_return(
|
||||
status: 200,
|
||||
body: onebox_response("threadsstatus_profile"),
|
||||
)
|
||||
end
|
||||
|
||||
it "includes threads content" do
|
||||
expect(html).to include("10M users later")
|
||||
end
|
||||
|
||||
it "includes name" do
|
||||
expect(html).to include("Joy Qiu")
|
||||
end
|
||||
|
||||
it "includes username" do
|
||||
expect(html).to include("@joyqiuu")
|
||||
end
|
||||
|
||||
it "includes user avatar" do
|
||||
expect(html).to include(
|
||||
"https://scontent.cdninstagram.com/v/t51.2885-19/358167674_306426985144380_6235341132840289293_n.jpg?stp=dst-jpg_s640x640&_nc_ht=scontent.cdninstagram.com&_nc_cat=1&_nc_ohc=KqFQdmSjeMsAX-OWNHA&edm=APs17CUBAAAA&ccb=7-5&oh=00_AfDrfi6q0GGPALemTc0YzaE-Bnxm0GJ3QTrswCox095yRA&oe=64AC85F1&_nc_sid=10d13b",
|
||||
)
|
||||
end
|
||||
|
||||
it "includes twitter link" do
|
||||
expect(html).to include("https://www.threads.net/t/CuWRRrQuql9")
|
||||
end
|
||||
|
||||
it "includes twitter likes" do
|
||||
expect(html).to include("5.8K")
|
||||
end
|
||||
|
||||
it "includes twitter retweets" do
|
||||
expect(html).to include("449")
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue