From f3edb885ed49b7f1405ca63f089e30f281821a95 Mon Sep 17 00:00:00 2001 From: Giorgi Tsirekidze Date: Thu, 6 Jul 2023 10:38:38 +0400 Subject: [PATCH 01/13] feat: push protocol integration --- .../push-protocol/opengraph-image.tsx | 9 + .../integration/push-protocol/page.tsx | 98 ++++ .../push-protocol/twitter-image.tsx | 9 + data/turbo-integrations.ts | 8 + integrations/push-protocol/README.md | 108 ++++ .../push-protocol/components/channel-card.tsx | 97 ++++ .../push-protocol/components/chat.tsx | 5 + .../push-protocol/components/index.ts | 6 + .../push-protocol/components/loadable.tsx | 13 + .../components/notification-feed.tsx | 62 +++ .../components/notification-item.tsx | 6 + .../push-protocol/components/types.ts | 18 + integrations/push-protocol/hooks/index.ts | 8 + .../push-protocol/hooks/use-channel.ts | 52 ++ integrations/push-protocol/hooks/use-chats.ts | 21 + .../push-protocol/hooks/use-create-user.ts | 21 + .../push-protocol/hooks/use-notifications.ts | 29 + .../push-protocol/hooks/use-push-action.ts | 27 + .../push-protocol/hooks/use-push-query.ts | 21 + .../hooks/use-search-channels.ts | 21 + .../hooks/use-send-notifications.ts | 20 + .../hooks/use-subscribe-channel.ts | 32 ++ .../hooks/use-user-subscriptions.ts | 21 + integrations/push-protocol/index.ts | 4 + integrations/push-protocol/utils/types.ts | 55 ++ lib/generated/blockchain.ts | 2 +- package.json | 2 + pnpm-lock.yaml | 499 ++++++++++++++++-- public/integrations/push.svg | 65 +++ 29 files changed, 1296 insertions(+), 43 deletions(-) create mode 100644 app/(general)/integration/push-protocol/opengraph-image.tsx create mode 100644 app/(general)/integration/push-protocol/page.tsx create mode 100644 app/(general)/integration/push-protocol/twitter-image.tsx create mode 100644 integrations/push-protocol/README.md create mode 100644 integrations/push-protocol/components/channel-card.tsx create mode 100644 integrations/push-protocol/components/chat.tsx create mode 100644 integrations/push-protocol/components/index.ts create mode 100644 integrations/push-protocol/components/loadable.tsx create mode 100644 integrations/push-protocol/components/notification-feed.tsx create mode 100644 integrations/push-protocol/components/notification-item.tsx create mode 100644 integrations/push-protocol/components/types.ts create mode 100644 integrations/push-protocol/hooks/index.ts create mode 100644 integrations/push-protocol/hooks/use-channel.ts create mode 100644 integrations/push-protocol/hooks/use-chats.ts create mode 100644 integrations/push-protocol/hooks/use-create-user.ts create mode 100644 integrations/push-protocol/hooks/use-notifications.ts create mode 100644 integrations/push-protocol/hooks/use-push-action.ts create mode 100644 integrations/push-protocol/hooks/use-push-query.ts create mode 100644 integrations/push-protocol/hooks/use-search-channels.ts create mode 100644 integrations/push-protocol/hooks/use-send-notifications.ts create mode 100644 integrations/push-protocol/hooks/use-subscribe-channel.ts create mode 100644 integrations/push-protocol/hooks/use-user-subscriptions.ts create mode 100644 integrations/push-protocol/index.ts create mode 100644 integrations/push-protocol/utils/types.ts create mode 100644 public/integrations/push.svg diff --git a/app/(general)/integration/push-protocol/opengraph-image.tsx b/app/(general)/integration/push-protocol/opengraph-image.tsx new file mode 100644 index 00000000..b7b17d10 --- /dev/null +++ b/app/(general)/integration/push-protocol/opengraph-image.tsx @@ -0,0 +1,9 @@ +import { IntegrationOgImage } from '@/components/ui/social/og-image-integrations' + +export const runtime = 'edge' +export const size = { + width: 1200, + height: 630, +} + +export default IntegrationOgImage('push_protocol') diff --git a/app/(general)/integration/push-protocol/page.tsx b/app/(general)/integration/push-protocol/page.tsx new file mode 100644 index 00000000..e62f6a10 --- /dev/null +++ b/app/(general)/integration/push-protocol/page.tsx @@ -0,0 +1,98 @@ +'use client' + +import { motion } from 'framer-motion' +import Balancer from 'react-wrap-balancer' +import { useAccount } from 'wagmi' + +import { WalletConnect } from '@/components/blockchain/wallet-connect' +import { IsWalletConnected } from '@/components/shared/is-wallet-connected' +import { IsWalletDisconnected } from '@/components/shared/is-wallet-disconnected' +import { LinkComponent } from '@/components/shared/link-component' +import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design' +import { turboIntegrations } from '@/data/turbo-integrations' +import { ChannelCard, ENV, NotificationFeed, useNotifications } from '@/integrations/push-protocol' + +export default function PageIntegration() { + const { address } = useAccount() + + const { data: notifications, isLoading: notificationsIsLoading } = useNotifications({ + user: address as string, + env: ENV.STAGING, + spam: false, + }) + + const { data: spamNotifications, isLoading: spamIsLoading } = useNotifications({ + user: address as string, + env: ENV.STAGING, + spam: true, + }) + + return ( + <> +
+ + + {turboIntegrations.push_protocol.name} + + + {turboIntegrations.push_protocol.description} + + + + + + + +
+
+
+ + + + +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ + ) +} diff --git a/app/(general)/integration/push-protocol/twitter-image.tsx b/app/(general)/integration/push-protocol/twitter-image.tsx new file mode 100644 index 00000000..dbca541e --- /dev/null +++ b/app/(general)/integration/push-protocol/twitter-image.tsx @@ -0,0 +1,9 @@ +import Image from './opengraph-image' + +export const runtime = 'edge' +export const size = { + width: 1200, + height: 630, +} + +export default Image diff --git a/data/turbo-integrations.ts b/data/turbo-integrations.ts index f2be00ae..6643921c 100644 --- a/data/turbo-integrations.ts +++ b/data/turbo-integrations.ts @@ -72,6 +72,14 @@ export const turboIntegrations = { imgLight: '/integrations/pooltogether.svg', imgDark: '/integrations/pooltogether.svg', }, + push_protocol: { + name: 'Push Protocol', + href: '/integration/push-protocol', + url: 'https://push.org/', + description: 'Push Protocol is a web3 communication network, enabling cross-chain notifications and messaging for dapps, wallets, and services.', + imgLight: '/integrations/push.svg', + imgDark: '/integrations/push.svg', + }, starter: { name: 'Starter Template', href: '/integration/starter', diff --git a/integrations/push-protocol/README.md b/integrations/push-protocol/README.md new file mode 100644 index 00000000..3a8cf80d --- /dev/null +++ b/integrations/push-protocol/README.md @@ -0,0 +1,108 @@ +# Push Protocol - TurboETH Integration + +[Push Protocol](https://push.org/) is a web3 communication network, enabling cross-chain notifications and messaging for dapps, wallets, and services. + +This integrations provides useful hooks and components from main Push Protocol features. + +## Features + +- Feeds/Notifications (Retrieve, Send) +- Channels (Retrieve, Search) +- Subscriptions (Retrieve, Subscribe/Unsubscribe) +- Chats + +--- + +## Components + +`NotificationFeed` +Renders inbox and spam notifications + +`NotificationItem` +Renders single notification item + +`ChannelCard` +Renders simple card with channel information and interactive subscribe button + +`Chat` +Renders native Push Protocol support chat window on bottom-right corner of the screen + + +--- + +## Hooks +This integration has two types of hooks for accessing and modifying data on PUSH. We can split them as query and action hooks. +They have standard rule of implementation, for example: + +Retrieve channel data hook. +```tsx +const { data, isLoading, error, refetch } = useNotifications({ + user: address as string, + env: ENV.STAGING, + spam: false, +}) +``` + +Subscribe to a channel hook +```tsx +const { data, isLoading, error, action: subscribe } = useSubscribeOrUnsubscribeToChannel({ action: 'subscribe' }) +``` + + +### Hooks for retrieving data + +`useChannel` : Returns Push channel based on parameters + +`useSearchChannels` : Returns Push channel based on search parameters + +`useNotifications` : Returns Push notifications based on parameters + +`useChats` : Returns chats based on parameters + +`useUserSubscriptions` : Returns user channel subscriptions based on parameters + +### Action Hooks + +`useSendNotifications` : Returns Push notifications based on parameters + +`useSubscribeOrUnsubscribeToChannel` : Returns Push notifications based on parameters + +`createUser` : Returns Push channel based on parameters + +### Lazy hooks + +Additionally, if you don't want to depend on state variables returned from the hooks above, you may add `Lazy` at the end of any hook, and it'll return fresh function for fetching the resource. For Example: + +```tsx +const [getChannel] = useChannelLazy(); +getChannel({...args}).then().catch() // +``` + +## File Structure + +``` +integrations/push +├── components +│ ├── channel-card.tsx +│ ├── chat.tsx +│ ├── index.ts +│ ├── loadable.tsx +│ ├── notification-feed.tsx +│ ├── notification-item.tsx +│ └── types.ts +├── hooks +│ ├── index.ts +│ ├── use-channel.ts +│ ├── use-chats.ts +│ ├── use-create-user.ts +│ ├── use-notifications.ts +│ ├── use-push-action.ts +│ ├── use-push-query.ts +│ ├── use-search-channels.ts +│ ├── use-send-notifications.ts +│ ├── use-subscribe-channel.ts +│ └── use-user-subscriptions.ts +├── index.ts +├── README.md +└── utils + └── types.ts \ No newline at end of file diff --git a/integrations/push-protocol/components/channel-card.tsx b/integrations/push-protocol/components/channel-card.tsx new file mode 100644 index 00000000..e5bf4fef --- /dev/null +++ b/integrations/push-protocol/components/channel-card.tsx @@ -0,0 +1,97 @@ +import { useEffect, useState } from 'react' + +import { SubscribeOptionsType } from '@pushprotocol/restapi/src/lib/channels' +import { useAccount } from 'wagmi' + +import { useEthersSigner } from '@/lib/hooks/web3/use-ethers-signer' + +import { Loadable } from './loadable' +import { ChannelCardProps } from './types' +import { useChannel, useSubscribeOrUnsubscribeToChannel, useUserSubscriptions } from '../hooks' + +function strLimit(text: string, count: number) { + return text.slice(0, count) + (text.length > count ? '...' : '') +} + +export function ChannelCard({ env, channelAddress }: ChannelCardProps) { + const { address } = useAccount() + const signer = useEthersSigner() + + const { isLoading: subLoading, action: subscribe } = useSubscribeOrUnsubscribeToChannel({ action: 'subscribe' }) + const { isLoading: unsubLoading, action: unsubscribe } = useSubscribeOrUnsubscribeToChannel({ action: 'unsubscribe' }) + + const { data: channel, isLoading: channelIsLoading } = useChannel({ + channel: channelAddress, + env: env, + }) + + const { data: userSubscriptions, isLoading: userSubsIsLoading } = useUserSubscriptions({ + user: address as string, + env, + }) + + const [userIsSubscribed, setUserIsSubscribed] = useState(false) + + useEffect(() => { + if (!address || !userSubscriptions) { + setUserIsSubscribed(false) + return + } + + setUserIsSubscribed(userSubscriptions.map((channel) => channel.channel.toLowerCase()).includes(channelAddress.toLowerCase())) + }, [userSubscriptions]) + + const toggleSubscribe = () => { + if (!signer || !address) return + + const args: SubscribeOptionsType = { + signer, + userAddress: address, + channelAddress, + env, + } + + const toggle = () => { + return userIsSubscribed ? unsubscribe(args) : subscribe(args) + } + + return toggle().then(() => { + setUserIsSubscribed(!userIsSubscribed) + }) + } + + return ( +
+ + {channel && ( +
+
+ {channel.name} +
+
+

{channel.name}

+

{strLimit(channel.info, 35)}

+
+ + {userIsSubscribed ? ( + + ) : ( + + )} + +
+
+
+ )} +
+
+ ) +} diff --git a/integrations/push-protocol/components/chat.tsx b/integrations/push-protocol/components/chat.tsx new file mode 100644 index 00000000..854af205 --- /dev/null +++ b/integrations/push-protocol/components/chat.tsx @@ -0,0 +1,5 @@ +import { ChatProps, Chat as PushChat } from '@pushprotocol/uiweb' + +export function Chat(props: ChatProps) { + return +} diff --git a/integrations/push-protocol/components/index.ts b/integrations/push-protocol/components/index.ts new file mode 100644 index 00000000..745f88a5 --- /dev/null +++ b/integrations/push-protocol/components/index.ts @@ -0,0 +1,6 @@ +export * from './types' +export * from './loadable' +export * from './notification-item' +export * from './notification-feed' +export * from './chat' +export * from './channel-card' diff --git a/integrations/push-protocol/components/loadable.tsx b/integrations/push-protocol/components/loadable.tsx new file mode 100644 index 00000000..61a0bb51 --- /dev/null +++ b/integrations/push-protocol/components/loadable.tsx @@ -0,0 +1,13 @@ +import { PropsWithChildren } from 'react' + +import { ImSpinner2 } from 'react-icons/im' + +export function Loadable(props: PropsWithChildren<{ isLoading: boolean }>) { + return props.isLoading ? ( + <> + + + ) : ( + <>{props.children} + ) +} diff --git a/integrations/push-protocol/components/notification-feed.tsx b/integrations/push-protocol/components/notification-feed.tsx new file mode 100644 index 00000000..93e07c0c --- /dev/null +++ b/integrations/push-protocol/components/notification-feed.tsx @@ -0,0 +1,62 @@ +import { chainNameType } from '@pushprotocol/uiweb' + +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' + +import { Loadable } from './loadable' +import { Notification } from './notification-item' +import { NotificationFeedProps } from './types' + +export function NotificationFeed({ notifications, spamNotifications, notificationsIsLoading, spamNotificationsIsLoading }: NotificationFeedProps) { + return ( + +
+ + Inbox + Spam + +
+ + + {notifications?.length == 0 && <>You currently have no notifications, try subscribing to some channels.} + {notifications?.map((notification, i) => { + return ( + + ) + })} + + + + + {spamNotifications?.length == 0 && <>You currently have no notifications, try subscribing to some channels.} + {spamNotifications?.map((notification, i) => { + return ( + + ) + })} + + +
+ ) +} diff --git a/integrations/push-protocol/components/notification-item.tsx b/integrations/push-protocol/components/notification-item.tsx new file mode 100644 index 00000000..78fac019 --- /dev/null +++ b/integrations/push-protocol/components/notification-item.tsx @@ -0,0 +1,6 @@ +import { NotificationItem } from '@pushprotocol/uiweb' + +// throws deprecated error otherwise. @@pushprotocol/uiweb is using deprecated defaultProps. +delete NotificationItem.defaultProps + +export const Notification = NotificationItem diff --git a/integrations/push-protocol/components/types.ts b/integrations/push-protocol/components/types.ts new file mode 100644 index 00000000..c6035cc3 --- /dev/null +++ b/integrations/push-protocol/components/types.ts @@ -0,0 +1,18 @@ +import { ApiNotificationType } from '@pushprotocol/restapi' +import { ENV } from '@pushprotocol/uiweb' + +export type NotificationProps = { + notification: ApiNotificationType +} + +export type NotificationFeedProps = { + notifications?: ApiNotificationType[] + spamNotifications?: ApiNotificationType[] + notificationsIsLoading: boolean + spamNotificationsIsLoading: boolean +} + +export type ChannelCardProps = { + channelAddress: string + env: ENV +} diff --git a/integrations/push-protocol/hooks/index.ts b/integrations/push-protocol/hooks/index.ts new file mode 100644 index 00000000..df492c3b --- /dev/null +++ b/integrations/push-protocol/hooks/index.ts @@ -0,0 +1,8 @@ +export * from './use-channel' +export * from './use-search-channels' +export * from './use-chats' +export * from './use-create-user' +export * from './use-notifications' +export * from './use-send-notifications' +export * from './use-subscribe-channel' +export * from './use-user-subscriptions' diff --git a/integrations/push-protocol/hooks/use-channel.ts b/integrations/push-protocol/hooks/use-channel.ts new file mode 100644 index 00000000..4bdf745f --- /dev/null +++ b/integrations/push-protocol/hooks/use-channel.ts @@ -0,0 +1,52 @@ +import * as PushAPI from '@pushprotocol/restapi' +import { z } from 'zod' + +import { usePushQuery } from './use-push-query' +import { Channel, UseChannelProps } from '../utils/types' + +const fetchChannel = async (props: UseChannelProps) => { + const schema = z.object({ + id: z.number(), + channel: z.string(), + ipfshash: z.string(), + name: z.string(), + info: z.string(), + url: z.string(), + icon: z.string(), + processed: z.number(), + attempts: z.number(), + alias_address: z.string().nullable().optional(), + alias_verification_event: z.null(), + is_alias_verified: z.number(), + alias_blockchain_id: z.string().nullable().optional(), + activation_status: z.number(), + verified_status: z.number(), + timestamp: z.string(), + blocked: z.number(), + subgraph_attempts: z.number(), + subscriber_count: z.number(), + }) + + const result = (await PushAPI.channels.getChannel(props)) as Channel + schema.parse(result) + + return result +} + +export const useChannel = (props: UseChannelProps) => { + return usePushQuery( + { + fetcher: async () => { + const result = await fetchChannel(props) + if (!result) throw new Error('Channel not found') // Push sdk doesn't throw errors :( + + return result + }, + }, + [props.channel, props.env] + ) +} + +export const useChannelLazy = () => { + return [fetchChannel] +} diff --git a/integrations/push-protocol/hooks/use-chats.ts b/integrations/push-protocol/hooks/use-chats.ts new file mode 100644 index 00000000..fb207aa1 --- /dev/null +++ b/integrations/push-protocol/hooks/use-chats.ts @@ -0,0 +1,21 @@ +import * as PushAPI from '@pushprotocol/restapi' + +import { usePushQuery } from './use-push-query' +import { UseChatsProps } from '../utils/types' + +const fetchChats = async (props: UseChatsProps) => { + return await PushAPI.chat.chats(props) +} + +export const useChats = (props: UseChatsProps) => { + return usePushQuery( + { + fetcher: () => fetchChats(props), + }, + [props.account, props.env, props.page, props.limit, props.pgpPrivateKey, props.toDecrypt] + ) +} + +export const useChatsLazy = () => { + return [fetchChats] +} diff --git a/integrations/push-protocol/hooks/use-create-user.ts b/integrations/push-protocol/hooks/use-create-user.ts new file mode 100644 index 00000000..68a38645 --- /dev/null +++ b/integrations/push-protocol/hooks/use-create-user.ts @@ -0,0 +1,21 @@ +import * as PushAPI from '@pushprotocol/restapi' +import { CreateUserProps } from '@pushprotocol/restapi/src/lib/user' + +import { usePushAction } from './use-push-action' + +const createUser = (args: CreateUserProps) => { + return PushAPI.user.create(args) +} + +export const useCreateUser = () => { + return usePushAction( + { + fetcher: createUser, + }, + [] + ) +} + +export const useCreateUserLazy = () => { + return [createUser] +} diff --git a/integrations/push-protocol/hooks/use-notifications.ts b/integrations/push-protocol/hooks/use-notifications.ts new file mode 100644 index 00000000..ad3e8ca0 --- /dev/null +++ b/integrations/push-protocol/hooks/use-notifications.ts @@ -0,0 +1,29 @@ +import * as PushAPI from '@pushprotocol/restapi' + +import { usePushQuery } from './use-push-query' +import { UseNotificationsProps } from '../utils/types' + +const fetchNotifications = async (props: UseNotificationsProps) => { + return (await PushAPI.user.getFeeds({ + ...props, + raw: true, + })) as PushAPI.ApiNotificationType[] +} + +export const useNotifications = (props: UseNotificationsProps) => { + return usePushQuery( + { + fetcher: async () => { + const result = await fetchNotifications(props) + if (!result) throw new Error('Can not find notifications') // Push SDK doesn't throw errors :( + + return result + }, + }, + [props.user, props.env, props.page, props.limit, props.raw] + ) +} + +export const useNotificationsLazy = () => { + return [fetchNotifications] +} diff --git a/integrations/push-protocol/hooks/use-push-action.ts b/integrations/push-protocol/hooks/use-push-action.ts new file mode 100644 index 00000000..c3ee123c --- /dev/null +++ b/integrations/push-protocol/hooks/use-push-action.ts @@ -0,0 +1,27 @@ +import { DependencyList, useCallback, useState } from 'react' + +import { UsePushActionProps } from '../utils/types' + +export const usePushAction = (props: UsePushActionProps, deps: DependencyList) => { + const [data, setData] = useState() + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState() + + const action = useCallback((args: F) => { + setIsLoading(true) + + return props + .fetcher(args) + .then((data) => { + setData(data) + return data + }) + .catch((e: Error) => { + setError(e) + throw e + }) + .finally(() => setIsLoading(false)) + }, deps) + + return { data, isLoading, error, action } +} diff --git a/integrations/push-protocol/hooks/use-push-query.ts b/integrations/push-protocol/hooks/use-push-query.ts new file mode 100644 index 00000000..cc79f4e4 --- /dev/null +++ b/integrations/push-protocol/hooks/use-push-query.ts @@ -0,0 +1,21 @@ +import { DependencyList, useCallback, useEffect, useState } from 'react' + +import { UsePushQueryProps, UsePushQueryReturn } from '../utils/types' + +export const usePushQuery = (props: UsePushQueryProps, deps: DependencyList): UsePushQueryReturn => { + const [data, setData] = useState() + const [error, setError] = useState() + const [isLoading, setIsLoading] = useState(false) + + const refetch = useCallback(async () => props.fetcher(), deps) + + useEffect(() => { + setIsLoading(true) + refetch() + .then((res) => setData(res)) + .catch((e) => setError(e as Error)) + .finally(() => setIsLoading(false)) + }, [refetch]) + + return { data, isLoading, error, refetch } +} diff --git a/integrations/push-protocol/hooks/use-search-channels.ts b/integrations/push-protocol/hooks/use-search-channels.ts new file mode 100644 index 00000000..d87a8ac9 --- /dev/null +++ b/integrations/push-protocol/hooks/use-search-channels.ts @@ -0,0 +1,21 @@ +import * as PushAPI from '@pushprotocol/restapi' + +import { usePushQuery } from './use-push-query' +import { Channel, UseSearchChannelProps } from '../utils/types' + +const searchChannels = async (props: UseSearchChannelProps) => { + return (await PushAPI.channels.search(props)) as Channel[] +} + +export const useSearchChannels = (props: UseSearchChannelProps) => { + return usePushQuery( + { + fetcher: () => searchChannels(props), + }, + [props.query, props.env, props.page, props.limit] + ) +} + +export const useSearchChannelsLazy = () => { + return [searchChannels] +} diff --git a/integrations/push-protocol/hooks/use-send-notifications.ts b/integrations/push-protocol/hooks/use-send-notifications.ts new file mode 100644 index 00000000..8d205070 --- /dev/null +++ b/integrations/push-protocol/hooks/use-send-notifications.ts @@ -0,0 +1,20 @@ +import * as PushAPI from '@pushprotocol/restapi' + +import { usePushAction } from './use-push-action' + +const sendNotification = (args: PushAPI.ISendNotificationInputOptions) => { + return PushAPI.payloads.sendNotification(args) +} + +export const useSendNotification = () => { + return usePushAction( + { + fetcher: sendNotification, + }, + [] + ) +} + +export const useSendNotificationLazy = () => { + return [sendNotification] +} diff --git a/integrations/push-protocol/hooks/use-subscribe-channel.ts b/integrations/push-protocol/hooks/use-subscribe-channel.ts new file mode 100644 index 00000000..1c530cab --- /dev/null +++ b/integrations/push-protocol/hooks/use-subscribe-channel.ts @@ -0,0 +1,32 @@ +import * as PushAPI from '@pushprotocol/restapi' +import { SubscribeOptionsType } from '@pushprotocol/restapi/src/lib/channels' + +import { usePushAction } from './use-push-action' +import { UseSubscribeOrUnsubscribeToChannelProps } from '../utils/types' + +const subOrUnsub = (action: UseSubscribeOrUnsubscribeToChannelProps['action'], args: SubscribeOptionsType) => { + return PushAPI.channels[action](args) +} + +export const useSubscribeOrUnsubscribeToChannel = (props: UseSubscribeOrUnsubscribeToChannelProps) => { + return usePushAction( + { + fetcher: async (args: SubscribeOptionsType) => { + const result = await subOrUnsub(props.action, args) + + if (result.status === 'error') throw new Error(result.message) // Push SDK doesn't throw error + + return result + }, + }, + [props.action] + ) +} + +export const useSubscribeToChannelLazy = () => { + return [PushAPI.channels.subscribe] +} + +export const useUnsubscribeToChannelLazy = () => { + return [PushAPI.channels.unsubscribe] +} diff --git a/integrations/push-protocol/hooks/use-user-subscriptions.ts b/integrations/push-protocol/hooks/use-user-subscriptions.ts new file mode 100644 index 00000000..cedf2d57 --- /dev/null +++ b/integrations/push-protocol/hooks/use-user-subscriptions.ts @@ -0,0 +1,21 @@ +import * as PushAPI from '@pushprotocol/restapi' + +import { usePushQuery } from './use-push-query' +import { UseUserSubscriptionProps, UserSubscription } from '../utils/types' + +const fetchUserSubscriptions = async (props: UseUserSubscriptionProps) => { + return (await PushAPI.user.getSubscriptions(props)) as UserSubscription[] +} + +export const useUserSubscriptions = (props: UseUserSubscriptionProps) => { + return usePushQuery( + { + fetcher: () => fetchUserSubscriptions(props), + }, + [props.env, props.user] + ) +} + +export const useUserSubscriptionsLazy = () => { + return [fetchUserSubscriptions] +} diff --git a/integrations/push-protocol/index.ts b/integrations/push-protocol/index.ts new file mode 100644 index 00000000..c1e0adf9 --- /dev/null +++ b/integrations/push-protocol/index.ts @@ -0,0 +1,4 @@ +export * from './utils/types' +export * from './hooks' +export * from './components' +export { ENV } from '@pushprotocol/uiweb' diff --git a/integrations/push-protocol/utils/types.ts b/integrations/push-protocol/utils/types.ts new file mode 100644 index 00000000..66e82fee --- /dev/null +++ b/integrations/push-protocol/utils/types.ts @@ -0,0 +1,55 @@ +import { GetChannelOptionsType, SearchChannelOptionsType } from '@pushprotocol/restapi/src/lib/channels' +import { ChatsOptionsType } from '@pushprotocol/restapi/src/lib/chat' +import { FeedsOptionsType, UserSubscriptionsOptionsType } from '@pushprotocol/restapi/src/lib/user' + +export type PartialBy = Omit & Partial> + +export type UsePushQueryReturn = { + data?: T + error?: Error + isLoading: boolean + refetch: () => Promise +} +export type UsePushQueryProps = { + fetcher: () => Promise +} + +export type UsePushActionReturn = UsePushQueryReturn +export type UsePushActionProps = Omit, 'fetcher'> & { + fetcher: (args: F) => Promise +} + +export type UseNotificationsProps = FeedsOptionsType +export type UseChannelProps = GetChannelOptionsType +export type UseSearchChannelProps = SearchChannelOptionsType +export type UseUserSubscriptionProps = UserSubscriptionsOptionsType +export type UseSubscribeOrUnsubscribeToChannelProps = { + action: 'subscribe' | 'unsubscribe' +} +export type UseChatsProps = ChatsOptionsType + +export type Channel = { + id: number + channel: string + ipfshash: string + name: string + info: string + url: string + icon: string + processed: number + attempts: number + alias_address: string | null + alias_verification_event: string | null + is_alias_verified: number + alias_blockchain_id: string | null + activation_status: number + verified_status: number + timestamp: string + blocked: number + subgraph_attempts: number + subscriber_count: number +} + +export type UserSubscription = { + channel: string +} diff --git a/lib/generated/blockchain.ts b/lib/generated/blockchain.ts index 9ef5dee7..5738ed6c 100644 --- a/lib/generated/blockchain.ts +++ b/lib/generated/blockchain.ts @@ -1,4 +1,4 @@ -// Generated by @wagmi/cli@1.1.0 on 6/14/2023 at 3:16:55 PM +// Generated by @wagmi/cli@1.1.0 on 7/6/2023 at 10:35:14 AM import { useContractRead, UseContractReadConfig, diff --git a/package.json b/package.json index d0100fc2..da1489fb 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,8 @@ "dependencies": { "@lit-protocol/lit-node-client": "2.1.161", "@prisma/client": "^4.8.1", + "@pushprotocol/restapi": "^1.3.9", + "@pushprotocol/uiweb": "1.0.1", "@radix-ui/react-accordion": "^1.1.0", "@radix-ui/react-alert-dialog": "^1.0.2", "@radix-ui/react-aspect-ratio": "^1.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2abfda09..8b0a0bdb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + dependencies: '@lit-protocol/lit-node-client': specifier: 2.1.161 @@ -7,6 +11,12 @@ dependencies: '@prisma/client': specifier: ^4.8.1 version: 4.11.0(prisma@4.11.0) + '@pushprotocol/restapi': + specifier: ^1.3.9 + version: 1.3.9(ethers@5.7.2) + '@pushprotocol/uiweb': + specifier: 1.0.1 + version: 1.0.1(@pushprotocol/restapi@1.3.9)(@pushprotocol/socket@0.4.2)(ethers@5.7.2)(react@18.2.0)(styled-components@5.3.11) '@radix-ui/react-accordion': specifier: ^1.1.0 version: 1.1.1(react-dom@18.2.0)(react@18.2.0) @@ -331,7 +341,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 - dev: true /@babel/compat-data@7.21.0: resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==} @@ -352,7 +361,7 @@ packages: '@babel/traverse': 7.21.2 '@babel/types': 7.21.2 convert-source-map: 1.9.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.0 @@ -374,7 +383,7 @@ packages: '@babel/traverse': 7.21.2 '@babel/types': 7.21.2 convert-source-map: 1.9.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.0 @@ -399,7 +408,6 @@ packages: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.18 jsesc: 2.5.2 - dev: true /@babel/helper-annotate-as-pure@7.18.6: resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} @@ -408,6 +416,13 @@ packages: '@babel/types': 7.21.5 dev: true + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.5 + dev: false + /@babel/helper-builder-binary-assignment-operator-visitor@7.18.9: resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==} engines: {node: '>=6.9.0'} @@ -481,7 +496,7 @@ packages: '@babel/core': 7.21.0 '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.21.0) '@babel/helper-plugin-utils': 7.20.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) lodash.debounce: 4.0.8 resolve: 1.22.2 semver: 6.3.0 @@ -496,7 +511,6 @@ packages: /@babel/helper-environment-visitor@7.21.5: resolution: {integrity: sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-explode-assignable-expression@7.18.6: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} @@ -531,6 +545,13 @@ packages: dependencies: '@babel/types': 7.21.2 + /@babel/helper-module-imports@7.22.5: + resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.5 + dev: false + /@babel/helper-module-transforms@7.21.2: resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} engines: {node: '>=6.9.0'} @@ -558,6 +579,11 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: false + /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.21.0): resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} engines: {node: '>=6.9.0'} @@ -581,7 +607,7 @@ packages: '@babel/helper-member-expression-to-functions': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/template': 7.20.7 - '@babel/traverse': 7.21.5 + '@babel/traverse': 7.21.5(supports-color@5.5.0) '@babel/types': 7.21.5 transitivePeerDependencies: - supports-color @@ -613,12 +639,21 @@ packages: /@babel/helper-string-parser@7.21.5: resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==} engines: {node: '>=6.9.0'} - dev: true + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: false /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.22.5: + resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} + engines: {node: '>=6.9.0'} + dev: false + /@babel/helper-validator-option@7.21.0: resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} engines: {node: '>=6.9.0'} @@ -629,7 +664,7 @@ packages: dependencies: '@babel/helper-function-name': 7.21.0 '@babel/template': 7.20.7 - '@babel/traverse': 7.21.5 + '@babel/traverse': 7.21.5(supports-color@5.5.0) '@babel/types': 7.21.5 transitivePeerDependencies: - supports-color @@ -666,7 +701,6 @@ packages: hasBin: true dependencies: '@babel/types': 7.21.5 - dev: true /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.21.0): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} @@ -949,6 +983,16 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.18.5): + resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.18.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.0): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -1627,12 +1671,12 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.21.2 '@babel/types': 7.21.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color - /@babel/traverse@7.21.5: + /@babel/traverse@7.21.5(supports-color@5.5.0): resolution: {integrity: sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==} engines: {node: '>=6.9.0'} dependencies: @@ -1644,11 +1688,10 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.21.8 '@babel/types': 7.21.5 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color - dev: true /@babel/types@7.21.2: resolution: {integrity: sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==} @@ -1665,7 +1708,15 @@ packages: '@babel/helper-string-parser': 7.21.5 '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - dev: true + + /@babel/types@7.22.5: + resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.5 + to-fast-properties: 2.0.0 + dev: false /@coinbase/wallet-sdk@3.6.6: resolution: {integrity: sha512-vX+epj/Ttjo7XRwlr3TFUUfW5GTRMvORpERPwiu7z2jl3DSVL4rXLmHt5y6LDPlUVreas2gumdcFbu0fLRG9Jg==} @@ -2003,11 +2054,29 @@ packages: dev: false optional: true + /@emotion/is-prop-valid@1.2.1: + resolution: {integrity: sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==} + dependencies: + '@emotion/memoize': 0.8.1 + dev: false + /@emotion/memoize@0.7.4: resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} dev: false optional: true + /@emotion/memoize@0.8.1: + resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} + dev: false + + /@emotion/stylis@0.8.5: + resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} + dev: false + + /@emotion/unitless@0.7.5: + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + dev: false + /@esbuild/android-arm@0.15.13: resolution: {integrity: sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==} engines: {node: '>=12'} @@ -2046,7 +2115,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) espree: 9.5.0 globals: 13.20.0 ignore: 5.2.4 @@ -2058,6 +2127,21 @@ packages: - supports-color dev: true + /@ethereumjs/rlp@4.0.1: + resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} + engines: {node: '>=14'} + hasBin: true + dev: false + + /@ethereumjs/util@8.1.0: + resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==} + engines: {node: '>=14'} + dependencies: + '@ethereumjs/rlp': 4.0.1 + ethereum-cryptography: 2.0.0 + micro-ftch: 0.3.1 + dev: false + /@ethersproject/abi@5.7.0: resolution: {integrity: sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==} dependencies: @@ -2372,7 +2456,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -2443,7 +2527,6 @@ packages: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.18 - dev: true /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} @@ -2458,7 +2541,6 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} @@ -2471,7 +2553,6 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 - dev: true /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -2739,6 +2820,18 @@ packages: dependencies: '@lit-labs/ssr-dom-shim': 1.1.1 + /@metamask/eth-sig-util@5.1.0: + resolution: {integrity: sha512-mlgziIHYlA9pi/XZerChqg4NocdOgBPB9NmxgXWQO2U2hH8RGOJQrz6j/AIKkYxgCMIE2PY000+joOwXfzeTDQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@ethereumjs/util': 8.1.0 + bn.js: 4.12.0 + ethereum-cryptography: 2.0.0 + ethjs-util: 0.1.6 + tweetnacl: 1.0.3 + tweetnacl-util: 0.15.1 + dev: false + /@metamask/safe-event-emitter@2.0.0: resolution: {integrity: sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==} @@ -2747,7 +2840,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@types/debug': 4.1.7 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) semver: 7.5.0 superstruct: 1.0.3 transitivePeerDependencies: @@ -3068,6 +3161,59 @@ packages: resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} dev: false + /@pushprotocol/restapi@1.3.9(ethers@5.7.2): + resolution: {integrity: sha512-aSvuxnWRO5tNrlLqfc7jDE8t3sSgHdxLWINPRpl+EE3D1eVIPZj5BfxGMyzTN9k96ZllUHHdzvR28vp6bxsAkw==} + peerDependencies: + ethers: ^5.6.8 + dependencies: + '@metamask/eth-sig-util': 5.1.0 + axios: 0.27.2 + buffer: 6.0.3 + crypto-js: 4.1.1 + ethers: 5.7.2 + immer: 10.0.2 + openpgp: 5.9.0 + simple-peer: 9.11.1 + tslib: 2.5.0 + uuid: 9.0.0 + transitivePeerDependencies: + - debug + - supports-color + dev: false + + /@pushprotocol/socket@0.4.2(ethers@5.7.2): + resolution: {integrity: sha512-Pcm0zCT9lB49stuFK+pMORD1UJkJjVzKXhWofQQxbIsUpG10pujZhVfoJtohDT99k+AN2EZLWCImU8j0rfwVlw==} + peerDependencies: + ethers: ^5.6.8 + dependencies: + ethers: 5.7.2 + socket.io-client: 4.7.1 + tslib: 2.5.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + + /@pushprotocol/uiweb@1.0.1(@pushprotocol/restapi@1.3.9)(@pushprotocol/socket@0.4.2)(ethers@5.7.2)(react@18.2.0)(styled-components@5.3.11): + resolution: {integrity: sha512-/jo0qQU1okiltr4YFoqa3cNaQDoC1iVXWU4xyCJtHf73WBrghsNW8uDuQupI1+AVXLCKoD6iJfTxlIjSqkw9pg==} + peerDependencies: + '@pushprotocol/restapi': ^1.2.8 + '@pushprotocol/socket': ^0.4.2 + ethers: ^5.7.1 + react: '>=16.8.0' + styled-components: ^5.3.5 + dependencies: + '@pushprotocol/restapi': 1.3.9(ethers@5.7.2) + '@pushprotocol/socket': 0.4.2(ethers@5.7.2) + date-fns: 2.30.0 + emoji-picker-react: 3.6.5 + ethers: 5.7.2 + html-react-parser: 1.4.14(react@18.2.0) + react: 18.2.0 + styled-components: 5.3.11(@babel/core@7.18.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + dev: false + /@radix-ui/number@1.0.0: resolution: {integrity: sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==} dependencies: @@ -3973,6 +4119,10 @@ packages: '@noble/hashes': 1.3.0 '@scure/base': 1.1.1 + /@socket.io/component-emitter@3.1.0: + resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + dev: false + /@solana/buffer-layout@4.0.1: resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} engines: {node: '>=5.10'} @@ -4639,7 +4789,7 @@ packages: '@typescript-eslint/scope-manager': 5.59.11 '@typescript-eslint/type-utils': 5.59.11(eslint@8.30.0)(typescript@4.9.4) '@typescript-eslint/utils': 5.59.11(eslint@8.30.0)(typescript@4.9.4) - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) eslint: 8.30.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 @@ -4664,7 +4814,7 @@ packages: '@typescript-eslint/scope-manager': 5.59.11 '@typescript-eslint/types': 5.59.11 '@typescript-eslint/typescript-estree': 5.59.11(typescript@4.9.4) - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) eslint: 8.30.0 typescript: 4.9.4 transitivePeerDependencies: @@ -4699,7 +4849,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 5.59.11(typescript@4.9.4) '@typescript-eslint/utils': 5.59.11(eslint@8.30.0)(typescript@4.9.4) - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) eslint: 8.30.0 tsutils: 3.21.0(typescript@4.9.4) typescript: 4.9.4 @@ -4728,7 +4878,7 @@ packages: dependencies: '@typescript-eslint/types': 5.54.1 '@typescript-eslint/visitor-keys': 5.54.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.0 @@ -4749,7 +4899,7 @@ packages: dependencies: '@typescript-eslint/types': 5.59.11 '@typescript-eslint/visitor-keys': 5.59.11 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.0 @@ -5636,7 +5786,7 @@ packages: resolution: {integrity: sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==} engines: {node: '>= 8.0.0'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) depd: 2.0.0 humanize-ms: 1.2.1 transitivePeerDependencies: @@ -5810,6 +5960,15 @@ packages: engines: {node: '>=0.10.0'} dev: true + /asn1.js@5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + dependencies: + bn.js: 4.12.0 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + dev: false + /asn1js@3.0.5: resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} engines: {node: '>=12.0.0'} @@ -5873,6 +6032,15 @@ packages: transitivePeerDependencies: - debug + /axios@0.27.2: + resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + transitivePeerDependencies: + - debug + dev: false + /axios@1.3.4: resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==} dependencies: @@ -5925,6 +6093,21 @@ packages: - supports-color dev: true + /babel-plugin-styled-components@2.1.4(@babel/core@7.18.5)(styled-components@5.3.11): + resolution: {integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==} + peerDependencies: + styled-components: '>= 2' + dependencies: + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-module-imports': 7.22.5 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.18.5) + lodash: 4.17.21 + picomatch: 2.3.1 + styled-components: 5.3.11(@babel/core@7.18.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + transitivePeerDependencies: + - '@babel/core' + dev: false + /bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} dev: false @@ -6134,6 +6317,10 @@ packages: engines: {node: '>=10'} dev: true + /camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + dev: false + /caniuse-lite@1.0.30001464: resolution: {integrity: sha512-oww27MtUmusatpRpCGSOneQk2/l5czXANDSFvsc7VuOQ86s3ANhZetpwXNf1zY/zdfP63Xvjz325DAdAoES13g==} dev: true @@ -6492,6 +6679,15 @@ packages: which: 2.0.2 dev: true + /crypto-js@4.1.1: + resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} + dev: false + + /css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + dev: false + /css-mediaquery@0.1.2: resolution: {integrity: sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==} dev: false @@ -6506,6 +6702,14 @@ packages: nth-check: 2.1.1 dev: true + /css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + dev: false + /css-tree@1.1.3: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} @@ -6569,7 +6773,6 @@ packages: engines: {node: '>=0.11'} dependencies: '@babel/runtime': 7.21.5 - dev: true /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} @@ -6582,7 +6785,7 @@ packages: ms: 2.1.3 dev: true - /debug@4.3.4: + /debug@4.3.4(supports-color@5.5.0): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -6592,6 +6795,7 @@ packages: optional: true dependencies: ms: 2.1.2 + supports-color: 5.5.0 /decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} @@ -6784,7 +6988,7 @@ packages: /dns-over-http-resolver@1.2.3(node-fetch@2.6.9): resolution: {integrity: sha512-miDiVSI6KSNbi4SVifzO/reD8rMnxgrlnkrlkugOLQpWQTe2qMdHsZp5DmfKjxNE+/T3VAAYLQUZMv9SMr6+AA==} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) native-fetch: 3.0.0(node-fetch@2.6.9) receptacle: 1.3.2 transitivePeerDependencies: @@ -6816,18 +7020,15 @@ packages: domelementtype: 2.3.0 domhandler: 4.3.1 entities: 2.2.0 - dev: true /domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - dev: true /domhandler@4.3.1: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} engines: {node: '>= 4'} dependencies: domelementtype: 2.3.0 - dev: true /domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} @@ -6835,7 +7036,6 @@ packages: dom-serializer: 1.4.1 domelementtype: 2.3.0 domhandler: 4.3.1 - dev: true /dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -6903,6 +7103,10 @@ packages: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + /emoji-picker-react@3.6.5: + resolution: {integrity: sha512-pfu3XkHSeqXjygyoKtRsmJdsNkRxhkE7hlnWrYBoPnm8V03aJ8Y9H5oRUQ+fF4WRZpjfJFsw5V7ewRVhuj/8cA==} + dev: false + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -6924,6 +7128,25 @@ packages: dependencies: once: 1.4.0 + /engine.io-client@6.5.1: + resolution: {integrity: sha512-hE5wKXH8Ru4L19MbM1GgYV/2Qo54JSMh1rlJbfpa40bEWkCKNo3ol2eOtGmowcr+ysgbI7+SGL+by42Q3pt/Ng==} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4(supports-color@5.5.0) + engine.io-parser: 5.1.0 + ws: 8.11.0 + xmlhttprequest-ssl: 2.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + + /engine.io-parser@5.1.0: + resolution: {integrity: sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==} + engines: {node: '>=10.0.0'} + dev: false + /enhanced-resolve@5.13.0: resolution: {integrity: sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==} engines: {node: '>=10.13.0'} @@ -6934,7 +7157,11 @@ packages: /entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - dev: true + + /entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + dev: false /entities@4.4.0: resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} @@ -7311,7 +7538,7 @@ packages: eslint: '*' eslint-plugin-import: '*' dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) enhanced-resolve: 5.13.0 eslint: 8.30.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.30.0) @@ -7643,7 +7870,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 @@ -7749,6 +7976,15 @@ packages: dependencies: fast-safe-stringify: 2.1.1 + /ethereum-cryptography@2.0.0: + resolution: {integrity: sha512-g25m4EtfQGjstWgVE1aIz7XYYjf3kH5kG17ULWVB5dH6uLahsoltOhACzSxyDV+fhn4gbR4xRrOXGe6r2uh4Bg==} + dependencies: + '@noble/curves': 1.0.0 + '@noble/hashes': 1.3.0 + '@scure/bip32': 1.3.0 + '@scure/bip39': 1.2.0 + dev: false + /ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} dependencies: @@ -7786,6 +8022,14 @@ packages: - bufferutil - utf-8-validate + /ethjs-util@0.1.6: + resolution: {integrity: sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==} + engines: {node: '>=6.5.0', npm: '>=3'} + dependencies: + is-hex-prefixed: 1.0.0 + strip-hex-prefix: 1.0.0 + dev: false + /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -8078,6 +8322,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + /get-browser-rtc@1.1.0: + resolution: {integrity: sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==} + dev: false + /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -8299,6 +8547,12 @@ packages: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + /hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + dependencies: + react-is: 16.13.1 + dev: false + /hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true @@ -8310,6 +8564,34 @@ packages: lru-cache: 6.0.0 dev: true + /html-dom-parser@1.2.0: + resolution: {integrity: sha512-2HIpFMvvffsXHFUFjso0M9LqM+1Lm22BF+Df2ba+7QHJXjk63pWChEnI6YG27eaWqUdfnh5/Vy+OXrNTtepRsg==} + dependencies: + domhandler: 4.3.1 + htmlparser2: 7.2.0 + dev: false + + /html-react-parser@1.4.14(react@18.2.0): + resolution: {integrity: sha512-pxhNWGie8Y+DGDpSh8cTa0k3g8PsDcwlfolA+XxYo1AGDeB6e2rdlyv4ptU9bOTiZ2i3fID+6kyqs86MN0FYZQ==} + peerDependencies: + react: 0.14 || 15 || 16 || 17 || 18 + dependencies: + domhandler: 4.3.1 + html-dom-parser: 1.2.0 + react: 18.2.0 + react-property: 2.0.0 + style-to-js: 1.1.1 + dev: false + + /htmlparser2@7.2.0: + resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 3.0.1 + dev: false + /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -8364,6 +8646,10 @@ packages: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} dev: false + /immer@10.0.2: + resolution: {integrity: sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==} + dev: false + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -8464,7 +8750,7 @@ packages: any-signal: 3.0.1 blob-to-it: 1.0.4 browser-readablestream-to-it: 1.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) err-code: 3.0.1 ipfs-core-types: 0.10.3(node-fetch@2.6.9) ipfs-unixfs: 6.0.9 @@ -8496,7 +8782,7 @@ packages: '@ipld/dag-pb': 2.1.18 any-signal: 3.0.1 dag-jose: 1.0.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) err-code: 3.0.1 ipfs-core-types: 0.10.3(node-fetch@2.6.9) ipfs-core-utils: 0.14.3(node-fetch@2.6.9) @@ -8716,6 +9002,11 @@ packages: dependencies: is-extglob: 2.1.1 + /is-hex-prefixed@1.0.0: + resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} + engines: {node: '>=6.5.0', npm: '>=3'} + dev: false + /is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -9213,7 +9504,7 @@ packages: chalk: 5.2.0 cli-truncate: 3.1.0 commander: 10.0.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) execa: 7.1.0 lilconfig: 2.1.0 listr2: 5.0.8 @@ -9548,6 +9839,10 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + /micro-ftch@0.3.1: + resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==} + dev: false + /micromark-core-commonmark@1.0.6: resolution: {integrity: sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==} dependencies: @@ -9704,7 +9999,7 @@ packages: resolution: {integrity: sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==} dependencies: '@types/debug': 4.1.7 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) decode-named-character-reference: 1.0.2 micromark-core-commonmark: 1.0.6 micromark-factory-space: 1.0.0 @@ -10145,6 +10440,13 @@ packages: is-wsl: 2.2.0 dev: true + /openpgp@5.9.0: + resolution: {integrity: sha512-wEI6TAinCAq8ZLZA4oZ3ZtJ2BhhHj+CiPCd8TzE7zCicr0V8tvG5UF76OtddLLOJcK63w3Aj3KiRd+VLMScirQ==} + engines: {node: '>= 8.0.0'} + dependencies: + asn1.js: 5.4.1 + dev: false + /optionator@0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} @@ -10724,7 +11026,7 @@ packages: dependencies: '@assemblyscript/loader': 0.9.4 bl: 5.1.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) minimist: 1.2.8 node-fetch: 2.6.9 readable-stream: 3.6.2 @@ -10818,6 +11120,10 @@ packages: p-defer: 3.0.0 dev: false + /react-property@2.0.0: + resolution: {integrity: sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==} + dev: false + /react-remove-scroll-bar@2.3.4(@types/react@18.0.26)(react@18.2.0): resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} engines: {node: '>=10'} @@ -11288,6 +11594,10 @@ packages: resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} dev: false + /shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + dev: false + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -11315,6 +11625,20 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /simple-peer@9.11.1: + resolution: {integrity: sha512-D1SaWpOW8afq1CZGWB8xTfrT3FekjQmPValrqncJMX7QFl8YwhrPTZvMCANLtgBwwdS+7zURyqxDDEmY558tTw==} + dependencies: + buffer: 6.0.3 + debug: 4.3.4(supports-color@5.5.0) + err-code: 3.0.1 + get-browser-rtc: 1.1.0 + queue-microtask: 1.2.3 + randombytes: 2.1.0 + readable-stream: 3.6.2 + transitivePeerDependencies: + - supports-color + dev: false + /siwe@1.1.6(ethers@5.7.2): resolution: {integrity: sha512-3WRdEil32Tc2vuNzqJ2/Z/MIvsvy0Nkzc2ov+QujmpHO7tM83dgcb47z0Pu236T4JQkOQCqQkq3AJ/rVIezniA==} peerDependencies: @@ -11369,6 +11693,30 @@ packages: tslib: 2.5.0 dev: true + /socket.io-client@4.7.1: + resolution: {integrity: sha512-Qk3Xj8ekbnzKu3faejo4wk2MzXA029XppiXtTF/PkbTg+fcwaTw1PlDrTrrrU4mKoYC4dvlApOnSeyLCKwek2w==} + engines: {node: '>=10.0.0'} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4(supports-color@5.5.0) + engine.io-client: 6.5.1 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + + /socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + dev: false + /sonic-boom@2.8.0: resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} dependencies: @@ -11566,6 +11914,13 @@ packages: engines: {node: '>=12'} dev: true + /strip-hex-prefix@1.0.0: + resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} + engines: {node: '>=6.5.0', npm: '>=3'} + dependencies: + is-hex-prefixed: 1.0.0 + dev: false + /strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -11578,12 +11933,49 @@ packages: engines: {node: '>=8'} dev: true + /style-to-js@1.1.1: + resolution: {integrity: sha512-RJ18Z9t2B02sYhZtfWKQq5uplVctgvjTfLWT7+Eb1zjUjIrWzX5SdlkwLGQozrqarTmEzJJ/YmdNJCUNI47elg==} + dependencies: + style-to-object: 0.3.0 + dev: false + + /style-to-object@0.3.0: + resolution: {integrity: sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==} + dependencies: + inline-style-parser: 0.1.1 + dev: false + /style-to-object@0.4.1: resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==} dependencies: inline-style-parser: 0.1.1 dev: false + /styled-components@5.3.11(@babel/core@7.18.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + react-is: '>= 16.8.0' + dependencies: + '@babel/helper-module-imports': 7.18.6 + '@babel/traverse': 7.21.5(supports-color@5.5.0) + '@emotion/is-prop-valid': 1.2.1 + '@emotion/stylis': 0.8.5 + '@emotion/unitless': 0.7.5 + babel-plugin-styled-components: 2.1.4(@babel/core@7.18.5)(styled-components@5.3.11) + css-to-react-native: 3.2.0 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 18.2.0 + shallowequal: 1.1.0 + supports-color: 5.5.0 + transitivePeerDependencies: + - '@babel/core' + dev: false + /styled-jsx@5.1.1(@babel/core@7.18.5)(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -12116,6 +12508,11 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + /uuid@9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: false + /uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} engines: {node: '>=8'} @@ -12391,6 +12788,19 @@ packages: utf-8-validate: optional: true + /ws@8.11.0: + resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /ws@8.12.0: resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} engines: {node: '>=10.0.0'} @@ -12418,6 +12828,11 @@ packages: bufferutil: 4.0.7 utf-8-validate: 5.0.10 + /xmlhttprequest-ssl@2.0.0: + resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} + engines: {node: '>=0.4.0'} + dev: false + /xstream@11.14.0: resolution: {integrity: sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw==} dependencies: diff --git a/public/integrations/push.svg b/public/integrations/push.svg new file mode 100644 index 00000000..31dc556b --- /dev/null +++ b/public/integrations/push.svg @@ -0,0 +1,65 @@ + + Push + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 401df2488ffeaad47ddd9734430dba2e46c14630 Mon Sep 17 00:00:00 2001 From: Giorgi Tsirekidze Date: Thu, 6 Jul 2023 11:03:26 +0400 Subject: [PATCH 02/13] fix: update readme --- integrations/push-protocol/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/integrations/push-protocol/README.md b/integrations/push-protocol/README.md index 3a8cf80d..a48b3f52 100644 --- a/integrations/push-protocol/README.md +++ b/integrations/push-protocol/README.md @@ -48,6 +48,13 @@ Subscribe to a channel hook const { data, isLoading, error, action: subscribe } = useSubscribeOrUnsubscribeToChannel({ action: 'subscribe' }) ``` +Send notification hook +```tsx +const { data, isLoading, error, action: sendNotification } = useSendNotification() + +sendNotification({..args}) +``` + ### Hooks for retrieving data From 210afc902261e28ef82f8393aad87b55917d664e Mon Sep 17 00:00:00 2001 From: Giorgi Tsirekidze Date: Thu, 6 Jul 2023 16:01:46 +0400 Subject: [PATCH 03/13] fix: formatted readme with prettier --- integrations/push-protocol/README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/integrations/push-protocol/README.md b/integrations/push-protocol/README.md index a48b3f52..f98568a2 100644 --- a/integrations/push-protocol/README.md +++ b/integrations/push-protocol/README.md @@ -27,14 +27,15 @@ Renders simple card with channel information and interactive subscribe button `Chat` Renders native Push Protocol support chat window on bottom-right corner of the screen - --- ## Hooks -This integration has two types of hooks for accessing and modifying data on PUSH. We can split them as query and action hooks. + +This integration has two types of hooks for accessing and modifying data on PUSH. We can split them as query and action hooks. They have standard rule of implementation, for example: Retrieve channel data hook. + ```tsx const { data, isLoading, error, refetch } = useNotifications({ user: address as string, @@ -44,18 +45,19 @@ const { data, isLoading, error, refetch } = useNotifications({ ``` Subscribe to a channel hook + ```tsx const { data, isLoading, error, action: subscribe } = useSubscribeOrUnsubscribeToChannel({ action: 'subscribe' }) ``` Send notification hook + ```tsx const { data, isLoading, error, action: sendNotification } = useSendNotification() sendNotification({..args}) ``` - ### Hooks for retrieving data `useChannel` : Returns Push channel based on parameters @@ -81,8 +83,11 @@ sendNotification({..args}) Additionally, if you don't want to depend on state variables returned from the hooks above, you may add `Lazy` at the end of any hook, and it'll return fresh function for fetching the resource. For Example: ```tsx -const [getChannel] = useChannelLazy(); -getChannel({...args}).then().catch() // +const [getChannel] = useChannelLazy() + +getChannel({ ...args }) + .then() + .catch() // ``` ## File Structure @@ -112,4 +117,5 @@ integrations/push ├── index.ts ├── README.md └── utils - └── types.ts \ No newline at end of file + └── types.ts +``` From b2745ec306657da5050e16c522a0d1c5da24c015 Mon Sep 17 00:00:00 2001 From: Giorgi Tsirekidze Date: Sat, 8 Jul 2023 06:01:57 +0400 Subject: [PATCH 04/13] fix: fixed UI and updated hooks to use react-query --- .../integration/push-protocol/page.tsx | 47 ++++++-- integrations/push-protocol/README.md | 39 +++---- .../push-protocol/components/channel-card.tsx | 101 ++++-------------- .../components/notification-feed.tsx | 4 +- .../components/subscribe-button.tsx | 83 ++++++++++++++ .../push-protocol/components/types.ts | 5 + .../push-protocol/hooks/use-channel.ts | 22 ++-- integrations/push-protocol/hooks/use-chats.ts | 16 +-- .../push-protocol/hooks/use-create-user.ts | 16 +-- .../push-protocol/hooks/use-notifications.ts | 21 +--- .../push-protocol/hooks/use-push-action.ts | 27 ----- .../push-protocol/hooks/use-push-query.ts | 21 ---- .../hooks/use-search-channels.ts | 16 +-- .../hooks/use-send-notifications.ts | 16 +-- .../hooks/use-subscribe-channel.ts | 33 ++---- .../hooks/use-unsubscribe-channel.ts | 13 +++ .../hooks/use-user-subscriptions.ts | 16 +-- integrations/push-protocol/index.ts | 1 + integrations/push-protocol/utils/helpers.ts | 5 + integrations/push-protocol/utils/types.ts | 20 ---- lib/generated/blockchain.ts | 2 +- 21 files changed, 226 insertions(+), 298 deletions(-) create mode 100644 integrations/push-protocol/components/subscribe-button.tsx delete mode 100644 integrations/push-protocol/hooks/use-push-action.ts delete mode 100644 integrations/push-protocol/hooks/use-push-query.ts create mode 100644 integrations/push-protocol/hooks/use-unsubscribe-channel.ts create mode 100644 integrations/push-protocol/utils/helpers.ts diff --git a/app/(general)/integration/push-protocol/page.tsx b/app/(general)/integration/push-protocol/page.tsx index e62f6a10..a3cc6881 100644 --- a/app/(general)/integration/push-protocol/page.tsx +++ b/app/(general)/integration/push-protocol/page.tsx @@ -1,5 +1,7 @@ 'use client' +import { useState } from 'react' + import { motion } from 'framer-motion' import Balancer from 'react-wrap-balancer' import { useAccount } from 'wagmi' @@ -8,6 +10,7 @@ import { WalletConnect } from '@/components/blockchain/wallet-connect' import { IsWalletConnected } from '@/components/shared/is-wallet-connected' import { IsWalletDisconnected } from '@/components/shared/is-wallet-disconnected' import { LinkComponent } from '@/components/shared/link-component' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design' import { turboIntegrations } from '@/data/turbo-integrations' import { ChannelCard, ENV, NotificationFeed, useNotifications } from '@/integrations/push-protocol' @@ -27,6 +30,9 @@ export default function PageIntegration() { spam: true, }) + const [channelAddress, setChannelAddress] = useState('0x74415Bc4C4Bf4Baecc2DD372426F0a1D016Fa924') + const [env, setEnv] = useState(ENV.STAGING) + return ( <>
@@ -73,21 +79,48 @@ export default function PageIntegration() { spamNotifications={spamNotifications} spamNotificationsIsLoading={spamIsLoading} /> +
+
+

Inbox&Spam

+

Check inbox&spam notifications on Push

+
-
-
- +
+
+ setChannelAddress(e.target.value)} + />
-
- +
+
-
- +
+
+
+
+
+
+

Channel Preview

+

Preview and subscribe channel

+
diff --git a/integrations/push-protocol/README.md b/integrations/push-protocol/README.md index f98568a2..f99d886e 100644 --- a/integrations/push-protocol/README.md +++ b/integrations/push-protocol/README.md @@ -30,11 +30,7 @@ Renders native Push Protocol support chat window on bottom-right corner of the s --- ## Hooks - -This integration has two types of hooks for accessing and modifying data on PUSH. We can split them as query and action hooks. -They have standard rule of implementation, for example: - -Retrieve channel data hook. +These hooks are just push protocol specific wrappers for `react-query`. So it utilizes all the features `react-query` has. ```tsx const { data, isLoading, error, refetch } = useNotifications({ @@ -47,18 +43,18 @@ const { data, isLoading, error, refetch } = useNotifications({ Subscribe to a channel hook ```tsx -const { data, isLoading, error, action: subscribe } = useSubscribeOrUnsubscribeToChannel({ action: 'subscribe' }) +const { data, isLoading, error, mutateAsync: subscribe } = useSubscribe() ``` Send notification hook ```tsx -const { data, isLoading, error, action: sendNotification } = useSendNotification() +const { data, isLoading, error, mutateAsync: sendNotification } = useSendNotification() -sendNotification({..args}) +await sendNotification({..args}) ``` -### Hooks for retrieving data +### Query Hooks `useChannel` : Returns Push channel based on parameters @@ -70,25 +66,15 @@ sendNotification({..args}) `useUserSubscriptions` : Returns user channel subscriptions based on parameters -### Action Hooks - -`useSendNotifications` : Returns Push notifications based on parameters - -`useSubscribeOrUnsubscribeToChannel` : Returns Push notifications based on parameters +### Mutation Hooks -`createUser` : Returns Push channel based on parameters +`useSendNotifications` : Returns mutation for sending notifications -### Lazy hooks +`useSubscribe` : Returns mutation for subscribe action -Additionally, if you don't want to depend on state variables returned from the hooks above, you may add `Lazy` at the end of any hook, and it'll return fresh function for fetching the resource. For Example: +`useUnsubscribe` : Returns mutation for unsubscribe action -```tsx -const [getChannel] = useChannelLazy() - -getChannel({ ...args }) - .then() - .catch() // -``` +`createUser` : Returns mutation for creating user ## File Structure @@ -101,6 +87,7 @@ integrations/push │ ├── loadable.tsx │ ├── notification-feed.tsx │ ├── notification-item.tsx +│ ├── subscribe-button.tsx │ └── types.ts ├── hooks │ ├── index.ts @@ -108,14 +95,14 @@ integrations/push │ ├── use-chats.ts │ ├── use-create-user.ts │ ├── use-notifications.ts -│ ├── use-push-action.ts -│ ├── use-push-query.ts │ ├── use-search-channels.ts │ ├── use-send-notifications.ts │ ├── use-subscribe-channel.ts +│ ├── use-unsubscribe-channel.ts │ └── use-user-subscriptions.ts ├── index.ts ├── README.md └── utils + ├── helpers.ts └── types.ts ``` diff --git a/integrations/push-protocol/components/channel-card.tsx b/integrations/push-protocol/components/channel-card.tsx index e5bf4fef..b11abe3e 100644 --- a/integrations/push-protocol/components/channel-card.tsx +++ b/integrations/push-protocol/components/channel-card.tsx @@ -1,97 +1,40 @@ -import { useEffect, useState } from 'react' - -import { SubscribeOptionsType } from '@pushprotocol/restapi/src/lib/channels' -import { useAccount } from 'wagmi' - -import { useEthersSigner } from '@/lib/hooks/web3/use-ethers-signer' - import { Loadable } from './loadable' +import { SubscribeButton } from './subscribe-button' import { ChannelCardProps } from './types' -import { useChannel, useSubscribeOrUnsubscribeToChannel, useUserSubscriptions } from '../hooks' +import { useChannel } from '../hooks' function strLimit(text: string, count: number) { return text.slice(0, count) + (text.length > count ? '...' : '') } export function ChannelCard({ env, channelAddress }: ChannelCardProps) { - const { address } = useAccount() - const signer = useEthersSigner() - - const { isLoading: subLoading, action: subscribe } = useSubscribeOrUnsubscribeToChannel({ action: 'subscribe' }) - const { isLoading: unsubLoading, action: unsubscribe } = useSubscribeOrUnsubscribeToChannel({ action: 'unsubscribe' }) - - const { data: channel, isLoading: channelIsLoading } = useChannel({ + const { + data: channel, + isLoading: channelIsLoading, + error, + } = useChannel({ channel: channelAddress, env: env, }) - const { data: userSubscriptions, isLoading: userSubsIsLoading } = useUserSubscriptions({ - user: address as string, - env, - }) - - const [userIsSubscribed, setUserIsSubscribed] = useState(false) - - useEffect(() => { - if (!address || !userSubscriptions) { - setUserIsSubscribed(false) - return - } - - setUserIsSubscribed(userSubscriptions.map((channel) => channel.channel.toLowerCase()).includes(channelAddress.toLowerCase())) - }, [userSubscriptions]) - - const toggleSubscribe = () => { - if (!signer || !address) return - - const args: SubscribeOptionsType = { - signer, - userAddress: address, - channelAddress, - env, - } - - const toggle = () => { - return userIsSubscribed ? unsubscribe(args) : subscribe(args) - } - - return toggle().then(() => { - setUserIsSubscribed(!userIsSubscribed) - }) - } + if (error) return <>Error loading channel... return ( -
- - {channel && ( -
-
- {channel.name} -
-
-

{channel.name}

-

{strLimit(channel.info, 35)}

-
- - {userIsSubscribed ? ( - - ) : ( - - )} - -
+ + {channel && ( +
+
+ {channel.name} +
+
+

{channel.name}

+

{strLimit(channel.info, 35)}

+
+
- )} - -
+
+ )} + ) } diff --git a/integrations/push-protocol/components/notification-feed.tsx b/integrations/push-protocol/components/notification-feed.tsx index 93e07c0c..44b6634f 100644 --- a/integrations/push-protocol/components/notification-feed.tsx +++ b/integrations/push-protocol/components/notification-feed.tsx @@ -15,7 +15,7 @@ export function NotificationFeed({ notifications, spamNotifications, notificatio Spam
- + {notifications?.length == 0 && <>You currently have no notifications, try subscribing to some channels.} {notifications?.map((notification, i) => { @@ -36,7 +36,7 @@ export function NotificationFeed({ notifications, spamNotifications, notificatio })} - + {spamNotifications?.length == 0 && <>You currently have no notifications, try subscribing to some channels.} {spamNotifications?.map((notification, i) => { diff --git a/integrations/push-protocol/components/subscribe-button.tsx b/integrations/push-protocol/components/subscribe-button.tsx new file mode 100644 index 00000000..79431786 --- /dev/null +++ b/integrations/push-protocol/components/subscribe-button.tsx @@ -0,0 +1,83 @@ +import { useEffect, useState } from 'react' + +import { SubscribeOptionsType } from '@pushprotocol/restapi/src/lib/channels' +import { useAccount, useNetwork, useSwitchNetwork } from 'wagmi' + +import { useEthersSigner } from '@/lib/hooks/web3/use-ethers-signer' + +import { Loadable } from './loadable' +import { SubscribeButtonProps } from './types' +import { useSubscribe, useUserSubscriptions } from '../hooks' +import { useUnsubscribe } from '../hooks/use-unsubscribe-channel' +import { pushEnvToChainId } from '../utils/helpers' + +export function SubscribeButton(props: SubscribeButtonProps) { + const { channelAddress, env } = props + const channelChainId = pushEnvToChainId(env) + + const { address } = useAccount() + const { chain } = useNetwork() + const signer = useEthersSigner() + const { switchNetworkAsync } = useSwitchNetwork({ + chainId: channelChainId, + }) + + const { isLoading: subLoading, mutateAsync: subscribe } = useSubscribe() + const { isLoading: unsubLoading, mutateAsync: unsubscribe } = useUnsubscribe() + const [userIsSubscribed, setUserIsSubscribed] = useState(false) + + const { data: userSubscriptions, isLoading: userSubsIsLoading } = useUserSubscriptions({ + user: address as string, + env, + }) + + console.log(userIsSubscribed) + + useEffect(() => { + if (!address || !userSubscriptions) { + setUserIsSubscribed(false) + return + } + + setUserIsSubscribed(userSubscriptions.map((channel) => channel.channel.toLowerCase()).includes(channelAddress.toLowerCase())) + }, [userSubscriptions]) + + const toggleSubscribe = async () => { + if (!signer || !address) return + + if (channelChainId !== chain?.id) { + await switchNetworkAsync?.() + } + + const args: SubscribeOptionsType = { + signer, + userAddress: address, + channelAddress: channelAddress, + env: props.env, + } + + return (userIsSubscribed ? unsubscribe(args) : subscribe(args)).then(() => { + setUserIsSubscribed(!userIsSubscribed) + }) + } + + return ( + <> + + {userIsSubscribed ? ( + + ) : ( + + )} + + + ) +} diff --git a/integrations/push-protocol/components/types.ts b/integrations/push-protocol/components/types.ts index c6035cc3..f9aa8f80 100644 --- a/integrations/push-protocol/components/types.ts +++ b/integrations/push-protocol/components/types.ts @@ -16,3 +16,8 @@ export type ChannelCardProps = { channelAddress: string env: ENV } + +export type SubscribeButtonProps = { + channelAddress: string + env: ENV +} diff --git a/integrations/push-protocol/hooks/use-channel.ts b/integrations/push-protocol/hooks/use-channel.ts index 4bdf745f..af61228f 100644 --- a/integrations/push-protocol/hooks/use-channel.ts +++ b/integrations/push-protocol/hooks/use-channel.ts @@ -1,7 +1,7 @@ import * as PushAPI from '@pushprotocol/restapi' +import { useQuery } from '@tanstack/react-query' import { z } from 'zod' -import { usePushQuery } from './use-push-query' import { Channel, UseChannelProps } from '../utils/types' const fetchChannel = async (props: UseChannelProps) => { @@ -34,19 +34,9 @@ const fetchChannel = async (props: UseChannelProps) => { } export const useChannel = (props: UseChannelProps) => { - return usePushQuery( - { - fetcher: async () => { - const result = await fetchChannel(props) - if (!result) throw new Error('Channel not found') // Push sdk doesn't throw errors :( - - return result - }, - }, - [props.channel, props.env] - ) -} - -export const useChannelLazy = () => { - return [fetchChannel] + return useQuery(['channel', props.channel, props.env], { + queryFn: () => fetchChannel(props), + refetchOnWindowFocus: false, + enabled: !!props.channel && !!props.env, + }) } diff --git a/integrations/push-protocol/hooks/use-chats.ts b/integrations/push-protocol/hooks/use-chats.ts index fb207aa1..62f2d908 100644 --- a/integrations/push-protocol/hooks/use-chats.ts +++ b/integrations/push-protocol/hooks/use-chats.ts @@ -1,6 +1,6 @@ import * as PushAPI from '@pushprotocol/restapi' +import { useQuery } from '@tanstack/react-query' -import { usePushQuery } from './use-push-query' import { UseChatsProps } from '../utils/types' const fetchChats = async (props: UseChatsProps) => { @@ -8,14 +8,8 @@ const fetchChats = async (props: UseChatsProps) => { } export const useChats = (props: UseChatsProps) => { - return usePushQuery( - { - fetcher: () => fetchChats(props), - }, - [props.account, props.env, props.page, props.limit, props.pgpPrivateKey, props.toDecrypt] - ) -} - -export const useChatsLazy = () => { - return [fetchChats] + return useQuery(['chats', props.account, props.env, props.page, props.limit, props.pgpPrivateKey, props.toDecrypt], { + queryFn: () => fetchChats(props), + refetchOnWindowFocus: false, + }) } diff --git a/integrations/push-protocol/hooks/use-create-user.ts b/integrations/push-protocol/hooks/use-create-user.ts index 68a38645..ceda5b70 100644 --- a/integrations/push-protocol/hooks/use-create-user.ts +++ b/integrations/push-protocol/hooks/use-create-user.ts @@ -1,21 +1,13 @@ import * as PushAPI from '@pushprotocol/restapi' import { CreateUserProps } from '@pushprotocol/restapi/src/lib/user' - -import { usePushAction } from './use-push-action' +import { useMutation } from '@tanstack/react-query' const createUser = (args: CreateUserProps) => { return PushAPI.user.create(args) } export const useCreateUser = () => { - return usePushAction( - { - fetcher: createUser, - }, - [] - ) -} - -export const useCreateUserLazy = () => { - return [createUser] + return useMutation({ + mutationFn: createUser, + }) } diff --git a/integrations/push-protocol/hooks/use-notifications.ts b/integrations/push-protocol/hooks/use-notifications.ts index ad3e8ca0..08cf0fce 100644 --- a/integrations/push-protocol/hooks/use-notifications.ts +++ b/integrations/push-protocol/hooks/use-notifications.ts @@ -1,6 +1,6 @@ import * as PushAPI from '@pushprotocol/restapi' +import { useQuery } from '@tanstack/react-query' -import { usePushQuery } from './use-push-query' import { UseNotificationsProps } from '../utils/types' const fetchNotifications = async (props: UseNotificationsProps) => { @@ -11,19 +11,8 @@ const fetchNotifications = async (props: UseNotificationsProps) => { } export const useNotifications = (props: UseNotificationsProps) => { - return usePushQuery( - { - fetcher: async () => { - const result = await fetchNotifications(props) - if (!result) throw new Error('Can not find notifications') // Push SDK doesn't throw errors :( - - return result - }, - }, - [props.user, props.env, props.page, props.limit, props.raw] - ) -} - -export const useNotificationsLazy = () => { - return [fetchNotifications] + return useQuery(['notifications', props.user, props.spam, props.env, props.page, props.limit, props.raw], { + queryFn: () => fetchNotifications(props), + refetchOnWindowFocus: false, + }) } diff --git a/integrations/push-protocol/hooks/use-push-action.ts b/integrations/push-protocol/hooks/use-push-action.ts deleted file mode 100644 index c3ee123c..00000000 --- a/integrations/push-protocol/hooks/use-push-action.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { DependencyList, useCallback, useState } from 'react' - -import { UsePushActionProps } from '../utils/types' - -export const usePushAction = (props: UsePushActionProps, deps: DependencyList) => { - const [data, setData] = useState() - const [isLoading, setIsLoading] = useState(false) - const [error, setError] = useState() - - const action = useCallback((args: F) => { - setIsLoading(true) - - return props - .fetcher(args) - .then((data) => { - setData(data) - return data - }) - .catch((e: Error) => { - setError(e) - throw e - }) - .finally(() => setIsLoading(false)) - }, deps) - - return { data, isLoading, error, action } -} diff --git a/integrations/push-protocol/hooks/use-push-query.ts b/integrations/push-protocol/hooks/use-push-query.ts deleted file mode 100644 index cc79f4e4..00000000 --- a/integrations/push-protocol/hooks/use-push-query.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { DependencyList, useCallback, useEffect, useState } from 'react' - -import { UsePushQueryProps, UsePushQueryReturn } from '../utils/types' - -export const usePushQuery = (props: UsePushQueryProps, deps: DependencyList): UsePushQueryReturn => { - const [data, setData] = useState() - const [error, setError] = useState() - const [isLoading, setIsLoading] = useState(false) - - const refetch = useCallback(async () => props.fetcher(), deps) - - useEffect(() => { - setIsLoading(true) - refetch() - .then((res) => setData(res)) - .catch((e) => setError(e as Error)) - .finally(() => setIsLoading(false)) - }, [refetch]) - - return { data, isLoading, error, refetch } -} diff --git a/integrations/push-protocol/hooks/use-search-channels.ts b/integrations/push-protocol/hooks/use-search-channels.ts index d87a8ac9..3d2d654c 100644 --- a/integrations/push-protocol/hooks/use-search-channels.ts +++ b/integrations/push-protocol/hooks/use-search-channels.ts @@ -1,6 +1,6 @@ import * as PushAPI from '@pushprotocol/restapi' +import { useQuery } from '@tanstack/react-query' -import { usePushQuery } from './use-push-query' import { Channel, UseSearchChannelProps } from '../utils/types' const searchChannels = async (props: UseSearchChannelProps) => { @@ -8,14 +8,8 @@ const searchChannels = async (props: UseSearchChannelProps) => { } export const useSearchChannels = (props: UseSearchChannelProps) => { - return usePushQuery( - { - fetcher: () => searchChannels(props), - }, - [props.query, props.env, props.page, props.limit] - ) -} - -export const useSearchChannelsLazy = () => { - return [searchChannels] + return useQuery(['search-channels', props.query, props.env, props.page, props.limit], { + queryFn: () => searchChannels(props), + refetchOnWindowFocus: false, + }) } diff --git a/integrations/push-protocol/hooks/use-send-notifications.ts b/integrations/push-protocol/hooks/use-send-notifications.ts index 8d205070..5708dd28 100644 --- a/integrations/push-protocol/hooks/use-send-notifications.ts +++ b/integrations/push-protocol/hooks/use-send-notifications.ts @@ -1,20 +1,12 @@ import * as PushAPI from '@pushprotocol/restapi' - -import { usePushAction } from './use-push-action' +import { useMutation } from '@tanstack/react-query' const sendNotification = (args: PushAPI.ISendNotificationInputOptions) => { return PushAPI.payloads.sendNotification(args) } export const useSendNotification = () => { - return usePushAction( - { - fetcher: sendNotification, - }, - [] - ) -} - -export const useSendNotificationLazy = () => { - return [sendNotification] + return useMutation({ + mutationFn: sendNotification, + }) } diff --git a/integrations/push-protocol/hooks/use-subscribe-channel.ts b/integrations/push-protocol/hooks/use-subscribe-channel.ts index 1c530cab..0124cf1d 100644 --- a/integrations/push-protocol/hooks/use-subscribe-channel.ts +++ b/integrations/push-protocol/hooks/use-subscribe-channel.ts @@ -1,32 +1,13 @@ import * as PushAPI from '@pushprotocol/restapi' import { SubscribeOptionsType } from '@pushprotocol/restapi/src/lib/channels' +import { useMutation } from '@tanstack/react-query' -import { usePushAction } from './use-push-action' -import { UseSubscribeOrUnsubscribeToChannelProps } from '../utils/types' - -const subOrUnsub = (action: UseSubscribeOrUnsubscribeToChannelProps['action'], args: SubscribeOptionsType) => { - return PushAPI.channels[action](args) -} - -export const useSubscribeOrUnsubscribeToChannel = (props: UseSubscribeOrUnsubscribeToChannelProps) => { - return usePushAction( - { - fetcher: async (args: SubscribeOptionsType) => { - const result = await subOrUnsub(props.action, args) - - if (result.status === 'error') throw new Error(result.message) // Push SDK doesn't throw error - - return result - }, - }, - [props.action] - ) -} - -export const useSubscribeToChannelLazy = () => { - return [PushAPI.channels.subscribe] +const subscribe = (args: SubscribeOptionsType) => { + return PushAPI.channels.subscribe(args) } -export const useUnsubscribeToChannelLazy = () => { - return [PushAPI.channels.unsubscribe] +export const useSubscribe = () => { + return useMutation({ + mutationFn: subscribe, + }) } diff --git a/integrations/push-protocol/hooks/use-unsubscribe-channel.ts b/integrations/push-protocol/hooks/use-unsubscribe-channel.ts new file mode 100644 index 00000000..ea83f31d --- /dev/null +++ b/integrations/push-protocol/hooks/use-unsubscribe-channel.ts @@ -0,0 +1,13 @@ +import * as PushAPI from '@pushprotocol/restapi' +import { UnSubscribeOptionsType } from '@pushprotocol/restapi/src/lib/channels' +import { useMutation } from '@tanstack/react-query' + +const unsubscribe = (args: UnSubscribeOptionsType) => { + return PushAPI.channels.unsubscribe(args) +} + +export const useUnsubscribe = () => { + return useMutation({ + mutationFn: unsubscribe, + }) +} diff --git a/integrations/push-protocol/hooks/use-user-subscriptions.ts b/integrations/push-protocol/hooks/use-user-subscriptions.ts index cedf2d57..7712a7d5 100644 --- a/integrations/push-protocol/hooks/use-user-subscriptions.ts +++ b/integrations/push-protocol/hooks/use-user-subscriptions.ts @@ -1,6 +1,6 @@ import * as PushAPI from '@pushprotocol/restapi' +import { useQuery } from '@tanstack/react-query' -import { usePushQuery } from './use-push-query' import { UseUserSubscriptionProps, UserSubscription } from '../utils/types' const fetchUserSubscriptions = async (props: UseUserSubscriptionProps) => { @@ -8,14 +8,8 @@ const fetchUserSubscriptions = async (props: UseUserSubscriptionProps) => { } export const useUserSubscriptions = (props: UseUserSubscriptionProps) => { - return usePushQuery( - { - fetcher: () => fetchUserSubscriptions(props), - }, - [props.env, props.user] - ) -} - -export const useUserSubscriptionsLazy = () => { - return [fetchUserSubscriptions] + return useQuery(['user-subscriptions', props.env, props.user], { + queryFn: () => fetchUserSubscriptions(props), + refetchOnWindowFocus: false, + }) } diff --git a/integrations/push-protocol/index.ts b/integrations/push-protocol/index.ts index c1e0adf9..c6cbc2f9 100644 --- a/integrations/push-protocol/index.ts +++ b/integrations/push-protocol/index.ts @@ -1,4 +1,5 @@ export * from './utils/types' +export * from './utils/helpers' export * from './hooks' export * from './components' export { ENV } from '@pushprotocol/uiweb' diff --git a/integrations/push-protocol/utils/helpers.ts b/integrations/push-protocol/utils/helpers.ts new file mode 100644 index 00000000..852fb69d --- /dev/null +++ b/integrations/push-protocol/utils/helpers.ts @@ -0,0 +1,5 @@ +import { ENV } from '@pushprotocol/uiweb' + +export const pushEnvToChainId = (env: ENV) => { + return env === ENV.PROD ? 1 : 5 +} diff --git a/integrations/push-protocol/utils/types.ts b/integrations/push-protocol/utils/types.ts index 66e82fee..3486e7b7 100644 --- a/integrations/push-protocol/utils/types.ts +++ b/integrations/push-protocol/utils/types.ts @@ -2,30 +2,10 @@ import { GetChannelOptionsType, SearchChannelOptionsType } from '@pushprotocol/r import { ChatsOptionsType } from '@pushprotocol/restapi/src/lib/chat' import { FeedsOptionsType, UserSubscriptionsOptionsType } from '@pushprotocol/restapi/src/lib/user' -export type PartialBy = Omit & Partial> - -export type UsePushQueryReturn = { - data?: T - error?: Error - isLoading: boolean - refetch: () => Promise -} -export type UsePushQueryProps = { - fetcher: () => Promise -} - -export type UsePushActionReturn = UsePushQueryReturn -export type UsePushActionProps = Omit, 'fetcher'> & { - fetcher: (args: F) => Promise -} - export type UseNotificationsProps = FeedsOptionsType export type UseChannelProps = GetChannelOptionsType export type UseSearchChannelProps = SearchChannelOptionsType export type UseUserSubscriptionProps = UserSubscriptionsOptionsType -export type UseSubscribeOrUnsubscribeToChannelProps = { - action: 'subscribe' | 'unsubscribe' -} export type UseChatsProps = ChatsOptionsType export type Channel = { diff --git a/lib/generated/blockchain.ts b/lib/generated/blockchain.ts index 5738ed6c..ae0b7df7 100644 --- a/lib/generated/blockchain.ts +++ b/lib/generated/blockchain.ts @@ -1,4 +1,4 @@ -// Generated by @wagmi/cli@1.1.0 on 7/6/2023 at 10:35:14 AM +// Generated by @wagmi/cli@1.1.0 on 7/8/2023 at 6:01:11 AM import { useContractRead, UseContractReadConfig, From f543f90eb2dfbb5bd6c3333fbe494a0242b6ca7f Mon Sep 17 00:00:00 2001 From: Giorgi Tsirekidze Date: Fri, 14 Jul 2023 03:42:38 +0400 Subject: [PATCH 05/13] fix: improved ui, added mocked notifications, chat, subs count & copy btn on channel card --- .../integration/push-protocol/page.tsx | 37 +++++-- .../push-protocol/components/channel-card.tsx | 33 +++++- .../components/notification-feed.tsx | 100 ++++++++++++------ .../components/subscribe-button.tsx | 8 +- .../push-protocol/components/types.ts | 9 +- .../push-protocol/hooks/use-channel.ts | 3 +- integrations/push-protocol/styles/index.css | 5 + integrations/push-protocol/utils/helpers.ts | 25 +++++ 8 files changed, 168 insertions(+), 52 deletions(-) create mode 100644 integrations/push-protocol/styles/index.css diff --git a/app/(general)/integration/push-protocol/page.tsx b/app/(general)/integration/push-protocol/page.tsx index a3cc6881..be8c2868 100644 --- a/app/(general)/integration/push-protocol/page.tsx +++ b/app/(general)/integration/push-protocol/page.tsx @@ -2,6 +2,8 @@ import { useState } from 'react' +import '@/integrations/push-protocol/styles/index.css' +import { ApiNotificationType, SignerType } from '@pushprotocol/restapi' import { motion } from 'framer-motion' import Balancer from 'react-wrap-balancer' import { useAccount } from 'wagmi' @@ -13,10 +15,14 @@ import { LinkComponent } from '@/components/shared/link-component' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design' import { turboIntegrations } from '@/data/turbo-integrations' -import { ChannelCard, ENV, NotificationFeed, useNotifications } from '@/integrations/push-protocol' +import { ChannelCard, Chat, ENV, NotificationFeed, getMockedNotification, useNotifications } from '@/integrations/push-protocol' +import { useEthersSigner } from '@/lib/hooks/web3/use-ethers-signer' export default function PageIntegration() { const { address } = useAccount() + const signer = useEthersSigner() + + const [mockedNotifications, setMockedNotifications] = useState([]) const { data: notifications, isLoading: notificationsIsLoading } = useNotifications({ user: address as string, @@ -33,6 +39,14 @@ export default function PageIntegration() { const [channelAddress, setChannelAddress] = useState('0x74415Bc4C4Bf4Baecc2DD372426F0a1D016Fa924') const [env, setEnv] = useState(ENV.STAGING) + // Shows mock notificatins in inbox after subscribing if there is no notifications to show. + const handleSubscribe = () => { + if ([...(notifications || []), ...mockedNotifications].length > 0) return + + const mockedNotification: ApiNotificationType = getMockedNotification({ env }) + setMockedNotifications([...mockedNotifications, mockedNotification]) + } + return ( <>
@@ -74,21 +88,21 @@ export default function PageIntegration() {

-

Inbox&Spam

+

Notifications

Check inbox&spam notifications on Push

-
+
setChannelAddress(e.target.value)} />
-
+
setSearchQuery(e.target.value)} /> +
+
+ +
+
+
+ +
+ {channels?.slice(0, 5).map((channel) => ( + + ))} +
+
+
+ + ) +} diff --git a/integrations/push-protocol/components/index.ts b/integrations/push-protocol/components/index.ts index 745f88a5..3c730e5c 100644 --- a/integrations/push-protocol/components/index.ts +++ b/integrations/push-protocol/components/index.ts @@ -4,3 +4,4 @@ export * from './notification-item' export * from './notification-feed' export * from './chat' export * from './channel-card' +export * from './channel-search' diff --git a/integrations/push-protocol/styles/index.css b/integrations/push-protocol/styles/index.css index 5ba259cc..37222f29 100644 --- a/integrations/push-protocol/styles/index.css +++ b/integrations/push-protocol/styles/index.css @@ -3,3 +3,8 @@ div[class^="chat__Container-sc"] { margin-left: auto; margin-right: auto; } + +div[class^="overlay__ImageWrapper-sc"] { + display: none !important; +} + From 4b982f1da972cbd7bad948154312220405d6a979 Mon Sep 17 00:00:00 2001 From: Giorgi Tsirekidze Date: Sat, 15 Jul 2023 01:08:41 +0400 Subject: [PATCH 08/13] feat: added NotificationBell for showing notifications --- .../integration/push-protocol/page.tsx | 35 ++--------- .../push-protocol/components/channel-card.tsx | 6 +- .../push-protocol/components/index.ts | 1 + .../components/notification-bell.tsx | 62 +++++++++++++++++++ .../components/notification-feed.tsx | 13 ++-- .../components/notification-item.tsx | 27 ++++++-- .../components/subscribe-button.tsx | 4 +- .../push-protocol/components/types.ts | 5 ++ integrations/push-protocol/utils/helpers.ts | 10 +-- 9 files changed, 114 insertions(+), 49 deletions(-) create mode 100644 integrations/push-protocol/components/notification-bell.tsx diff --git a/app/(general)/integration/push-protocol/page.tsx b/app/(general)/integration/push-protocol/page.tsx index b8bdc12f..782daeb9 100644 --- a/app/(general)/integration/push-protocol/page.tsx +++ b/app/(general)/integration/push-protocol/page.tsx @@ -15,7 +15,8 @@ import { LinkComponent } from '@/components/shared/link-component' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design' import { turboIntegrations } from '@/data/turbo-integrations' -import { ChannelCard, ChannelSearch, Chat, ENV, NotificationFeed, getMockedNotification, useNotifications } from '@/integrations/push-protocol' +import { ChannelCard, ChannelSearch, Chat, ENV, getMockedNotification } from '@/integrations/push-protocol' +import { NotificationBell } from '@/integrations/push-protocol/components/notification-bell' import { useEthersSigner } from '@/lib/hooks/web3/use-ethers-signer' export default function PageIntegration() { @@ -24,27 +25,13 @@ export default function PageIntegration() { const [mockedNotifications, setMockedNotifications] = useState([]) - const { data: notifications, isLoading: notificationsIsLoading } = useNotifications({ - user: address as string, - env: ENV.STAGING, - spam: false, - }) - - const { data: spamNotifications, isLoading: spamIsLoading } = useNotifications({ - user: address as string, - env: ENV.STAGING, - spam: true, - }) - const [channelAddress, setChannelAddress] = useState('0x74415Bc4C4Bf4Baecc2DD372426F0a1D016Fa924') const [env, setEnv] = useState(ENV.STAGING) // Shows mock notificatins in inbox after subscribing if there is no notifications to show. const handleSubscribe = () => { - if ([...(notifications || []), ...mockedNotifications].length > 0) return - const mockedNotification: ApiNotificationType = getMockedNotification({ env }) - setMockedNotifications([...mockedNotifications, mockedNotification]) + setMockedNotifications([mockedNotification]) } return ( @@ -85,19 +72,9 @@ export default function PageIntegration() { -
-
- -
-
-

Notifications

-

Check inbox&spam notifications on Push

-
+
+
+
diff --git a/integrations/push-protocol/components/channel-card.tsx b/integrations/push-protocol/components/channel-card.tsx index 6ee62dab..aa94dbd4 100644 --- a/integrations/push-protocol/components/channel-card.tsx +++ b/integrations/push-protocol/components/channel-card.tsx @@ -7,11 +7,7 @@ import { Loadable } from './loadable' import { SubscribeButton } from './subscribe-button' import { ChannelCardProps } from './types' import { useChannel } from '../hooks' -import { truncateAddress } from '../utils/helpers' - -function strLimit(text: string, count: number) { - return text.slice(0, count) + (text.length > count ? '...' : '') -} +import { strLimit, truncateAddress } from '../utils/helpers' export function ChannelCard(props: ChannelCardProps) { const { env, channelAddress, onSubscribe, onUnsubscribe } = props diff --git a/integrations/push-protocol/components/index.ts b/integrations/push-protocol/components/index.ts index 3c730e5c..a57ed9ee 100644 --- a/integrations/push-protocol/components/index.ts +++ b/integrations/push-protocol/components/index.ts @@ -2,6 +2,7 @@ export * from './types' export * from './loadable' export * from './notification-item' export * from './notification-feed' +export * from './notification-bell' export * from './chat' export * from './channel-card' export * from './channel-search' diff --git a/integrations/push-protocol/components/notification-bell.tsx b/integrations/push-protocol/components/notification-bell.tsx new file mode 100644 index 00000000..caae884a --- /dev/null +++ b/integrations/push-protocol/components/notification-bell.tsx @@ -0,0 +1,62 @@ +import { useState } from 'react' + +import * as Popover from '@radix-ui/react-popover' +import { BsBell } from 'react-icons/bs' +import { IoIosClose } from 'react-icons/io' +import { useAccount } from 'wagmi' + +import { NotificationFeed } from './notification-feed' +import { NotificationBellProps } from './types' +import { useNotifications } from '../hooks' + +export function NotificationBell(props: NotificationBellProps) { + const { env, mockedNotifications } = props + const { address } = useAccount() + + const { data: notifications, isLoading: notificationsIsLoading } = useNotifications({ + user: address as string, + env: env, + spam: false, + }) + + const { data: spamNotifications, isLoading: spamIsLoading } = useNotifications({ + user: address as string, + env: env, + spam: true, + }) + + const [read, setRead] = useState(false) + + const allNotifications = [...(notifications || []), ...(mockedNotifications || [])] + + return ( + <> + + + + + + +
+ +
+ + + + +
+
+
+ + ) +} diff --git a/integrations/push-protocol/components/notification-feed.tsx b/integrations/push-protocol/components/notification-feed.tsx index 9013d9ba..2bd16984 100644 --- a/integrations/push-protocol/components/notification-feed.tsx +++ b/integrations/push-protocol/components/notification-feed.tsx @@ -5,6 +5,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design' import { Loadable } from './loadable' +// import { NotificationPush as Notification } from './notification-item' import { Notification } from './notification-item' import { NotificationFeedProps } from './types' @@ -17,7 +18,7 @@ export function NotificationFeed({ notifications, spamNotifications, notificatio Spam
- + @@ -53,7 +54,7 @@ export function NotificationFeed({ notifications, spamNotifications, notificatio - + diff --git a/integrations/push-protocol/components/notification-item.tsx b/integrations/push-protocol/components/notification-item.tsx index 78fac019..77daa878 100644 --- a/integrations/push-protocol/components/notification-item.tsx +++ b/integrations/push-protocol/components/notification-item.tsx @@ -1,6 +1,25 @@ -import { NotificationItem } from '@pushprotocol/uiweb' +import { NotificationItemProps } from '@pushprotocol/uiweb' +import { BsGlobe } from 'react-icons/bs' -// throws deprecated error otherwise. @@pushprotocol/uiweb is using deprecated defaultProps. -delete NotificationItem.defaultProps +import { strLimit } from '../utils/helpers' -export const Notification = NotificationItem +export function Notification(props: NotificationItemProps) { + const handleOpen = () => { + if (!props.url) return + window.open(props.url) + } + + return ( +
handleOpen()}> +
{props.notificationTitle}
+
{props.notificationBody}
+ {props.image && {props.notificationTitle}} + {props.url && ( +
+ +
{strLimit(props.url, 50)}
+
+ )} +
+ ) +} diff --git a/integrations/push-protocol/components/subscribe-button.tsx b/integrations/push-protocol/components/subscribe-button.tsx index fd0e2fa3..de11816f 100644 --- a/integrations/push-protocol/components/subscribe-button.tsx +++ b/integrations/push-protocol/components/subscribe-button.tsx @@ -54,7 +54,9 @@ export function SubscribeButton(props: SubscribeButtonProps) { env: props.env, } - return (userIsSubscribed ? unsubscribe(args) : subscribe(args)).then(() => { + return (userIsSubscribed ? unsubscribe(args) : subscribe(args)).then((res) => { + if (res.status === 'error') return + const isSubscribed = !userIsSubscribed setUserIsSubscribed(isSubscribed) isSubscribed ? props.onSubscribe?.() : props.onUnsubscribe?.() diff --git a/integrations/push-protocol/components/types.ts b/integrations/push-protocol/components/types.ts index c822556e..2256519d 100644 --- a/integrations/push-protocol/components/types.ts +++ b/integrations/push-protocol/components/types.ts @@ -26,3 +26,8 @@ export type SubscribeButtonProps = { channelAddress: string env: ENV } & ChannelSubscribeCallbacks + +export type NotificationBellProps = { + env: ENV + mockedNotifications?: ApiNotificationType[] +} diff --git a/integrations/push-protocol/utils/helpers.ts b/integrations/push-protocol/utils/helpers.ts index a42773fb..69452360 100644 --- a/integrations/push-protocol/utils/helpers.ts +++ b/integrations/push-protocol/utils/helpers.ts @@ -19,12 +19,14 @@ export const getMockedNotification = ({ env }: { env: ENV }): ApiNotificationTyp icon: '', aimg: 'https://staging.push.org/push.svg', url: 'https://staging.push.org', - }, - notification: { - body: 'This is an example notification received on PUSH!', - title: 'Hello from PUSH!', + amsg: 'This is an example notification received on PUSH!', + asub: 'Hello from PUSH!', }, }, source: env === ENV.STAGING ? 'ETH_TEST_GOERLI' : 'ETH_TEST_MAINNET', } as ApiNotificationType } + +export const strLimit = (text: string, count: number) => { + return text.slice(0, count) + (text.length > count ? '...' : '') +} From a8c2dbdfd0431968f2d48f5a22b6bfe24102958c Mon Sep 17 00:00:00 2001 From: Giorgi Tsirekidze Date: Tue, 18 Jul 2023 03:08:24 +0400 Subject: [PATCH 09/13] chore: cleanup/code style improvements --- .../integration/push-protocol/page.tsx | 26 ++----- integrations/push-protocol/README.md | 23 +++--- .../push-protocol/components/channel-card.tsx | 25 ++++--- .../components/channel-search.tsx | 4 +- .../push-protocol/components/index.ts | 2 +- .../push-protocol/components/loadable.tsx | 12 +--- .../components/notification-bell.tsx | 70 ++++++++++--------- .../components/notification-feed.tsx | 16 +++-- .../components/notification-item.tsx | 16 ++--- .../components/subscribe-button.tsx | 55 ++++++++------- .../push-protocol/components/types.ts | 33 --------- integrations/push-protocol/hooks/index.ts | 1 + .../push-protocol/hooks/use-channel.ts | 54 +++++++------- integrations/push-protocol/hooks/use-chats.ts | 6 +- .../push-protocol/hooks/use-notifications.ts | 10 +-- .../hooks/use-search-channels.ts | 10 +-- .../hooks/use-user-subscriptions.ts | 10 +-- integrations/push-protocol/utils/constants.ts | 1 + next.config.mjs | 2 +- 19 files changed, 172 insertions(+), 204 deletions(-) delete mode 100644 integrations/push-protocol/components/types.ts create mode 100644 integrations/push-protocol/utils/constants.ts diff --git a/app/(general)/integration/push-protocol/page.tsx b/app/(general)/integration/push-protocol/page.tsx index 782daeb9..521ed616 100644 --- a/app/(general)/integration/push-protocol/page.tsx +++ b/app/(general)/integration/push-protocol/page.tsx @@ -2,11 +2,9 @@ import { useState } from 'react' -import '@/integrations/push-protocol/styles/index.css' -import { ApiNotificationType, SignerType } from '@pushprotocol/restapi' +import { ApiNotificationType } from '@pushprotocol/restapi' import { motion } from 'framer-motion' import Balancer from 'react-wrap-balancer' -import { useAccount } from 'wagmi' import { WalletConnect } from '@/components/blockchain/wallet-connect' import { IsWalletConnected } from '@/components/shared/is-wallet-connected' @@ -15,22 +13,19 @@ import { LinkComponent } from '@/components/shared/link-component' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design' import { turboIntegrations } from '@/data/turbo-integrations' -import { ChannelCard, ChannelSearch, Chat, ENV, getMockedNotification } from '@/integrations/push-protocol' +import { ChannelCard, ChannelSearch, ENV, getMockedNotification } from '@/integrations/push-protocol' import { NotificationBell } from '@/integrations/push-protocol/components/notification-bell' -import { useEthersSigner } from '@/lib/hooks/web3/use-ethers-signer' +import { PUSH_CHANNEL_ADDRESS } from '@/integrations/push-protocol/utils/constants' export default function PageIntegration() { - const { address } = useAccount() - const signer = useEthersSigner() - const [mockedNotifications, setMockedNotifications] = useState([]) - const [channelAddress, setChannelAddress] = useState('0x74415Bc4C4Bf4Baecc2DD372426F0a1D016Fa924') + const [channelAddress, setChannelAddress] = useState(PUSH_CHANNEL_ADDRESS) const [env, setEnv] = useState(ENV.STAGING) // Shows mock notificatins in inbox after subscribing if there is no notifications to show. const handleSubscribe = () => { - const mockedNotification: ApiNotificationType = getMockedNotification({ env }) + const mockedNotification = getMockedNotification({ env }) setMockedNotifications([mockedNotification]) } @@ -126,17 +121,6 @@ export default function PageIntegration() {
-
-
- - -
-
-

Support Chat

-

Chat with specified wallet

-
-
-
diff --git a/integrations/push-protocol/README.md b/integrations/push-protocol/README.md index 4e8c46c8..216625e6 100644 --- a/integrations/push-protocol/README.md +++ b/integrations/push-protocol/README.md @@ -2,7 +2,7 @@ [Push Protocol](https://push.org/) is a web3 communication network, enabling cross-chain notifications and messaging for dapps, wallets, and services. -This integrations provides useful hooks and components from main Push Protocol features. +This integration provides useful hooks and components from the main Push Protocol features. ## Features @@ -22,19 +22,19 @@ Renders inbox and spam notifications Renders single notification item `ChannelCard` -Renders simple card with channel information and interactive subscribe button +Renders simple card with channel information and an interactive subscribe button `ChannelSearch` Renders channel search inputs along channel cards for search results - `Chat` -Renders native Push Protocol support chat window on bottom-right corner of the screen +Renders native Push Protocol support chat window on the bottom-right corner of the screen --- ## Hooks -These hooks are just push protocol specific wrappers for `react-query`. So it utilizes all the features `react-query` has. + +These hooks are just push protocol-specific wrappers for `react-query`. So it utilizes all the features `react-query` has. ```tsx const { data, isLoading, error, refetch } = useNotifications({ @@ -78,7 +78,7 @@ await sendNotification({..args}) `useUnsubscribe` : Returns mutation for unsubscribe action -`createUser` : Returns mutation for creating user +`useCreateUser` : Returns mutation for creating user ## File Structure @@ -90,10 +90,10 @@ integrations/push │ ├── chat.tsx │ ├── index.ts │ ├── loadable.tsx +│ ├── notification-bell.tsx │ ├── notification-feed.tsx │ ├── notification-item.tsx -│ ├── subscribe-button.tsx -│ └── types.ts +│ └── subscribe-button.tsx ├── hooks │ ├── index.ts │ ├── use-channel.ts @@ -105,9 +105,12 @@ integrations/push │ ├── use-subscribe-channel.ts │ ├── use-unsubscribe-channel.ts │ └── use-user-subscriptions.ts -├── index.ts -├── README.md +├── styles +│ └── index.css └── utils + ├── constants.ts ├── helpers.ts └── types.ts +├── index.ts +└── README.md ``` diff --git a/integrations/push-protocol/components/channel-card.tsx b/integrations/push-protocol/components/channel-card.tsx index aa94dbd4..1de637d9 100644 --- a/integrations/push-protocol/components/channel-card.tsx +++ b/integrations/push-protocol/components/channel-card.tsx @@ -1,16 +1,25 @@ import { useEffect, useState } from 'react' +import Image from 'next/image' import CopyToClipboard from 'react-copy-to-clipboard' import { HiUser } from 'react-icons/hi' -import { Loadable } from './loadable' -import { SubscribeButton } from './subscribe-button' -import { ChannelCardProps } from './types' +import { ENV } from '..' import { useChannel } from '../hooks' import { strLimit, truncateAddress } from '../utils/helpers' +import { Loadable } from './loadable' +import { SubscribeButton } from './subscribe-button' + +export type ChannelCardProps = { + channelAddress: string + env: ENV + onSubscribe?: () => void + onUnsubscribe?: () => void +} + +export function ChannelCard({ env, channelAddress, onSubscribe, onUnsubscribe }: ChannelCardProps) { + const [copied, setCopied] = useState(false) -export function ChannelCard(props: ChannelCardProps) { - const { env, channelAddress, onSubscribe, onUnsubscribe } = props const { data: channel, isLoading: channelIsLoading, @@ -20,8 +29,6 @@ export function ChannelCard(props: ChannelCardProps) { env: env, }) - const [copied, setCopied] = useState(false) - useEffect(() => { if (!copied) return setTimeout(() => setCopied(false), 3000) @@ -34,8 +41,8 @@ export function ChannelCard(props: ChannelCardProps) { {channel && (
-
- {channel.name} +
+ {channel.name}

{channel.name}

diff --git a/integrations/push-protocol/components/channel-search.tsx b/integrations/push-protocol/components/channel-search.tsx index 434cc108..02891aa9 100644 --- a/integrations/push-protocol/components/channel-search.tsx +++ b/integrations/push-protocol/components/channel-search.tsx @@ -3,9 +3,9 @@ import { useState } from 'react' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { ENV } from '@/integrations/push-protocol' +import { useSearchChannels } from '../hooks' import { ChannelCard } from './channel-card' import { Loadable } from './loadable' -import { useSearchChannels } from '../hooks' export function ChannelSearch() { const [searchQuery, setSearchQuery] = useState('') @@ -44,7 +44,7 @@ export function ChannelSearch() {
-
+
{channels?.slice(0, 5).map((channel) => ( ))} diff --git a/integrations/push-protocol/components/index.ts b/integrations/push-protocol/components/index.ts index a57ed9ee..3f52c9c3 100644 --- a/integrations/push-protocol/components/index.ts +++ b/integrations/push-protocol/components/index.ts @@ -1,4 +1,3 @@ -export * from './types' export * from './loadable' export * from './notification-item' export * from './notification-feed' @@ -6,3 +5,4 @@ export * from './notification-bell' export * from './chat' export * from './channel-card' export * from './channel-search' +export * from './subscribe-button' diff --git a/integrations/push-protocol/components/loadable.tsx b/integrations/push-protocol/components/loadable.tsx index 61a0bb51..7e9bc51a 100644 --- a/integrations/push-protocol/components/loadable.tsx +++ b/integrations/push-protocol/components/loadable.tsx @@ -1,13 +1,5 @@ import { PropsWithChildren } from 'react' -import { ImSpinner2 } from 'react-icons/im' - -export function Loadable(props: PropsWithChildren<{ isLoading: boolean }>) { - return props.isLoading ? ( - <> - - - ) : ( - <>{props.children} - ) +export function Loadable({ isLoading, children }: PropsWithChildren<{ isLoading: boolean }>) { + return isLoading ?
: <>{children} } diff --git a/integrations/push-protocol/components/notification-bell.tsx b/integrations/push-protocol/components/notification-bell.tsx index caae884a..6569ac28 100644 --- a/integrations/push-protocol/components/notification-bell.tsx +++ b/integrations/push-protocol/components/notification-bell.tsx @@ -1,15 +1,23 @@ import { useState } from 'react' -import * as Popover from '@radix-ui/react-popover' +import { ApiNotificationType } from '@pushprotocol/restapi' import { BsBell } from 'react-icons/bs' -import { IoIosClose } from 'react-icons/io' import { useAccount } from 'wagmi' -import { NotificationFeed } from './notification-feed' -import { NotificationBellProps } from './types' +import { Popover, PopoverContent, PopoverTrigger } from 'components/ui/popover' + +import { ENV } from '..' import { useNotifications } from '../hooks' +import { NotificationFeed } from './notification-feed' + +export type NotificationBellProps = { + env: ENV + mockedNotifications?: ApiNotificationType[] +} export function NotificationBell(props: NotificationBellProps) { + const [read, setRead] = useState(false) + const { env, mockedNotifications } = props const { address } = useAccount() @@ -25,38 +33,32 @@ export function NotificationBell(props: NotificationBellProps) { spam: true, }) - const [read, setRead] = useState(false) - const allNotifications = [...(notifications || []), ...(mockedNotifications || [])] return ( - <> - - - - - - -
- -
- - - - -
-
-
- + + + + + +
+ +
+ {/* + + + */} +
+
) } diff --git a/integrations/push-protocol/components/notification-feed.tsx b/integrations/push-protocol/components/notification-feed.tsx index 2bd16984..36e9e914 100644 --- a/integrations/push-protocol/components/notification-feed.tsx +++ b/integrations/push-protocol/components/notification-feed.tsx @@ -1,3 +1,4 @@ +import { ApiNotificationType } from '@pushprotocol/restapi' import { chainNameType } from '@pushprotocol/uiweb' import { motion } from 'framer-motion' @@ -5,9 +6,14 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design' import { Loadable } from './loadable' -// import { NotificationPush as Notification } from './notification-item' -import { Notification } from './notification-item' -import { NotificationFeedProps } from './types' +import { NotificationItem } from './notification-item' + +export type NotificationFeedProps = { + notifications?: ApiNotificationType[] + spamNotifications?: ApiNotificationType[] + notificationsIsLoading: boolean + spamNotificationsIsLoading: boolean +} export function NotificationFeed({ notifications, spamNotifications, notificationsIsLoading, spamNotificationsIsLoading }: NotificationFeedProps) { return ( @@ -37,7 +43,7 @@ export function NotificationFeed({ notifications, spamNotifications, notificatio {notifications?.map((notification, i) => { return ( - { return ( - { - if (!props.url) return - window.open(props.url) + if (!url) return + window.open(url) } return (
handleOpen()}> -
{props.notificationTitle}
-
{props.notificationBody}
- {props.image && {props.notificationTitle}} - {props.url && ( +
{notificationTitle}
+
{notificationBody}
+ {image && {notificationTitle}} + {url && (
-
{strLimit(props.url, 50)}
+
{strLimit(url, 50)}
)}
diff --git a/integrations/push-protocol/components/subscribe-button.tsx b/integrations/push-protocol/components/subscribe-button.tsx index de11816f..737e8bbe 100644 --- a/integrations/push-protocol/components/subscribe-button.tsx +++ b/integrations/push-protocol/components/subscribe-button.tsx @@ -1,18 +1,26 @@ import { useEffect, useState } from 'react' import { SubscribeOptionsType } from '@pushprotocol/restapi/src/lib/channels' +import { ImSpinner2 } from 'react-icons/im' import { useAccount, useNetwork, useSwitchNetwork } from 'wagmi' import { useEthersSigner } from '@/lib/hooks/web3/use-ethers-signer' -import { Loadable } from './loadable' -import { SubscribeButtonProps } from './types' +import { ENV } from '..' import { useSubscribe, useUserSubscriptions } from '../hooks' import { useUnsubscribe } from '../hooks/use-unsubscribe-channel' import { pushEnvToChainId } from '../utils/helpers' -export function SubscribeButton(props: SubscribeButtonProps) { - const { channelAddress, env } = props +export type SubscribeButtonProps = { + channelAddress: string + env: ENV + onSubscribe?: () => void + onUnsubscribe?: () => void +} + +export function SubscribeButton({ channelAddress, env, onSubscribe, onUnsubscribe }: SubscribeButtonProps) { + const [userIsSubscribed, setUserIsSubscribed] = useState(false) + const channelChainId = pushEnvToChainId(env) const { address } = useAccount() @@ -24,7 +32,6 @@ export function SubscribeButton(props: SubscribeButtonProps) { const { isLoading: subLoading, mutateAsync: subscribe } = useSubscribe() const { isLoading: unsubLoading, mutateAsync: unsubscribe } = useUnsubscribe() - const [userIsSubscribed, setUserIsSubscribed] = useState(false) const { data: userSubscriptions, isLoading: userSubsIsLoading } = useUserSubscriptions({ user: address as string, @@ -51,7 +58,7 @@ export function SubscribeButton(props: SubscribeButtonProps) { signer, userAddress: address, channelAddress: channelAddress, - env: props.env, + env: env, } return (userIsSubscribed ? unsubscribe(args) : subscribe(args)).then((res) => { @@ -59,27 +66,25 @@ export function SubscribeButton(props: SubscribeButtonProps) { const isSubscribed = !userIsSubscribed setUserIsSubscribed(isSubscribed) - isSubscribed ? props.onSubscribe?.() : props.onUnsubscribe?.() + isSubscribed ? onSubscribe?.() : onUnsubscribe?.() }) } - return ( - <> - - {userIsSubscribed ? ( - - ) : ( - - )} - - + const buttonIsLoading = userSubsIsLoading || subLoading || unsubLoading + + return userIsSubscribed ? ( + + ) : ( + ) } diff --git a/integrations/push-protocol/components/types.ts b/integrations/push-protocol/components/types.ts deleted file mode 100644 index 2256519d..00000000 --- a/integrations/push-protocol/components/types.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ApiNotificationType } from '@pushprotocol/restapi' -import { ENV } from '@pushprotocol/uiweb' - -export type NotificationProps = { - notification: ApiNotificationType -} - -export type NotificationFeedProps = { - notifications?: ApiNotificationType[] - spamNotifications?: ApiNotificationType[] - notificationsIsLoading: boolean - spamNotificationsIsLoading: boolean -} - -export type ChannelSubscribeCallbacks = { - onSubscribe?: () => void - onUnsubscribe?: () => void -} - -export type ChannelCardProps = { - channelAddress: string - env: ENV -} & ChannelSubscribeCallbacks - -export type SubscribeButtonProps = { - channelAddress: string - env: ENV -} & ChannelSubscribeCallbacks - -export type NotificationBellProps = { - env: ENV - mockedNotifications?: ApiNotificationType[] -} diff --git a/integrations/push-protocol/hooks/index.ts b/integrations/push-protocol/hooks/index.ts index df492c3b..574dab91 100644 --- a/integrations/push-protocol/hooks/index.ts +++ b/integrations/push-protocol/hooks/index.ts @@ -5,4 +5,5 @@ export * from './use-create-user' export * from './use-notifications' export * from './use-send-notifications' export * from './use-subscribe-channel' +export * from './use-unsubscribe-channel' export * from './use-user-subscriptions' diff --git a/integrations/push-protocol/hooks/use-channel.ts b/integrations/push-protocol/hooks/use-channel.ts index 2c170cb5..af928066 100644 --- a/integrations/push-protocol/hooks/use-channel.ts +++ b/integrations/push-protocol/hooks/use-channel.ts @@ -4,38 +4,38 @@ import { z } from 'zod' import { Channel, UseChannelProps } from '../utils/types' -const fetchChannel = async (props: UseChannelProps) => { - const schema = z.object({ - id: z.number(), - channel: z.string(), - ipfshash: z.string(), - name: z.string(), - info: z.string(), - url: z.string(), - icon: z.string(), - processed: z.number(), - attempts: z.number(), - alias_address: z.string().nullable().optional(), - alias_verification_event: z.string().nullable().optional(), - is_alias_verified: z.number(), - alias_blockchain_id: z.string().nullable().optional(), - activation_status: z.number(), - verified_status: z.number(), - timestamp: z.string(), - blocked: z.number(), - subgraph_attempts: z.number(), - subscriber_count: z.number(), - }) +const channelSchema = z.object({ + id: z.number(), + channel: z.string(), + ipfshash: z.string(), + name: z.string(), + info: z.string(), + url: z.string(), + icon: z.string(), + processed: z.number(), + attempts: z.number(), + alias_address: z.string().nullable().optional(), + alias_verification_event: z.string().nullable().optional(), + is_alias_verified: z.number(), + alias_blockchain_id: z.string().nullable().optional(), + activation_status: z.number(), + verified_status: z.number(), + timestamp: z.string(), + blocked: z.number(), + subgraph_attempts: z.number(), + subscriber_count: z.number(), +}) - const result = (await PushAPI.channels.getChannel(props)) as Channel - schema.parse(result) +const fetchChannel = async ({ channel, env }: UseChannelProps) => { + const result = (await PushAPI.channels.getChannel({ channel, env })) as Channel + channelSchema.parse(result) return result } -export const useChannel = (props: UseChannelProps) => { - return useQuery(['channel', props.channel, props.env], { - queryFn: () => fetchChannel(props), +export const useChannel = ({ channel, env }: UseChannelProps) => { + return useQuery(['channel', channel, env], { + queryFn: () => fetchChannel({ channel, env }), refetchOnWindowFocus: false, }) } diff --git a/integrations/push-protocol/hooks/use-chats.ts b/integrations/push-protocol/hooks/use-chats.ts index 62f2d908..8662945a 100644 --- a/integrations/push-protocol/hooks/use-chats.ts +++ b/integrations/push-protocol/hooks/use-chats.ts @@ -7,9 +7,9 @@ const fetchChats = async (props: UseChatsProps) => { return await PushAPI.chat.chats(props) } -export const useChats = (props: UseChatsProps) => { - return useQuery(['chats', props.account, props.env, props.page, props.limit, props.pgpPrivateKey, props.toDecrypt], { - queryFn: () => fetchChats(props), +export const useChats = ({ account, env, page, limit, pgpPrivateKey, toDecrypt }: UseChatsProps) => { + return useQuery(['chats', account, env, page, limit, pgpPrivateKey, toDecrypt], { + queryFn: () => fetchChats({ account, env, page, limit, pgpPrivateKey, toDecrypt }), refetchOnWindowFocus: false, }) } diff --git a/integrations/push-protocol/hooks/use-notifications.ts b/integrations/push-protocol/hooks/use-notifications.ts index 08cf0fce..8cd52832 100644 --- a/integrations/push-protocol/hooks/use-notifications.ts +++ b/integrations/push-protocol/hooks/use-notifications.ts @@ -3,16 +3,16 @@ import { useQuery } from '@tanstack/react-query' import { UseNotificationsProps } from '../utils/types' -const fetchNotifications = async (props: UseNotificationsProps) => { +const fetchNotifications = async ({ user, spam, env, page, limit, raw }: UseNotificationsProps) => { return (await PushAPI.user.getFeeds({ - ...props, + ...{ user, spam, env, page, limit, raw }, raw: true, })) as PushAPI.ApiNotificationType[] } -export const useNotifications = (props: UseNotificationsProps) => { - return useQuery(['notifications', props.user, props.spam, props.env, props.page, props.limit, props.raw], { - queryFn: () => fetchNotifications(props), +export const useNotifications = ({ user, spam, env, page, limit, raw }: UseNotificationsProps) => { + return useQuery(['notifications', user, spam, env, page, limit, raw], { + queryFn: () => fetchNotifications({ user, spam, env, page, limit, raw }), refetchOnWindowFocus: false, }) } diff --git a/integrations/push-protocol/hooks/use-search-channels.ts b/integrations/push-protocol/hooks/use-search-channels.ts index 3d2d654c..ac31f768 100644 --- a/integrations/push-protocol/hooks/use-search-channels.ts +++ b/integrations/push-protocol/hooks/use-search-channels.ts @@ -3,13 +3,13 @@ import { useQuery } from '@tanstack/react-query' import { Channel, UseSearchChannelProps } from '../utils/types' -const searchChannels = async (props: UseSearchChannelProps) => { - return (await PushAPI.channels.search(props)) as Channel[] +const searchChannels = async ({ query, env, page, limit }: UseSearchChannelProps) => { + return (await PushAPI.channels.search({ query, env, page, limit })) as Channel[] } -export const useSearchChannels = (props: UseSearchChannelProps) => { - return useQuery(['search-channels', props.query, props.env, props.page, props.limit], { - queryFn: () => searchChannels(props), +export const useSearchChannels = ({ query, env, page, limit }: UseSearchChannelProps) => { + return useQuery(['search-channels', query, env, page, limit], { + queryFn: () => searchChannels({ query, env, page, limit }), refetchOnWindowFocus: false, }) } diff --git a/integrations/push-protocol/hooks/use-user-subscriptions.ts b/integrations/push-protocol/hooks/use-user-subscriptions.ts index 7712a7d5..b9c5febc 100644 --- a/integrations/push-protocol/hooks/use-user-subscriptions.ts +++ b/integrations/push-protocol/hooks/use-user-subscriptions.ts @@ -3,13 +3,13 @@ import { useQuery } from '@tanstack/react-query' import { UseUserSubscriptionProps, UserSubscription } from '../utils/types' -const fetchUserSubscriptions = async (props: UseUserSubscriptionProps) => { - return (await PushAPI.user.getSubscriptions(props)) as UserSubscription[] +const fetchUserSubscriptions = async ({ env, user }: UseUserSubscriptionProps) => { + return (await PushAPI.user.getSubscriptions({ env, user })) as UserSubscription[] } -export const useUserSubscriptions = (props: UseUserSubscriptionProps) => { - return useQuery(['user-subscriptions', props.env, props.user], { - queryFn: () => fetchUserSubscriptions(props), +export const useUserSubscriptions = ({ env, user }: UseUserSubscriptionProps) => { + return useQuery(['user-subscriptions', env, user], { + queryFn: () => fetchUserSubscriptions({ env, user }), refetchOnWindowFocus: false, }) } diff --git a/integrations/push-protocol/utils/constants.ts b/integrations/push-protocol/utils/constants.ts new file mode 100644 index 00000000..5afeb071 --- /dev/null +++ b/integrations/push-protocol/utils/constants.ts @@ -0,0 +1 @@ +export const PUSH_CHANNEL_ADDRESS = '0x74415Bc4C4Bf4Baecc2DD372426F0a1D016Fa924' diff --git a/next.config.mjs b/next.config.mjs index eaf65b95..c15267fd 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -4,7 +4,7 @@ import './env.mjs' const nextConfig = { reactStrictMode: true, images: { - domains: ['avatars.githubusercontent.com', 'images.unsplash.com', 'cloudflare-ipfs.com'], + domains: ['avatars.githubusercontent.com', 'images.unsplash.com', 'cloudflare-ipfs.com', 'gateway.ipfs.io'], }, env: { mode: process.env.NODE_ENV, From 0bdf74a512d5cdfb3c1dc674d74238bb1e03b141 Mon Sep 17 00:00:00 2001 From: Giorgi Tsirekidze Date: Tue, 18 Jul 2023 05:02:01 +0400 Subject: [PATCH 10/13] chore: removed styles, fixed texts --- integrations/push-protocol/README.md | 2 -- .../push-protocol/components/channel-search.tsx | 2 +- .../push-protocol/components/notification-bell.tsx | 4 ---- integrations/push-protocol/styles/index.css | 10 ---------- 4 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 integrations/push-protocol/styles/index.css diff --git a/integrations/push-protocol/README.md b/integrations/push-protocol/README.md index 216625e6..2d619527 100644 --- a/integrations/push-protocol/README.md +++ b/integrations/push-protocol/README.md @@ -105,8 +105,6 @@ integrations/push │ ├── use-subscribe-channel.ts │ ├── use-unsubscribe-channel.ts │ └── use-user-subscriptions.ts -├── styles -│ └── index.css └── utils ├── constants.ts ├── helpers.ts diff --git a/integrations/push-protocol/components/channel-search.tsx b/integrations/push-protocol/components/channel-search.tsx index 02891aa9..9cf0883a 100644 --- a/integrations/push-protocol/components/channel-search.tsx +++ b/integrations/push-protocol/components/channel-search.tsx @@ -26,7 +26,7 @@ export function ChannelSearch() { <>
- setSearchQuery(e.target.value)} /> + setSearchQuery(e.target.value)} />