ib source links
This commit is contained in:
@@ -4,6 +4,7 @@ module Domain::PostsHelper
|
||||
extend T::Helpers
|
||||
include HelpersInterface
|
||||
include LogEntriesHelper
|
||||
include Domain::UsersHelper
|
||||
abstract!
|
||||
|
||||
sig { params(post: Domain::Post).returns(String) }
|
||||
@@ -346,13 +347,117 @@ module Domain::PostsHelper
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(source: String).returns(T.nilable(Domain::Post)) }
|
||||
def post_for_source(source)
|
||||
class LinkForSource < T::ImmutableStruct
|
||||
include T::Struct::ActsAsComparable
|
||||
const :model, ReduxApplicationRecord
|
||||
const :title, String
|
||||
const :model_path, String
|
||||
const :icon_path, T.nilable(String)
|
||||
end
|
||||
|
||||
class SourceResult < T::ImmutableStruct
|
||||
const :model, ReduxApplicationRecord
|
||||
const :title, String
|
||||
end
|
||||
|
||||
class SourceMatcher < T::ImmutableStruct
|
||||
extend T::Generic
|
||||
include T::Struct::ActsAsComparable
|
||||
|
||||
const :hosts, T::Array[String]
|
||||
const :pattern, Regexp
|
||||
const :find_proc, T.proc.params(id: String).returns(T.nilable(SourceResult))
|
||||
end
|
||||
|
||||
FA_HOSTS = %w[www.furaffinity.net furaffinity.net]
|
||||
IB_HOSTS = %w[www.inkbunny.net inkbunny.net]
|
||||
|
||||
MATCHERS =
|
||||
T.let(
|
||||
[
|
||||
SourceMatcher.new(
|
||||
hosts: FA_HOSTS,
|
||||
pattern: %r{/view/(\d+)/?},
|
||||
find_proc: ->(id) do
|
||||
if post = Domain::Post::FaPost.find_by(fa_id: id)
|
||||
SourceResult.new(model: post, title: post.title_for_view)
|
||||
end
|
||||
end,
|
||||
),
|
||||
SourceMatcher.new(
|
||||
hosts: FA_HOSTS,
|
||||
pattern: %r{/user/([^/]+)/?},
|
||||
find_proc: ->(url_name) do
|
||||
if user = Domain::User::FaUser.find_by(url_name: url_name)
|
||||
SourceResult.new(
|
||||
model: user,
|
||||
title: user.name_for_view || "unknown",
|
||||
)
|
||||
end
|
||||
end,
|
||||
),
|
||||
SourceMatcher.new(
|
||||
hosts: IB_HOSTS,
|
||||
pattern: %r{/s/(\d+)/?},
|
||||
find_proc: ->(id) do
|
||||
if post = Domain::Post::InkbunnyPost.find_by(ib_id: id)
|
||||
SourceResult.new(model: post, title: post.title_for_view)
|
||||
end
|
||||
end,
|
||||
),
|
||||
SourceMatcher.new(
|
||||
hosts: IB_HOSTS,
|
||||
pattern: %r{/([^/]+)/?},
|
||||
find_proc: ->(name) do
|
||||
if user =
|
||||
Domain::User::InkbunnyUser.where(
|
||||
"json_attributes->>'name' ILIKE ?",
|
||||
name,
|
||||
).first
|
||||
SourceResult.new(
|
||||
model: user,
|
||||
title: user.name_for_view || "unknown",
|
||||
)
|
||||
end
|
||||
end,
|
||||
),
|
||||
],
|
||||
T::Array[SourceMatcher],
|
||||
)
|
||||
|
||||
sig { params(source: String).returns(T.nilable(LinkForSource)) }
|
||||
def link_for_source(source)
|
||||
uri = URI.parse(source)
|
||||
return unless %w[www.furaffinity.net furaffinity.net].include?(uri.host)
|
||||
fa_id = uri.path&.match(%r{/view/(\d+)})&.[](1)
|
||||
return unless fa_id
|
||||
Domain::Post::FaPost.find_by(fa_id: fa_id)
|
||||
|
||||
for matcher in MATCHERS
|
||||
if matcher.hosts.include?(uri.host)
|
||||
if (match = matcher.pattern.match(uri.path)) && (id = match[1])
|
||||
object = matcher.find_proc.call(id)
|
||||
return nil unless object
|
||||
model = object.model
|
||||
|
||||
if model.is_a?(Domain::Post)
|
||||
model_path = domain_post_path(model)
|
||||
elsif model.is_a?(Domain::User)
|
||||
model_path = domain_user_path(model)
|
||||
icon_path =
|
||||
domain_user_avatar_img_src_path(model.avatar, thumb: "64-avatar")
|
||||
else
|
||||
model_path = "#"
|
||||
end
|
||||
|
||||
return(
|
||||
LinkForSource.new(
|
||||
model:,
|
||||
title: object.title,
|
||||
model_path:,
|
||||
icon_path:,
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
@@ -368,20 +473,6 @@ module Domain::PostsHelper
|
||||
|
||||
private
|
||||
|
||||
# TODO - these exist on LogEntriesHelper?
|
||||
# sig { params(content_type: String).returns(T::Boolean) }
|
||||
# def is_renderable_image_type?(content_type)
|
||||
# %w[image/jpeg image/jpg image/png image/gif].any? do |ct|
|
||||
# content_type.starts_with?(ct)
|
||||
# end
|
||||
# end
|
||||
|
||||
# sig { params(content_type: String).returns(T::Boolean) }
|
||||
# def is_thumbable_content_type?(content_type)
|
||||
# %w[video/webm].any? { |ct| content_type.starts_with?(ct) } ||
|
||||
# is_renderable_image_type?(content_type)
|
||||
# end
|
||||
|
||||
TAG_CATEGORY_ORDER =
|
||||
T.let(
|
||||
%i[artist copyright character species general meta lore invalid].freeze,
|
||||
|
||||
@@ -124,8 +124,6 @@ class Domain::Inkbunny::Job::ApiSearchPageProcessor
|
||||
base_url.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Update a post from a submission JSON. Submission JSONs are returned from
|
||||
# the API search endpoint, which contains a shallow version of post data.
|
||||
#
|
||||
@@ -204,6 +202,8 @@ class Domain::Inkbunny::Job::ApiSearchPageProcessor
|
||||
is_new_post
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Update a user from a submission JSON. Submission JSONs are returned from
|
||||
# the API search endpoint, which contains a shallow version of user data.
|
||||
#
|
||||
|
||||
@@ -99,10 +99,12 @@ class Domain::Inkbunny::Job::UpdatePostsJob < Domain::Inkbunny::Job::Base
|
||||
Domain::Post::InkbunnyPost.includes(:pools).find_by(ib_id: ib_post_id)
|
||||
|
||||
if post.blank?
|
||||
logger.error(
|
||||
format_tags(make_tag("ib_post_id", ib_post_id), "post not found"),
|
||||
processor = Domain::Inkbunny::Job::ApiSearchPageProcessor.new
|
||||
processor.upsert_post_from_submission_json!(
|
||||
submission_json,
|
||||
caused_by_entry: log_entry,
|
||||
)
|
||||
return
|
||||
post = T.must(Domain::Post::InkbunnyPost.find_by(ib_id: ib_post_id))
|
||||
end
|
||||
|
||||
logger.tagged(make_arg_tag(post)) do
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
<div class="flex items-start gap-2 py-1 first:pt-0 last:pb-0">
|
||||
<% if icon_path = icon_asset_for_url(source) %>
|
||||
<%= image_tag icon_path, class: "h-4 w-4 mt-1 flex-shrink-0" %>
|
||||
<% else %>
|
||||
<i class="fa-solid fa-link mt-1 h-4 w-4 flex-shrink-0"></i>
|
||||
<% end %>
|
||||
<%= link_to source,
|
||||
<% unless source.include?("://")%>
|
||||
<% source = "https://#{source}" %>
|
||||
<% end %>
|
||||
<div class="flex items-center justify-between gap-2 py-1 first:pt-0 last:pb-0">
|
||||
<div class="flex items-center gap-2 justify-center h-full max-w-full">
|
||||
<% if icon_path = icon_asset_for_url(source) %>
|
||||
<%= image_tag icon_path, class: "h-6 w-6 mt-1 flex-shrink-0" %>
|
||||
<% else %>
|
||||
<i class="fa-solid fa-link mt-1 h-6 w-6 flex-shrink-0"></i>
|
||||
<% end %>
|
||||
<%= link_to source,
|
||||
source,
|
||||
class: "text-blue-600 hover:underline truncate",
|
||||
target: "_blank" %>
|
||||
<% post = post_for_source(source) %>
|
||||
<% if post %>
|
||||
<%= link_to domain_post_path(post),
|
||||
</div>
|
||||
<% if lfs = link_for_source(source) %>
|
||||
<%= link_to lfs.model_path,
|
||||
class:
|
||||
"float-right inline-flex items-center gap-2 rounded bg-blue-600 px-3 py-1 text-sm text-white hover:bg-blue-700" do %>
|
||||
<span class="truncate"><%= post.title %></span>
|
||||
"flex items-center rounded overflow-hidden bg-blue-600 text-sm text-white hover:bg-blue-700 h-8" do %>
|
||||
<% if lfs.icon_path %>
|
||||
<%= image_tag lfs.icon_path, class: "h-full" %>
|
||||
<% end %>
|
||||
<span class="truncate overflow-ellipsis px-3 py-1"><%= lfs.title %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user