ib source links

This commit is contained in:
Dylan Knutson
2025-02-21 23:54:28 +00:00
parent 2700ef0f99
commit 8fb884c92c
4 changed files with 137 additions and 37 deletions

View File

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

View File

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

View File

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

View File

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