推送到 LINE 改為兩段式確認,避免誤觸

根本原因:
「推送到 LINE」按鈕與「上傳資料」「讀取指定日期」並排,單擊即送出,容易手滑把配對結果直接推進 LINE 群組,且推出去無法收回。

影響:
- 第一次按只會把按鈕切換成橘色「確認推送到 LINE」並提示,第二次按才真的送出
- 6 秒內沒按第二次會自動取消並提示
- 重新產生配對、讀取指定日期、載入範例名單、更改目標日期時一律解除確認狀態,避免殘留後誤觸

修法:
新增 linePushArmed 狀態與逾時 effect,pushToLine 未確認時先進入確認狀態;各動作函式與日期輸入同步重設;App.css 新增 .is-armed 橘色脈動樣式。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 11:33:16 +08:00
co-authored by Claude Fable 5
parent db67c19e85
commit 4633412407
2 changed files with 50 additions and 3 deletions
+17
View File
@@ -260,6 +260,23 @@
outline-offset: 2px;
}
.upload-button.is-armed {
background: linear-gradient(135deg, #c96a1c, #e6892e);
box-shadow: 0 16px 28px rgba(201, 106, 28, 0.28);
animation: armed-pulse 1.2s ease-in-out infinite;
}
@keyframes armed-pulse {
0%,
100% {
box-shadow: 0 16px 28px rgba(201, 106, 28, 0.28);
}
50% {
box-shadow: 0 16px 34px rgba(201, 106, 28, 0.5);
}
}
.upload-button:disabled {
opacity: 0.55;
cursor: not-allowed;
+33 -3
View File
@@ -37,6 +37,7 @@ const STORAGE_KEYS = {
const PLACEHOLDER_NAME = '那個'
const TOTAL_ROUNDS = 3
const LINE_PUSH_CONFIRM_TIMEOUT_MS = 6000
const defaultAreaA = ['建喵', '柏威', '景涵', 'Gary', '昱翔']
const defaultAreaB = ['小念', '玟瑄', 'RuRu', '肉肉', '蓉蓉']
@@ -54,6 +55,20 @@ function App() {
const [actionState, setActionState] = useState<ActionState>('idle')
const [actionMessage, setActionMessage] = useState('')
const [showTestBadge, setShowTestBadge] = useState(false)
const [linePushArmed, setLinePushArmed] = useState(false)
useEffect(() => {
if (!linePushArmed) {
return
}
const timer = window.setTimeout(() => {
setLinePushArmed(false)
setActionState('idle')
setActionMessage('已逾時取消推送,需要時請再按一次「推送到 LINE」。')
}, LINE_PUSH_CONFIRM_TIMEOUT_MS)
return () => window.clearTimeout(timer)
}, [linePushArmed])
useEffect(() => {
window.localStorage.setItem(STORAGE_KEYS.areaA, areaAInput)
@@ -94,6 +109,7 @@ function App() {
}))
setResults(nextResults)
setLinePushArmed(false)
setError('')
setActionState('idle')
setActionMessage('已產生配對,請按「上傳資料」。')
@@ -147,6 +163,7 @@ function App() {
return
}
setLinePushArmed(false)
setActionState('loading')
setActionMessage('讀取指定日期資料中...')
@@ -181,6 +198,15 @@ function App() {
return
}
if (!linePushArmed) {
setLinePushArmed(true)
setActionState('idle')
setActionMessage('再按一次「確認推送到 LINE」才會真的送出,6 秒內沒按會自動取消。')
return
}
setLinePushArmed(false)
try {
const timeKey = convertDateToKey(targetDate)
setActionState('sharing')
@@ -201,6 +227,7 @@ function App() {
setAreaAInput(defaultAreaA.join('\n'))
setAreaBInput(defaultAreaB.join('\n'))
setResults([])
setLinePushArmed(false)
setError('')
setActionState('idle')
setActionMessage('')
@@ -261,7 +288,10 @@ function App() {
<input
type="date"
value={targetDate}
onChange={(event) => setTargetDate(event.target.value)}
onChange={(event) => {
setTargetDate(event.target.value)
setLinePushArmed(false)
}}
/>
</label>
<button
@@ -282,12 +312,12 @@ function App() {
</button>
{results.length > 0 ? (
<button
className="primary-button upload-button"
className={`primary-button upload-button${linePushArmed ? ' is-armed' : ''}`}
type="button"
onClick={() => void pushToLine()}
disabled={actionState === 'saving' || actionState === 'loading'}
>
LINE
{linePushArmed ? '確認推送到 LINE' : '推送到 LINE'}
</button>
) : null}
</div>