push notifications but installable to home screen yet
This commit is contained in:
@ -13,7 +13,7 @@ type SessionTwilioAccount = Pick<
|
||||
TwilioAccount,
|
||||
"accountSid" | "subAccountSid" | "subAccountAuthToken" | "apiKeySid" | "apiKeySecret" | "twimlAppSid"
|
||||
>;
|
||||
type SessionOrganization = Pick<Organization, "id"> & { role: MembershipRole };
|
||||
type SessionOrganization = Pick<Organization, "id"> & { role: MembershipRole; membershipId: string };
|
||||
type SessionPhoneNumber = Pick<PhoneNumber, "id" | "number">;
|
||||
export type SessionUser = Pick<User, "id" | "role" | "email" | "fullName">;
|
||||
export type SessionData = {
|
||||
@ -190,6 +190,7 @@ async function buildSessionData(id: string): Promise<SessionData> {
|
||||
},
|
||||
},
|
||||
role: true,
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -203,6 +204,7 @@ async function buildSessionData(id: string): Promise<SessionData> {
|
||||
const organizations = memberships.map((membership) => ({
|
||||
...membership.organization,
|
||||
role: membership.role,
|
||||
membershipId: membership.id,
|
||||
}));
|
||||
const { twilioAccount, ...organization } = organizations[0];
|
||||
const phoneNumber = await db.phoneNumber.findUnique({
|
||||
|
70
app/utils/pwa.client.ts
Normal file
70
app/utils/pwa.client.ts
Normal file
@ -0,0 +1,70 @@
|
||||
type ResponseObject = {
|
||||
status: "success" | "bad";
|
||||
message: string;
|
||||
};
|
||||
|
||||
// use case: prevent making phone calls / queue messages when offline
|
||||
export async function checkConnectivity(online: () => void, offline: () => void): Promise<ResponseObject> {
|
||||
try {
|
||||
if (navigator.onLine) {
|
||||
online();
|
||||
return {
|
||||
status: "success",
|
||||
message: "Connected to the internet",
|
||||
};
|
||||
} else {
|
||||
offline();
|
||||
return {
|
||||
status: "bad",
|
||||
message: "No internet connection available",
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
console.debug(err);
|
||||
throw new Error("Unable to check network connectivity!");
|
||||
}
|
||||
}
|
||||
|
||||
// use case: display unread messages + missed phone calls count
|
||||
export async function addBadge(numberCount: number): Promise<ResponseObject> {
|
||||
try {
|
||||
//@ts-ignore
|
||||
if (navigator.setAppBadge) {
|
||||
//@ts-ignore
|
||||
await navigator.setAppBadge(numberCount);
|
||||
return {
|
||||
status: "success",
|
||||
message: "Badge successfully added",
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: "bad",
|
||||
message: "Badging API not supported",
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
console.debug(err);
|
||||
throw new Error("Error adding badge!");
|
||||
}
|
||||
}
|
||||
export async function removeBadge(): Promise<ResponseObject> {
|
||||
try {
|
||||
//@ts-ignore
|
||||
if (navigator.clearAppBadge) {
|
||||
//@ts-ignore
|
||||
await navigator.clearAppBadge();
|
||||
return {
|
||||
status: "success",
|
||||
message: "Cleared badges",
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: "bad",
|
||||
message: "Badging API not supported in this browser!",
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
throw new Error("Error removing badge!");
|
||||
}
|
||||
}
|
@ -4,5 +4,5 @@ export const { getSeo, getSeoMeta, getSeoLinks } = initSeo({
|
||||
title: "",
|
||||
titleTemplate: "%s | Shellphone",
|
||||
description: "",
|
||||
defaultTitle: "Shellphone",
|
||||
defaultTitle: "Shellphone: Your Personal Cloud Phone",
|
||||
});
|
||||
|
@ -15,7 +15,7 @@ export default function getTwilioClient({
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
|
||||
return twilio(subAccountSid, subAccountAuthToken ?? serverConfig.twilio.authToken, {
|
||||
return twilio(subAccountSid, serverConfig.twilio.authToken, {
|
||||
accountSid,
|
||||
});
|
||||
}
|
||||
|
57
app/utils/web-push.server.ts
Normal file
57
app/utils/web-push.server.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import webpush, { type PushSubscription, WebPushError } from "web-push";
|
||||
|
||||
import serverConfig from "~/config/config.server";
|
||||
import db from "~/utils/db.server";
|
||||
import logger from "~/utils/logger.server";
|
||||
|
||||
export type NotificationPayload = NotificationOptions & {
|
||||
title: string;
|
||||
body: string;
|
||||
};
|
||||
|
||||
export async function notify(phoneNumberId: string, payload: NotificationPayload) {
|
||||
webpush.setVapidDetails("mailto:mokht@rmi.al", serverConfig.webPush.publicKey, serverConfig.webPush.privateKey);
|
||||
|
||||
const phoneNumber = await db.phoneNumber.findUnique({
|
||||
where: { id: phoneNumberId },
|
||||
select: {
|
||||
organization: {
|
||||
select: {
|
||||
memberships: {
|
||||
select: { notificationSubscription: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!phoneNumber) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
const subscriptions = phoneNumber.organization.memberships.flatMap(
|
||||
(membership) => membership.notificationSubscription,
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
subscriptions.map(async (subscription) => {
|
||||
const webPushSubscription: PushSubscription = {
|
||||
endpoint: subscription.endpoint,
|
||||
keys: {
|
||||
p256dh: subscription.keys_p256dh,
|
||||
auth: subscription.keys_auth,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
await webpush.sendNotification(webPushSubscription, JSON.stringify(payload));
|
||||
} catch (error: any) {
|
||||
logger.error(error);
|
||||
if (error instanceof WebPushError) {
|
||||
// subscription most likely expired or has been revoked
|
||||
await db.notificationSubscription.delete({ where: { id: subscription.id } });
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user