separate table for fa post favs
This commit is contained in:
@@ -18,7 +18,7 @@ class Domain::PostsController < DomainController
|
||||
visual_results
|
||||
]
|
||||
before_action :set_post!, only: %i[show]
|
||||
before_action :set_user!, only: %i[user_favorite_posts user_created_posts]
|
||||
before_action :set_user!, only: %i[user_created_posts]
|
||||
before_action :set_post_group!, only: %i[posts_in_group]
|
||||
|
||||
class PostsIndexViewConfig < T::ImmutableStruct
|
||||
@@ -65,29 +65,6 @@ class Domain::PostsController < DomainController
|
||||
authorize @post
|
||||
end
|
||||
|
||||
sig(:final) { void }
|
||||
def user_favorite_posts
|
||||
@posts_index_view_config =
|
||||
PostsIndexViewConfig.new(
|
||||
show_domain_filters: false,
|
||||
show_creator_links: true,
|
||||
index_type_header: "user_favorites",
|
||||
)
|
||||
|
||||
@user = T.must(@user)
|
||||
authorize @user
|
||||
|
||||
@posts = @user.faved_posts
|
||||
@post_favs =
|
||||
Domain::UserPostFav.where(user: @user, post: @posts).index_by(&:post_id)
|
||||
|
||||
# Apply pagination through posts_relation
|
||||
@posts = posts_relation(@posts, skip_ordering: true)
|
||||
|
||||
authorize @posts
|
||||
render :index
|
||||
end
|
||||
|
||||
sig(:final) { void }
|
||||
def user_created_posts
|
||||
@posts_index_view_config =
|
||||
|
||||
29
app/controllers/domain/user_post_favs_controller.rb
Normal file
29
app/controllers/domain/user_post_favs_controller.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Domain::UserPostFavsController < DomainController
|
||||
before_action :set_user!, only: %i[favorites]
|
||||
|
||||
def self.param_config
|
||||
DomainParamConfig.new(
|
||||
post_id_param: :domain_post_id,
|
||||
user_id_param: :domain_user_id,
|
||||
post_group_id_param: :domain_post_group_id,
|
||||
)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def favorites
|
||||
@posts_index_view_config =
|
||||
Domain::PostsController::PostsIndexViewConfig.new(
|
||||
show_domain_filters: false,
|
||||
show_creator_links: true,
|
||||
index_type_header: "user_favorites",
|
||||
)
|
||||
user = T.cast(@user, Domain::User)
|
||||
@user_post_favs =
|
||||
user.user_post_favs.includes(:post).page(params[:page]).per(50)
|
||||
authorize @user_post_favs
|
||||
render :favorites
|
||||
end
|
||||
end
|
||||
@@ -42,7 +42,7 @@ module Stats::Helpers
|
||||
|
||||
records_array.map do |record|
|
||||
Stats::DataPoint.new(
|
||||
x: record.fav_id.to_f,
|
||||
x: record.fa_fav_id.to_f,
|
||||
y: T.must(record.explicit_time).to_f,
|
||||
)
|
||||
end
|
||||
|
||||
@@ -102,11 +102,22 @@ class Domain::Post < ReduxApplicationRecord
|
||||
inverse_of: :post,
|
||||
dependent: :destroy
|
||||
|
||||
sig { params(klass: T.class_of(Domain::User)).void }
|
||||
def self.has_faving_users!(klass)
|
||||
self.class_has_faving_users = klass
|
||||
sig do
|
||||
params(
|
||||
user_klass: T.class_of(Domain::User),
|
||||
fav_klass: T.class_of(Domain::UserPostFav),
|
||||
).void
|
||||
end
|
||||
def self.has_faving_users!(user_klass, fav_klass = Domain::UserPostFav)
|
||||
self.class_has_faving_users = user_klass
|
||||
|
||||
has_many :user_post_favs,
|
||||
class_name: fav_klass.name,
|
||||
inverse_of: :post,
|
||||
dependent: :destroy
|
||||
|
||||
has_many :faving_users,
|
||||
class_name: klass.name,
|
||||
class_name: user_klass.name,
|
||||
through: :user_post_favs,
|
||||
source: :user
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ class Domain::Post::FaPost < Domain::Post
|
||||
|
||||
has_single_file!
|
||||
has_single_creator! Domain::User::FaUser
|
||||
has_faving_users! Domain::User::FaUser
|
||||
has_faving_users! Domain::User::FaUser, Domain::UserPostFav::FaUserPostFav
|
||||
|
||||
after_initialize { self.state ||= "ok" if new_record? }
|
||||
|
||||
|
||||
@@ -170,13 +170,11 @@ class Domain::User < ReduxApplicationRecord
|
||||
sig { params(post_ids: T::Array[Integer], log_entry: HttpLogEntry).void }
|
||||
def upsert_new_favs(post_ids, log_entry:)
|
||||
self.class.transaction do
|
||||
type = self.class.fav_model_type.name
|
||||
if post_ids.any?
|
||||
post_ids.each_slice(1000) do |slice|
|
||||
self.user_post_favs.upsert_all(
|
||||
slice.map { |post_id| { post_id:, type: } },
|
||||
slice.map { |post_id| { post_id: } },
|
||||
unique_by: %i[user_id post_id],
|
||||
update_only: %i[type],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,8 @@ class Domain::User::FaUser < Domain::User
|
||||
|
||||
# todo - set this to be the right fav model type
|
||||
has_many :user_post_favs,
|
||||
class_name: "Domain::UserPostFav",
|
||||
-> { order(fa_fav_id: :desc) },
|
||||
class_name: "Domain::UserPostFav::FaUserPostFav",
|
||||
foreign_key: :user_id,
|
||||
inverse_of: :user,
|
||||
dependent: :destroy
|
||||
@@ -132,17 +133,10 @@ class Domain::User::FaUser < Domain::User
|
||||
).returns(Domain::UserPostFav)
|
||||
end
|
||||
def update_fav_model(post_id:, fav_id: nil, explicit_time: nil)
|
||||
model =
|
||||
self.user_post_favs.find_by(post_id:) ||
|
||||
self.user_post_favs.build(
|
||||
type: self.class.fav_model_type.name,
|
||||
post_id:,
|
||||
)
|
||||
if model.is_a?(Domain::UserPostFav::FaUserPostFav)
|
||||
model.fav_id = fav_id if fav_id.present?
|
||||
model.explicit_time = explicit_time if explicit_time.present?
|
||||
model.save!
|
||||
end
|
||||
model = self.user_post_favs.find_or_initialize_by(post_id:)
|
||||
model.fa_fav_id = fav_id if fav_id.present?
|
||||
model.explicit_time = explicit_time.in_time_zone if explicit_time.present?
|
||||
model.save!
|
||||
model
|
||||
end
|
||||
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
# typed: strict
|
||||
class Domain::UserPostFav::FaUserPostFav < Domain::UserPostFav
|
||||
scope :with_explicit_time_and_id,
|
||||
-> { where.not(explicit_time: nil).where.not(fav_id: nil) }
|
||||
|
||||
scope :with_inferred_time_and_id,
|
||||
-> { where.not(inferred_time: nil).where.not(fav_id: nil) }
|
||||
|
||||
scope :with_fav_id, -> { where.not(fav_id: nil) }
|
||||
|
||||
attr_json :fav_id, :integer
|
||||
attr_json :inferred_time, ActiveModelUtcTimeIntValue.new
|
||||
attr_json :explicit_time, ActiveModelUtcTimeIntValue.new
|
||||
|
||||
validates :fav_id, uniqueness: true, if: :fav_id?
|
||||
self.table_name = "domain_user_post_favs_fa"
|
||||
|
||||
belongs_to_with_counter_cache :user,
|
||||
class_name: "Domain::User::FaUser",
|
||||
inverse_of: :user_post_favs,
|
||||
counter_cache: :user_post_favs_count
|
||||
|
||||
belongs_to :post,
|
||||
class_name: "Domain::Post::FaPost",
|
||||
inverse_of: :user_post_favs
|
||||
|
||||
scope :with_explicit_time_and_id,
|
||||
-> { where.not(explicit_time: nil).where.not(fa_fav_id: nil) }
|
||||
|
||||
scope :with_inferred_time_and_id,
|
||||
-> { where.not(inferred_time: nil).where.not(fa_fav_id: nil) }
|
||||
|
||||
scope :with_fa_fav_id, -> { where.not(fa_fav_id: nil) }
|
||||
|
||||
validates :fa_fav_id, uniqueness: true, if: :fa_fav_id?
|
||||
|
||||
sig(:final) { override.returns(T.nilable(FavedAt)) }
|
||||
def faved_at
|
||||
if explicit_time.present?
|
||||
return FavedAt.new(time: explicit_time, type: FavedAtType::Explicit)
|
||||
if (e = explicit_time)
|
||||
return(FavedAt.new(time: e.to_time, type: FavedAtType::Explicit))
|
||||
end
|
||||
|
||||
if inferred_time.present?
|
||||
return(FavedAt.new(time: inferred_time, type: FavedAtType::Inferred))
|
||||
if (i = inferred_time)
|
||||
return(FavedAt.new(time: i.to_time, type: FavedAtType::Inferred))
|
||||
end
|
||||
|
||||
regression_model =
|
||||
@@ -34,13 +36,13 @@ class Domain::UserPostFav::FaUserPostFav < Domain::UserPostFav
|
||||
name: "fa_fav_id_and_date",
|
||||
model_type: "square_root",
|
||||
)
|
||||
if regression_model.nil? || fav_id.nil?
|
||||
if regression_model.nil? || fa_fav_id.nil?
|
||||
return(
|
||||
FavedAt.new(time: post&.posted_at&.to_time, type: FavedAtType::PostedAt)
|
||||
)
|
||||
end
|
||||
|
||||
date_i = regression_model.predict(fav_id.to_f).to_i
|
||||
date_i = regression_model.predict(fa_fav_id.to_f).to_i
|
||||
FavedAt.new(time: Time.at(date_i), type: FavedAtType::InferredNow)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# typed: strict
|
||||
class Domain::UserPostFav::FaUserPostFavPolicy < ApplicationPolicy
|
||||
extend T::Sig
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def favorites?
|
||||
true
|
||||
end
|
||||
end
|
||||
@@ -58,7 +58,7 @@
|
||||
<% end %>
|
||||
<% end %>
|
||||
<span class="flex-grow text-right">
|
||||
<% user_post_fav = @post_favs && @post_favs[post.id] %>
|
||||
<% user_post_fav = local_assigns[:user_post_fav] %>
|
||||
<% if (faved_at = user_post_fav&.faved_at) && (time = faved_at.time) %>
|
||||
<span class="flex items-center gap-1 justify-end">
|
||||
<span
|
||||
|
||||
@@ -1,25 +1,6 @@
|
||||
<% content_for :head do %>
|
||||
<style>
|
||||
.grid-cell {
|
||||
padding: 0.25rem;
|
||||
border-right: 1px solid #e2e8f0;
|
||||
}
|
||||
.grid-cell:last-child {
|
||||
padding-left: 0;
|
||||
padding-right: 1rem;
|
||||
border-right: none;
|
||||
}
|
||||
.grid-cell:first-child {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.grid-row:hover .grid-cell {
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
</style>
|
||||
<% end %>
|
||||
<div class="w-full max-w-2xl mx-auto mt-4 text-center sm:mt-6">
|
||||
<% index_type_header_partial = "domain/posts/index_type_headers/#{@posts_index_view_config.index_type_header}" %>
|
||||
<%= render partial: index_type_header_partial, locals: { user: @user, params: params, posts: @posts } %>
|
||||
<%= render partial: index_type_header_partial, locals: { user: @user, params: params, relation: @posts } %>
|
||||
</div>
|
||||
<% if @posts_index_view_config.show_domain_filters %>
|
||||
<%= render partial: "domain_filter_controls" %>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<h1 class="text-2xl">
|
||||
<%= link_to user.name_for_view, domain_user_path(user), class: "text-blue-600 hover:text-blue-800" %>'s favorited posts
|
||||
<%= page_str(params) %>
|
||||
(<%= posts.total_count %> total)
|
||||
(<%= relation.total_count %> total)
|
||||
</h1>
|
||||
|
||||
14
app/views/domain/user_post_favs/favorites.html.erb
Normal file
14
app/views/domain/user_post_favs/favorites.html.erb
Normal file
@@ -0,0 +1,14 @@
|
||||
<div class="w-full max-w-2xl mx-auto mt-4 text-center sm:mt-6">
|
||||
<% index_type_header_partial = "domain/posts/index_type_headers/#{@posts_index_view_config.index_type_header}" %>
|
||||
<%= render partial: index_type_header_partial, locals: { user: @user, params: params, relation: @user_post_favs } %>
|
||||
</div>
|
||||
<% if @posts_index_view_config.show_domain_filters %>
|
||||
<%= render partial: "domain_filter_controls" %>
|
||||
<% end %>
|
||||
<%= render partial: "shared/pagination_controls", locals: { collection: @user_post_favs } %>
|
||||
<div class="mx-auto flex flex-wrap justify-center">
|
||||
<% @user_post_favs.each do |user_post_fav| %>
|
||||
<%= render partial: "domain/posts/as_gallery_item", locals: { post: user_post_fav.post, user_post_fav: user_post_fav } %>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render partial: "shared/pagination_controls", locals: { collection: @user_post_favs } %>
|
||||
@@ -1,10 +1,9 @@
|
||||
<%# nasty hack, otherwise postgres uses a bad query plan %>
|
||||
<% if user.is_a?(Domain::User::FaUser) || user.is_a?(Domain::User::InkbunnyUser) %>
|
||||
<% fav_posts = user.faved_posts.includes(:creator).limit(5) %>
|
||||
<% post_favs = user.user_post_favs.includes(post: :creator).limit(5) %>
|
||||
<% else %>
|
||||
<% fav_posts = user.faved_posts.limit(5) %>
|
||||
<% post_favs = user.user_post_favs.limit(5) %>
|
||||
<% end%>
|
||||
<% post_favs = Domain::UserPostFav.where(user: user, post: fav_posts).index_by(&:post_id) %>
|
||||
<section class="animated-shadow-sky sky-section">
|
||||
<h2 class="section-header">
|
||||
<span class="font-medium text-slate-900">Favorited Posts</span>
|
||||
@@ -12,20 +11,20 @@
|
||||
<%= link_to "#{user.user_post_favs.size} total", domain_user_favorites_path(user), class: "blue-link" %>
|
||||
</span>
|
||||
</h2>
|
||||
<% if fav_posts.any? %>
|
||||
<% fav_posts.each do |post| %>
|
||||
<% if post_favs.any? %>
|
||||
<% post_favs.each do |user_post_fav| %>
|
||||
<% post = user_post_fav.post %>
|
||||
<div class="flex flex-col px-4 py-2 group">
|
||||
<span class="flex gap-2 group-hover:flex-grow-1">
|
||||
<%= render(
|
||||
partial: "domain/has_description_html/inline_link_domain_post",
|
||||
locals: {
|
||||
post: post,
|
||||
post:,
|
||||
visual_style: "sky-link",
|
||||
domain_icon: false
|
||||
}
|
||||
) %>
|
||||
<span class="whitespace-nowrap flex-grow text-right text-slate-500">
|
||||
<% user_post_fav = post_favs[post.id] %>
|
||||
<% if (faved_at = user_post_fav&.faved_at) && (time = faved_at.time) %>
|
||||
<%= time_ago_in_words(faved_at.time) %> ago
|
||||
<% else %>
|
||||
|
||||
Reference in New Issue
Block a user