Skip to content

Commit

Permalink
custome push notification with utiles.js
Browse files Browse the repository at this point in the history
  • Loading branch information
appsaeed committed Mar 15, 2024
1 parent f8b7f2a commit 3702f03
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 41 deletions.
63 changes: 39 additions & 24 deletions src/MainProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,58 @@
import { ReactNode, useEffect } from 'react';
import settings from './app/settings';
import { urlBase64ToUint8Array } from './app/utiles';
import { useDispatch } from 'react-redux';
import { pushSubscribe } from './app/utiles';
import useNotify from './hooks/useNotify';
import { notifyTokenUpdate } from './redux/notifyStore';

export default function MainProvider({ children }: { children: ReactNode }) {


const notify = useNotify();
const dispatch = useDispatch();

useEffect(() => {

Notification.requestPermission().then(function (permission) {
if (!notify.token) {
console.log('loading ')
pushSubscribe(function (token) {
if (token) dispatch(notifyTokenUpdate(token));
})
}

// Notification.requestPermission().then(function (permission) {

// if (permission === 'granted') {

if (permission === 'granted') {
// const serviceWorker = settings.url + '/worker.js';

const serviceWorker = settings.url + '/worker.js';
// if ("serviceWorker" in navigator) {
// //register service worker
// navigator.serviceWorker.register(serviceWorker).then((register) => {

if ("serviceWorker" in navigator) {
//register service worker
navigator.serviceWorker.register(serviceWorker).then((register) => {
// //register subscription
// register.pushManager.subscribe({
// userVisibleOnly: true,
// applicationServerKey: urlBase64ToUint8Array('BNt0ygqWTSXEd9AJ_Vv2e0jaK73vAjCykOD58lXwinRrnkpwX0lN1cGETwjS10Tvby3d9fDSNZMy6ZdA4xmA30U')
// }).then(subscription => {

//register subscription
register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('BNt0ygqWTSXEd9AJ_Vv2e0jaK73vAjCykOD58lXwinRrnkpwX0lN1cGETwjS10Tvby3d9fDSNZMy6ZdA4xmA30U')
}).then(subscription => {
// //create token to base64 encode
// const token = btoa(JSON.stringify(subscription));

//create token to base64 encode
const token = btoa(JSON.stringify(subscription));
// dispatch(notifyTokenAdd(token))

//save to local storage
localStorage.setItem('notify_token', token)
// //save to local storage
// localStorage.setItem('notify_token', token)

})
// })

}).catch(e => console.error(e))
// }).catch(e => console.error(e))

}
// }

} else {
alert('Your notification is not allowed please check permissions')
}
})
// } else {
// alert('Your notification is not allowed please check permissions')
// }
// })
}, [])


Expand Down
10 changes: 10 additions & 0 deletions src/app/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@

export type Action = {
type: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
payload: any
}
export type TodoType = {
title: string,
id: number,
Expand All @@ -18,4 +24,8 @@ export type User = {
email: string
user_id: string | number
image_url?: string
}

export type NotifyType = {
token: string | null
}
28 changes: 28 additions & 0 deletions src/app/utiles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import clsx, { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
import settings from "./settings";
import { TodoType } from "./types";

export function cn(...inputs: ClassValue[]) {
Expand Down Expand Up @@ -54,4 +55,31 @@ export const grammarlyItem = (count: number, items: [string, string, string | un
default:
return count + ' ' + items[1]
}
}



export async function pushSubscribe(callback: (token: string) => void) {

const permission = await Notification.requestPermission();
if (permission !== 'granted') {
throw ('Your notification is not allowed please check permissions')
}

if (!("serviceWorker" in navigator)) {
throw ('Your notification is not allowed please check permissions');
}

const worker_path = settings.url + '/worker.js';
const register = await navigator.serviceWorker.register(worker_path);

const subscription = await register.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('BNt0ygqWTSXEd9AJ_Vv2e0jaK73vAjCykOD58lXwinRrnkpwX0lN1cGETwjS10Tvby3d9fDSNZMy6ZdA4xmA30U')
})

const token = btoa(JSON.stringify(subscription));

localStorage.setItem('notification_token', token)
return callback(token);
}
7 changes: 7 additions & 0 deletions src/hooks/useNotify.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useSelector } from "react-redux";
import { NotifyType } from "../app/types";
import reducer from "../redux/reducer";

export default function useNotify(): NotifyType {
return useSelector((state: ReturnType<typeof reducer>) => state.notify);
}
35 changes: 22 additions & 13 deletions src/pages/Tokenization.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { useState } from "react";
import { useDispatch } from "react-redux";
import { pushSubscribe } from "../app/utiles";
import useNotify from "../hooks/useNotify";
import { notifyTokenUpdate } from "../redux/notifyStore";

export default function Tokenization() {
const updateTokenization = () => {
const currentToken = localStorage.getItem('notify_token');
localStorage.setItem('prevous_token', currentToken || '')
localStorage.removeItem('notify_token')
location.reload()

const [loading, setLoading] = useState(false)

const notify = useNotify();
const dispatch = useDispatch();

function updateToken() {
setLoading(true)
pushSubscribe(function (token) {
if (token) dispatch(notifyTokenUpdate(token));
}).finally(() => setLoading(false));
}

return (
<div className="w-full mx-10 my-20">
<div className="mx-10 my-20">
<div className="my-10">
<span className=" font-bold mr-5">Current Token:</span>
<span className=" ">{localStorage.getItem('notify_token')}</span>
<div className="font-bold mb-6 text-center">Token</div>
<span className=" break-words">{notify.token}</span>
</div>
<div className="flex justify-center">
<button onClick={updateTokenization} type="button" className="text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2">Clear Token and replace new</button>
</div>
<div className="my-10">
<span className=" font-bold mr-5">Prevous Token:</span>
<span className=" ">{localStorage.getItem('prevous_token')}</span>
<button disabled={loading} onClick={updateToken} type="button" className="text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2">Generate Token</button>
</div>
<div className="flex justify-center my-2">{loading && "Loading..."}</div>
</div>
)
}
10 changes: 7 additions & 3 deletions src/pages/todo/redux/todoMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
import { ref, set } from "firebase/database";
import { MiddlewareAPI } from "redux";
import { NotifyType } from "../../../app/types";
import { database } from "../../../firebase";
import { notifyReducer } from "../../../redux/notifyStore";
import { todoListReducer } from "./todoReducer";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Store = MiddlewareAPI<any, any>;
type Next = (action: unknown) => void;

const notify_token = localStorage.getItem('notify_token');
const finali_token = notify_token || "base64_" + btoa(navigator.userAgent);
const db_ref = ref(database, 'todos/' + finali_token)
const base64_token = "base64_" + btoa(navigator.userAgent);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const saveTodoStorage = (store: Store) => (next: Next) => (action: any) => {

//get prevous state
const todos_state = store.getState()['todos'];
const notify = store.getState()['notify']
const notify_state: NotifyType = [action].reduce(notifyReducer, notify);
const notify_token = notify_state.token || base64_token;

//get next state
const updatedTodos = [action].reduce(todoListReducer, todos_state);

localStorage.setItem('todos', JSON.stringify(updatedTodos))

try {
const db_ref = ref(database, 'todos/' + notify_token)
set(db_ref, updatedTodos)
} catch (error) {
console.log('Error in redux middleware: ', error)
Expand Down
48 changes: 48 additions & 0 deletions src/redux/notifyStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Action, NotifyType } from "../app/types";

export const NOTIFY_TOKEN_ADD = "notify-token-add";
export const NOTIFY_TOKEN_UPDATE = "notify-token-update";


export function notifyTokenAdd(token: string) {
return {
type: NOTIFY_TOKEN_ADD,
payload: token
}
}

export function notifyTokenUpdate(token: string) {
return {
type: NOTIFY_TOKEN_UPDATE,
payload: token
}
}

const initState: NotifyType = {
token: localStorage.getItem('notification_token'),
}

export const notifyReducer = (state = initState, action: Action): NotifyType => {

switch (action.type) {

//add new todo in todo list
case NOTIFY_TOKEN_ADD:

return {
...state,
token: action.payload
}


case NOTIFY_TOKEN_UPDATE:

return {
...state,
token: action.payload
}

default:
return state
}
}
4 changes: 3 additions & 1 deletion src/redux/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@

import { combineReducers } from "redux";
import todoReducer from '../pages/todo/redux/todoReducer';
import { notifyReducer } from './notifyStore';


export default combineReducers({
...todoReducer
...todoReducer,
notify: notifyReducer
});

0 comments on commit 3702f03

Please sign in to comment.