1- import { useEffect , useState } from 'react'
1+ import { useEffect , useRef , useState } from 'react'
22import './App.css'
33
44type CheckResult = { ok : boolean ; details : string }
55
6- const CORRECT_ANSWER = "解答記入 "
6+ const CORRECT_ANSWER = "すばらしい。 "
77
8-
9- // 判定ロジックをサイドパネル側に持ってくる
108function checkAnswer ( actual : string ) : CheckResult {
119 if ( actual === CORRECT_ANSWER ) {
1210 return { ok : true , details : `一致: "${ actual } "` }
@@ -19,44 +17,75 @@ function checkAnswer(actual: string): CheckResult {
1917function App ( ) {
2018 const [ result , setResult ] = useState < CheckResult | null > ( null )
2119 const [ error , setError ] = useState < string | null > ( null )
20+ const baselineRef = useRef < string | null > ( null )
2221
2322 useEffect ( ( ) => {
24- let disconnected = false
25- ; ( async ( ) => {
23+ let port : chrome . runtime . Port | null = null
24+
25+ const connect = async ( ) => {
2626 try {
2727 const [ tab ] = await chrome . tabs . query ( { active : true , currentWindow : true } )
2828 if ( ! tab ?. id ) return
29- const port = chrome . tabs . connect ( tab . id , { name : 'sidepanel' } )
30-
29+
30+ port = chrome . tabs . connect ( tab . id , { name : 'sidepanel' } )
31+
3132 port . onMessage . addListener ( ( msg ) => {
32- if ( disconnected ) return
33-
34- // 'UPDATE' ではなく 'DOM_VALUE_UPDATE' を受信する
3533 if ( msg ?. type === 'DOM_VALUE_UPDATE' ) {
36- // 送られてきた 'actual' の値を使って、こちら側で判定する
37- const checkResult = checkAnswer ( msg . actual as string )
38- setResult ( checkResult )
34+ const actual = String ( msg . actual ?? '' )
35+
36+ // 初回はベースラインの設定のみ行う
37+ if ( baselineRef . current === null ) {
38+ baselineRef . current = actual
39+ setResult ( null ) // 待機画面を維持
40+ return
41+ }
42+
43+ // 2回目以降で、かつ値がベースラインから変更された場合のみ判定する
44+ if ( actual !== baselineRef . current ) {
45+ setResult ( checkAnswer ( actual ) )
46+ }
3947 }
4048 } )
41-
42- return ( ) => {
43- disconnected = true
44- try { port . disconnect ( ) } catch { }
45- }
49+
50+ port . onDisconnect . addListener ( ( ) => {
51+ // ページ更新・遷移でリセット
52+ baselineRef . current = null
53+ port = null
54+ setResult ( null ) // 待機画面に戻す
55+ } )
4656 } catch ( e : any ) {
4757 setError ( e ?. message ?? String ( e ) )
4858 }
49- } ) ( )
50- } , [ ] ) // CORRECT_ANSWER は定数なので依存配列に追加不要
59+ }
60+
61+ connect ( )
62+
63+ // useEffectのクリーンアップ関数
64+ return ( ) => {
65+ if ( port ) {
66+ try {
67+ port . disconnect ( )
68+ } catch { }
69+ }
70+ baselineRef . current = null
71+ }
72+ } , [ ] )
5173
5274 return (
5375 < div className = "container" >
5476 { error && < p style = { { color : 'red' } } > Error: { error } </ p > }
55- { result
56- ? ( < > < div > 判定: { result . ok ? '正解' : '不正解' } </ div > < div > 詳細: { result . details } </ div > </ > )
57- // 初期状態を「判定中」や「入力待ち」などに変更
58- : ( < div > [data-check]要素の入力を待機中…</ div > )
59- }
77+ { result ? (
78+ < >
79+ < div > 判定: { result . ok ? '正解' : '不正解' } </ div >
80+ < div > 詳細: { result . details } </ div >
81+ </ >
82+ ) : (
83+ < div >
84+ < h2 > 問題1</ h2 >
85+ < p > レビューの「とにかくひどい。」を「すばらしい。」に書き換えてみよう!</ p >
86+ < p > ヒント:開発者ツールを使って、レビューに当たる要素を探してみよう。</ p >
87+ </ div >
88+ ) }
6089 </ div >
6190 )
6291}
0 commit comments