diff --git a/package-lock.json b/package-lock.json index ee28d0a1..ccca33b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15016,6 +15016,53 @@ "node": ">=18" } }, + "node_modules/@xmtp/xmtp-js/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -20643,53 +20690,6 @@ "rlp": "^2.2.3" } }, - "node_modules/ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" - } - }, "node_modules/ethjs-util": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", @@ -44494,6 +44494,45 @@ "elliptic": "^6.5.4", "ethers": "^5.5.3", "long": "^5.2.0" + }, + "dependencies": { + "ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "requires": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + } } }, "@xtuc/ieee754": { @@ -48873,43 +48912,6 @@ } } }, - "ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", - "requires": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" - } - }, "ethjs-util": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", diff --git a/src/component-library/components/AddressInput/AddressInput.tsx b/src/component-library/components/AddressInput/AddressInput.tsx index 954c146d..5e3076f9 100644 --- a/src/component-library/components/AddressInput/AddressInput.tsx +++ b/src/component-library/components/AddressInput/AddressInput.tsx @@ -1,7 +1,4 @@ -import { - ChevronLeftIcon, - InformationCircleIcon, -} from "@heroicons/react/outline"; +import { ChevronLeftIcon, XCircleIcon } from "@heroicons/react/outline"; import { useTranslation } from "react-i18next"; import { Avatar } from "../Avatar/Avatar"; import { classNames } from "../../../helpers"; @@ -41,10 +38,6 @@ interface AddressInputProps { * Upon submit, is something loading? */ isLoading?: boolean; - /** - * Is there a tooltip click event that needs to be handled? - */ - onTooltipClick?: () => void; /** * Input Value */ @@ -53,6 +46,10 @@ interface AddressInputProps { * Is there a left icon click event that needs to be handled? */ onLeftIconClick?: () => void; + /** + * Is there a right icon click event that needs to be handled? + */ + onRightIconClick?: () => void; } export const AddressInput = ({ @@ -61,9 +58,9 @@ export const AddressInput = ({ avatarUrlProps, onChange, isError, - onTooltipClick, value, onLeftIconClick, + onRightIconClick, }: AddressInputProps) => { const { t } = useTranslation(); const subtextColor = isError ? "text-red-600" : "text-gray-500"; @@ -127,8 +124,12 @@ export const AddressInput = ({ - {onTooltipClick && ( - + {onRightIconClick && ( + )} ); diff --git a/src/component-library/components/FullConversation/FullConversation.tsx b/src/component-library/components/FullConversation/FullConversation.tsx index 96effeb7..3876988a 100644 --- a/src/component-library/components/FullConversation/FullConversation.tsx +++ b/src/component-library/components/FullConversation/FullConversation.tsx @@ -35,7 +35,8 @@ const AcceptOrDeny = ({ address }: { address: string }) => { const { t } = useTranslation(); const { allow, deny } = useConsent(); const activeTab = useXmtpStore((s) => s.activeTab); - const setActiveTab = useXmtpStore((s) => s.setActiveTab); + const changedConsentCount = useXmtpStore((s) => s.changedConsentCount); + const setChangedConsentCount = useXmtpStore((s) => s.setChangedConsentCount); const [modalOpen, setModalOpen] = useState(true); @@ -51,8 +52,8 @@ const AcceptOrDeny = ({ address }: { address: string }) => { className="text-indigo-600 flex w-full justify-center border border-2 border-indigo-600 rounded-md p-2 hover:bg-indigo-600 hover:text-white" onClick={() => { void allow([address]); - setActiveTab("messages"); setModalOpen(false); + setChangedConsentCount(changedConsentCount + 1); }}> {t("consent.accept")} @@ -61,8 +62,8 @@ const AcceptOrDeny = ({ address }: { address: string }) => { className="text-red-600 flex w-full justify-center border border-2 border-red-600 rounded-md p-2 hover:bg-red-600 hover:text-white" onClick={() => { void deny([address]); - setActiveTab("blocked"); setModalOpen(false); + setChangedConsentCount(changedConsentCount + 1); }}> {t("consent.block")} diff --git a/src/controllers/AddressInputController.tsx b/src/controllers/AddressInputController.tsx index 133e2a1e..97e9c141 100644 --- a/src/controllers/AddressInputController.tsx +++ b/src/controllers/AddressInputController.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { useConversation } from "@xmtp/react-sdk"; +import { useConversation, useConsent } from "@xmtp/react-sdk"; import { AddressInput } from "../component-library/components/AddressInput/AddressInput"; import { getRecipientInputSubtext, shortAddress } from "../helpers"; import useWindowSize from "../hooks/useWindowSize"; @@ -20,7 +20,11 @@ export const AddressInputController = () => { const setRecipientInput = useXmtpStore((s) => s.setRecipientInput); const setStartedFirstMessage = useXmtpStore((s) => s.setStartedFirstMessage); const setConversationTopic = useXmtpStore((s) => s.setConversationTopic); + const changedConsentCount = useXmtpStore((s) => s.changedConsentCount); + const setChangedConsentCount = useXmtpStore((s) => s.setChangedConsentCount); + const { getCachedByPeerAddress, getCachedByTopic } = useConversation(); + const { deny } = useConsent(); // manage address input state useAddressInput(); @@ -100,6 +104,10 @@ export const AddressInputController = () => { setStartedFirstMessage(false); setConversationTopic(""); }} + onRightIconClick={() => { + void deny([recipientAddress]); + setChangedConsentCount(changedConsentCount + 1); + }} /> ); }; diff --git a/src/controllers/ConversationListController.tsx b/src/controllers/ConversationListController.tsx index 30643741..d5547fb2 100644 --- a/src/controllers/ConversationListController.tsx +++ b/src/controllers/ConversationListController.tsx @@ -1,5 +1,6 @@ import { useEffect, useMemo } from "react"; -import { useConsent, useDb } from "@xmtp/react-sdk"; +import { useClient, useConsent, useDb } from "@xmtp/react-sdk"; +import type { CachedConversation } from "@xmtp/react-sdk"; import type { ActiveTab } from "../store/xmtp"; import { useXmtpStore } from "../store/xmtp"; import useListConversations from "../hooks/useListConversations"; @@ -14,6 +15,7 @@ type ConversationListControllerProps = { type ConsentProps = { tab: ActiveTab; + convo: CachedConversation; }; type NodeWithConsent = React.ReactElement; @@ -25,8 +27,14 @@ export const ConversationListController = ({ const { isAllowed, isDenied } = useConsent(); const { db } = useDb(); + // const [messages, setMessages] = useState([]); + // const messagesDb = db.table("messages"); + useStreamAllMessages(); + const { client: walletAddress } = useClient(); const recipientInput = useXmtpStore((s) => s.recipientInput); + const changedConsentCount = useXmtpStore((s) => s.changedConsentCount); + const activeTab = useXmtpStore((s) => s.activeTab); // when the conversations are loaded, update their identities @@ -38,38 +46,76 @@ export const ConversationListController = ({ }; void runUpdate(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isLoaded, activeTab]); + }, [isLoaded, activeTab, changedConsentCount]); - const filteredConversations = useMemo(() => { - const convos = conversations.map((conversation) => ( - - )); - return convos; - }, [conversations, isAllowed, isDenied]); + // To-do: remove if not needed after consent goes out + // useEffect(() => { + // // This may make more sense to come from the React SDK, but we're pulling from here for now + // const fetchMessages = async () => + // messagesDb + // .where("senderAddress") + // .equals(walletAddress?.address as string) + // .toArray() + // .then((dbMessages: CachedMessage[]) => { + // setMessages(dbMessages); + // }) + // .catch((error: Error) => { + // console.error("Error querying messages:", error); + // }); + + // void fetchMessages(); + // }, [conversations.length, messagesDb, walletAddress?.address]); - const messagesToPass = useMemo( - () => - filteredConversations.filter((item: NodeWithConsent) => { - if (!isLoading && activeTab === "messages") { - return item.props.tab === "messages"; + const messagesToPass = useMemo(() => { + const conversationsWithTab = conversations.map( + (conversation: CachedConversation) => { + const tab = isAllowed(conversation.peerAddress) + ? "messages" + : isDenied(conversation.peerAddress) + ? "blocked" + : "requests"; + return ( + + ); + }, + ); + const sortedConvos = conversationsWithTab.filter( + (item: NodeWithConsent) => { + // To-do: remove commented out code in this block if not needed after consent goes out + // const hasSentMessages = messages.find( + // (message) => message?.conversationTopic === item.props.convo.topic, + // ); + const isAddressBlocked = isDenied(item.props.convo.peerAddress); + const isAddressAllowed = isAllowed(item.props.convo.peerAddress); + + if (activeTab === "messages") { + return isAddressAllowed; } - if (!isLoading && activeTab === "blocked") { - return item.props.tab === "blocked"; + if (activeTab === "blocked") { + return isAddressBlocked; } - return item.props.tab === "requests"; - }), - [filteredConversations, isLoading, activeTab], - ); + if (activeTab === "requests") { + return !isAddressBlocked && !isAddressAllowed; + } + return null; + }, + ); + return sortedConvos; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + conversations, + // messages, + isLoading, + walletAddress, + // db, + changedConsentCount, + isAllowed, + isDenied, + ]); return ( void; activeTab: ActiveTab; setActiveTab: (activeTab: ActiveTab) => void; + changedConsentCount: number; + setChangedConsentCount: (changedConsentCount: number) => void; } export const useXmtpStore = create((set) => ({ @@ -93,4 +95,7 @@ export const useXmtpStore = create((set) => ({ setActiveMessage: (activeMessage) => set(() => ({ activeMessage })), activeTab: "messages", setActiveTab: (activeTab) => set(() => ({ activeTab })), + changedConsentCount: 0, + setChangedConsentCount: (changedConsentCount) => + set(() => ({ changedConsentCount })), }));