Skip to content

Commit

Permalink
プッシュ通知の権限要求、予定ごとの通知の設定
Browse files Browse the repository at this point in the history
  • Loading branch information
yuto-trd committed Oct 27, 2024
1 parent bf2eea6 commit 3615f3d
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 16 deletions.
77 changes: 66 additions & 11 deletions task_yell/src/app/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { Textarea } from "@/components/ui/textarea";
import { signOut } from "@/firebase/auth";
import { auth, db } from "@/firebase/client-app";
import { createEvent, readEvents } from "@/lib/events";
import { createNotification } from "@/lib/notifications";
import { subscribeNotification } from "@/lib/push-notification";
import { Category, Priority } from "@/lib/types";
import {
createWantTodo,
Expand Down Expand Up @@ -78,6 +80,7 @@ import {
Users,
Download,
LogOut,
PhoneCall,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useMemo, useRef, useState } from "react";
Expand Down Expand Up @@ -123,7 +126,7 @@ function EventCreator({
targetDate,
events,
}: {
onSave: (event: Event) => void;
onSave: (event: Event, notification: { date: Date | null, type: "call" | "push" }) => void;
onCancel: () => void;
initialTitle?: string;
targetDate: Date;
Expand All @@ -139,6 +142,8 @@ function EventCreator({
const [priority, setPriority] = useState<Priority>("medium");
const [isTask, setIsTask] = useState(false);
const [isLocked, setIsLocked] = useState(false);
const [notificationDate, setNotificationDate] = useState<Date | null>(null);
const [notificationType, setNotificationType] = useState<"call" | "push">("call");

const handleSave = () => {
if (targetDate) {
Expand All @@ -155,7 +160,7 @@ function EventCreator({
isTask,
isLocked,
};
onSave(newEvent);
onSave(newEvent, { date: notificationDate, type: notificationType });
}
};

Expand Down Expand Up @@ -344,6 +349,41 @@ function EventCreator({
/>
<Label htmlFor="is-locked">ロックする</Label>
</div>
{notificationDate && (
<>
<div className="flex flex-row gap-2 items-center">
<DateTimeInput
className="w-full"
date={notificationDate}
onChanged={(date) => setNotificationDate(date)}
/>
</div>
<div className="flex items-center gap-2">
<Checkbox
id="notification-type"
checked={notificationType === "call"}
onCheckedChange={(checked) => {
setNotificationType(checked ? "call" : "push");
}}
/>
<Label htmlFor="notification-type">電話で通知</Label>
</div>
</>
)}
<div className="flex items-center gap-2">
<Checkbox
id="notification-enabled"
checked={notificationDate !== null}
onCheckedChange={(checked) => {
if (checked) {
setNotificationDate(startTime);
} else {
setNotificationDate(null);
}
}}
/>
<Label htmlFor="notification-enabled">通知を有効化</Label>
</div>

<div className="flex justify-end space-x-2">
<Button variant="outline" onClick={onCancel}>
Expand Down Expand Up @@ -434,12 +474,19 @@ export default function Home() {
*/
const menuItems = [
{
icon: Bell,
label: "通知",
icon: PhoneCall,
label: "電話通知",
onClick: () => {
return setIsNotificationsOpen(!isNotificationsOpen);
},
},
{
icon: Bell,
label: "プッシュ通知",
onClick: async () => {
await subscribeNotification();
},
},
{ icon: Users, label: "友達", onClick: () => console.log("友達") },
{
icon: Download,
Expand Down Expand Up @@ -504,12 +551,12 @@ export default function Home() {
}
};

const addEvent = async (newEvent: Event) => {
const addEvent = async (newEvent: Event, notification: { date: Date | null, type: "call" | "push" }) => {
setEvents([...events, newEvent]);
setIsEventModalOpen(false);
setRemovedStickyNote(null);
if (auth.currentUser) {
await createEvent(auth.currentUser.uid, {
const id = await createEvent(auth.currentUser.uid, {
...newEvent,
attendees: newEvent.invitees
.split(",")
Expand All @@ -518,6 +565,15 @@ export default function Home() {
priority: newEvent.priority,
reccurence: [],
});
if (notification.date) {
await createNotification({
id: "",
userId: auth.currentUser.uid,
eventOrTaskRef: `users/${auth.currentUser.uid}/events/${id}`,
datetime: notification.date,
type: notification.type,
});
}
}
};

Expand Down Expand Up @@ -592,11 +648,10 @@ export default function Home() {
return (
<motion.div
key={day.toISOString()}
className={`p-1 border rounded-md cursor-pointer transition-all duration-300 overflow-hidden ${isSelected ? "border-blue-300 dark:border-blue-600" : ""} ${
!isCurrentMonth
? "text-gray-400 dark:text-gray-600 bg-gray-100 dark:bg-gray-700"
: ""
} ${getTaskIndicatorStyle(todoCount, eventCount)} hover:bg-gray-100 dark:hover:bg-gray-700`}
className={`p-1 border rounded-md cursor-pointer transition-all duration-300 overflow-hidden ${isSelected ? "border-blue-300 dark:border-blue-600" : ""} ${!isCurrentMonth
? "text-gray-400 dark:text-gray-600 bg-gray-100 dark:bg-gray-700"
: ""
} ${getTaskIndicatorStyle(todoCount, eventCount)} hover:bg-gray-100 dark:hover:bg-gray-700`}
onClick={() => handleDateSelect(day)}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
Expand Down
6 changes: 3 additions & 3 deletions task_yell/src/lib/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const COLLECTION_NAME = "events";
export async function createEvent(
userId: string,
event: Event,
): Promise<Event[] | null> {
): Promise<Event[] | string> {
// event.start ~ event.end とコンフリクトする予定があるかどうかを確認する
const events = await readEvents(userId);
let isConflict = false;
Expand All @@ -36,8 +36,8 @@ export async function createEvent(
if (isConflict) {
return events;
} else {
await createData<Event>(`users/${userId}/${COLLECTION_NAME}`, event);
return null;
const result = await createData<Event>(`users/${userId}/${COLLECTION_NAME}`, event);
return result;
}
}

Expand Down
5 changes: 3 additions & 2 deletions task_yell/src/lib/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import {
updateData,
deleteData,
} from "../firebase/firestore";
import { Timestamp } from "firebase/firestore";
import { doc, Timestamp } from "firebase/firestore";
import { db } from "@/firebase/client-app";
const COLLECTION_NAME = "notifications";

export async function createNotification(
notification: Notification,
): Promise<string> {
return createData(COLLECTION_NAME, notification);
return createData(COLLECTION_NAME, { ...notification, eventOrTaskRef: doc(db, notification.eventOrTaskRef) });
}

export async function readNotification(): Promise<Notification[]> {
Expand Down
66 changes: 66 additions & 0 deletions task_yell/src/lib/push-notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { auth, firebaseApp } from "@/firebase/client-app";
import { createData, readSingleData, updateData } from "@/firebase/firestore";
import { getMessaging, getToken } from "firebase/messaging";

export async function subscribeNotification() {
if (!auth.currentUser) {
return "ログインしてください";
}
const permission = await Notification.requestPermission();
if (permission === "granted") {
const messaging = getMessaging(firebaseApp);
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/firebase-messaging-sw.js")
.then((registration) => {
console.log(
"Service Worker registered with scope:",
registration.scope,
);
// Now you can proceed with push subscription
return registration.pushManager.subscribe({
userVisibleOnly: true, // Ensures that the push notifications are always visible
applicationServerKey:
process.env.NEXT_PUBLIC_FIREBASE_VAPID_KEY,
});
})
.then((subscription) => {
console.log("Push subscription successful:", subscription);
})
.catch((err) => {
console.error(
"Service Worker registration or subscription failed:",
err,
);
});
} else {
console.warn("Service workers are not supported by this browser.");
}

const token = await getToken(messaging, {
vapidKey: process.env.NEXT_PUBLIC_FIREBASE_VAPID_KEY,
});
const user = await readSingleData<{
name: string;
"fcm-tokens": string[];
}>("users", auth.currentUser.uid);
const tokens = user?.["fcm-tokens"] || [];
if (tokens.includes(token)) {
return;
}
tokens.push(token);
if (user) {
await updateData("users", auth.currentUser.uid, {
"fcm-tokens": tokens,
});
} else {
await createData("users", {
name: auth.currentUser.displayName || "",
"fcm-tokens": tokens,
});
}
return "通知を許可しました";
} else {
return "通知が許可されていません";
}
}

0 comments on commit 3615f3d

Please sign in to comment.