From 66e84d56b6619af960afb0ae7beda555cf964026 Mon Sep 17 00:00:00 2001 From: ikprk Date: Tue, 6 Aug 2024 07:58:47 +0200 Subject: [PATCH 1/3] Add new revenue share ended reminder notification --- schema/auth.graphql | 1 + schema/notifications.graphql | 18 ++++++++++++++++++ src/utils/notification/notificationsData.ts | 10 ++++++++++ 3 files changed, 29 insertions(+) diff --git a/schema/auth.graphql b/schema/auth.graphql index b87d6c99a..1779a96c9 100644 --- a/schema/auth.graphql +++ b/schema/auth.graphql @@ -184,6 +184,7 @@ type AccountNotificationPreferences { crtRevenueShareStarted: NotificationPreference! crtRevenueSharePlanned: NotificationPreference! crtRevenueShareEnded: NotificationPreference! + crtRevenueShareEndedReminder: NotificationPreference! } type NotificationPreference { diff --git a/schema/notifications.graphql b/schema/notifications.graphql index 411407e28..4c173afc7 100644 --- a/schema/notifications.graphql +++ b/schema/notifications.graphql @@ -124,6 +124,7 @@ union NotificationType = | CreatorTokenRevenueShareStarted | CreatorTokenRevenueSharePlanned | CreatorTokenRevenueShareEnded + | CreatorTokenRevenueShareEndedReminder type ChannelSuspended @variant { phantom: Int @@ -587,3 +588,20 @@ type CreatorTokenRevenueShareEnded @variant { "id of token" tokenId: String! } + +type CreatorTokenRevenueShareEndedReminder @variant { + "channel title for notification text" + channelTitle: String! + + "channel title for notification avatar" + channelId: String! + + "symbol of the token" + tokenSymbol: String! + + "id of created revenue share to verify its' viability in future" + revenueShareId: String! + + "id of token" + tokenId: String! +} diff --git a/src/utils/notification/notificationsData.ts b/src/utils/notification/notificationsData.ts index bd209be7a..0417efd62 100644 --- a/src/utils/notification/notificationsData.ts +++ b/src/utils/notification/notificationsData.ts @@ -404,5 +404,15 @@ export const getNotificationData = async ( subject: `🔓 ${channelTitle} ended revenue share for $${tokenSymbol} token. Unlock your locked tokens!`, } } + case 'CreatorTokenRevenueShareEndedReminder': { + const { tokenSymbol, channelId } = notificationType + return { + icon: await getNotificationIcon(em, 'payout'), + link: await getNotificationLink(em, 'portfolio'), + avatar: await getNotificationAvatar(em, 'channelId', channelId), + text: `🔓 Your $${tokenSymbol} revenue share is waiting to be closed. Unlock locked tokens and open your market!`, + subject: `🔓 Your $${tokenSymbol} revenue share is waiting to be closed. Unlock locked tokens and open your market!`, + } + } } } From 428d94741b65a5100302d7d345922de25dd47fe8 Mon Sep 17 00:00:00 2001 From: ikprk Date: Tue, 6 Aug 2024 08:00:33 +0200 Subject: [PATCH 2/3] Schedule reminder on revenue share start and purge on manual close --- src/mappings/token/index.ts | 27 +++++++++++++++++++ src/utils/notification/helpers.ts | 45 ++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/mappings/token/index.ts b/src/mappings/token/index.ts index dedf543f1..33cc708eb 100644 --- a/src/mappings/token/index.ts +++ b/src/mappings/token/index.ts @@ -20,6 +20,7 @@ import { CreatorTokenMarketStarted, CreatorTokenMarketStartedEventData, CreatorTokenRevenueShareEnded, + CreatorTokenRevenueShareEndedReminder, CreatorTokenRevenueSharePlanned, CreatorTokenRevenueShareStarted, CreatorTokenRevenueSplitIssuedEventData, @@ -41,10 +42,12 @@ import { VestedSale, VestingSchedule, } from '../../model' +import { BLOCKS_PER_DAY } from '../../server-extension/resolvers/CreatorToken' import { getCurrentBlockHeight } from '../../utils/blockHeight' import { EventHandlerContext } from '../../utils/events' import { criticalError } from '../../utils/misc' import { addNotification } from '../../utils/notification' +import { removeFutureNotification, removeNotification } from '../../utils/notification/helpers' import { getChannelOwnerAccount, notifyChannelFollowers, parseChannelTitle } from '../content/utils' import { deserializeMetadata, genericEventFields } from '../utils' import { @@ -695,6 +698,26 @@ export async function processRevenueSplitIssuedEvent({ event, endsAt // schedule for end block ) + + // This event should have dispatch block of ending block plus about 3 days + // If user closes the share before its execution, this notification will be removing during share closing mapping + const revenueSharedEndedReminderNotification = new CreatorTokenRevenueShareEndedReminder({ + revenueShareId: revenueShare.id, + channelTitle: parseChannelTitle(channel), + channelId: channel.id, + tokenSymbol: parseCreatorTokenSymbol(token), + tokenId: tokenId.toString(), + }) + + const channelOwnerAccount = await getChannelOwnerAccount(overlay, channel) + await addNotification( + overlay, + channelOwnerAccount, + new ChannelRecipient({ channel: channel.id }), + revenueSharedEndedReminderNotification, + undefined, + endsAt + BLOCKS_PER_DAY * 3 // schedule for end block + ) } export async function processMemberJoinedWhitelistEvent({ @@ -799,6 +822,10 @@ export async function processRevenueSplitFinalizedEvent({ .getByIdOrFail(token.currentRevenueShareId!) revenueShare.finalized = true token.currentRevenueShareId = null + + await removeFutureNotification(overlay.getEm(), 'CreatorTokenRevenueShareEndedReminder', 1, { + tokenId: token.id, + }) } export async function processUserParticipatedInSplitEvent({ diff --git a/src/utils/notification/helpers.ts b/src/utils/notification/helpers.ts index 738e901de..8a699012d 100644 --- a/src/utils/notification/helpers.ts +++ b/src/utils/notification/helpers.ts @@ -1,5 +1,5 @@ import { Flat } from 'lodash' -import { EntityManager } from 'typeorm' +import { EntityManager, FindOptionsWhere } from 'typeorm' import { Account, AccountNotificationPreferences, @@ -72,6 +72,7 @@ export function defaultNotificationPreferences(): AccountNotificationPreferences crtRevenueShareStarted: notificationPrefAllTrue(), crtRevenueSharePlanned: notificationPrefAllTrue(), crtRevenueShareEnded: notificationPrefAllTrue(), + crtRevenueShareEndedReminder: notificationPrefAllTrue(), }) } @@ -144,6 +145,9 @@ export function preferencesForNotification( return preferences.crtRevenueSharePlanned case 'CreatorTokenRevenueShareEnded': return preferences.crtRevenueShareEnded + case 'CreatorTokenRevenueShareEndedReminder': + return preferences.crtRevenueShareEnded + default: // all the remaining notifications (v2 scope) are not enabled by default return new NotificationPreference({ inAppEnabled: false, emailEnabled: false }) } @@ -299,6 +303,45 @@ export const addNotification = async ( } } +export async function removeFutureNotification( + em: EntityManager, + notificationType: Notification['notificationType']['isTypeOf'], + currentBlockHeight: number, + filters: Record +) { + const result: { id: string }[] = await em.query( + ` + SELECT + id + FROM + public.notification + WHERE + notification_type ->> 'isTypeOf'=$1 + AND + dispatch_block > $2 + ${Object.keys(filters).map((key, idx) => { + return ` + AND + notification_type ->> '${key}'=$${idx + 3} + ` + })} + `, + [notificationType, currentBlockHeight, ...Object.values(filters)] + ) + const notificationToRemove = result[0] + + if (!notificationToRemove) { + return + } + + await em.getRepository(Notification).delete({ + id: notificationToRemove.id, + }) + await em.getRepository(NotificationEmailDelivery).delete({ + notificationId: notificationToRemove.id, + }) +} + async function saveNextNotificationId( em: EntityManager, nextNotificationId: number, From edb3d4ae569cdd52c96c6d0bee761f79e164d7e9 Mon Sep 17 00:00:00 2001 From: ikprk Date: Tue, 6 Aug 2024 08:10:26 +0200 Subject: [PATCH 3/3] Fix build --- src/mappings/token/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mappings/token/index.ts b/src/mappings/token/index.ts index 33cc708eb..e4f7071f4 100644 --- a/src/mappings/token/index.ts +++ b/src/mappings/token/index.ts @@ -47,7 +47,7 @@ import { getCurrentBlockHeight } from '../../utils/blockHeight' import { EventHandlerContext } from '../../utils/events' import { criticalError } from '../../utils/misc' import { addNotification } from '../../utils/notification' -import { removeFutureNotification, removeNotification } from '../../utils/notification/helpers' +import { removeFutureNotification } from '../../utils/notification/helpers' import { getChannelOwnerAccount, notifyChannelFollowers, parseChannelTitle } from '../content/utils' import { deserializeMetadata, genericEventFields } from '../utils' import {