ba-cafe

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 0bf44ab297780698ec331655d9f4b0bbc4d8f246
parent 1e006a284f1dcde4062d1b1b50a3a47893ac8eec
Author: Sunny <122193933+Sunny-JP@users.noreply.github.com>
Date:   Thu, 29 Jan 2026 12:35:06 +0900

Merge pull request #35 from Sunny-JP/v4-sunny-dev

fix api
Diffstat:
Msrc/app/api/tap/route.ts | 33+++++++++++++++++++++++++--------
Msrc/app/page.tsx | 5++++-
Msrc/components/Settings.tsx | 61+++++++++++++++++++++++++++++++++++++++++++++----------------
3 files changed, 74 insertions(+), 25 deletions(-)

diff --git a/src/app/api/tap/route.ts b/src/app/api/tap/route.ts @@ -8,7 +8,7 @@ export const runtime = 'edge'; export async function POST(request: Request) { try { const body = await request.json(); - const { tapTime, isPushEnabled, ticket1Time, ticket2Time } = body; + const { tapTime, isPushEnabled, onesignalId, ticket1Time, ticket2Time } = body; const authHeader = request.headers.get('Authorization'); const supabase = createClient( @@ -22,16 +22,22 @@ export async function POST(request: Request) { const { data: { user }, error: authError } = await supabase.auth.getUser(); if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); - // --- DB更新処理 --- + // --- 1. DB更新用データの構築 --- const upsertData: any = { id: user.id, updated_at: new Date().toISOString() }; - if (isPushEnabled !== undefined) upsertData.is_push_enabled = isPushEnabled; + if (isPushEnabled !== undefined) { + upsertData.is_push_enabled = Boolean(isPushEnabled); + } - if (ticket1Time) upsertData.ticket1_time = new Date(ticket1Time).toISOString(); - if (ticket2Time) upsertData.ticket2_time = new Date(ticket2Time).toISOString(); + if (onesignalId) { + upsertData.onesignal_id = onesignalId; + } + + if (ticket1Time !== undefined) upsertData.ticket1_time = ticket1Time ? new Date(ticket1Time).toISOString() : null; + if (ticket2Time !== undefined) upsertData.ticket2_time = ticket2Time ? new Date(ticket2Time).toISOString() : null; if (tapTime) { const { data: profile } = await supabase.from('profiles').select('tap_history').eq('id', user.id).single(); @@ -44,17 +50,23 @@ export async function POST(request: Request) { upsertData.tap_history = newHistory; } + // DBへの書き込みを実行 await supabase.from('profiles').upsert(upsertData); - // --- 通知予約処理 --- - if (tapTime && shouldScheduleNotification(new Date(tapTime))) { + const { data: currentProfile } = await supabase + .from('profiles') + .select('is_push_enabled') + .eq('id', user.id) + .single(); + + if (tapTime && currentProfile?.is_push_enabled && shouldScheduleNotification(new Date(tapTime))) { const sendAfter = new Date(tapTime); sendAfter.setSeconds(0, 0); sendAfter.setHours(sendAfter.getHours() + 3); const randomMsg = messages[Math.floor(Math.random() * messages.length)]; - await fetch("https://onesignal.com/api/v1/notifications", { + const osResponse = await fetch("https://onesignal.com/api/v1/notifications", { method: "POST", headers: { "Content-Type": "application/json", @@ -69,6 +81,11 @@ export async function POST(request: Request) { send_after: sendAfter.toISOString(), }) }); + + if (!osResponse.ok) { + const errorMsg = await osResponse.text(); + console.error("OneSignal API Error:", errorMsg); + } } return NextResponse.json({ success: true }); diff --git a/src/app/page.tsx b/src/app/page.tsx @@ -111,7 +111,9 @@ export default function Home() { try { const { data: { session } } = await supabase.auth.getSession(); - const isPushEnabled = OneSignal.Notifications.permission; + + const isPushEnabled = OneSignal.User.PushSubscription.optedIn; + const onesignalId = OneSignal.User.PushSubscription.id; await fetch('/api/tap', { method: 'POST', @@ -122,6 +124,7 @@ export default function Home() { body: JSON.stringify({ tapTime: tapISO, isPushEnabled: isPushEnabled, + onesignalId: onesignalId, ticket1Time: t1ISO, ticket2Time: t2ISO }) diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx @@ -40,26 +40,55 @@ const Settings = () => { ]; const handleNotificationClick = async () => { + if (isPushLoading) return; setIsPushLoading(true); + try { - await OneSignal.Notifications.requestPermission(); - const isEnabled = OneSignal.Notifications.permission; - + if (!OneSignal.User) throw new Error("通知システムが未ロードです。"); + + // 1. 通知権限のリクエスト (booleanが返る) + const isAllowed = await OneSignal.Notifications.requestPermission(); + if (!isAllowed) { + alert("通知がブロックされています。ブラウザの設定で許可してください。"); + return; + } + const { data: { session } } = await supabase.auth.getSession(); - if (session) { - await fetch('/api/tap', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${session.access_token}` - }, - body: JSON.stringify({ isPushEnabled: isEnabled }), - }); + if (!session?.user) throw new Error("ログインが必要です。"); + + // 2. ユーザーIDを同期 + await OneSignal.login(session.user.id); + + // 3. 購読状態の切り替え (optedIn を使用) + const isCurrentlyOptedIn = OneSignal.User.PushSubscription.optedIn; + if (isCurrentlyOptedIn) { + await OneSignal.User.PushSubscription.optOut(); + alert("通知をOFFにしました。"); + } else { + await OneSignal.User.PushSubscription.optIn(); + alert("通知をONにしました!"); } - alert(isEnabled ? "通知をオンにしました" : "通知はオフのままです"); - } catch (error) { - console.error(error); - alert("設定に失敗しました"); + + // 4. 最新の状態をDBに同期 + // optOut/In 後の最新の状態を取得して送信 + const finalOptedIn = OneSignal.User.PushSubscription.optedIn; + const currentSubscriptionId = OneSignal.User.PushSubscription.id; + + await fetch('/api/tap', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${session.access_token}` + }, + body: JSON.stringify({ + onesignalId: currentSubscriptionId, + isPushEnabled: finalOptedIn // ここで真偽値を送る + }) + }); + + } catch (e: any) { + console.error("Notification Error:", e); + alert(e.message); } finally { setIsPushLoading(false); }