Skip to content

Commit f214c61

Browse files
Moved subscribe functionality to separate component.
1 parent fca8f85 commit f214c61

File tree

2 files changed

+40
-103
lines changed

2 files changed

+40
-103
lines changed

src/components/jobs/PostsTable.tsx

+3-89
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
'use client'
22

3-
import React, { useEffect, useState } from 'react'
3+
import React from 'react'
44
import Link from 'next/link'
55
import { Post } from '@prisma/client'
66
import 'animate.css'
7+
import SubscribeButton from './SubscribeButton'
78

89
type Props = {
910
posts: Post[]
@@ -13,96 +14,9 @@ type Props = {
1314
}
1415

1516
const PostsTable: React.FC<Props> = ({ posts, totalPosts, postsPerPage, currentPage }) => {
16-
const [isSubscribed, setIsSubscribed] = useState(false)
17-
18-
useEffect(() => {
19-
if ('serviceWorker' in navigator) {
20-
navigator.serviceWorker
21-
.register('/sw.js')
22-
.then((registration) => console.log('Service Worker registered:', registration))
23-
.catch((error) => console.error('Service Worker registration failed:', error))
24-
}
25-
}, [])
26-
27-
useEffect(() => {
28-
// Check subscription status on component mount
29-
const checkSubscriptionStatus = async () => {
30-
if ('serviceWorker' in navigator && 'PushManager' in window) {
31-
const registration = await navigator.serviceWorker.ready
32-
const subscription = await registration.pushManager.getSubscription()
33-
setIsSubscribed(!!subscription) // Set subscription status
34-
}
35-
}
36-
checkSubscriptionStatus()
37-
}, [])
38-
39-
const handleSubscribe = async () => {
40-
if ('serviceWorker' in navigator && 'PushManager' in window) {
41-
try {
42-
const registration = await navigator.serviceWorker.ready
43-
44-
// Fetch the public VAPID key from the server
45-
const response = await fetch('/api/notifications/get-public-key')
46-
if (!response.ok) throw new Error('Failed to fetch public key')
47-
const { publicKey } = await response.json()
48-
49-
// Convert VAPID key to the format required by PushManager
50-
const convertedVapidKey = urlBase64ToUint8Array(publicKey)
51-
52-
// Subscribe the user to push notifications
53-
const subscription = await registration.pushManager.subscribe({
54-
userVisibleOnly: true,
55-
applicationServerKey: convertedVapidKey,
56-
})
57-
58-
// Send the subscription to the server
59-
const subscribeResponse = await fetch('/api/notifications/subscribe', {
60-
method: 'POST',
61-
body: JSON.stringify(subscription),
62-
headers: {
63-
'Content-Type': 'application/json',
64-
},
65-
})
66-
if (!subscribeResponse.ok) throw new Error('Failed to save subscription on server')
67-
68-
alert('Successfully subscribed to notifications!')
69-
setIsSubscribed(true)
70-
} catch (error) {
71-
console.error('Error during subscription:', error)
72-
}
73-
} else {
74-
alert('Push messaging is not supported in your browser.')
75-
}
76-
}
77-
78-
// Helper function to convert the VAPID key
79-
function urlBase64ToUint8Array(base64String: string) {
80-
if (!base64String) {
81-
throw new Error('base64String is undefined or null')
82-
}
83-
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
84-
const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/')
85-
const rawData = atob(base64)
86-
const outputArray = new Uint8Array(rawData.length)
87-
for (let i = 0; i < rawData.length; ++i) {
88-
outputArray[i] = rawData.charCodeAt(i)
89-
}
90-
return outputArray
91-
}
92-
9317
return (
9418
<div className="animate__animated animate__fadeIn">
95-
<button
96-
onClick={handleSubscribe}
97-
disabled={isSubscribed}
98-
className={`mb-4 px-4 py-2 rounded-lg transition duration-300 ease-in-out animate__animated animate__pulse ${
99-
isSubscribed
100-
? 'bg-gray-400 cursor-not-allowed'
101-
: 'bg-blue-600 hover:bg-blue-700 text-white'
102-
}`}
103-
>
104-
{isSubscribed ? 'Already Subscribed' : 'Subscribe to Notifications'}
105-
</button>
19+
<SubscribeButton />
10620
<div className="overflow-x-auto py-4">
10721
<table className="min-w-full bg-white shadow-md rounded-lg overflow-hidden">
10822
<thead className="bg-blue-600 text-white">

src/components/jobs/SubscribeButton.tsx

+37-14
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,52 @@
1-
import React, { useState } from 'react'
1+
'use client'
2+
3+
import React, { useState, useEffect } from 'react'
24
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
35
import { faBell, faSpinner } from '@fortawesome/free-solid-svg-icons'
46

57
const SubscribeButton: React.FC = () => {
68
const [loading, setLoading] = useState(false)
9+
const [isSubscribed, setIsSubscribed] = useState(false)
10+
11+
useEffect(() => {
12+
const checkSubscriptionStatus = async () => {
13+
if ('serviceWorker' in navigator && 'PushManager' in window) {
14+
const registration = await navigator.serviceWorker.ready
15+
const subscription = await registration.pushManager.getSubscription()
16+
setIsSubscribed(!!subscription)
17+
}
18+
}
19+
checkSubscriptionStatus()
20+
}, [])
21+
22+
const urlBase64ToUint8Array = (base64String: string) => {
23+
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
24+
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
25+
const rawData = atob(base64)
26+
return new Uint8Array([...rawData].map((char) => char.charCodeAt(0)))
27+
}
728

829
const subscribe = async () => {
930
setLoading(true)
1031
try {
1132
const registration = await navigator.serviceWorker.ready
33+
const response = await fetch('/api/notifications/get-public-key')
34+
if (!response.ok) throw new Error('Failed to fetch public key')
35+
const { publicKey } = await response.json()
36+
37+
const convertedVapidKey = urlBase64ToUint8Array(publicKey)
1238
const subscription = await registration.pushManager.subscribe({
1339
userVisibleOnly: true,
14-
applicationServerKey: process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY,
40+
applicationServerKey: convertedVapidKey,
1541
})
1642

17-
// Convert PushSubscription to plain object
18-
const subData = {
19-
...JSON.parse(JSON.stringify(subscription)),
20-
type: 'web', // Add type explicitly
21-
}
22-
23-
console.log('Subscribing to notifications:', subData)
24-
2543
await fetch('/api/notifications/subscribe', {
2644
method: 'POST',
2745
headers: { 'Content-Type': 'application/json' },
28-
body: JSON.stringify(subData),
46+
body: JSON.stringify(subscription),
2947
})
30-
alert('Subscribed to notifications')
48+
alert('Successfully subscribed to notifications!')
49+
setIsSubscribed(true)
3150
} catch (error) {
3251
console.error('Subscription error', error)
3352
} finally {
@@ -38,14 +57,18 @@ const SubscribeButton: React.FC = () => {
3857
return (
3958
<button
4059
onClick={subscribe}
41-
disabled={loading}
42-
className="p-2 bg-blue-500 text-white rounded flex items-center gap-2"
60+
disabled={loading || isSubscribed}
61+
className={`p-2 rounded flex items-center gap-2 transition duration-300 ease-in-out ${
62+
isSubscribed ? 'bg-gray-400 cursor-not-allowed' : 'bg-blue-500 text-white hover:bg-blue-700'
63+
}`}
4364
>
4465
{loading ? (
4566
<>
4667
<FontAwesomeIcon icon={faSpinner} spin />
4768
Loading...
4869
</>
70+
) : isSubscribed ? (
71+
<>✅ Subscribed</>
4972
) : (
5073
<>
5174
<FontAwesomeIcon icon={faBell} />

0 commit comments

Comments
 (0)