このコミットが含まれているのは:
@@ -6,15 +6,15 @@ class TheatreSkipEventsController < ApplicationController
|
||||
events =
|
||||
TheatreSkipEvent
|
||||
.where(theatre_id: params[:theatre_id])
|
||||
.includes(:tags, post: { tags: :tag_name })
|
||||
.includes(:post, tags: :tag_name)
|
||||
.order(created_at: :desc)
|
||||
.limit(limit)
|
||||
|
||||
render json: events.map { |event|
|
||||
{ id: event.id,
|
||||
theatre_id: event.theatre_id,
|
||||
post: PostRepr.base(event.post),
|
||||
tags: event.tags.map { |tag| TagRepr.inline(tag) },
|
||||
post: { id: event.post.id, title: event.post.title, url: event.post.url },
|
||||
tags: event.tags.map { |tag| { id: tag.id, name: tag.name } },
|
||||
programme_position: event.programme_position,
|
||||
created_at: event.created_at }
|
||||
}
|
||||
|
||||
@@ -24,11 +24,9 @@ class TheatrePostSelector
|
||||
candidates = weighted_candidates
|
||||
sorted = candidates.sort_by { |candidate| [candidate.weight, candidate.post.id] }
|
||||
|
||||
{
|
||||
tag_penalties: tag_penalty_json,
|
||||
{ tag_penalties: tag_penalty_json,
|
||||
lightest_posts: post_weight_json(sorted.first(limit)),
|
||||
heaviest_posts: post_weight_json(sorted.reverse.first(limit))
|
||||
}
|
||||
heaviest_posts: post_weight_json(sorted.reverse.first(limit)) }
|
||||
end
|
||||
|
||||
private
|
||||
@@ -45,11 +43,10 @@ class TheatrePostSelector
|
||||
penalty = post_tags.sum { |tag| penalties[tag.id].to_i }
|
||||
|
||||
Candidate.new(
|
||||
post:,
|
||||
penalty:,
|
||||
tags: post_tags,
|
||||
weight: 1.0 / (1.0 + penalty)
|
||||
)
|
||||
post:,
|
||||
penalty:,
|
||||
tags: post_tags,
|
||||
weight: 1.0 / (1.0 + penalty))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -87,10 +84,8 @@ class TheatrePostSelector
|
||||
tag = tags[tag_id]
|
||||
next unless tag
|
||||
|
||||
{
|
||||
tag: light_tag_json(tag),
|
||||
penalty:
|
||||
}
|
||||
{ tag: light_tag_json(tag),
|
||||
penalty: }
|
||||
}
|
||||
.compact
|
||||
.sort_by { |row| [-row[:penalty], row[:tag][:name].to_s] }
|
||||
@@ -98,27 +93,22 @@ class TheatrePostSelector
|
||||
|
||||
def post_weight_json candidates
|
||||
candidates.map { |candidate|
|
||||
{
|
||||
post: light_post_json(candidate.post),
|
||||
{ post: light_post_json(candidate.post),
|
||||
weight: candidate.weight,
|
||||
penalty: candidate.penalty,
|
||||
tags: candidate.tags.map { |tag| light_tag_json(tag) }
|
||||
}
|
||||
tags: candidate.tags.map { |tag| light_tag_json(tag) } }
|
||||
}
|
||||
end
|
||||
|
||||
def light_post_json post
|
||||
{
|
||||
id: post.id,
|
||||
{ id: post.id,
|
||||
title: post.title,
|
||||
url: post.url
|
||||
}
|
||||
url: post.url }
|
||||
end
|
||||
|
||||
def light_tag_json tag
|
||||
{
|
||||
id: tag.id,
|
||||
name: tag.name
|
||||
}
|
||||
{ id: tag.id,
|
||||
name: tag.name,
|
||||
category: tag.category }
|
||||
end
|
||||
end
|
||||
|
||||
生成ファイル
-1
@@ -339,7 +339,6 @@ ActiveRecord::Schema[8.0].define(version: 2026_06_06_000000) do
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["expires_at"], name: "index_theatre_watching_users_on_expires_at"
|
||||
t.index ["theatre_id", "expires_at"], name: "idx_on_theatre_id_skip_expires_at_4c8de1dd42"
|
||||
t.index ["theatre_id", "expires_at"], name: "index_theatre_watching_users_on_theatre_id_and_expires_at"
|
||||
t.index ["theatre_id"], name: "index_theatre_watching_users_on_theatre_id"
|
||||
t.index ["user_id"], name: "index_theatre_watching_users_on_user_id"
|
||||
|
||||
@@ -22,6 +22,7 @@ import { useValidationErrors } from '@/lib/useValidationErrors'
|
||||
import type { FC, FormEvent, ReactNode } from 'react'
|
||||
|
||||
import type { NiconicoMetadata,
|
||||
NiconicoVideoInfo,
|
||||
NiconicoViewerHandle,
|
||||
Post,
|
||||
Category,
|
||||
@@ -459,6 +460,19 @@ const TheatreDetailPage: FC<Props> = ({ user }: Props) => {
|
||||
await advancePost ()
|
||||
}
|
||||
|
||||
const handleNiconicoLoadComplete = (info: NiconicoVideoInfo) => {
|
||||
const lengthMs = info.lengthInSeconds * 1_000
|
||||
setVideoLength (lengthMs)
|
||||
|
||||
if (lengthMs <= 0)
|
||||
{
|
||||
void handlePlaybackError ()
|
||||
return
|
||||
}
|
||||
|
||||
embedRef.current?.play ()
|
||||
}
|
||||
|
||||
const handleSkipVote = async () => {
|
||||
if (!(id) || !(post))
|
||||
return
|
||||
@@ -797,10 +811,7 @@ const TheatreDetailPage: FC<Props> = ({ user }: Props) => {
|
||||
key={post.id}
|
||||
ref={embedRef}
|
||||
post={post}
|
||||
onLoadComplete={info => {
|
||||
embedRef.current?.play ()
|
||||
setVideoLength (info.lengthInSeconds * 1_000)
|
||||
}}
|
||||
onLoadComplete={handleNiconicoLoadComplete}
|
||||
onMetadataChange={syncPlayback}
|
||||
onError={handlePlaybackError}/>) : (
|
||||
<div className="grid min-h-72 place-items-center text-zinc-400">
|
||||
|
||||
新しい課題から参照
ユーザをブロックする