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:
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);
}