291 lines
9.2 KiB
Ruby
291 lines
9.2 KiB
Ruby
# typed: strict
|
|
module Domain::UsersHelper
|
|
extend T::Sig
|
|
extend T::Helpers
|
|
include HelpersInterface
|
|
include Pundit::Authorization
|
|
abstract!
|
|
|
|
sig { params(user: Domain::User).returns(T.nilable(String)) }
|
|
def user_avatar_path_for_view(user)
|
|
if avatar = user.avatar
|
|
domain_user_avatar_img_src_path(avatar, thumb: "32-avatar")
|
|
end
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(T.nilable(String)) }
|
|
def user_name_for_view(user)
|
|
if avatar = user.avatar
|
|
domain_user_avatar_img_src_path(avatar, thumb: "32-avatar")
|
|
end
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(T.nilable(String)) }
|
|
def domain_user_registered_at_string_for_view(user)
|
|
ts = domain_user_registered_at_ts_for_view(user)
|
|
ts ? time_ago_in_words(ts) : nil
|
|
end
|
|
|
|
sig do
|
|
params(user: Domain::User).returns(T.nilable(ActiveSupport::TimeWithZone))
|
|
end
|
|
def domain_user_registered_at_ts_for_view(user)
|
|
case user
|
|
when Domain::User::FaUser, Domain::User::E621User, Domain::User::BlueskyUser
|
|
user.registered_at
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
sig do
|
|
params(
|
|
avatar: T.nilable(Domain::UserAvatar),
|
|
thumb: T.nilable(String),
|
|
).returns(String)
|
|
end
|
|
def domain_user_avatar_img_src_path(avatar, thumb: nil)
|
|
cache_key = ["domain_user_avatar_img_src_path", avatar&.id, thumb]
|
|
Rails
|
|
.cache
|
|
.fetch(cache_key, expires_in: 1.day) do
|
|
if (sha256 = avatar&.log_entry&.response_sha256)
|
|
Rails.application.routes.url_helpers.blob_path(
|
|
HexUtil.bin2hex(sha256),
|
|
format: "jpg",
|
|
thumb: thumb,
|
|
)
|
|
elsif avatar && avatar.state_file_404? &&
|
|
(sha256 = avatar.last_log_entry&.response_sha256)
|
|
Rails.application.routes.url_helpers.blob_path(
|
|
HexUtil.bin2hex(sha256),
|
|
format: "jpg",
|
|
thumb: thumb,
|
|
)
|
|
else
|
|
# default / 'not found' avatar image
|
|
# "/blobs/9080fd4e7e23920eb2dccfe2d86903fc3e748eebb2e5aa8c657bbf6f3d941cdc/contents.jpg"
|
|
asset_path("user-circle.svg")
|
|
end
|
|
end
|
|
end
|
|
sig do
|
|
params(
|
|
avatar: T.nilable(Domain::UserAvatar),
|
|
thumb: T.nilable(String),
|
|
).returns(String)
|
|
end
|
|
def domain_user_avatar_img_tag(avatar, thumb: nil)
|
|
if (sha256 = avatar&.log_entry&.response_sha256)
|
|
image_tag(
|
|
blob_path(HexUtil.bin2hex(sha256), format: "jpg", thumb: thumb),
|
|
class: "inline-block h-4 w-4 flex-shrink-0 rounded-sm object-cover",
|
|
alt: avatar&.user&.name_for_view || "user avatar",
|
|
)
|
|
else
|
|
raw <<~SVG
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" />
|
|
</svg>
|
|
SVG
|
|
end
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(String) }
|
|
def site_icon_path_for_user(user)
|
|
case user
|
|
when Domain::User::E621User
|
|
asset_path("domain-icons/e621.png")
|
|
when Domain::User::FaUser
|
|
asset_path("domain-icons/fa.png")
|
|
when Domain::User::InkbunnyUser
|
|
asset_path("domain-icons/inkbunny.png")
|
|
when Domain::User::SofurryUser
|
|
asset_path("domain-icons/sofurry.png")
|
|
when Domain::User::BlueskyUser
|
|
asset_path("domain-icons/bluesky.png")
|
|
else
|
|
Kernel.raise "Unknown user type: #{user.class}"
|
|
end
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(String) }
|
|
def domain_user_path(user)
|
|
"#{domain_users_path}/#{user.to_param}"
|
|
end
|
|
|
|
sig { returns(String) }
|
|
def domain_users_path
|
|
"/users"
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(String) }
|
|
def domain_user_posts_path(user)
|
|
"#{domain_user_path(user)}/posts"
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(String) }
|
|
def domain_user_favorites_path(user)
|
|
"#{domain_user_path(user)}/favorites"
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(String) }
|
|
def domain_user_followed_by_path(user)
|
|
"#{domain_user_path(user)}/followed_by"
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(String) }
|
|
def domain_user_following_path(user)
|
|
"#{domain_user_path(user)}/following"
|
|
end
|
|
|
|
sig { params(user: Domain::User, kind: String).returns(String) }
|
|
def tracked_objects_domain_user_job_events_path(user, kind:)
|
|
unless Domain::UserJobEvent::AddTrackedObject.kinds.include?(kind)
|
|
Kernel.raise "invalid kind: #{kind}"
|
|
end
|
|
"#{domain_user_path(user)}/job_events/tracked_objects/#{kind}"
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(String) }
|
|
def tracked_objects_domain_user_job_events_kinds_path(user)
|
|
"#{domain_user_path(user)}/job_events/tracked_objects"
|
|
end
|
|
|
|
sig { params(user: Domain::User, kind: String).returns(String) }
|
|
def enqueue_scan_job_domain_user_job_events_path(user, kind:)
|
|
unless Domain::UserJobEvent::AddTrackedObject.kinds.include?(kind)
|
|
Kernel.raise "invalid kind: #{kind}"
|
|
end
|
|
"#{domain_user_path(user)}/job_events/enqueue_scan_job/#{kind}"
|
|
end
|
|
|
|
class StatRow < T::ImmutableStruct
|
|
include T::Struct::ActsAsComparable
|
|
|
|
const :name, String
|
|
const :value,
|
|
T.nilable(
|
|
T.any(String, Integer, HasTimestampsWithDueAt::TimestampScanInfo),
|
|
)
|
|
const :link_to, T.nilable(String)
|
|
const :fa_icon_class, T.nilable(String)
|
|
const :hover_title, T.nilable(String)
|
|
end
|
|
|
|
sig { params(user: Domain::User).returns(T::Array[StatRow]) }
|
|
def stat_rows_for_user(user)
|
|
rows = T.let([], T::Array[StatRow])
|
|
if user.has_faved_posts?
|
|
rows << StatRow.new(name: "Favorites", value: user.user_post_favs.size)
|
|
end
|
|
if user.has_followed_by_users?
|
|
can_view_link = policy(user).followed_by?
|
|
rows << StatRow.new(
|
|
name: "Followed by",
|
|
value: user.user_user_follows_to.size,
|
|
link_to: can_view_link ? domain_user_followed_by_path(user) : nil,
|
|
)
|
|
end
|
|
if user.has_followed_users?
|
|
can_view_link = policy(user).following?
|
|
rows << StatRow.new(
|
|
name: "Following",
|
|
value: user.user_user_follows_from.size,
|
|
link_to: can_view_link ? domain_user_following_path(user) : nil,
|
|
)
|
|
end
|
|
|
|
can_view_timestamps = policy(user).view_page_scanned_at_timestamps?
|
|
can_view_log_entries = policy(user).view_log_entries?
|
|
icon_for =
|
|
Kernel.proc do |due_for_scan|
|
|
due_for_scan ? "fa-hourglass-half" : "fa-check"
|
|
end
|
|
|
|
if user.is_a?(Domain::User::BlueskyUser) && can_view_timestamps
|
|
rows << StatRow.new(
|
|
name: "Page scanned",
|
|
value: user.profile_scan,
|
|
link_to:
|
|
user.last_scan_log_entry && log_entry_path(user.last_scan_log_entry),
|
|
fa_icon_class: icon_for.call(user.profile_scan.due?),
|
|
hover_title: user.profile_scan.interval.inspect,
|
|
)
|
|
|
|
rows << StatRow.new(
|
|
name: "Posts scanned",
|
|
value: user.posts_scan,
|
|
link_to:
|
|
user.last_posts_scan_log_entry &&
|
|
log_entry_path(user.last_posts_scan_log_entry),
|
|
fa_icon_class: icon_for.call(user.posts_scan.due?),
|
|
hover_title: user.posts_scan.interval.inspect,
|
|
)
|
|
end
|
|
|
|
if user.is_a?(Domain::User::FaUser) && can_view_timestamps
|
|
if can_view_log_entries && hle = user.guess_last_user_page_log_entry
|
|
rows << StatRow.new(
|
|
name: "Page scanned",
|
|
value: user.page_scan,
|
|
link_to: log_entry_path(hle),
|
|
fa_icon_class: icon_for.call(user.page_scan.due?),
|
|
hover_title: user.page_scan.interval.inspect,
|
|
)
|
|
else
|
|
rows << StatRow.new(
|
|
name: "Page",
|
|
value: user.page_scan,
|
|
fa_icon_class: icon_for.call(user.page_scan.due?),
|
|
hover_title: user.page_scan.interval.inspect,
|
|
)
|
|
end
|
|
|
|
rows << StatRow.new(
|
|
name: "Favs",
|
|
value: user.favs_scan,
|
|
link_to:
|
|
tracked_objects_domain_user_job_events_path(user, kind: "favs"),
|
|
fa_icon_class: icon_for.call(user.favs_scan.due?),
|
|
hover_title: user.favs_scan.interval.inspect,
|
|
)
|
|
rows << StatRow.new(
|
|
name: "Followed by",
|
|
value: user.followed_by_scan,
|
|
fa_icon_class: icon_for.call(user.followed_by_scan.due?),
|
|
hover_title: user.followed_by_scan.interval.inspect,
|
|
)
|
|
rows << StatRow.new(
|
|
name: "Following",
|
|
value: user.follows_scan,
|
|
fa_icon_class: icon_for.call(user.follows_scan.due?),
|
|
hover_title: user.follows_scan.interval.inspect,
|
|
)
|
|
rows << StatRow.new(
|
|
name: "Gallery",
|
|
value: user.gallery_scan,
|
|
fa_icon_class: icon_for.call(user.gallery_scan.due?),
|
|
hover_title: user.gallery_scan.interval.inspect,
|
|
)
|
|
elsif user.is_a?(Domain::User::E621User) && can_view_timestamps
|
|
if user.favs_are_hidden
|
|
rows << StatRow.new(name: "Favorites hidden", value: "yes")
|
|
else
|
|
rows << StatRow.new(
|
|
name: "Server favorites",
|
|
value: user.num_other_favs_cached,
|
|
)
|
|
end
|
|
rows << StatRow.new(
|
|
name: "Favorites",
|
|
value: user.favs_scan,
|
|
fa_icon_class: icon_for.call(user.favs_scan.due?),
|
|
hover_title: user.favs_scan.interval.inspect,
|
|
)
|
|
end
|
|
|
|
rows
|
|
end
|
|
end
|