Skip to content

Commit

Permalink
Introduced a new way of pendingNotifs to cater rate-limit of inApp no…
Browse files Browse the repository at this point in the history
…tif from snap
  • Loading branch information
0xNilesh committed Feb 8, 2024
1 parent dae644c commit 47c4db9
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 122 deletions.
2 changes: 1 addition & 1 deletion snap/dist/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/ethereum-push-notification-service/push-protocol-snaps"
},
"source": {
"shasum": "E92WYxVcFeu9gtJzbctpTdjxmBwVajf+4747fGIma6o=",
"shasum": "Qeij5+T2NeMuJ7Iv4l+vciRfSjoLUguxA9LAXeYhFVY=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
3 changes: 2 additions & 1 deletion snap/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export const BASE_URL = 'https://backend.epns.io/apis/v1'; // Modify this as nee

export const defaultLatestSnapState: LatestSnapState = {
version: 1,
addresses: {}
addresses: {},
pendingInAppNotifs: []
}
54 changes: 4 additions & 50 deletions snap/src/methods/cronJobs/notifCronJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
fetchAllAddrNotifs,
getCurrentTimestamp,
getModifiedSnapState,
popupHelper,
notifyInMetamaskApp,
sleep,
updateSnapState,
} from "../../utils";
Expand All @@ -18,72 +18,26 @@ export const notifCronJob = async (): Promise<void> => {
try {
// Fetch notifications for all subscribed addresses
const notifs = await fetchAllAddrNotifs();
console.log(notifs);

// Generate popup messages based on notifications
const msgs = popupHelper(notifs);
console.log(msgs);

// if user is receiving more than 25 notifications, then remind them to turn on snooze
// if (Number(popuptoggle) <= 15 && currentTimeEpoch > Number(persistedData.snoozeDuration)) {

// Display an alert for new notifications
if (msgs.length > 0) {
if (notifs.length > 0) {
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: panel([
heading("You have a new notification!"),
divider(),
...msgs.map((msg) => text(msg)),
...notifs.map((notif) => text(notif.popupMsg)),
]),
},
});
}

// } else if (Number(popuptoggle) == 16 && currentTimeEpoch >= Number(persistedData.snoozeDuration)) {
// await SnapStorageCheck();

// const result = await snap.request({
// method: 'snap_dialog',
// params: {
// type: 'confirmation',
// content: panel([
// heading('Snooze Notifications'),
// divider(),
// text('Too many notifications to keep up with? You can temporarily snooze them to take a break. Approving will enable notification snooze.'),
// ]),
// },
// });

// if (result) {
// const snoozeDuration = await snoozeNotifs();
// setSnoozeDuration(Number(snoozeDuration));
// }
// break;
// }

// Display in-app notifications
if (msgs.length > 0) {
const maxlength = msgs.length > 11 ? 11 : msgs.length;
for (let i = 0; i < maxlength; i++) {
let msg = msgs[i];
msg = String(msg);
msg = msg.slice(0, 47);
await snap.request({
method: "snap_notify",
params: {
type: "inApp",
message: msg,
},
});
await sleep(5000); // Wait for 5 seconds between notifications
}
}
await notifyInMetamaskApp(notifs);

const state = await getModifiedSnapState({ encrypted: false });
console.log(state);
const currentTimeStamp = getCurrentTimestamp();

// Iterate over addresses in state
Expand Down
6 changes: 3 additions & 3 deletions snap/src/services/getFeeds.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BASE_URL } from "../config";
import { fetchGet } from "../utils";

interface Payload {
export interface Payload {
data: {
app: string;
sid: string;
Expand All @@ -27,7 +27,7 @@ interface Payload {
verificationProof: string;
}

interface Feed {
export interface Feed {
payload_id: number;
sender: string;
epoch: string;
Expand All @@ -36,7 +36,7 @@ interface Feed {
etime: string | null;
}

interface IFeeds {
export interface IFeeds {
feeds: Feed[];
itemCount: number;
}
Expand Down
16 changes: 15 additions & 1 deletion snap/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
export * from "./snapApi";
export * from "./snapState";
export * from "./snapState";

/**
* Represents a notification object format.
*/
export interface INotification {
address: string; // Address associated with the notification
timestamp: number; // Timestamp when the notification was received
notification: {
body: string; // Body content of the notification
title: string; // Title of the notification
};
popupMsg: string; // Message for displaying in a popup
inAppNotifMsg: string; // Message for displaying in an in-app notification of metamask
}
18 changes: 13 additions & 5 deletions snap/src/types/snapState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export type UnifiedSnapState = SnapStateV0 | SnapStateV1;

export type LatestSnapState = SnapStateV1;

// snap persisted state (non-encrypted) till v1.1.12
// snap persisted state (non-encrypted) till v1.1.12
export type SnapStateV0 = {
addresses: Array<string>;
popuptoggle: number;
Expand All @@ -13,8 +13,9 @@ export type SnapStateV0 = {

// snap persisted state (non-encrypted) from v1.1.13
export type SnapStateV1 = {
version: 1;
addresses: { [address: string]: AddressMetadata };
version: 1; // Represents version of state
addresses: { [address: string]: AddressMetadata }; // Map of addresses to their metadata
pendingInAppNotifs: NotificationMetaData[]; // Array of pending in-app notifications (notifs that are not added in metamask inApp notifs tab)
};

export type AddressMetadata = {
Expand All @@ -29,6 +30,13 @@ export type AddressMetadata = {
// Add any other metadata fields you may need in the future
};

export type NotificationMetaData = {
address: string; // Unique identifier for the notification
message: string; // Message content of the notification
timestamp: number; // Timestamp when the notification was created
// Add more properties as needed
};

export interface ISnapStateParam {
encrypted: boolean;
}
Expand All @@ -37,6 +45,6 @@ export interface IUpdateSnapState extends ISnapStateParam {
newState: LatestSnapState;
}

export type IGetSnapState = ISnapStateParam
export type IGetSnapState = ISnapStateParam;

export type IGetModifiedSnapState = ISnapStateParam
export type IGetModifiedSnapState = ISnapStateParam;
2 changes: 1 addition & 1 deletion snap/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export * from "./notifs";
export * from "./snapStateUtils";
// export * from "./snapstoragecheck";
// export * from "./snooze";
export * from "./toggle";
// export * from "./toggle";
export * from "./helperFn";
export * from "./api";
export * from "./time";
152 changes: 114 additions & 38 deletions snap/src/utils/notifs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { getFeeds } from "../services";
import { Feed, getFeeds } from "../services";
import { fetchAddress } from "./address";
import { ethers } from "ethers";
import { getModifiedSnapState } from "./snapStateUtils";
import { getModifiedSnapState, updateSnapState } from "./snapStateUtils";
import { convertEpochToMilliseconds } from "./time";
import { INotification } from "../types";

/**
* Retrieves notifications for a specific address.
Expand Down Expand Up @@ -36,46 +37,26 @@ export const getNotifications = async (address: string) => {
*/
export const filterNotifications = async (
address: string
): Promise<string[]> => {
): Promise<INotification[]> => {
try {
const state = await getModifiedSnapState({ encrypted: false });
const fetchedNotifications = await getNotifications(address);
console.log(fetchedNotifications);
console.log(state);
console.log(address);
console.log(state.addresses[address]);
let notiffeeds: string[] = [];
const processedLastEpoch = state.addresses[address].lastFeedsProcessedTimestamp;
console.log("processedLastEpoch: ", processedLastEpoch);

if (fetchedNotifications.length > 0) {
for (let i = 0; i < fetchedNotifications.length; i++) {
const feedEpoch = convertEpochToMilliseconds(fetchedNotifications[i].payload.data.epoch);
console.log("feedEpoch: ", feedEpoch);
console.log("i: ", i);
console.log("");
let emoji;
const aimg = fetchedNotifications[i].payload.data.aimg;

if (feedEpoch > processedLastEpoch) {
if (aimg) {
emoji = `📸`;
} else {
emoji = `🔔`;
}

const msg = emoji +
fetchedNotifications[i].payload.data.app +
" : " +
convertText(fetchedNotifications[i].payload.data.amsg);
console.log(msg);

notiffeeds.push(msg);
}
let notiffeeds: Feed[] = [];
const processedLastEpoch =
state.addresses[address].lastFeedsProcessedTimestamp;

for (let i = 0; i < fetchedNotifications.length; i++) {
const feedEpoch = convertEpochToMilliseconds(
fetchedNotifications[i].payload.data.epoch
);

if (feedEpoch > processedLastEpoch) {
notiffeeds.push(fetchedNotifications[i]);
}
}
notiffeeds = notiffeeds.reverse();
return notiffeeds;
const formattedFeeds = getFormattedNotifList(notiffeeds, address);
return formattedFeeds;
} catch (error) {
console.error(`Error in filterNotifications for ${address}:`, error);
throw error;
Expand All @@ -86,10 +67,10 @@ export const filterNotifications = async (
* Fetches notifications for all stored addresses.
* @returns An array of notifications for all stored addresses.
*/
export const fetchAllAddrNotifs = async (): Promise<string[]> => {
export const fetchAllAddrNotifs = async (): Promise<INotification[]> => {
try {
const addresses = await fetchAddress();
let notifs: string[] = [];
let notifs: INotification[] = [];

if (addresses.length === 0) {
return notifs;
Expand All @@ -106,6 +87,37 @@ export const fetchAllAddrNotifs = async (): Promise<string[]> => {
}
};

/**
* Formats the notifs from Feed format into INotification format to be used in snap
* @param address - The Ethereum address.
* @returns An array of formatted notifs.
*/
export const getFormattedNotifList = (
notifList: Feed[],
address: string
): INotification[] => {
const formattedNotifList = notifList.map((notif) => {
const emoji = notif.payload.data.aimg ? `📸` : `🔔`;
const msg =
emoji +
notif.payload.data.app +
": " +
convertText(notif.payload.data.amsg);

return {
address: address,
timestamp: convertEpochToMilliseconds(notif.payload.data.epoch),
notification: {
body: notif.payload.notification.body,
title: notif.payload.notification.title,
},
popupMsg: msg,
inAppNotifMsg: msg.slice(0, 47),
};
});
return formattedNotifList;
};

/**
* Converts text by replacing tags and timestamps.
* @param text The text to be converted.
Expand Down Expand Up @@ -138,3 +150,67 @@ const convertText = (text: string): string => {
throw error;
}
};

/**
* Adds in App notifications in Metamask.
* @param notifs Array of notifications to be in proper format.
*/
export const notifyInMetamaskApp = async (notifs: INotification[]) => {
try {
const state = await getModifiedSnapState({ encrypted: false });
const maxToAdd = 5;
const pendingNotifsCount = state.pendingInAppNotifs.length;

// Determine how many notifications to add from pendingInAppNotifs
const pendingNotifsToAdd = Math.min(pendingNotifsCount, maxToAdd);

// Add pending notifications from pendingInAppNotifs
for (let i = 0; i < pendingNotifsToAdd; i++) {
const msg = state.pendingInAppNotifs.shift(); // Remove the first pending notification
await snap.request({
method: "snap_notify",
params: {
type: "inApp",
message: msg.message,
},
});
}

// Calculate the remaining number of notifications to add
const remainingToAdd = maxToAdd - pendingNotifsToAdd;

// Add notifications from notifs array
for (let i = 0; i < remainingToAdd && i < notifs.length; i++) {
const msg = notifs[i].inAppNotifMsg;
await snap.request({
method: "snap_notify",
params: {
type: "inApp",
message: msg,
},
});
}

// Add remaining notifications to pendingInAppNotifs if any
if (notifs.length > remainingToAdd) {
const remainingNotifs = notifs.slice(remainingToAdd);
state.pendingInAppNotifs.push(
...remainingNotifs.map((notif) => {
return {
address: notif.address,
message: notif.inAppNotifMsg,
timestamp: notif.timestamp,
};
})
);
}

await updateSnapState({
newState: state,
encrypted: false,
});
} catch (error) {
console.error("Error in notifyInMetamaskApp:", error);
throw error;
}
};
Loading

0 comments on commit 47c4db9

Please sign in to comment.