diff --git a/src/components/settings/PushNotifications/PushNotificationsBanner/PushNotificationsBanner.test.ts b/src/components/settings/PushNotifications/PushNotificationsBanner/PushNotificationsBanner.test.ts
deleted file mode 100644
index e091df0af6..0000000000
--- a/src/components/settings/PushNotifications/PushNotificationsBanner/PushNotificationsBanner.test.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import { _getSafesToRegister } from '.'
-import type { AddedSafesOnChain } from '@/store/addedSafesSlice'
-import type { PushNotificationPreferences } from '@/services/push-notifications/preferences'
-
-describe('PushNotificationsBanner', () => {
- describe('getSafesToRegister', () => {
- it('should return all added safes if no preferences exist', () => {
- const addedSafesOnChain = {
- '0x123': {},
- '0x456': {},
- } as unknown as AddedSafesOnChain
- const allPreferences = undefined
-
- const result = _getSafesToRegister('1', addedSafesOnChain, allPreferences)
-
- expect(result).toEqual({
- '1': ['0x123', '0x456'],
- })
- })
-
- it('should return only newly added safes if preferences exist', () => {
- const addedSafesOnChain = {
- '0x123': {},
- '0x456': {},
- } as unknown as AddedSafesOnChain
- const allPreferences = {
- '1:0x123': {
- safeAddress: '0x123',
- chainId: '1',
- },
- '4:0x789': {
- safeAddress: '0x789',
- chainId: '4',
- },
- } as unknown as PushNotificationPreferences
-
- const result = _getSafesToRegister('1', addedSafesOnChain, allPreferences)
-
- expect(result).toEqual({
- '1': ['0x456'],
- })
- })
-
- it('should return all added safes if no preferences match', () => {
- const addedSafesOnChain = {
- '0x123': {},
- '0x456': {},
- } as unknown as AddedSafesOnChain
- const allPreferences = {
- '1:0x111': {
- safeAddress: '0x111',
- chainId: '1',
- },
- '4:0x222': {
- safeAddress: '0x222',
- chainId: '4',
- },
- } as unknown as PushNotificationPreferences
-
- const result = _getSafesToRegister('1', addedSafesOnChain, allPreferences)
-
- expect(result).toEqual({
- '1': ['0x123', '0x456'],
- })
- })
- })
-})
diff --git a/src/components/settings/PushNotifications/PushNotificationsBanner/PushNotificationsBanner.test.tsx b/src/components/settings/PushNotifications/PushNotificationsBanner/PushNotificationsBanner.test.tsx
new file mode 100644
index 0000000000..2ed0aa6aac
--- /dev/null
+++ b/src/components/settings/PushNotifications/PushNotificationsBanner/PushNotificationsBanner.test.tsx
@@ -0,0 +1,310 @@
+import 'fake-indexeddb/auto'
+import { set } from 'idb-keyval'
+import { hexZeroPad } from 'ethers/lib/utils'
+import * as navigation from 'next/navigation'
+import type { ChainInfo, SafeInfo } from '@safe-global/safe-gateway-typescript-sdk'
+
+import { PushNotificationsBanner, _getSafesToRegister } from '.'
+import { createPushNotificationPrefsIndexedDb } from '@/services/push-notifications/preferences'
+import { render } from '@/tests/test-utils'
+import type { AddedSafesOnChain } from '@/store/addedSafesSlice'
+import type { PushNotificationPreferences } from '@/services/push-notifications/preferences'
+
+Object.defineProperty(globalThis, 'crypto', {
+ value: {
+ randomUUID: () => Math.random().toString(),
+ },
+})
+
+describe('PushNotificationsBanner', () => {
+ describe('getSafesToRegister', () => {
+ it('should return all added safes if no preferences exist', () => {
+ const addedSafesOnChain = {
+ '0x123': {},
+ '0x456': {},
+ } as unknown as AddedSafesOnChain
+ const allPreferences = undefined
+
+ const result = _getSafesToRegister('1', addedSafesOnChain, allPreferences)
+
+ expect(result).toEqual({
+ '1': ['0x123', '0x456'],
+ })
+ })
+
+ it('should return only newly added safes if preferences exist', () => {
+ const addedSafesOnChain = {
+ '0x123': {},
+ '0x456': {},
+ } as unknown as AddedSafesOnChain
+ const allPreferences = {
+ '1:0x123': {
+ safeAddress: '0x123',
+ chainId: '1',
+ },
+ '4:0x789': {
+ safeAddress: '0x789',
+ chainId: '4',
+ },
+ } as unknown as PushNotificationPreferences
+
+ const result = _getSafesToRegister('1', addedSafesOnChain, allPreferences)
+
+ expect(result).toEqual({
+ '1': ['0x456'],
+ })
+ })
+
+ it('should return all added safes if no preferences match', () => {
+ const addedSafesOnChain = {
+ '0x123': {},
+ '0x456': {},
+ } as unknown as AddedSafesOnChain
+ const allPreferences = {
+ '1:0x111': {
+ safeAddress: '0x111',
+ chainId: '1',
+ },
+ '4:0x222': {
+ safeAddress: '0x222',
+ chainId: '4',
+ },
+ } as unknown as PushNotificationPreferences
+
+ const result = _getSafesToRegister('1', addedSafesOnChain, allPreferences)
+
+ expect(result).toEqual({
+ '1': ['0x123', '0x456'],
+ })
+ })
+ })
+
+ describe('PushNotificationsBanner', () => {
+ beforeEach(() => {
+ // Reset indexedDB
+ indexedDB = new IDBFactory()
+
+ window.localStorage.clear()
+
+ jest.spyOn(navigation, 'useParams').mockReturnValue({
+ safe: `eth:${hexZeroPad('0x123', 20)}`,
+ })
+ })
+
+ it('should display the banner', () => {
+ const result = render(
+
+ <>>
+ ,
+ {
+ routerProps: {
+ query: {
+ safe: `eth:${hexZeroPad('0x123', 20)}`,
+ },
+ },
+ initialReduxState: {
+ chains: {
+ loading: false,
+ error: undefined,
+ data: [
+ {
+ chainId: '1',
+ features: ['PUSH_NOTIFICATIONS'],
+ } as unknown as ChainInfo,
+ ],
+ },
+ addedSafes: {
+ '1': {
+ [hexZeroPad('0x123', 20)]: {},
+ } as unknown as AddedSafesOnChain,
+ },
+ safeInfo: {
+ loading: false,
+ error: undefined,
+ data: {
+ chainId: '1',
+ address: {
+ value: hexZeroPad('0x123', 20),
+ },
+ } as unknown as SafeInfo,
+ },
+ },
+ },
+ )
+
+ expect(result.getByText('Get notified about pending signatures', { exact: false })).toBeInTheDocument()
+ })
+
+ it('should not show the banner if notifications are not enabled', () => {
+ const result = render(
+
+ <>>
+ ,
+ {
+ routerProps: {
+ query: {
+ safe: `eth:${hexZeroPad('0x123', 20)}`,
+ },
+ },
+ initialReduxState: {
+ chains: {
+ loading: false,
+ error: undefined,
+ data: [
+ {
+ chainId: '1',
+ features: [], // Not enabled
+ } as unknown as ChainInfo,
+ ],
+ },
+ addedSafes: {
+ '1': {
+ [hexZeroPad('0x123', 20)]: {},
+ } as unknown as AddedSafesOnChain,
+ },
+ safeInfo: {
+ loading: false,
+ error: undefined,
+ data: {
+ chainId: '1',
+ address: {
+ value: hexZeroPad('0x123', 20),
+ },
+ } as unknown as SafeInfo,
+ },
+ },
+ },
+ )
+
+ expect(result.queryByText('Get notified about pending signatures', { exact: false })).not.toBeInTheDocument()
+ })
+
+ it('should not show the banner if the user has dismissed it', () => {
+ window.localStorage.setItem(
+ 'SAFE_v2__dismissPushNotifications',
+ JSON.stringify({ '1': { [hexZeroPad('0x123', 20)]: true } }),
+ )
+
+ const result = render(
+
+ <>>
+ ,
+ {
+ initialReduxState: {
+ chains: {
+ loading: false,
+ error: undefined,
+ data: [
+ {
+ chainId: '1',
+ features: ['PUSH_NOTIFICATIONS'],
+ } as unknown as ChainInfo,
+ ],
+ },
+ addedSafes: {
+ '1': {
+ [hexZeroPad('0x123', 20)]: {},
+ } as unknown as AddedSafesOnChain,
+ },
+ safeInfo: {
+ loading: false,
+ error: undefined,
+ data: {
+ chainId: '1',
+ address: {
+ value: hexZeroPad('0x123', 20),
+ },
+ } as unknown as SafeInfo,
+ },
+ },
+ },
+ )
+
+ expect(result.queryByText('Get notified about pending signatures', { exact: false })).not.toBeInTheDocument()
+ })
+
+ it('should not show the banner if the Safe is not added', () => {
+ const result = render(
+
+ <>>
+ ,
+ {
+ initialReduxState: {
+ chains: {
+ loading: false,
+ error: undefined,
+ data: [
+ {
+ chainId: '1',
+ features: ['PUSH_NOTIFICATIONS'],
+ } as unknown as ChainInfo,
+ ],
+ },
+ addedSafes: {}, // Not added
+ safeInfo: {
+ loading: false,
+ error: undefined,
+ data: {
+ chainId: '1',
+ address: {
+ value: hexZeroPad('0x123', 20),
+ },
+ } as unknown as SafeInfo,
+ },
+ },
+ },
+ )
+
+ expect(result.queryByText('Get notified about pending signatures', { exact: false })).not.toBeInTheDocument()
+ })
+
+ it('should not show the banner if the user has already registered for notifications', () => {
+ set(
+ `1:${hexZeroPad('0x123', 20)}`, // Registered
+ {
+ safeAddress: hexZeroPad('0x123', 20),
+ chainId: '1',
+ preferences: {},
+ },
+ createPushNotificationPrefsIndexedDb(),
+ )
+
+ const result = render(
+
+ <>>
+ ,
+ {
+ initialReduxState: {
+ chains: {
+ loading: false,
+ error: undefined,
+ data: [
+ {
+ chainId: '1',
+ features: ['PUSH_NOTIFICATIONS'],
+ } as unknown as ChainInfo,
+ ],
+ },
+ addedSafes: {
+ '1': {
+ [hexZeroPad('0x123', 20)]: {},
+ } as unknown as AddedSafesOnChain,
+ },
+ safeInfo: {
+ loading: false,
+ error: undefined,
+ data: {
+ chainId: '1',
+ address: {
+ value: hexZeroPad('0x123', 20),
+ },
+ } as unknown as SafeInfo,
+ },
+ },
+ },
+ )
+
+ expect(result.queryByText('Get notified about pending signatures', { exact: false })).not.toBeInTheDocument()
+ })
+ })
+})
diff --git a/src/components/settings/PushNotifications/PushNotificationsBanner/index.tsx b/src/components/settings/PushNotifications/PushNotificationsBanner/index.tsx
index 96332f1d2b..5627acd456 100644
--- a/src/components/settings/PushNotifications/PushNotificationsBanner/index.tsx
+++ b/src/components/settings/PushNotifications/PushNotificationsBanner/index.tsx
@@ -87,7 +87,7 @@ export const _getSafesToRegister = (
}
export const PushNotificationsBanner = ({ children }: { children: ReactElement }): ReactElement => {
- const isNotificationsEnabled = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
+ const isNotificationFeatureEnabled = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
const chain = useCurrentChain()
const totalAddedSafes = useAppSelector(selectTotalAdded)
const { safe, safeAddress } = useSafeInfo()
@@ -95,13 +95,15 @@ export const PushNotificationsBanner = ({ children }: { children: ReactElement }
const { query } = useRouter()
const onboard = useOnboard()
+ const { getPreferences, getAllPreferences } = useNotificationPreferences()
const { dismissPushNotificationBanner, isPushNotificationBannerDismissed } = useDismissPushNotificationsBanner()
const isSafeAdded = !!addedSafesOnChain?.[safeAddress]
- const shouldShowBanner = isNotificationsEnabled && !isPushNotificationBannerDismissed && isSafeAdded
+ const isSafeRegistered = getPreferences(safe.chainId, safeAddress)
+ const shouldShowBanner =
+ isNotificationFeatureEnabled && !isPushNotificationBannerDismissed && isSafeAdded && !isSafeRegistered
const { registerNotifications } = useNotificationRegistrations()
- const { getAllPreferences } = useNotificationPreferences()
const dismissBanner = useCallback(() => {
trackEvent(PUSH_NOTIFICATION_EVENTS.DISMISS_BANNER)
diff --git a/src/components/settings/PushNotifications/hooks/useNotificationTracking.ts b/src/components/settings/PushNotifications/hooks/useNotificationTracking.ts
index 5c0500422e..0a6b37d238 100644
--- a/src/components/settings/PushNotifications/hooks/useNotificationTracking.ts
+++ b/src/components/settings/PushNotifications/hooks/useNotificationTracking.ts
@@ -70,11 +70,11 @@ const handleTrackCachedNotificationEvents = async (
}
export const useNotificationTracking = (): void => {
- const isNotificationsEnabled = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
+ const isNotificationFeatureEnabled = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
useEffect(() => {
- if (typeof indexedDB !== 'undefined' && isNotificationsEnabled) {
+ if (typeof indexedDB !== 'undefined' && isNotificationFeatureEnabled) {
handleTrackCachedNotificationEvents(createNotificationTrackingIndexedDb())
}
- }, [isNotificationsEnabled])
+ }, [isNotificationFeatureEnabled])
}
diff --git a/src/components/settings/SettingsHeader/index.tsx b/src/components/settings/SettingsHeader/index.tsx
index 63f3e54be8..5d35b01d1b 100644
--- a/src/components/settings/SettingsHeader/index.tsx
+++ b/src/components/settings/SettingsHeader/index.tsx
@@ -11,10 +11,10 @@ import { FEATURES } from '@/utils/chains'
const SettingsHeader = (): ReactElement => {
const safeAddress = useSafeAddress()
- const isNotificationsEnabled = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
+ const isNotificationFeatureEnabled = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
const navItems = safeAddress ? settingsNavItems : generalSettingsNavItems
- const filteredNavItems = isNotificationsEnabled
+ const filteredNavItems = isNotificationFeatureEnabled
? navItems
: navItems.filter((item) => item.href !== AppRoutes.settings.notifications)
diff --git a/src/pages/settings/notifications.tsx b/src/pages/settings/notifications.tsx
index a739f3d5f4..66dc3cda37 100644
--- a/src/pages/settings/notifications.tsx
+++ b/src/pages/settings/notifications.tsx
@@ -7,9 +7,9 @@ import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'
const NotificationsPage: NextPage = () => {
- const isNotificationsEnabled = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
+ const isNotificationFeatureEnabled = useHasFeature(FEATURES.PUSH_NOTIFICATIONS)
- if (!isNotificationsEnabled) {
+ if (!isNotificationFeatureEnabled) {
return null
}