Skip to content

Commit

Permalink
feat: notifications (#2550)
Browse files Browse the repository at this point in the history
* feat: add walletconnect notify api (#2512)

* initialize notify client

* notify client store

* register and subscribe to notifications

* implement test subscription

* improve initialization

* subscribe to gm walletconnect

* create notify manager class

Co-authored-by: Nicole O'Brien <[email protected]>
Co-authored-by: Tuditi <[email protected]>

* set notifications to store

* update yarn lock

* initialize notifications and subscriptions

Co-authored-by: Tuditi <[email protected]>
Co-authored-by: Nicole O'Brien <[email protected]>

* cleanup handlers

Co-authored-by: Tuditi <[email protected]>
Co-authored-by: Nicole O'Brien <[email protected]>
Co-authored-by: Jean Ribeiro <[email protected]>

---------

Co-authored-by: Nicole O'Brien <[email protected]>
Co-authored-by: Tuditi <[email protected]>
Co-authored-by: Jean Ribeiro <[email protected]>

* chore: cleanup notification manager init (#2513)

* cleanup notification manager initialization

Co-authored-by: Nicole O'Brien <[email protected]>

* add message handler

---------

Co-authored-by: Nicole O'Brien <[email protected]>

* fix: fix notify client (#2516)

* fix notify client initialize

* fix dependencies

Co-authored-by: Jean Ribeiro <[email protected]>

---------

Co-authored-by: Jean Ribeiro <[email protected]>

* feat: add notification components (#2514)

* WIP

* improve notification popup

* use dapp icon as main image

* add small badge

* add notificationavatar

* only show fixed amount + add localizations

Co-authored-by: Jean Ribeiro <[email protected]>

* add time test

* cleanup

---------

Co-authored-by: Jean Ribeiro <[email protected]>

* feat: adds Link in NotificationTile (#2528)

* feat: highlight unread notifications (#2527)

* chore: refactor for multi chain support (#2511)

* chore: remove BASE_TOKEN_CONTRACT_ADDRESS map

* chore: rename getL1Network to getStardustNetwork

* fix: type

* feat: highlight unread notification

* fix incoming notifications

* restructure

---------

Co-authored-by: Tuditi <[email protected]>
Co-authored-by: Mark Nardi <[email protected]>

* feat: mark notification as read if visible (#2532)

* fix: removes view all button and show all notifications in dropdown

* feat: mark notifications as read if visible

* chore: update @bloomwalletio/ui

* fix: updates notifications store only after closing the dropdown

* decrease threshold

---------

Co-authored-by: Mark Nardi <[email protected]>

* fix: type errors

* feat: notifications unread tab (#2540)

* feat: notifications unread tab

* add optional chain

---------

Co-authored-by: Mark Nardi <[email protected]>

* enhancement: uses VirtualList in notifications (#2541)

* enhancement: uses VirtualList in notifications

* fix merge issue

---------

Co-authored-by: Mark Nardi <[email protected]>

* cleanup

* feat: notification details popup (#2554)

* show basetoken amount on smartcontract popups (#2549)

* feat: adds notification details popup

Co-authored-by: Mark Nardi <[email protected]>
Co-authored-by: Nicole O'Brien <[email protected]>
Co-authored-by: Tuditi <[email protected]>

* fix: PopupComponentMap type

---------

Co-authored-by: Mark Nardi <[email protected]>
Co-authored-by: Nicole O'Brien <[email protected]>
Co-authored-by: Tuditi <[email protected]>

* fix linter

* fix: notification read state

Co-authored-by: Mark Nardi <[email protected]>

* polish nav bar with notification button

* split notification components

* remove notification tabs

* enable notifications for multiple networks

* fix notifications initialization

* improve siwe validation check

* fix: notification tile hover

* fix: popover closing behaviour for notifications

* ui: empty placeholder in notifications

* enable notifications in network drawer

* fix notification toggle state

* improve notification registering

Co-authored-by: Mark Nardi <[email protected]>

* register for notifications on some generate addresses

* register account on evm address generation

* only register if not registered

* fix login if account is unregistered

* Fix notification registration on onboarding

Co-authored-by: Nicole O'Brien <[email protected]>

* remove mulitple instances of notifiation body

* WIP

* fix: whitelist notification links

* fix tracked address initialization

* remove console logs

* ui: polish notification details popup

Co-authored-by: Mark Nardi <[email protected]>

* ui: polish notification tile

* improve logic in complete onboarding

Co-authored-by: Mark Nardi <[email protected]>

* chore: fix formatting

---------

Co-authored-by: Nicole O'Brien <[email protected]>
Co-authored-by: Tuditi <[email protected]>
Co-authored-by: Jean Ribeiro <[email protected]>
Co-authored-by: Tuditi <[email protected]>
Co-authored-by: Nicole O'Brien <[email protected]>
  • Loading branch information
6 people authored Jun 12, 2024
1 parent ba2e2e7 commit a1a8a86
Show file tree
Hide file tree
Showing 52 changed files with 1,243 additions and 110 deletions.
18 changes: 11 additions & 7 deletions packages/desktop/components/EmptyListPlaceholder.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { Icon, IconName, Text } from '@bloomwalletio/ui'
export let title: string
export let title: string | undefined = undefined
export let subtitle: string | undefined = undefined
export let icon: IconName
</script>
Expand All @@ -10,12 +10,16 @@
<empty-list-icon>
<Icon name={icon} size="md" customColor="primary-500" />
</empty-list-icon>
<div class="flex flex-col items-center justify-center px-10 gap-4">
<Text type="h6" align="center">{title}</Text>
{#if subtitle}
<Text textColor="secondary" align="center">{subtitle}</Text>
{/if}
</div>
{#if title || subtitle}
<div class="flex flex-col items-center justify-center px-10 gap-4">
{#if title}
<Text type="h6" align="center">{title}</Text>
{/if}
{#if subtitle}
<Text textColor="secondary" align="center">{subtitle}</Text>
{/if}
</div>
{/if}
</div>

<style lang="postcss">
Expand Down
18 changes: 11 additions & 7 deletions packages/desktop/components/NetworkCard.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import { notificationsManager } from '@auxiliary/wallet-connect/notifications'
import { Button, Copyable, IconButton, IconName, Text, Tile } from '@bloomwalletio/ui'
import { IAccountState, getAddressFromAccountForNetwork } from '@core/account'
import { selectedAccount } from '@core/account/stores'
Expand Down Expand Up @@ -51,7 +52,7 @@
}
async function onGenerateAddressClick(): Promise<void> {
if (!network || network.namespace !== NetworkNamespace.Evm) {
if (!network || network.namespace !== NetworkNamespace.Evm || !$selectedAccount) {
return
}
Expand All @@ -63,12 +64,15 @@
try {
setSelectedNetworkForNetworkDrawer(network)
await generateAndStoreEvmAddressForAccounts(
$activeProfile.type,
network.coinType,
$selectedAccount as IAccountState
)
pollEvmBalancesForAccount($activeProfile.id, $selectedAccount as IAccountState)
await generateAndStoreEvmAddressForAccounts($activeProfile.type, network.coinType, $selectedAccount)
if ($selectedAccount.index === 0 && $activeProfile.type === ProfileType.Software) {
try {
await notificationsManager.registerAccount($selectedAccount, network.id, network.coinType)
} catch (error) {
console.error(error)
}
}
pollEvmBalancesForAccount($activeProfile.id, $selectedAccount)
if ($activeProfile.type === ProfileType.Ledger) {
$networkConfigRouter.goTo(NetworkConfigRoute.ConfirmLedgerEvmAddress)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/desktop/components/popup/Popup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import MintNftCollectionFormPopup from './popups/MintNftCollectionFormPopup.svelte'
import MintNftConfirmationPopup from './popups/MintNftConfirmationPopup.svelte'
import MintNftFormPopup from './popups/MintNftFormPopup.svelte'
import NotificationDetailsPopup from './popups/NotificationDetailsPopup.svelte'
import NodeAuthRequiredPopup from './popups/NodeAuthRequiredPopup.svelte'
import NodeInfoPopup from './popups/NodeInfoPopup.svelte'
import ProfileDiagnosticsPopup from './popups/ProfileDiagnosticsPopup.svelte'
Expand Down Expand Up @@ -131,6 +132,7 @@
[PopupId.MintNftCollectionConfirmation]: MintNftCollectionConfirmationPopup,
[PopupId.NodeAuthRequired]: NodeAuthRequiredPopup,
[PopupId.NodeInfo]: NodeInfoPopup,
[PopupId.NotificationDetails]: NotificationDetailsPopup,
[PopupId.ProfileDiagnostics]: ProfileDiagnosticsPopup,
[PopupId.ReceiveAddress]: ReceiveAddressPopup,
[PopupId.RemoveProposal]: RemoveProposalPopup,
Expand Down
16 changes: 9 additions & 7 deletions packages/desktop/components/popup/PopupTemplate.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,15 @@
{/if}
</popup-header>
{/if}
<div class="flex-1 h-0 flex flex-col">
{#if $$slots.default}
<slot />
{:else if $$slots.content}
<slot name="content" />
{/if}
</div>
{#if $$slots.default || $$slots.content}
<div class="flex-1 h-0 flex flex-col">
{#if $$slots.default}
<slot />
{:else if $$slots.content}
<slot name="content" />
{/if}
</div>
{/if}
{#if backButton || continueButton}
<popup-footer class="flex-none flex flex-row gap-3">
{#if backButton}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script lang="ts">
import { Pill, Text } from '@bloomwalletio/ui'
import { openUrlInBrowser } from '@core/app/utils'
import { formatDate, localize } from '@core/i18n'
import { NotificationSubscriptionInfo } from '@ui'
import { NotifyClientTypes } from '@walletconnect/notify-client'
import PopupTemplate from '../PopupTemplate.svelte'
export let notification: NotifyClientTypes.NotifyNotification
export let subscription: NotifyClientTypes.NotifySubscription
$: notificationType = subscription?.scope[notification.type]
function onVisitClick(url: string): void {
openUrlInBrowser(url)
}
</script>

<PopupTemplate
continueButton={notification.url
? {
text: localize('actions.visit'),
onClick: () => onVisitClick(notification.url ?? ''),
}
: undefined}
>
<NotificationSubscriptionInfo
slot="banner"
metadata={subscription.metadata}
classes="bg-surface-1 dark:bg-surface-1-dark pb-4"
></NotificationSubscriptionInfo>

<div class="space-y-2">
<div class="flex flex-row items-center justify-between gap-2">
<Pill compact color="primary">{notificationType.name}</Pill>
<Text size="sm" textColor="secondary">
{formatDate(new Date(notification.sentAt), { dateStyle: 'long', timeStyle: 'short' })}
</Text>
</div>
<div class="space-y-1">
<Text type="body1">{notification.title}</Text>
<Text type="sm" textColor="secondary">{notification.body}</Text>
</div>
</div>
</PopupTemplate>
33 changes: 21 additions & 12 deletions packages/desktop/components/popup/popups/ReceiveAddressPopup.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
<script lang="ts">
import { onMount } from 'svelte'
import { AddressBox, NetworkInput } from '@ui'
import { localize } from '@core/i18n'
import { notificationsManager } from '@auxiliary/wallet-connect/notifications'
import { IAccountState, getAddressFromAccountForNetwork } from '@core/account'
import { selectedAccount } from '@core/account/stores'
import { setClipboard } from '@core/utils'
import { getActiveNetworkId, getNetwork, NetworkId, NetworkNamespace } from '@core/network'
import { handleError } from '@core/error/handlers'
import { localize } from '@core/i18n'
import { generateAndStoreEvmAddressForAccounts, pollEvmBalancesForAccount } from '@core/layer-2/actions'
import { activeProfile } from '@core/profile/stores'
import { checkActiveProfileAuth } from '@core/profile/actions'
import { LedgerAppName } from '@core/ledger'
import { IEvmNetwork, NetworkId, NetworkNamespace, getActiveNetworkId, getNetwork } from '@core/network'
import { checkActiveProfileAuth } from '@core/profile/actions'
import { activeProfile } from '@core/profile/stores'
import { setClipboard } from '@core/utils'
import { AddressBox, NetworkInput } from '@ui'
import { onMount } from 'svelte'
import PopupTemplate from '../PopupTemplate.svelte'
import { handleError } from '@core/error/handlers'
import { IAccountState, getAddressFromAccountForNetwork } from '@core/account'
import { ProfileType } from '@core/profile'
let selectedNetworkId: NetworkId | undefined = getActiveNetworkId()
$: selectedNetworkId, updateNetworkNameAndAddress()
Expand All @@ -29,19 +31,26 @@
networkName = network?.name
receiveAddress = getAddressFromAccountForNetwork(account, selectedNetworkId)
if (!receiveAddress && network?.namespace === NetworkNamespace.Evm) {
generateAddress(account, network.coinType)
generateAddress(account, network)
}
}
async function generateAddress(account: IAccountState, coinType: number): Promise<void> {
async function generateAddress(account: IAccountState, network: IEvmNetwork): Promise<void> {
try {
await checkActiveProfileAuth(LedgerAppName.Ethereum)
} catch {
return
}
try {
await generateAndStoreEvmAddressForAccounts($activeProfile.type, coinType, account)
await generateAndStoreEvmAddressForAccounts($activeProfile.type, network.coinType, account)
if (account.index === 0 && $activeProfile.type === ProfileType.Software) {
try {
await notificationsManager.registerAccount(account, network.id, network.coinType)
} catch (error) {
console.error(error)
}
}
pollEvmBalancesForAccount($activeProfile.id, account)
updateNetworkNameAndAddress()
} catch (error) {
Expand Down
6 changes: 6 additions & 0 deletions packages/desktop/features/wallet-connect.features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import { IWalletConnectFeatures } from '@lib/features/interfaces'

const walletConnectFeatures: IWalletConnectFeatures = {
enabled: true,
web3Wallet: {
enabled: true,
},
notifications: {
enabled: true,
},
}

export default walletConnectFeatures
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export enum PopupId {
MintNftForm = 'mintNftForm',
MintNftCollectionForm = 'mintNftCollectionForm',
MintNftCollectionConfirmation = 'mintNftCollectionConfirmation',
NotificationDetails = 'notificationDetails',
NodeAuthRequired = 'nodeAuthRequired',
NodeInfo = 'nodeInfo',
ProfileDiagnostics = 'profileDiagnostics',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SvelteComponent } from 'svelte'
import { PopupId } from '../enums'

export type PopupComponentMap = { [key in PopupId]: SvelteComponent }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type PopupComponentMap = { [key in PopupId]: typeof SvelteComponent<any> }
14 changes: 9 additions & 5 deletions packages/desktop/views/dashboard/components/Navbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@
import { IS_MAC } from '@core/app'
import { localize } from '@core/i18n'
import { toggleDashboardDrawer } from '@desktop/auxiliary/drawer'
import features from '@features/features'
import { DashboardDrawerRoute } from '../drawers'
import Breadcrumbs from './Breadcrumbs.svelte'
import NotificationsButton from '../notifications/NotificationsButton.svelte'
import { isFeatureEnabled } from '@lib/features/utils'
</script>

<NavbarContainer draggable={IS_MAC}>
<div class="flex flex-row justify-between items-center px-4" style:height="var(--navbar-height)">
<Breadcrumbs />

<div class="right-button flex items-center justify-end gap-2">
{#if features.contacts.enabled}
{#if isFeatureEnabled('walletConnect.notifications')}
<NotificationsButton />
{/if}
{#if isFeatureEnabled('contacts')}
<IconButton
on:click={() => toggleDashboardDrawer({ id: DashboardDrawerRoute.ContactBook })}
icon={IconName.Users}
Expand All @@ -23,16 +27,16 @@
size="sm"
/>
{/if}
{#if features?.walletConnect?.enabled}
{#if isFeatureEnabled('walletConnect.web3Wallet')}
<IconButton
on:click={() => toggleDashboardDrawer({ id: DashboardDrawerRoute.DappConfig })}
icon={IconName.Grid}
tooltip={localize('general.apps')}
tooltip={localize('general.connectedDapps')}
textColor="primary"
size="sm"
/>
{/if}
{#if features?.network?.config?.enabled}
{#if isFeatureEnabled('network.config')}
<IconButton
on:click={() => toggleDashboardDrawer({ id: DashboardDrawerRoute.NetworkConfig })}
icon={IconName.Globe}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,57 @@
<script lang="ts">
import { notificationsManager } from '@auxiliary/wallet-connect/notifications'
import { Table, Text, Toggle } from '@bloomwalletio/ui'
import { DrawerTemplate } from '@components/drawers'
import { handleError } from '@core/error/handlers'
import { localize } from '@core/i18n'
import { LedgerAppName } from '@core/ledger'
import { IEvmNetwork, isIscNetwork } from '@core/network'
import { Table } from '@bloomwalletio/ui'
import { DrawerTemplate } from '@components/drawers'
import { NetworkConfigRoute } from '../../network-config-route.enum'
import { checkActiveProfileAuth } from '@core/profile/actions'
import { activeAccounts } from '@core/profile/stores'
import { Router } from '@core/router'
import { NetworkConfigRoute } from '../../network-config-route.enum'
export let drawerRouter: Router<NetworkConfigRoute>
export let network: IEvmNetwork
const localeKey = 'views.dashboard.drawers.networkConfig.chain'
// Register for account index 0 only
$: notificationAccount = $activeAccounts[0]
const trackedAccounts = notificationsManager.trackedNetworkAddresses
let isRegistered = false
$: $trackedAccounts,
(isRegistered = !!notificationAccount && notificationsManager.isRegistered(notificationAccount, network.id))
let busy = false
async function enableNotifications(): Promise<void> {
busy = true
try {
if (!isRegistered) {
await checkActiveProfileAuth(LedgerAppName.Ethereum)
}
} catch (error) {
busy = false
return
}
try {
if (isRegistered) {
await notificationsManager.unregisterAccount(notificationAccount, network.id)
} else {
await notificationsManager.registerAccount(notificationAccount, network.id, network.coinType)
}
} catch (err) {
handleError(err)
} finally {
busy = false
}
}
</script>

<DrawerTemplate title={network.name} {drawerRouter}>
<div class="w-full h-full px-6">
<div class="w-full h-full px-6 space-y-4">
<Table
orientation="vertical"
items={[
Expand Down Expand Up @@ -43,5 +81,20 @@
: []),
]}
/>
<hr />
<Text type="body2">{localize('views.dashboard.dappNotifications.title')}</Text>
<div class="flex gap-4">
<Toggle
label={localize('views.dashboard.dappNotifications.enable')}
checked={isRegistered}
size="md"
rounded="rounded-full"
onClick={() => enableNotifications()}
disabled={busy}
/>
<Text type="sm" align="center"
>{localize(`general.${busy ? 'saving' : isRegistered ? 'enabled' : 'disabled'}`)}</Text
>
</div>
</div>
</DrawerTemplate>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import { notificationsManager } from '@auxiliary/wallet-connect/notifications'
import { IconButton, IconName, Indicator } from '@bloomwalletio/ui'
import { localize } from '@core/i18n'
import NotificationsPopoverContent from './NotificationsPopoverContent.svelte'
const notifications = notificationsManager.notificationsPerSubscription
$: hasUnreadNotifications = Object.values($notifications).some((notifications) =>
notifications.some((notification) => !notification.isRead)
)
let anchor: HTMLElement | undefined = undefined
</script>

<button bind:this={anchor} type="button" class="relative flex items-center">
<IconButton
icon={IconName.Bell}
tooltip={localize('views.dashboard.dappNotifications.title')}
textColor="primary"
size="sm"
/>
{#if hasUnreadNotifications}
<Indicator
size="sm"
class="absolute top-0 right-0 box-content rounded-full
border-2 border-solid border-surface dark:border-surface-dark"
/>
{/if}
</button>

<NotificationsPopoverContent {anchor} />
Loading

0 comments on commit a1a8a86

Please sign in to comment.