このコミットが含まれているのは:
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 559 KiB |
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 146 KiB |
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 1.2 MiB |
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 188 KiB |
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 201 KiB |
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 196 KiB |
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 179 KiB |
@@ -66,8 +66,8 @@ export const menuOutline = ({ tag, wikiId, user, pathName }: {
|
|||||||
{ name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
{ name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg },
|
||||||
{ name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`, visible: wikiPageFlg }] },
|
{ name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`, visible: wikiPageFlg }] },
|
||||||
{ name: 'おたのしみ', visible: false, subMenu: [
|
{ name: 'おたのしみ', visible: false, subMenu: [
|
||||||
{ name: 'グカネータ', to: '/gekanator' },
|
{ name: '上映会 (β)', to: '/theatres/1' },
|
||||||
{ name: '上映会 (β)', to: '/theatres/1' }] },
|
{ name: 'グカネータ (β)', to: '/gekanator' }] },
|
||||||
{ name: 'ユーザ', to: '/users/settings', visible: false, subMenu: [
|
{ name: 'ユーザ', to: '/users/settings', visible: false, subMenu: [
|
||||||
{ name: '一覧', to: '/users', visible: false },
|
{ name: '一覧', to: '/users', visible: false },
|
||||||
{ name: 'お前', to: `/users/${ user?.id }`, visible: false },
|
{ name: 'お前', to: `/users/${ user?.id }`, visible: false },
|
||||||
|
|||||||
@@ -790,7 +790,7 @@ const indexedQuestionTextForTag = (key: string): string => {
|
|||||||
case 'material':
|
case 'material':
|
||||||
return `素材「${ label }」に関係している?`
|
return `素材「${ label }」に関係している?`
|
||||||
case 'nico':
|
case 'nico':
|
||||||
return `ニコニコに「${ label }」といふタグがついている?`
|
return `ニコニコに「${ label }」というタグがついている?`
|
||||||
default:
|
default:
|
||||||
return `「${ label }」が含まれる?`
|
return `「${ label }」が含まれる?`
|
||||||
}
|
}
|
||||||
@@ -1260,14 +1260,16 @@ const candidatePostsForState = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const allConcreteAnswerOptionsExhaustedForQuestion = ({
|
const hasDiscriminatingHardSplitForQuestion = ({
|
||||||
candidateIds,
|
candidateIds,
|
||||||
question,
|
question,
|
||||||
|
posts,
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex,
|
matchIndex,
|
||||||
}: {
|
}: {
|
||||||
candidateIds: number[]
|
candidateIds: number[]
|
||||||
question: GekanatorQuestion | null
|
question: GekanatorQuestion | null
|
||||||
|
posts: Post[]
|
||||||
materialIndex: GekanatorQuestionMaterialIndex
|
materialIndex: GekanatorQuestionMaterialIndex
|
||||||
matchIndex: GekanatorMatchIndex
|
matchIndex: GekanatorMatchIndex
|
||||||
}): boolean => {
|
}): boolean => {
|
||||||
@@ -1275,17 +1277,16 @@ const allConcreteAnswerOptionsExhaustedForQuestion = ({
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
const dynamicMatchIndex = new Map<string, Set<number>> ()
|
const dynamicMatchIndex = new Map<string, Set<number>> ()
|
||||||
const posts = [...materialIndex.postById.values ()]
|
const yesCount = matchingPostCountInIds ({
|
||||||
|
candidateIds,
|
||||||
|
posts,
|
||||||
|
materialIndex,
|
||||||
|
matchIndex,
|
||||||
|
question,
|
||||||
|
dynamicMatchIndex })
|
||||||
|
const noCount = candidateIds.length - yesCount
|
||||||
|
|
||||||
return (['yes', 'no'] as GekanatorAnswerValue[]).every (answer =>
|
return yesCount > 0 && noCount > 0
|
||||||
postIdsForHardAnswer ({
|
|
||||||
candidateIds,
|
|
||||||
question,
|
|
||||||
answer,
|
|
||||||
posts,
|
|
||||||
materialIndex,
|
|
||||||
matchIndex,
|
|
||||||
dynamicMatchIndex }).length === 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2266,6 +2267,9 @@ const chooseFallbackQuestion = ({
|
|||||||
materialIndex: GekanatorQuestionMaterialIndex
|
materialIndex: GekanatorQuestionMaterialIndex
|
||||||
matchIndex: GekanatorMatchIndex
|
matchIndex: GekanatorMatchIndex
|
||||||
}): GekanatorQuestion | null => {
|
}): GekanatorQuestion | null => {
|
||||||
|
if (posts.length === 0)
|
||||||
|
return null
|
||||||
|
|
||||||
const fallbackPosts = posts
|
const fallbackPosts = posts
|
||||||
.map (post => ({ post, score: scores.get (post.id) ?? 0 }))
|
.map (post => ({ post, score: scores.get (post.id) ?? 0 }))
|
||||||
.sort ((a, b) => b.score - a.score)
|
.sort ((a, b) => b.score - a.score)
|
||||||
@@ -2291,25 +2295,19 @@ const chooseFallbackQuestion = ({
|
|||||||
question,
|
question,
|
||||||
dynamicMatchIndex })
|
dynamicMatchIndex })
|
||||||
const noCount = candidateIds.length - yesCount
|
const noCount = candidateIds.length - yesCount
|
||||||
const knownCount = yesCount + noCount
|
if (yesCount === 0 || noCount === 0)
|
||||||
if (knownCount === 0)
|
|
||||||
return null
|
return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
question,
|
question,
|
||||||
hasSplit: yesCount > 0 && noCount > 0,
|
knownCount: candidateIds.length,
|
||||||
knownCount,
|
|
||||||
balance: Math.abs (yesCount - noCount) }
|
balance: Math.abs (yesCount - noCount) }
|
||||||
})
|
})
|
||||||
.filter ((item): item is {
|
.filter ((item): item is {
|
||||||
question: GekanatorQuestion
|
question: GekanatorQuestion
|
||||||
hasSplit: boolean
|
|
||||||
knownCount: number
|
knownCount: number
|
||||||
balance: number } => item !== null)
|
balance: number } => item !== null)
|
||||||
.sort ((a, b) => {
|
.sort ((a, b) => {
|
||||||
if (a.hasSplit !== b.hasSplit)
|
|
||||||
return a.hasSplit ? -1 : 1
|
|
||||||
|
|
||||||
if (a.balance !== b.balance)
|
if (a.balance !== b.balance)
|
||||||
return a.balance - b.balance
|
return a.balance - b.balance
|
||||||
|
|
||||||
@@ -2421,7 +2419,6 @@ const nextQuestionPlanFor = (
|
|||||||
winningRunStartAnswerCount }
|
winningRunStartAnswerCount }
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextQuestionsSinceLastGuess = answers.length - lastGuessQuestionCount
|
|
||||||
const nextWinningRunTargetId =
|
const nextWinningRunTargetId =
|
||||||
eligiblePosts.length === 1
|
eligiblePosts.length === 1
|
||||||
? eligiblePosts[0]?.id ?? null
|
? eligiblePosts[0]?.id ?? null
|
||||||
@@ -2454,14 +2451,6 @@ const nextQuestionPlanFor = (
|
|||||||
&& winningRunQuestionCount (
|
&& winningRunQuestionCount (
|
||||||
answers,
|
answers,
|
||||||
nextWinningRunStartAnswerCount) >= winningRunQuestionLimit
|
nextWinningRunStartAnswerCount) >= winningRunQuestionLimit
|
||||||
if (answers.length >= hardMaxQuestions)
|
|
||||||
return {
|
|
||||||
question: null,
|
|
||||||
guess: bestPost (eligiblePosts, scores),
|
|
||||||
guessReason: 'hard_max_questions',
|
|
||||||
questionMode: null,
|
|
||||||
winningRunTargetId: nextWinningRunTargetId,
|
|
||||||
winningRunStartAnswerCount: nextWinningRunStartAnswerCount }
|
|
||||||
if (winningRunFinished)
|
if (winningRunFinished)
|
||||||
return {
|
return {
|
||||||
question: null,
|
question: null,
|
||||||
@@ -2504,9 +2493,7 @@ const nextQuestionPlanFor = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const evaluationPosts =
|
const evaluationPosts =
|
||||||
nextQuestionsSinceLastGuess >= minQuestionsBeforeCertainGuess
|
eligiblePosts
|
||||||
? eligiblePosts
|
|
||||||
: availablePosts
|
|
||||||
|
|
||||||
const evaluationQuestions = buildQuestionsForPosts (evaluationPosts)
|
const evaluationQuestions = buildQuestionsForPosts (evaluationPosts)
|
||||||
const normalQuestion = chooseQuestion ({
|
const normalQuestion = chooseQuestion ({
|
||||||
@@ -2523,7 +2510,7 @@ const nextQuestionPlanFor = (
|
|||||||
matchIndex })
|
matchIndex })
|
||||||
|
|
||||||
const fallbackQuestion = normalQuestion ?? chooseFallbackQuestion ({
|
const fallbackQuestion = normalQuestion ?? chooseFallbackQuestion ({
|
||||||
posts: evaluationPosts.length > 0 ? evaluationPosts : availablePosts,
|
posts: evaluationPosts,
|
||||||
allPosts: posts,
|
allPosts: posts,
|
||||||
questions: evaluationQuestions,
|
questions: evaluationQuestions,
|
||||||
answers,
|
answers,
|
||||||
@@ -2545,14 +2532,8 @@ const nextQuestionPlanFor = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
question: null,
|
question: null,
|
||||||
guess:
|
guess: null,
|
||||||
answers.length >= hardMaxQuestions
|
guessReason: null,
|
||||||
? bestPost (guessablePosts, scores)
|
|
||||||
: null,
|
|
||||||
guessReason:
|
|
||||||
answers.length >= hardMaxQuestions
|
|
||||||
? 'hard_max_questions'
|
|
||||||
: null,
|
|
||||||
questionMode: null,
|
questionMode: null,
|
||||||
winningRunTargetId: nextWinningRunTargetId,
|
winningRunTargetId: nextWinningRunTargetId,
|
||||||
winningRunStartAnswerCount: nextWinningRunStartAnswerCount }
|
winningRunStartAnswerCount: nextWinningRunStartAnswerCount }
|
||||||
@@ -2595,12 +2576,17 @@ const mascotStateFor = (
|
|||||||
bestConfidencePercent: number,
|
bestConfidencePercent: number,
|
||||||
winningRunActive: boolean,
|
winningRunActive: boolean,
|
||||||
): MascotState => {
|
): MascotState => {
|
||||||
if (phase === 'learned' && resultWon === true)
|
const resultPhase =
|
||||||
return 'celebrate'
|
phase === 'end'
|
||||||
|
|| phase === 'review'
|
||||||
|
|| phase === 'learned'
|
||||||
|
|
||||||
if (phase === 'learned' && resultWon === false)
|
if (resultPhase && !(resultWon))
|
||||||
return 'failed'
|
return 'failed'
|
||||||
|
|
||||||
|
if (resultPhase && resultWon)
|
||||||
|
return 'celebrate'
|
||||||
|
|
||||||
switch (phase)
|
switch (phase)
|
||||||
{
|
{
|
||||||
case 'question':
|
case 'question':
|
||||||
@@ -2660,21 +2646,20 @@ const backgroundPostsFor = ({
|
|||||||
|
|
||||||
const GekanatorBackdrop: FC<{
|
const GekanatorBackdrop: FC<{
|
||||||
posts: Post[]
|
posts: Post[]
|
||||||
|
mascotAsset: string
|
||||||
phase: Phase
|
phase: Phase
|
||||||
displayedGuess?: Post | null
|
displayedGuess?: Post | null
|
||||||
visualSeed: string
|
visualSeed: string
|
||||||
motionMode: BackgroundMotionMode
|
motionMode: BackgroundMotionMode
|
||||||
winningRunTargetPost?: Post | null
|
winningRunTargetPost?: Post | null
|
||||||
winningRunQuestionCount?: number
|
winningRunQuestionCount?: number }> = ({ posts,
|
||||||
}> = ({
|
mascotAsset,
|
||||||
posts,
|
phase,
|
||||||
phase,
|
displayedGuess = null,
|
||||||
displayedGuess = null,
|
visualSeed,
|
||||||
visualSeed,
|
motionMode,
|
||||||
motionMode,
|
winningRunTargetPost = null,
|
||||||
winningRunTargetPost = null,
|
winningRunQuestionCount = 0 }) => {
|
||||||
winningRunQuestionCount = 0,
|
|
||||||
}) => {
|
|
||||||
const guessFocusOffset = useMemo (() => {
|
const guessFocusOffset = useMemo (() => {
|
||||||
const focusTiles = [
|
const focusTiles = [
|
||||||
{ x: 'calc(max(100vw, 100vh) * 0.5)',
|
{ x: 'calc(max(100vw, 100vh) * 0.5)',
|
||||||
@@ -2996,7 +2981,7 @@ const GekanatorBackdrop: FC<{
|
|||||||
to-pink-50 dark:from-red-950 dark:via-red-975 dark:to-red-900"/>)
|
to-pink-50 dark:from-red-950 dark:via-red-975 dark:to-red-900"/>)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
<div className="fixed [inset:48px_0_0_0] z-0 overflow-hidden pointer-events-none">
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="relative shrink-0"
|
className="relative shrink-0"
|
||||||
@@ -3060,13 +3045,16 @@ const GekanatorBackdrop: FC<{
|
|||||||
layout={displayedBackdropMode !== 'normal'}
|
layout={displayedBackdropMode !== 'normal'}
|
||||||
transition={{ duration: tileFlipDuration, ease: 'easeInOut' }}
|
transition={{ duration: tileFlipDuration, ease: 'easeInOut' }}
|
||||||
style={{ perspective: 1600 }}>
|
style={{ perspective: 1600 }}>
|
||||||
{displayedBackdropMode !== 'normal' || !(isFlippingTiles) ? (
|
{(displayedBackdropMode !== 'normal' || !(isFlippingTiles))
|
||||||
|
? (
|
||||||
<img
|
<img
|
||||||
src={thumbnail}
|
src={['intro', 'end'].includes (phase)
|
||||||
|
? mascotAsset
|
||||||
|
: thumbnail}
|
||||||
alt=""
|
alt=""
|
||||||
className="absolute inset-0 h-full w-full object-cover"
|
className="absolute inset-0 h-full w-full object-cover"
|
||||||
style={{ opacity: renderedSettings.opacity }}/>
|
style={{ opacity: renderedSettings.opacity }}/>)
|
||||||
) : (
|
: (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute inset-0"
|
className="absolute inset-0"
|
||||||
initial={{ rotateY: 0 }}
|
initial={{ rotateY: 0 }}
|
||||||
@@ -3098,7 +3086,7 @@ const GekanatorBackdrop: FC<{
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-yellow-50/76 via-white/58
|
<div className="fixed inset-0 z-0 bg-gradient-to-br from-yellow-50/76 via-white/58
|
||||||
to-pink-100/62 dark:from-red-950/78 dark:via-red-975/60
|
to-pink-100/62 dark:from-red-950/78 dark:via-red-975/60
|
||||||
dark:to-red-900/66"/>
|
dark:to-red-900/66"/>
|
||||||
</div>)
|
</div>)
|
||||||
@@ -3503,12 +3491,8 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
}:${ questionPlan.questionMode ?? '' }:${ winningRunQuestionsAsked }:${
|
}:${ questionPlan.questionMode ?? '' }:${ winningRunQuestionsAsked }:${
|
||||||
rejectedPostIds.size
|
rejectedPostIds.size
|
||||||
}:${ backgroundPosts.slice (0, 8).map (post => post.id).join ('|') }`
|
}:${ backgroundPosts.slice (0, 8).map (post => post.id).join ('|') }`
|
||||||
const mascot = mascotStateFor (
|
const mascot = mascotStateFor (phase, effectiveResultWon, eligiblePosts.length,
|
||||||
phase,
|
bestConfidencePercent, winningRunActive)
|
||||||
effectiveResultWon,
|
|
||||||
eligiblePosts.length,
|
|
||||||
bestConfidencePercent,
|
|
||||||
winningRunActive)
|
|
||||||
const mascotAsset = mascotAssetByState[mascot]
|
const mascotAsset = mascotAssetByState[mascot]
|
||||||
const mascotAlt = mascotAltByState[mascot]
|
const mascotAlt = mascotAltByState[mascot]
|
||||||
const saveMutation = useMutation ({
|
const saveMutation = useMutation ({
|
||||||
@@ -3535,7 +3519,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await queryClient.refetchQueries ({ queryKey: gekanatorKeys.questions () })
|
await queryClient.refetchQueries ({ queryKey: gekanatorKeys.questions () })
|
||||||
setExtraQuestionState ('saved')
|
setExtraQuestionState ('saved')
|
||||||
setPhase ('learned')
|
setPhase ('end')
|
||||||
}})
|
}})
|
||||||
|
|
||||||
const resetExtraQuestionState = () => {
|
const resetExtraQuestionState = () => {
|
||||||
@@ -3697,11 +3681,12 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
matchIndex: acceptedQuestionMatchIndex })
|
matchIndex: acceptedQuestionMatchIndex })
|
||||||
|
|
||||||
return !(fallbackQuestion)
|
return !(fallbackQuestion)
|
||||||
|| allConcreteAnswerOptionsExhaustedForQuestion ({
|
|| !(hasDiscriminatingHardSplitForQuestion ({
|
||||||
candidateIds: recoveredEligiblePosts.map (post => post.id),
|
candidateIds: recoveredEligiblePosts.map (post => post.id),
|
||||||
question: fallbackQuestion,
|
question: fallbackQuestion,
|
||||||
|
posts,
|
||||||
materialIndex,
|
materialIndex,
|
||||||
matchIndex: acceptedQuestionMatchIndex })
|
matchIndex: acceptedQuestionMatchIndex }))
|
||||||
}
|
}
|
||||||
|
|
||||||
while (recoveredEligiblePosts.length === 0 || needsPreQuestionRecovery ())
|
while (recoveredEligiblePosts.length === 0 || needsPreQuestionRecovery ())
|
||||||
@@ -3962,12 +3947,12 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
const saveAndLearn = () => {
|
const saveAndLearn = () => {
|
||||||
if (!(canPersistGame))
|
if (!(canPersistGame))
|
||||||
{
|
{
|
||||||
setPhase ('learned')
|
setPhase ('end')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resetExtraQuestionState ()
|
resetExtraQuestionState ()
|
||||||
saveReviewedResult (() => setPhase ('learned'))
|
saveReviewedResult (() => setPhase ('end'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitQuestionSuggestion = () => {
|
const submitQuestionSuggestion = () => {
|
||||||
@@ -4197,10 +4182,19 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
[String (questionId)]: value })
|
[String (questionId)]: value })
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialogue =
|
const introDialogue =
|
||||||
phase === 'learned' && resultWon
|
<>私は<ruby>洗澡鹿<rt>シーザオグカ</rt></ruby>。質問から投稿を何でも当ててみせるよ。</>
|
||||||
? <>グカカカカwwwww <ruby>洗澡鹿<rt>シーザオグカ</rt></ruby>は何でもお見通し!</>
|
|
||||||
: <>私は<ruby>洗澡鹿<rt>シーザオグカ</rt></ruby>。質問から投稿を何でも当ててみせるよ。</>
|
const winDialogue =
|
||||||
|
<>グカカカカwwwww <ruby>洗澡鹿<rt>シーザオグカ</rt></ruby>は何でもお見通し!</>
|
||||||
|
|
||||||
|
const loseDialogue =
|
||||||
|
<>ぬわーん! <ruby>洗澡鹿<rt>シーザオグカ</rt></ruby>外しちゃったグカー!!!!!</>
|
||||||
|
|
||||||
|
const resultDialogue = effectiveResultWon ? winDialogue : loseDialogue
|
||||||
|
|
||||||
|
const dialogue = phase === 'learned' ? resultDialogue : introDialogue
|
||||||
|
|
||||||
const introLoading = isLoading || acceptedQuestionsLoading
|
const introLoading = isLoading || acceptedQuestionsLoading
|
||||||
const readyToStart =
|
const readyToStart =
|
||||||
!(introLoading)
|
!(introLoading)
|
||||||
@@ -4287,7 +4281,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
acceptedQuestionsLoading])
|
acceptedQuestionsLoading])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainArea className="relative overflow-hidden bg-yellow-50 dark:bg-red-975">
|
<MainArea className="relative isolate overflow-x-hidden bg-yellow-50 dark:bg-red-975">
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{`グカネータ | ${ SITE_TITLE }`}</title>
|
<title>{`グカネータ | ${ SITE_TITLE }`}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
@@ -4295,6 +4289,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
{performanceMode !== 'lite' && (
|
{performanceMode !== 'lite' && (
|
||||||
<GekanatorBackdrop
|
<GekanatorBackdrop
|
||||||
posts={backgroundPosts}
|
posts={backgroundPosts}
|
||||||
|
mascotAsset={mascotAsset}
|
||||||
phase={phase}
|
phase={phase}
|
||||||
displayedGuess={displayedGuess}
|
displayedGuess={displayedGuess}
|
||||||
visualSeed={backgroundVisualSeed}
|
visualSeed={backgroundVisualSeed}
|
||||||
@@ -4367,14 +4362,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
</p>)}
|
</p>)}
|
||||||
{(Boolean (error) || Boolean (acceptedQuestionsError))
|
{(Boolean (error) || Boolean (acceptedQuestionsError))
|
||||||
&& <p>グカネータの質問データを読み込めませんでした.</p>}
|
&& <p>グカネータの質問データを読み込めませんでした.</p>}
|
||||||
{!(canPersistGame) && (
|
|
||||||
<p className="text-sm text-neutral-600 dark:text-neutral-300">
|
|
||||||
未ログインのまま遊べますが、結果保存・質問追加・追加学習はできません。
|
|
||||||
<PrefetchLink to="/users/settings" className="ml-1 underline">
|
|
||||||
設定
|
|
||||||
</PrefetchLink>
|
|
||||||
から引継ぎコードを復元すると記録できます。
|
|
||||||
</p>)}
|
|
||||||
|
|
||||||
{phase === 'intro' && readyToStart && restorePromptVisible && (
|
{phase === 'intro' && readyToStart && restorePromptVisible && (
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
@@ -4509,7 +4496,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
</div>)}
|
</div>)}
|
||||||
{phase === 'guess' && displayedGuess && (
|
{phase === 'guess' && displayedGuess && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-xl font-bold">これを想像していたね?</p>
|
<p className="text-xl font-bold">思い浮かべているのは、これだね?</p>
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<div className="rounded border border-yellow-100 px-3 py-2
|
<div className="rounded border border-yellow-100 px-3 py-2
|
||||||
text-sm dark:border-red-900">
|
text-sm dark:border-red-900">
|
||||||
@@ -4595,8 +4582,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
{phase === 'end' && (
|
{phase === 'end' && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-neutral-500">ゲーム終了</p>
|
<p className="text-xl font-bold">{resultDialogue}</p>
|
||||||
<p className="text-xl font-bold">グカカカカwwwww <ruby>洗澡鹿<rt>シーザオグカ</rt></ruby>は何でもお見通し!</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{reviewGuessedPost && (
|
{reviewGuessedPost && (
|
||||||
@@ -4682,7 +4668,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
|| extraQuestionState === 'loading'
|
|| extraQuestionState === 'loading'
|
||||||
|| extraQuestionAnswersMutation.isPending}
|
|| extraQuestionAnswersMutation.isPending}
|
||||||
onClick={startExtraQuestions}>
|
onClick={startExtraQuestions}>
|
||||||
追加で質問に答へる
|
追加で質問に答える
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>)}
|
</div>)}
|
||||||
@@ -4690,8 +4676,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
{phase === 'review' && (
|
{phase === 'review' && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-neutral-500">保存前確認</p>
|
<p className="text-xl font-bold">結果修正</p>
|
||||||
<p className="text-xl font-bold">今回の結果を確認してね。</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{reviewGuessedPost && (
|
{reviewGuessedPost && (
|
||||||
@@ -4766,7 +4751,8 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
|
|
||||||
{reviewGuessedPostId !== null && reviewCorrectPostId !== null && (
|
{reviewGuessedPostId !== null && reviewCorrectPostId !== null && (
|
||||||
<p className="text-sm text-neutral-600 dark:text-neutral-300">
|
<p className="text-sm text-neutral-600 dark:text-neutral-300">
|
||||||
判定: {reviewGuessedPostId === reviewCorrectPostId ? '当たり' : '違ひ'}
|
判定:
|
||||||
|
{reviewGuessedPostId === reviewCorrectPostId ? '当たり' : 'はずれ'}
|
||||||
</p>)}
|
</p>)}
|
||||||
|
|
||||||
{saveMutation.isError && (
|
{saveMutation.isError && (
|
||||||
@@ -4775,6 +4761,14 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
</p>)}
|
</p>)}
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="rounded border border-neutral-300 px-4 py-2
|
||||||
|
hover:bg-neutral-100 dark:border-neutral-700
|
||||||
|
dark:hover:bg-red-900"
|
||||||
|
onClick={() => setPhase ('end')}>
|
||||||
|
戻る
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded bg-pink-600 px-4 py-2 font-bold text-white
|
className="rounded bg-pink-600 px-4 py-2 font-bold text-white
|
||||||
@@ -4788,14 +4782,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
onClick={saveAndLearn}>
|
onClick={saveAndLearn}>
|
||||||
完了
|
完了
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="rounded border border-neutral-300 px-4 py-2
|
|
||||||
hover:bg-neutral-100 dark:border-neutral-700
|
|
||||||
dark:hover:bg-red-900"
|
|
||||||
onClick={() => setPhase ('end')}>
|
|
||||||
戻る
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>)}
|
</div>)}
|
||||||
|
|
||||||
@@ -4945,7 +4931,7 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
question => !(extraQuestionAnswers[String (question.id)]))
|
question => !(extraQuestionAnswers[String (question.id)]))
|
||||||
}
|
}
|
||||||
onClick={saveExtraQuestions}>
|
onClick={saveExtraQuestions}>
|
||||||
学習する
|
送信
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{!(canPersistGame) && (
|
{!(canPersistGame) && (
|
||||||
@@ -4953,18 +4939,6 @@ const GekanatorPage: FC<{ user: User | null }> = ({ user }) => {
|
|||||||
未ログインのため追加学習は保存されません。
|
未ログインのため追加学習は保存されません。
|
||||||
</p>)}
|
</p>)}
|
||||||
</div>)}
|
</div>)}
|
||||||
|
|
||||||
{phase === 'learned' && (
|
|
||||||
<div className="space-y-3">
|
|
||||||
<p>{extraQuestionState === 'saved' ? '学習しました。' : '覚えたよ.次はもっと見通す.'}</p>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="rounded bg-pink-600 px-4 py-2 font-bold text-white
|
|
||||||
hover:bg-pink-500"
|
|
||||||
onClick={reset}>
|
|
||||||
もう一回
|
|
||||||
</button>
|
|
||||||
</div>)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする