From a08a4f6e84879373f22a30812f2901980a7fd9c0 Mon Sep 17 00:00:00 2001 From: ItzNotABug Date: Thu, 19 Sep 2024 20:39:34 +0530 Subject: [PATCH 01/28] add: notification management. --- src/lib/helpers/notifications.ts | 93 ++++++++++++++++++++++++++++++++ src/lib/stores/user.ts | 8 ++- 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/lib/helpers/notifications.ts diff --git a/src/lib/helpers/notifications.ts b/src/lib/helpers/notifications.ts new file mode 100644 index 000000000..bc216d9c8 --- /dev/null +++ b/src/lib/helpers/notifications.ts @@ -0,0 +1,93 @@ +import { sdk } from '$lib/stores/sdk'; +import { get } from 'svelte/store'; +import { user } from '$lib/stores/user'; + +export type NotificationPrefItem = { + expiry: number; + hideCount: number; + state: 'hidden' | 'shown' | undefined; +}; + +export type NotificationCoolOffOptions = { + coolOffPeriod?: number; + exponentialBackoff?: boolean; + exponentialBackoffFactor?: number; +}; + +const userPreferences = () => get(user).prefs; + +const notificationPrefs = (): Record => { + const prefs = userPreferences(); + return prefs.notificationPrefs ? prefs.notificationPrefs : {}; +}; + +function updateNotificationPrefs(parsedPrefs: Record) { + const currentPrefs = userPreferences(); + + const newPrefs = { + ...currentPrefs, + notificationPrefs: parsedPrefs + }; + + sdk.forConsole.account.updatePrefs(newPrefs); +} + +/** + * Hides the notification banner by marking it as 'hidden' and setting an expiry time. + * Supports normal cool-off periods or exponential backoff based on the options passed. + * + * @param {string} id - The ID of the notification. + * @param {NotificationCoolOffOptions} [options] - Configuration for cool-off behavior. + * @param {number} [options.coolOffPeriod=24] - Cool-off period in hours, defaults to 24 hours. + * @param {boolean} [options.exponentialBackoff=false] - If true, the cool-off period doubles with each hide. Consider using a smaller `coolOffPeriod` when this option is enabled. + * @param {number} [options.exponentialBackoffFactor=2] - The factor by which the cool-off period is multiplied with each hide. Default is 2. + */ +export function hideNotificationBanner(id: string, options: NotificationCoolOffOptions = {}) { + const { + coolOffPeriod = 24, + exponentialBackoff = false, + exponentialBackoffFactor = 2 + } = options; + + const parsedBannerPrefs = notificationPrefs(); + + let expiryTime = Date.now() + coolOffPeriod * 3600000; + + let hideCount = parsedBannerPrefs[id]?.hideCount || 0; + + if (exponentialBackoff) { + hideCount += 1; + expiryTime = + Date.now() + coolOffPeriod * 3600000 * exponentialBackoffFactor ** (hideCount - 1); + } + + parsedBannerPrefs[id] = { + hideCount, + state: 'hidden', + expiry: expiryTime + }; + + updateNotificationPrefs(parsedBannerPrefs); +} + +/** + * Checks if the notification banner should be shown based on the expiry time. + * + * @param {string} id - The ID of the notification. + * @returns {boolean} - Returns true if the banner should be shown, false otherwise. + */ +export function shouldShowNotificationBanner(id: string): boolean { + const parsedBannerPrefs = notificationPrefs(); + + const notificationPref = parsedBannerPrefs[id]; + + if (!notificationPref) return true; + + if (Date.now() >= notificationPref.expiry) { + notificationPref.state = 'shown'; + updateNotificationPrefs(parsedBannerPrefs); + return true; + } + + return false; +} diff --git a/src/lib/stores/user.ts b/src/lib/stores/user.ts index 6dbb23e99..21f0eb636 100644 --- a/src/lib/stores/user.ts +++ b/src/lib/stores/user.ts @@ -2,8 +2,14 @@ import { page } from '$app/stores'; import { derived } from 'svelte/store'; import type { Models } from '@appwrite.io/console'; import { browser } from '$app/environment'; +import type { NotificationPrefItem } from '$lib/helpers/notifications'; -export type Account = Models.User<{ organization?: string } & Record>; +export type Account = Models.User< + { + organization?: string; + notificationPrefs: Record; + } & Record +>; export const user = derived(page, ($page) => { if (browser) sessionStorage.setItem('account', JSON.stringify($page.data.account)); From b9b1d9d3e687d82c5f01a3b72c8c3bce2c38d40f Mon Sep 17 00:00:00 2001 From: ItzNotABug Date: Fri, 20 Sep 2024 13:12:41 +0530 Subject: [PATCH 02/28] improve: notification management. --- src/lib/helpers/notifications.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/lib/helpers/notifications.ts b/src/lib/helpers/notifications.ts index bc216d9c8..306a80995 100644 --- a/src/lib/helpers/notifications.ts +++ b/src/lib/helpers/notifications.ts @@ -42,7 +42,7 @@ function updateNotificationPrefs(parsedPrefs: Record Date: Fri, 20 Sep 2024 13:15:08 +0530 Subject: [PATCH 03/28] add: bottom modal for promotions or alerts. --- src/lib/components/bottomModalAlert.svelte | 218 +++++++++++++++++++++ src/lib/components/index.ts | 1 + src/lib/stores/bottom-alerts.ts | 44 +++++ 3 files changed, 263 insertions(+) create mode 100644 src/lib/components/bottomModalAlert.svelte create mode 100644 src/lib/stores/bottom-alerts.ts diff --git a/src/lib/components/bottomModalAlert.svelte b/src/lib/components/bottomModalAlert.svelte new file mode 100644 index 000000000..6de328e2b --- /dev/null +++ b/src/lib/components/bottomModalAlert.svelte @@ -0,0 +1,218 @@ + + +{#if filteredModalAlerts.length > 0} +
+
+ {#if currentModalAlert} + {#key currentModalAlert.id} +
+
+ + + {currentModalAlert.title} +
+ + {#if filteredModalAlerts.length > 1} + + Feature {currentIndex + 1} of {filteredModalAlerts.length} + + {/if} + +
+

{currentModalAlert.title}

+ + + {#if currentModalAlert.isHtml} + {@html currentModalAlert.message} + {:else} + {currentModalAlert.message} + {/if} + +
+ +
+
+ + + {#if currentModalAlert.learnMore && currentModalAlert.learnMore.link} + + {/if} +
+ +
+
+
+
+ {/key} + {/if} +
+
+{/if} + + diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index 4d3f8ccf6..13b166c36 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -75,3 +75,4 @@ export { default as ModalSideCol } from './modalSideCol.svelte'; export { default as EmptyCardImageCloud } from './emptyCardImageCloud.svelte'; export { default as ImagePreview } from './imagePreview.svelte'; export { default as MfaChallengeFormList } from './mfaChallengeFormList.svelte'; +export { default as BottomModalAlert } from './bottomModalAlert.svelte'; diff --git a/src/lib/stores/bottom-alerts.ts b/src/lib/stores/bottom-alerts.ts new file mode 100644 index 000000000..a55950eca --- /dev/null +++ b/src/lib/stores/bottom-alerts.ts @@ -0,0 +1,44 @@ +import { writable } from 'svelte/store'; +import type { NotificationCoolOffOptions } from '$lib/helpers/notifications'; + +export type BottomModalAlertItem = { + id: string; + src: string; + title: string; + message: string; + cta: { + text: string; + link: string; + }; + learnMore?: { + text?: string; + link?: string; + }; + + closed?: () => void; + + show?: boolean; + isHtml?: boolean; + importance?: number; + + notificationHideOptions?: NotificationCoolOffOptions; +}; + +export const bottomModalAlerts = writable([]); + +export const dismissBottomModalAlert = (id: string) => { + bottomModalAlerts.update((all) => all.filter((t) => t.id !== id)); +}; + +export const showBottomModalAlert = (notification: BottomModalAlertItem) => { + const defaults: Partial = { + show: true, + importance: 5, + isHtml: false, // TODO: implement this. + ...notification + }; + + bottomModalAlerts.update((all) => { + return [...all, defaults as BottomModalAlertItem]; + }); +}; From 870fb05838743af3097180b5ffd15beb0802bff2 Mon Sep 17 00:00:00 2001 From: ItzNotABug Date: Fri, 20 Sep 2024 13:15:58 +0530 Subject: [PATCH 04/28] remove: todo comment. --- src/lib/stores/bottom-alerts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/stores/bottom-alerts.ts b/src/lib/stores/bottom-alerts.ts index a55950eca..e3e077915 100644 --- a/src/lib/stores/bottom-alerts.ts +++ b/src/lib/stores/bottom-alerts.ts @@ -34,7 +34,7 @@ export const showBottomModalAlert = (notification: BottomModalAlertItem) => { const defaults: Partial = { show: true, importance: 5, - isHtml: false, // TODO: implement this. + isHtml: false, ...notification }; From 9fcd1fd270d7b586c38ee8d4c34d8e564cf37387 Mon Sep 17 00:00:00 2001 From: ItzNotABug Date: Fri, 20 Sep 2024 13:16:39 +0530 Subject: [PATCH 05/28] add: bottom alert modal to project level layout. --- src/routes/(console)/project-[project]/+layout.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/project-[project]/+layout.svelte b/src/routes/(console)/project-[project]/+layout.svelte index a168e58f3..a7fb16954 100644 --- a/src/routes/(console)/project-[project]/+layout.svelte +++ b/src/routes/(console)/project-[project]/+layout.svelte @@ -1,5 +1,5 @@ -{#if filteredModalAlerts.length > 0} -
-
- {#if currentModalAlert} +{#if filteredModalAlerts.length > 0 && currentModalAlert} +
+
+
{#key currentModalAlert.id} -
-
- +
+ {currentModalAlert.title} - {currentModalAlert.title} +
+
{#if filteredModalAlerts.length > 1} - - Feature {currentIndex + 1} of {filteredModalAlerts.length} - +
+ + Feature {currentIndex + 1} of {filteredModalAlerts.length} + + +
+
+
{/if}
@@ -72,134 +105,85 @@
-
-
+
+ + + {#if currentModalAlert.learnMore && currentModalAlert.learnMore.link} - - {#if currentModalAlert.learnMore && currentModalAlert.learnMore.link} - - {/if} -
- -
-
+ {/if}
{/key} - {/if} -
+
+
{/if} From a8a474d05a0d1ae919d329c0e7a6225b8b11a811 Mon Sep 17 00:00:00 2001 From: ItzNotABug Date: Fri, 20 Sep 2024 20:03:13 +0530 Subject: [PATCH 07/28] misc: fix. --- src/lib/components/bottomModalAlert.svelte | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib/components/bottomModalAlert.svelte b/src/lib/components/bottomModalAlert.svelte index 49c1b7d19..fe7e1a276 100644 --- a/src/lib/components/bottomModalAlert.svelte +++ b/src/lib/components/bottomModalAlert.svelte @@ -140,9 +140,6 @@